diff --git a/.php_cs.dist b/.php_cs.dist index a8a78a9a..59a7eea8 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -34,6 +34,7 @@ return PhpCsFixer\Config::create() 'hash_to_slash_comment' => true, 'header_comment' => false, // We don't use common header in all our files 'heredoc_to_nowdoc' => false, // Not sure about this one + 'is_null' => false, // Risky 'include' => true, 'indentation_type' => true, 'line_ending' => true, @@ -46,6 +47,7 @@ return PhpCsFixer\Config::create() 'method_separation' => true, 'modernize_types_casting' => true, 'native_function_casing' => true, + 'native_function_invocation'=> false, // This is risky and seems to be micro-optimization that make code uglier so not worth it, at least for now 'new_with_braces' => true, 'no_alias_functions' => true, 'no_blank_lines_after_class_opening' => true, @@ -88,7 +90,7 @@ return PhpCsFixer\Config::create() 'php_unit_construct' => true, 'php_unit_dedicate_assert' => true, 'php_unit_fqcn_annotation' => true, - 'php_unit_strict' => false, // We sometime actually need assertEquals 'phpdoc_align' => false, // Waste of time + 'php_unit_strict' => false, // We sometime actually need assertEquals 'phpdoc_add_missing_param_annotation' => true, 'phpdoc_align' => false, // Waste of time 'phpdoc_annotation_without_dot' => true, @@ -98,7 +100,9 @@ return PhpCsFixer\Config::create() 'phpdoc_no_alias_tag' => true, 'phpdoc_no_empty_return' => true, 'phpdoc_no_package' => true, + 'phpdoc_no_useless_inheritdoc' => true, 'phpdoc_order' => true, + 'phpdoc_return_self_reference' => true, 'phpdoc_scalar' => true, 'phpdoc_separation' => true, 'phpdoc_single_line_var_spacing' => true, @@ -132,6 +136,7 @@ return PhpCsFixer\Config::create() 'switch_case_semicolon_to_colon' => true, 'switch_case_space' => true, 'ternary_operator_spaces' => true, + 'ternary_to_null_coalescing' => false, // Cannot use that with PHP 5.6 'trailing_comma_in_multiline_array' => true, 'trim_array_spaces' => true, 'unary_operator_spaces' => true, diff --git a/.travis.yml b/.travis.yml index 9897777d..67bf8db2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: php +dist: trusty +sudo: false php: - 5.6 @@ -11,7 +13,6 @@ matrix: - php: 7.0 env: COVERAGE=1 allow_failures: - - php: 7.1 - php: hhvm cache: diff --git a/CHANGELOG.PHPExcel.md b/CHANGELOG.PHPExcel.md new file mode 100644 index 00000000..389a1314 --- /dev/null +++ b/CHANGELOG.PHPExcel.md @@ -0,0 +1,1626 @@ +# Changelog for PHPExcel + +This is the historic changelog of the project when it was still called PHPExcel. +It exists only for historical purposes and versions mentioned here should not be +confused with PhpSpreadsheet versions. + +## [1.8.1] - 2015-04-30 + +### Bugfixes + +- Fix for Writing an Open Document cell with non-numeric formula - @goncons [#397](https://github.com/PHPOffice/PHPExcel/issues/397) +- Avoid potential divide by zero in basedrawing - @sarciszewski [#329](https://github.com/PHPOffice/PHPExcel/issues/329) +- XML External Entity (XXE) Processing, different behaviour between simplexml_load_string() and simplexml_load_file(). - @ymaerschalck [#405](https://github.com/PHPOffice/PHPExcel/issues/405) +- Fix to ensure that current cell is maintained when executing formula calculations - @MarkBaker +- Keep/set the value on Reader _loadSheetsOnly as NULL, courtesy of Restless-ET - @MarkBaker [#350](https://github.com/PHPOffice/PHPExcel/issues/350) +- Loading an Excel 2007 spreadsheet throws an "Autofilter must be set on a range of cells" exception - @MarkBaker [CodePlex #18105](https://phpexcel.codeplex.com/workitem/18105) +- Fix to autoloader registration for backward compatibility with PHP 5.2.0 not accepting the prepend flag - @MarkBaker [#388](https://github.com/PHPOffice/PHPExcel/issues/388) +- DOM loadHTMLFile() failing with options flags when using PHP < 5.4.0 - @MarkBaker [#384](https://github.com/PHPOffice/PHPExcel/issues/384) +- Fix for percentage operator in formulae for BIFF Writer - @MarkBaker +- Fix to getStyle() call for cell object - @MarkBaker +- Discard Autofilters in Excel2007 Reader when filter range isn't a valid range - @MarkBaker +- Fix invalid NA return in VLOOKUP - @frozenstupidity [#423](https://github.com/PHPOffice/PHPExcel/issues/423) +- "No Impact" conditional formatting fix for NumberFormat - @wiseloren [CodePlex #21454](https://phpexcel.codeplex.com/workitem/21454) +- Bug in Excel2003XML reader, parsing merged cells - @bobwitlox [#467](https://github.com/PHPOffice/PHPExcel/issues/467) +- Fix for CEIL() and FLOOR() when number argument is zero - @MarkBaker [#302](https://github.com/PHPOffice/PHPExcel/issues/302) + +### General + +- Remove cells cleanly when calling RemoveRow() or RemoveColumn() - @MarkBaker +- Small performance improvement for autosize columns - @MarkBaker +- Change the getter/setter for zeroHeight to camel case - @frost-nzcr4 [#379](https://github.com/PHPOffice/PHPExcel/issues/379) +- DefaultValueBinder is too much aggressive when converting string to numeric - @MarkBaker [#394](https://github.com/PHPOffice/PHPExcel/issues/394) +- Default precalculate formulas to false for writers - @MarkBaker +- Set default Cyclic Reference behaviour to 1 to eliminate exception when using a single cyclic iteration in formulae - @MarkBaker + +### Features + +- Some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE - @MarkBaker [#396](https://github.com/PHPOffice/PHPExcel/issues/396) +- Methods to manage most of the existing options for Chart Axis, Major Grid-lines and Minor Grid-lines - @WiktrzGE [#404](https://github.com/PHPOffice/PHPExcel/issues/404) +- ODS read/write comments in the cell - @frost-nzcr4 [#403](https://github.com/PHPOffice/PHPExcel/issues/403) +- Additional Mac CJK codepage definitions - @CQD [#389](https://github.com/PHPOffice/PHPExcel/issues/389) +- Update Worksheet.php getStyleByColumnAndRow() to allow a range of cells rather than just a single cell - @bolovincev [#269](https://github.com/PHPOffice/PHPExcel/issues/269) +- New methods added for testing cell status within merge groups - @MarkBaker +- Handling merge cells in HTML Reader - @cifren/MBaker [#205](https://github.com/PHPOffice/PHPExcel/issues/205) +- Helper to convert basic HTML markup to a Rich Text object - @MarkBaker +- Improved Iterators - @MarkBaker + - New Column Iterator + - Support for row and column ranges + - Improved handling for next/prev + +### Security + +- XML filescan in XML-based Readers to prevent XML Entity Expansion (XEE) - @MarkBaker + - (see http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion for an explanation of XEE injection) attacks + - Reference CVE-2015-3542 - Identification of problem courtesy of Dawid Golunski (Pentest Ltd.) + +## [1.8.0] - 2014-03-02 + +### Bugfixes + +- Undefined variable: fileHandle in CSV Reader - @MarkBaker [CodePlex #19830](https://phpexcel.codeplex.com/workitem/19830) +- Out of memory in style/supervisor.php - @MarkBaker [CodePlex #19968](https://phpexcel.codeplex.com/workitem/19968) +- Style error with merged cells in PDF Writer - @MarkBaker +- Problem with cloning worksheets - @MarkBaker +- Bug fix reading Open Office files - @tavoarcila [#259](https://github.com/PHPOffice/PHPExcel/issues/259) +- Serious bug in absolute cell reference used in shared formula - @MarkBaker [CodePlex #20397](https://phpexcel.codeplex.com/workitem/20397) + - Would also have affected insert/delete column/row- CHOOSE() returns "#VALUE!" if the 1st entry is chosen - @RomanSyroeshko [#267](https://github.com/PHPOffice/PHPExcel/issues/267) +- When duplicating styles, styles shifted by one column to the right - @Gemorroj [#268](https://github.com/PHPOffice/PHPExcel/issues/268) + - Fix also applied to duplicating conditional styles- Fix for formulae that reference a sheet whose name begins with a digit: - @IndrekHaav [#212](https://github.com/PHPOffice/PHPExcel/issues/212) + - these were erroneously identified as numeric values, causing the parser to throw an undefined variable error.- Fixed undefined variable error due to $styleArray being used before it's initialised - @IndrekHaav [CodePlex #16208](https://phpexcel.codeplex.com/workitem/16208) +- ISTEXT() return wrong result if referencing an empty but formatted cell - @PowerKiKi [#273](https://github.com/PHPOffice/PHPExcel/issues/273) +- Binary comparison of strings are case insensitive - @PowerKiKi [#270](https://github.com/PHPOffice/PHPExcel/issues/270), [#31](https://github.com/PHPOffice/PHPExcel/issues/31) +- Insert New Row/Column Before is not correctly updating formula references - @MarkBaker [#275](https://github.com/PHPOffice/PHPExcel/issues/275) +- Passing an array of cells to _generateRow() in the HTML/PDF Writer causes caching problems with last cell in the range - @MarkBaker [#257](https://github.com/PHPOffice/PHPExcel/issues/257) +- Fix to empty worksheet garbage collection when using cell caching - @MarkBaker [#193](https://github.com/PHPOffice/PHPExcel/issues/193) +- Excel2007 does not correctly mark rows as hidden - @Jazzo [#248](https://github.com/PHPOffice/PHPExcel/issues/248) +- Fixed typo in Chart/Layout set/getYMode() - @Roy Shahbazian [#299](https://github.com/PHPOffice/PHPExcel/issues/299) +- Fatal error: Call to a member function cellExists() line: 3327 in calculation.php if referenced worksheet doesn't exist - @EliuFlorez [#279](https://github.com/PHPOffice/PHPExcel/issues/279) +- AdvancedValueBinder "Division by zero"-error - @MarkBaker [#290](https://github.com/PHPOffice/PHPExcel/issues/290) +- Adding Sheet to Workbook Bug - @MarkBaker [CodePlex #20604](https://phpexcel.codeplex.com/workitem/20604) +- Calculation engine incorrectly evaluates empty cells as #VALUE - @MarkBaker [CodePlex #20703](https://phpexcel.codeplex.com/workitem/20703) +- Formula references to cell on another sheet in ODS files - @MarkBaker [CodePlex #20760](https://phpexcel.codeplex.com/workitem/20760) + +### Features + +- LibreOffice created XLSX files results in an empty file. - @MarkBaker [#321](https://github.com/PHPOffice/PHPExcel/issues/321), [#158](https://github.com/PHPOffice/PHPExcel/issues/158), [CodePlex #17824](https://phpexcel.codeplex.com/workitem/17824) +- Implementation of the Excel HLOOKUP() function - @amerov +- Added "Quote Prefix" to style settings (Excel2007 Reader and Writer only) - @MarkBaker +- Added Horizontal FILL alignment for Excel5 and Excel2007 Readers/Writers, and Horizontal DISTRIBUTED alignment for Excel2007 Reader/Writer - @MarkBaker +- Add support for reading protected (RC4 encrypted) .xls files - @trvrnrth [#261](https://github.com/PHPOffice/PHPExcel/issues/261) + +### General + +- Adding support for macros, Ribbon in Excel 2007 - @LWol [#252](https://github.com/PHPOffice/PHPExcel/issues/252) +- Remove array_shift in ReferenceHelper::insertNewBefore improves column or row delete speed - @cdhutch [CodePlex #20055](https://phpexcel.codeplex.com/workitem/20055) +- Improve stock chart handling and rendering, with help from Swashata Ghosh - @MarkBaker +- Fix to calculation properties for Excel2007 so that the opening application will only recalculate on load if it's actually required - @MarkBaker +- Modified Excel2007 Writer to default preCalculateFormulas to false - @MarkBaker + - Note that autosize columns will still recalculate affected formulae internally- Functionality to getHighestRow() for a specified column, and getHighestColumn() for a specified row - @dresenhista [#242](https://github.com/PHPOffice/PHPExcel/issues/242) +- Modify PHPExcel_Reader_Excel2007 to use zipClass from PHPExcel_Settings::getZipClass() - @adamriyadi [#247](https://github.com/PHPOffice/PHPExcel/issues/247) + - This allows the use of PCLZip when reading for people that don't have access to ZipArchive +### Security + +- Convert properties to string in OOCalc reader - @infojunkie [#276](https://github.com/PHPOffice/PHPExcel/issues/276) +- Disable libxml external entity loading by default. - @maartenba [#322](https://github.com/PHPOffice/PHPExcel/issues/322) + - This is to prevent XML External Entity Processing (XXE) injection attacks (see http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html for an explanation of XXE injection). + - Reference CVE-2014-2054 + +## [1.7.9] - 2013-06-02 + +### Features + +- Include charts option for HTML Writer - @MarkBaker +- Added composer file - @MarkBaker +- cache_in_memory_gzip "eats" last worksheet line, cache_in_memory doesn't - @MarkBaker [CodePlex #18844](https://phpexcel.codeplex.com/workitem/18844) +- echo statements in HTML.php - @MarkBaker [#104](https://github.com/PHPOffice/PHPExcel/issues/104) + +### Bugfixes + +- Added getStyle() method to Cell object - @MarkBaker +- Error in PHPEXCEL/Calculation.php script on line 2976 (stack pop check) - @Asker [CodePlex #18777](https://phpexcel.codeplex.com/workitem/18777) +- CSV files without a file extension being identified as HTML - @MarkBaker [CodePlex #18794](https://phpexcel.codeplex.com/workitem/18794) +- Wrong check for maximum number of rows in Excel5 Writer - @AndreKR [#66](https://github.com/PHPOffice/PHPExcel/issues/66) +- Cache directory for DiscISAM cache storage cannot be set - @MarkBaker [#67](https://github.com/PHPOffice/PHPExcel/issues/67) +- Fix to Excel2007 Reader for hyperlinks with an anchor fragment (following a #), otherwise they were treated as sheet references - @MarkBaker [CodePlex #17976](https://phpexcel.codeplex.com/workitem/17976) +- getSheetNames() fails on numeric (floating point style) names with trailing zeroes - @MarkBaker [CodePlex #18963](https://phpexcel.codeplex.com/workitem/18963) +- Modify cell's getCalculatedValue() method to return the content of RichText objects rather than the RichText object itself - @MarkBaker +- Fixed formula/formatting bug when removing rows - @techhead [#70](https://github.com/PHPOffice/PHPExcel/issues/70) +- Fix to cellExists for non-existent namedRanges - @alexgann [#63](https://github.com/PHPOffice/PHPExcel/issues/63) +- Sheet View in Excel5 Writer - @Progi1984 [#22](https://github.com/PHPOffice/PHPExcel/issues/22) +- PHPExcel_Worksheet::getCellCollection() may not return last cached cell - @amironov [#82](https://github.com/PHPOffice/PHPExcel/issues/82) +- Rich Text containing UTF-8 characters creating unreadable content with Excel5 Writer - @teso [CodePlex #18551](https://phpexcel.codeplex.com/workitem/18551) +- Work item GH-8/CP11704 : Conditional formatting in Excel 5 Writer - @Progi1984 +- canRead() Error for GoogleDocs ODS files: in ODS files from Google Docs there is no mimetype file - @MarkBaker [#113](https://github.com/PHPOffice/PHPExcel/issues/113) +- "Sheet index is out of bounds." Exception - @MarkBaker [#80](https://github.com/PHPOffice/PHPExcel/issues/80) +- Fixed number format fatal error - @ccorliss [#105](https://github.com/PHPOffice/PHPExcel/issues/105) +- Add DROP TABLE in destructor for SQLite and SQLite3 cache controllers - @MarkBaker +- Fix merged-cell borders on HTML/PDF output - @alexgann [#154](https://github.com/PHPOffice/PHPExcel/issues/154) +- Fix: Hyperlinks break when removing rows - @Shanto [#161](https://github.com/PHPOffice/PHPExcel/issues/161) +- Fix Extra Table Row From Images and Charts - @neclimdul [#166](https://github.com/PHPOffice/PHPExcel/issues/166) + +### General + +- Single cell print area - @MarkBaker [#130](https://github.com/PHPOffice/PHPExcel/issues/130) +- Improved AdvancedValueBinder for currency - @kea [#69](https://github.com/PHPOffice/PHPExcel/issues/69) +- Fix for environments where there is no access to /tmp but to upload_tmp_dir - @MarkBaker + - Provided an option to set the sys_get_temp_dir() call to use the upload_tmp_dir; though by default the standard temp directory will still be used- Search style by identity in PHPExcel_Worksheet::duplicateStyle() - @amironov [#84](https://github.com/PHPOffice/PHPExcel/issues/84) +- Fill SheetView IO in Excel5 - @karak [#85](https://github.com/PHPOffice/PHPExcel/issues/85) +- Memory and Speed improvements in PHPExcel_Reader_Excel5 - @cfhay [CodePlex #18958](https://phpexcel.codeplex.com/workitem/18958) +- Modify listWorksheetNames() and listWorksheetInfo to use XMLReader with streamed XML rather than SimpleXML - @MarkBaker [#78](https://github.com/PHPOffice/PHPExcel/issues/78) +- Restructuring of PHPExcel Exceptions - @dbonsch +- Refactor Calculation Engine from singleton to a Multiton - @MarkBaker + - Ensures that calculation cache is maintained independently for different workbooks + +## [1.7.8] - 2012-10-12 + +### Features + +- Phar builder script to add phar file as a distribution option - @kkamkou +- Refactor PDF Writer to allow use with a choice of PDF Rendering library - @MarkBaker + - rather than restricting to tcPDF + - Current options are tcPDF, mPDF, DomPDF + - tcPDF Library has now been removed from the deployment bundle- Initial version of HTML Reader - @MarkBaker +- Implement support for AutoFilter in PHPExcel_Writer_Excel5 - @Progi1984 +- Modified ERF and ERFC Engineering functions to accept Excel 2010's modified acceptance of negative arguments - @MarkBaker +- Support SheetView `view` attribute (Excel2007) - @k1LoW +- Excel compatibility option added for writing CSV files - @MarkBaker + - While Excel 2010 can read CSV files with a simple UTF-8 BOM, Excel2007 and earlier require UTF-16LE encoded tab-separated files. + - The new setExcelCompatibility(TRUE) option for the CSV Writer will generate files with this formatting for easy import into Excel2007 and below.- Language implementations for Turkish (tr) - @MarkBaker +- Added fraction tests to advanced value binder - @MarkBaker + +### General + +- Allow call to font setUnderline() for underline format to specify a simple boolean for UNDERLINE_NONE or UNDERLINE_SINGLE - @MarkBaker +- Add Currency detection to the Advanced Value Binder - @alexgann +- setCellValueExplicitByColumnAndRow() do not return PHPExcel_Worksheet - @MarkBaker [CodePlex #18404](https://phpexcel.codeplex.com/workitem/18404) +- Reader factory doesn't read anymore XLTX and XLT files - @MarkBaker [CodePlex #18324](https://phpexcel.codeplex.com/workitem/18324) +- Magic __toString() method added to Cell object to return raw data value as a string - @MarkBaker +- Add cell indent to html rendering - @alexgann + +### Bugfixes + +- ZeroHeight for rows in sheet format - @Raghav1981 +- OOCalc cells containing inside the tag - @cyberconte +- Fix to listWorksheetInfo() method for OOCalc Reader - @schir1964 +- Support for "e" (epoch) date format mask - @MarkBaker + - Rendered as a 4-digit CE year in non-Excel outputs- Background color cell is always black when editing cell - @MarkBaker +- Allow "no impact" to formats on Conditional Formatting - @MarkBaker +- OOCalc Reader fix for NULL cells - @wackonline +- Fix to excel2007 Chart Writer when a $plotSeriesValues is empty - @seltzlab +- Various fixes to Chart handling - @MarkBaker +- Error loading xlsx file with column breaks - @MarkBaker [CodePlex #18370](https://phpexcel.codeplex.com/workitem/18370) +- OOCalc Reader now handles percentage and currency data types - @MarkBaker +- mb_stripos empty delimiter - @MarkBaker +- getNestingLevel() Error on Excel5 Read - @takaakik +- Fix to Excel5 Reader when cell annotations are defined before their referenced text objects - @MarkBaker +- OOCalc Reader modified to process number-rows-repeated - @MarkBaker +- Chart Title compatibility on Excel 2007 - @MarkBaker [CodePlex #18377](https://phpexcel.codeplex.com/workitem/18377) +- Chart Refresh returning cell reference rather than values - @MarkBaker [CodePlex #18146](https://phpexcel.codeplex.com/workitem/18146) +- Autoshape being identified in twoCellAnchor when includeCharts is TRUE triggering load error - @MarkBaker [CodePlex #18145](https://phpexcel.codeplex.com/workitem/18145) +- v-type texts for series labels now recognised and parsed correctly - @MarkBaker [CodePlex #18325](https://phpexcel.codeplex.com/workitem/18325) +- load file failed if the file has no extensionType - @wolf5x [CodePlex #18492](https://phpexcel.codeplex.com/workitem/18492) +- Pattern fill colours in Excel2007 Style Writer - @dverspui +- Excel2007 Writer order of font style elements to conform with Excel2003 using compatibility pack - @MarkBaker +- Problems with $_activeSheetIndex when decreased below 0. - @MarkBaker [CodePlex #18425](https://phpexcel.codeplex.com/workitem/18425) +- PHPExcel_CachedObjectStorage_SQLite3::cacheMethodIsAvailable() uses class_exists - autoloader throws error - @MarkBaker [CodePlex #18597](https://phpexcel.codeplex.com/workitem/18597) +- Cannot access private property PHPExcel_CachedObjectStorageFactory::$_cacheStorageMethod - @MarkBaker [CodePlex #18598](https://phpexcel.codeplex.com/workitem/18598) +- Data titles for charts - @MarkBaker [CodePlex #18397](https://phpexcel.codeplex.com/workitem/18397) + - PHPExcel_Chart_Layout now has methods for getting/setting switches for displaying/hiding chart data labels- Discard single cell merge ranges when reading (stupid that Excel allows them in the first place) - @MarkBaker +- Discard hidden autoFilter named ranges - @MarkBaker + + +## [1.7.7] - 2012-05-19 + +### Bugfixes + +- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916) +- Change iterators to implement Iterator rather than extend CachingIterator, as a fix for PHP 5.4. changes in SPL - @MarkBaker +- Invalid cell coordinate in Autofilter for Excel2007 Writer - @MarkBaker [CodePlex #15459](https://phpexcel.codeplex.com/workitem/15459) +- PCLZip library issue - @MarkBaker [CodePlex #15518](https://phpexcel.codeplex.com/workitem/15518) +- Excel2007 Reader canRead function bug - @MarkBaker [CodePlex #15537](https://phpexcel.codeplex.com/workitem/15537) +- Support for Excel functions whose return can be used as either a value or as a cell reference depending on its context within a formula - @MarkBaker +- ini_set() call in Calculation class destructor - @gilles06 [CodePlex #15707](https://phpexcel.codeplex.com/workitem/15707) +- RangeToArray strange array keys - @MarkBaker [CodePlex #15786](https://phpexcel.codeplex.com/workitem/15786) +- INDIRECT() function doesn't work with named ranges - @MarkBaker [CodePlex #15762](https://phpexcel.codeplex.com/workitem/15762) +- Locale-specific fix to text functions when passing a boolean argument instead of a string - @MarkBaker +- reader/CSV fails on this file - @MarkBaker [CodePlex #16246](https://phpexcel.codeplex.com/workitem/16246) + - auto_detect_line_endings now set in CSV reader- $arguments improperly used in CachedObjectStorage/PHPTemp.php - @MarkBaker [CodePlex #16212](https://phpexcel.codeplex.com/workitem/16212) +- Bug In Cache System (cell reference when throwing caching errors) - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643) +- PHP Invalid index notice on writing excel file when active sheet has been deleted - @MarkBaker [CodePlex #16895](https://phpexcel.codeplex.com/workitem/16895) +- External links in Excel2010 files cause Fatal error - @MarkBaker [CodePlex #16956](https://phpexcel.codeplex.com/workitem/16956) +- Previous calculation engine error conditions trigger cyclic reference errors - @MarkBaker [CodePlex #16960](https://phpexcel.codeplex.com/workitem/16960) +- PHPExcel_Style::applyFromArray() returns null rather than style object in advanced mode - @mkopinsky [CodePlex #16266](https://phpexcel.codeplex.com/workitem/16266) +- Cell::getFormattedValue returns RichText object instead of string - @fauvel [CodePlex #16958](https://phpexcel.codeplex.com/workitem/16958) +- Indexed colors do not refer to Excel's indexed colors? - @MarkBaker [CodePlex #17166](https://phpexcel.codeplex.com/workitem/17166) +- Indexed colors should be consistent with Excel and start from 1 (current index starts at 0) - @MarkBaker [CodePlex #17199](https://phpexcel.codeplex.com/workitem/17199) +- Named Range definition in .xls when sheet reeference is quote wrapped - @MarkBaker [CodePlex #17262](https://phpexcel.codeplex.com/workitem/17262) +- duplicateStyle() method doesn't duplicate conditional formats - @MarkBaker [CodePlex #17403](https://phpexcel.codeplex.com/workitem/17403) + - Added an equivalent duplicateConditionalStyle() method for duplicating conditional styles- =sumproduct(A,B) <> =sumproduct(B,A) in xlsx - @bnr [CodePlex #17501](https://phpexcel.codeplex.com/workitem/17501) + +### Features + +- OOCalc cells contain same data bug? - @cyberconte [CodePlex #17471](https://phpexcel.codeplex.com/workitem/17471) +- listWorksheetInfo() method added to Readers... courtesy of Christopher Mullins - @schir1964 +- Options for cell caching using Igbinary and SQLite/SQlite3. - @MarkBaker +- Additional row iterator options: allow a start row to be defined in the constructor; seek(), and prev() methods added. - @MarkBaker +- Implement document properties in Excel5 writer - @Progi1984 [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759) + +### General + +- Implement chart functionality (EXPERIMENTAL) - @MarkBaker [CodePlex #16](https://phpexcel.codeplex.com/workitem/16) + - Initial definition of chart objects. + - Reading Chart definitions through the Excel2007 Reader + - Facility to render charts to images using the 3rd-party jpgraph library + - Writing Charts using the Excel2007 Writer- Fix to build to ensure that Examples are included with the documentation - @MarkBaker +- Reduce cell caching overhead using dirty flag to ensure that cells are only rewritten to the cache if they have actually been changed - @MarkBaker +- Improved memory usage in CSV Writer - @MarkBaker +- Improved speed and memory usage in Excel5 Writer - @MarkBaker +- Experimental - @MarkBaker + - Added getHighestDataColumn(), getHighestDataRow(), getHighestRowAndColumn() and calculateWorksheetDataDimension() methods for the worksheet that return the highest row and column that have cell records- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916) +- Two easy to fix Issues concerning PHPExcel_Token_Stack (l10n/UC) - @MarkBaker [CodePlex #15405](https://phpexcel.codeplex.com/workitem/15405) +- Locale file paths not fit for windows - @MarkBaker [CodePlex #15461](https://phpexcel.codeplex.com/workitem/15461) +- Add file directory as a cache option for cache_to_discISAM - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643) +- Datatype.php & constant TYPE_NULL - @MarkBaker [CodePlex #16923](https://phpexcel.codeplex.com/workitem/16923) +- Ensure use of system temp directory for all temporary work files, unless explicitly specified - @MarkBaker +- [Patch] faster stringFromColumnIndex() - @char101 [CodePlex #16359](https://phpexcel.codeplex.com/workitem/16359) +- Fix for projects that still use old autoloaders - @whit1206 [CodePlex #16028](https://phpexcel.codeplex.com/workitem/16028) +- Unknown codepage: 10007 - @atz [CodePlex #17024](https://phpexcel.codeplex.com/workitem/17024) + - Additional Mac codepages + +## [1.7.6] - 2011-02-27 + +### Features + +- Provide option to use PCLZip as an alternative to ZipArchive. - @MarkBaker + - This allows the writing of Excel2007 files, even without ZipArchive enabled (it does require zlib), or when php_zip is one of the buggy PHP 5.2.6 or 5.2.8 versions + - It can be enabled using PHPExcel_Settings::setZipClass(PHPExcel_Settings::PCLZIP); + - Note that it is not yet implemented as an alternative to ZipArchive for those Readers that are extracting from zips- Added listWorksheetNames() method to Readers that support multiple worksheets in a workbook, allowing a user to extract a list of all the worksheet names from a file without parsing/loading the whole file. - @MarkBaker [CodePlex #14979](https://phpexcel.codeplex.com/workitem/14979) +- Speed boost and memory reduction in the Worksheet toArray() method. - @MarkBaker +- Added new rangeToArray() and namedRangeToArray() methods to the PHPExcel_Worksheet object. - @MarkBaker + - Functionally, these are identical to the toArray() method, except that they take an additional first parameter of a Range (e.g. 'B2:C3') or a Named Range name. + - Modified the toArray() method so that it actually uses rangeToArray().- Added support for cell comments in the OOCalc, Gnumeric and Excel2003XML Readers, and in the Excel5 Reader - @MarkBaker +- Improved toFormattedString() handling for Currency and Accounting formats to render currency symbols - @MarkBaker + +### Bugfixes + +- Implement more Excel calculation functions - @MarkBaker + - Implemented the DAVERAGE(), DCOUNT(), DCOUNTA(), DGET(), DMAX(), DMIN(), DPRODUCT(), DSTDEV(), DSTDEVP(), DSUM(), DVAR() and DVARP() Database functions- Simple =IF() formula disappears - @MarkBaker [CodePlex #14888](https://phpexcel.codeplex.com/workitem/14888) +- PHP Warning: preg_match(): Compilation failed: PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X - @MarkBaker [CodePlex #14898](https://phpexcel.codeplex.com/workitem/14898) +- VLOOKUP choking on parameters in PHPExcel.1.7.5/PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #14901](https://phpexcel.codeplex.com/workitem/14901) +- PHPExcel_Cell::isInRange() incorrect results - offset by one column - @MarkBaker [CodePlex #14973](https://phpexcel.codeplex.com/workitem/14973) +- Treat CodePage of 0 as CP1251 (for .xls files written by applications that don't set the CodePage correctly, such as Apple Numbers) - @MarkBaker +- Need method for removing autoFilter - @MarkBaker [CodePlex #11583](https://phpexcel.codeplex.com/workitem/11583) +- coordinateFromString throws exception for rows greater than 99,999 - @MarkBaker [CodePlex #15029](https://phpexcel.codeplex.com/workitem/15029) +- PHPExcel Excel2007 Reader colour problems with solidfill - @MarkBaker [CodePlex #14999](https://phpexcel.codeplex.com/workitem/14999) +- Formatting get lost and edit a template XLSX file - @MarkBaker [CodePlex #13215](https://phpexcel.codeplex.com/workitem/13215) +- Excel 2007 Reader /writer lost fontcolor - @MarkBaker [CodePlex #14029](https://phpexcel.codeplex.com/workitem/14029) +- file that makes cells go black - @MarkBaker [CodePlex #13374](https://phpexcel.codeplex.com/workitem/13374) +- Minor patchfix for Excel2003XML Reader when XML is defined with a charset attribute - @MarkBaker +- PHPExcel_Worksheet->toArray() index problem - @MarkBaker [CodePlex #15089](https://phpexcel.codeplex.com/workitem/15089) +- Merge cells 'un-merge' when using an existing spreadsheet - @MarkBaker [CodePlex #15094](https://phpexcel.codeplex.com/workitem/15094) +- Worksheet fromArray() only working with 2-D arrays - @MarkBaker [CodePlex #15129](https://phpexcel.codeplex.com/workitem/15129) +- rangeToarray function modified for non-existent cells - @xkeshav [CodePlex #15172](https://phpexcel.codeplex.com/workitem/15172) +- Images not getting copyied with the ->clone function - @MarkBaker [CodePlex #14980](https://phpexcel.codeplex.com/workitem/14980) +- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @MarkBaker [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576) +- Fix Excel5 Writer so that it only writes column dimensions for columns that are actually used rather than the full range (A to IV) - @MarkBaker +- FreezePane causing damaged or modified error - @MarkBaker [CodePlex #15198](https://phpexcel.codeplex.com/workitem/15198) + - The freezePaneByColumnAndRow() method row argument should default to 1 rather than 0. + - Default row argument for all __ByColumnAndRow() methods should be 1- Column reference rather than cell reference in Print Area definition - @MarkBaker [CodePlex #15121](https://phpexcel.codeplex.com/workitem/15121) + - Fix Excel2007 Writer to handle print areas that are defined as row or column ranges rather than just as cell ranges- Reduced false positives from isDateTimeFormatCode() method by suppressing testing within quoted strings - @MarkBaker +- Caching and tmp partition exhaustion - @MarkBaker [CodePlex #15312](https://phpexcel.codeplex.com/workitem/15312) +- Writing to Variable No Longer Works. $_tmp_dir Missing in PHPExcel\PHPExcel\Shared\OLE\PPS\Root.php - @MarkBaker [CodePlex #15308](https://phpexcel.codeplex.com/workitem/15308) +- Named ranges with dot don't get parsed properly - @MarkBaker [CodePlex #15379](https://phpexcel.codeplex.com/workitem/15379) +- insertNewRowBefore fails to consistently update references - @MarkBaker [CodePlex #15096](https://phpexcel.codeplex.com/workitem/15096) +- "i" is not a valid character for Excel date format masks (in isDateTimeFormatCode() method) - @MarkBaker +- PHPExcel_ReferenceHelper::insertNewBefore() is missing an 'Update worksheet: comments' section - @MKunert [CodePlex #15421](https://phpexcel.codeplex.com/workitem/15421) + +### General + +- Full column/row references in named ranges not supported by updateCellReference() - @MarkBaker [CodePlex #15409](https://phpexcel.codeplex.com/workitem/15409) +- Improved performance (speed), for building the Shared Strings table in the Excel2007 Writer. - @MarkBaker +- Improved performance (speed), for PHP to Excel date conversions - @MarkBaker +- Enhanced SheetViews element structures in the Excel2007 Writer for frozen panes. - @MarkBaker +- Removed Serialized Reader/Writer as these no longer work. - @MarkBaker + + +## [1.7.5] - 2010-12-10 + +### Features + +- Implement Gnumeric File Format - @MarkBaker [CodePlex #8769](https://phpexcel.codeplex.com/workitem/8769) + - Initial work on Gnumeric Reader (Worksheet Data, Document Properties and basic Formatting)- Support for Extended Workbook Properties in Excel2007, Excel5 and OOCalc Readers; support for User-defined Workbook Properties in Excel2007 and OOCalc Readers - @MarkBaker +- Support for Extended and User-defined Workbook Properties in Excel2007 Writer - @MarkBaker +- Provided a setGenerateSheetNavigationBlock(false); option to suppress generation of the sheet navigation block when writing multiple worksheets to HTML - @MarkBaker +- Advanced Value Binder now recognises TRUE/FALSE strings (locale-specific) and converts to boolean - @MarkBaker +- PHPExcel_Worksheet->toArray() is returning truncated values - @MarkBaker [CodePlex #14301](https://phpexcel.codeplex.com/workitem/14301) +- Configure PDF Writer margins based on Excel Worksheet Margin Settings value - @MarkBaker +- Added Contiguous flag for the CSV Reader, when working with Read Filters - @MarkBaker +- Added getFormattedValue() method for cell object - @MarkBaker +- Added strictNullComparison argument to the worksheet fromArray() method - @MarkBaker + +### Bugfixes + +- Fix to toFormattedString() method in PHPExcel_Style_NumberFormat to handle fractions with a # code for the integer part - @MarkBaker +- NA() doesn't propagate in matrix calc - quick fix in JAMA/Matrix.php - @MarkBaker [CodePlex #14143](https://phpexcel.codeplex.com/workitem/14143) +- Excel5 : Formula : String constant containing double quotation mark - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) +- Excel5 : Formula : Percent - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) +- Excel5 : Formula : Error constant - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) +- Excel5 : Formula : Concatenation operator - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) +- Worksheet clone broken for CachedObjectStorage_Memory - @MarkBaker [CodePlex #14146](https://phpexcel.codeplex.com/workitem/14146) +- PHPExcel_Reader_Excel2007 fails when gradient fill without type is present in a file - @MarkBaker [CodePlex #12998](https://phpexcel.codeplex.com/workitem/12998) +- @ format for numeric strings in XLSX to CSV conversion - @MarkBaker [CodePlex #14176](https://phpexcel.codeplex.com/workitem/14176) +- Advanced Value Binder Not Working? - @MarkBaker [CodePlex #14223](https://phpexcel.codeplex.com/workitem/14223) +- unassigned object variable in PHPExcel->removeCellXfByIndex - @MarkBaker [CodePlex #14226](https://phpexcel.codeplex.com/workitem/14226) +- problem with getting cell values from another worksheet... (if cell doesn't exist) - @MarkBaker [CodePlex #14236](https://phpexcel.codeplex.com/workitem/14236) +- Setting cell values to one char strings & Trouble reading one character string (thanks gorfou) - @MarkBaker +- Worksheet title exception when duplicate worksheet is being renamed but exceeds the 31 character limit - @MarkBaker [CodePlex #14256](https://phpexcel.codeplex.com/workitem/14256) +- Named range with sheet name that contains the $ throws exception when getting the cell - @MarkBaker [CodePlex #14086](https://phpexcel.codeplex.com/workitem/14086) +- Added autoloader to DefaultValueBinder and AdvancedValueBinder - @MarkBaker +- Modified PHPExcel_Shared_Date::isDateTimeFormatCode() to return false if format code begins with "_" or with "0 " to prevent false positives - @MarkBaker + - These leading characters are most commonly associated with number, currency or accounting (or occasionally fraction) formats- BUG : Excel5 and setReadFilter ? - @MarkBaker [CodePlex #14374](https://phpexcel.codeplex.com/workitem/14374) +- Wrong exception message while deleting column - @MarkBaker [CodePlex #14425](https://phpexcel.codeplex.com/workitem/14425) +- Formula evaluation fails with Japanese sheet refs - @MarkBaker [CodePlex #14679](https://phpexcel.codeplex.com/workitem/14679) +- PHPExcel_Writer_PDF does not handle cell borders correctly - @MarkBaker [CodePlex #13559](https://phpexcel.codeplex.com/workitem/13559) +- Style : applyFromArray() for 'allborders' not working - @MarkBaker [CodePlex #14831](https://phpexcel.codeplex.com/workitem/14831) + +### General + +- Using $this when not in object context in Excel5 Reader - @MarkBaker [CodePlex #14837](https://phpexcel.codeplex.com/workitem/14837) +- Removes a unnecessary loop through each cell when applying conditional formatting to a range. - @MarkBaker +- Removed spurious PHP end tags (?>) - @MarkBaker +- Improved performance (speed) and reduced memory overheads, particularly for the Writers, but across the whole library. - @MarkBaker + + +## [1.7.4] - 2010-08-26 + +### Bugfixes + +- Excel5 : Formula : Power - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) +- Excel5 : Formula : Unary plus - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) +- Excel5 : Just write the Escher stream if necessary in Worksheet - @Progi1984 +- Syntax errors in memcache.php 1.7.3c - @MarkBaker [CodePlex #13433](https://phpexcel.codeplex.com/workitem/13433) +- Support for row or column ranges in the calculation engine, e.g. =SUM(C:C) or =SUM(1:2) - @MarkBaker + - Also support in the calculation engine for absolute row or column ranges e.g. =SUM($C:$E) or =SUM($3:5)- Picture problem with Excel 2003 - @Erik Tilt [CodePlex #13455](https://phpexcel.codeplex.com/workitem/13455) +- Wrong variable used in addExternalSheet in PHPExcel.php - @MarkBaker [CodePlex #13484](https://phpexcel.codeplex.com/workitem/13484) +- "Invalid cell coordinate" error when formula access data from an other sheet - @MarkBaker [CodePlex #13515](https://phpexcel.codeplex.com/workitem/13515) +- (related to Work item 13515) Calculation engine confusing cell range worksheet when referencing cells in a different worksheet to the formula - @MarkBaker +- Wrong var naming in Worksheet->garbageCollect() - @MarkBaker [CodePlex #13752](https://phpexcel.codeplex.com/workitem/13752) +- PHPExcel_Style_*::__clone() methods cause cloning loops? - @MarkBaker [CodePlex #13764](https://phpexcel.codeplex.com/workitem/13764) +- Recent builds causing problems loading xlsx files? (ZipArchive issue?) - @MarkBaker [CodePlex #11488](https://phpexcel.codeplex.com/workitem/11488) +- cache_to_apc causes fatal error when processing large data sets - @MarkBaker [CodePlex #13856](https://phpexcel.codeplex.com/workitem/13856) +- OOCalc reader misses first line if it's a 'table-header-row' - @MarkBaker [CodePlex #13880](https://phpexcel.codeplex.com/workitem/13880) +- using cache with copy or clone bug? - @MarkBaker [CodePlex #14011](https://phpexcel.codeplex.com/workitem/14011) + - Fixed $worksheet->copy() or clone $worksheet when using cache_in_memory, cache_in_memory_gzip, cache_in_memory_serialized, cache_to_discISAM, cache_to_phpTemp, cache_to_apc and cache_to_memcache; + - Fixed but untested when using cache_to_wincache. +### Features + +- Standard Deviation functions returning DIV/0 Error when Standard Deviation is zero - @MarkBaker [CodePlex #13450](https://phpexcel.codeplex.com/workitem/13450) +- Support for print area with several ranges in the Excel2007 reader, and improved features for editing print area with several ranges - @MarkBaker +- Improved Cell Exception Reporting - @MarkBaker [CodePlex #13769](https://phpexcel.codeplex.com/workitem/13769) + +### General + +- Fixed problems with reading Excel2007 Properties - @MarkBaker +- PHP Strict Standards: Non-static method PHPExcel_Shared_String::utf16_decode() should not be called statically - @MarkBaker +- Array functions were ignored when loading an existing file containing them, and as a result, they would lose their 'cse' status. - @MarkBaker +- Minor memory tweaks to Excel2007 Writer - @MarkBaker +- Modified ReferenceHelper updateFormulaReferences() method to handle updates to row and column cell ranges (including absolute references e.g. =SUM(A:$E) or =SUM($5:5), and range/cell references that reference a worksheet by name), and to provide both performance and memory improvements. - @MarkBaker +- Modified Excel2007 Reader so that ReferenceHelper class is instantiated only once rather than for every shared formula in a workbook. - @MarkBaker +- Correct handling for additional (synonym) formula tokens in Excel5 Reader - @MarkBaker +- Additional reading of some Excel2007 Extended Properties (Company, Manager) - @MarkBaker + + +## [1.7.3c] - 2010-06-01 + +### Bugfixes + +- Fatal error: Class 'ZipArchive' not found... ...Reader/Excel2007.php on line 217 - @MarkBaker [CodePlex #13012](https://phpexcel.codeplex.com/workitem/13012) +- PHPExcel_Writer_Excel2007 error after 1.7.3b - @MarkBaker [CodePlex #13398](https://phpexcel.codeplex.com/workitem/13398) + + +## [1.7.3b] - 2010-05-31 + +### Bugfixes + +- Infinite loop when reading - @MarkBaker [CodePlex #12903](https://phpexcel.codeplex.com/workitem/12903) +- Wrong method chaining on PHPExcel_Worksheet class - @MarkBaker [CodePlex #13381](https://phpexcel.codeplex.com/workitem/13381) + + +## [1.7.3] - 2010-05-17 + +### General + +- Applied patch 4990 (modified) - @Erik Tilt +- Applied patch 5568 (modified) - @MarkBaker +- Applied patch 5943 - @MarkBaker +- Upgrade build script to use Phing - @MarkBaker [CodePlex #13042](https://phpexcel.codeplex.com/workitem/13042) +- Replacing var with public/private - @Erik Tilt [CodePlex #11586](https://phpexcel.codeplex.com/workitem/11586) +- Applied Anthony's Sterling's Class Autoloader to reduce memory overhead by "Lazy Loading" of classes - @MarkBaker +- Modification to functions that accept a date parameter to support string values containing ordinals as per Excel (English language only) - @MarkBaker +- Modify PHPExcel_Style_NumberFormat::toFormattedString() to handle dates that fall outside of PHP's 32-bit date range - @MarkBaker +- Applied patch 5207 - @MarkBaker + +### Features + +- PHPExcel developer documentation: Set page margins - @Erik Tilt [CodePlex #11970](https://phpexcel.codeplex.com/workitem/11970) +- Special characters and accents in SYLK reader - @Erik Tilt [CodePlex #11038](https://phpexcel.codeplex.com/workitem/11038) +- Implement more Excel calculation functions - @MarkBaker + - Implemented the COUPDAYS(), COUPDAYBS(), COUPDAYSNC(), COUPNCD(), COUPPCD() and PRICE() Financial functions + - Implemented the N() and TYPE() Information functions + - Implemented the HYPERLINK() Lookup and Reference function- Horizontal page break support in PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11526](https://phpexcel.codeplex.com/workitem/11526) +- Introduce method setActiveSheetIndexByName() - @Erik Tilt [CodePlex #11529](https://phpexcel.codeplex.com/workitem/11529) +- AdvancedValueBinder.php: Automatically wrap text when there is new line in string (ALT+"Enter") - @Erik Tilt [CodePlex #11550](https://phpexcel.codeplex.com/workitem/11550) +- Data validation support in PHPExcel_Reader_Excel5 and PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10300](https://phpexcel.codeplex.com/workitem/10300) +- Improve autosize calculation - @MarkBaker [CodePlex #11616](https://phpexcel.codeplex.com/workitem/11616) +- Methods to translate locale-specific function names in formulae - @MarkBaker + - Language implementations for Czech (cs), Danish (da), German (de), English (uk), Spanish (es), Finnish (fi), French (fr), Hungarian (hu), Italian (it), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt), Brazilian Portuguese (pt_br), Russian (ru) and Swedish (sv)- Implement document properties in Excel5 reader/writer - @Erik Tilt [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759) + - Fixed so far for PHPExcel_Reader_Excel5- Show/hide row and column headers in worksheet - @Erik Tilt [CodePlex #11849](https://phpexcel.codeplex.com/workitem/11849) +- Can't set font on writing PDF (by key) - @Erik Tilt [CodePlex #11919](https://phpexcel.codeplex.com/workitem/11919) +- Thousands scale (1000^n) support in PHPExcel_Style_NumberFormat::toFormattedString - @Erik Tilt [CodePlex #12096](https://phpexcel.codeplex.com/workitem/12096) +- Implement repeating rows in PDF and HTML writer - @Erik Tilt +- Sheet tabs in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #12289](https://phpexcel.codeplex.com/workitem/12289) +- Add Wincache CachedObjectProvider - @MarkBaker [CodePlex #13041](https://phpexcel.codeplex.com/workitem/13041) +- Configure PDF Writer paper size based on Excel Page Settings value, and provided methods to override paper size and page orientation with the writer - @MarkBaker + - Note PHPExcel defaults to Letter size, while the previous PDF writer enforced A4 size, so PDF writer will now default to Letter- Initial implementation of cell caching: allowing larger workbooks to be managed, but at a cost in speed - @MarkBaker + +### Bugfixes + +- Added an identify() method to the IO Factory that identifies the reader which will be used to load a particular file without actually loading it. - @MarkBaker +- Warning messages with INDEX function having 2 arguments - @MarkBaker [CodePlex #10979](https://phpexcel.codeplex.com/workitem/10979) +- setValue('=') should result in string instead of formula - @Erik Tilt [CodePlex #11473](https://phpexcel.codeplex.com/workitem/11473) +- method _raiseFormulaError should no be private - @MarkBaker [CodePlex #11471](https://phpexcel.codeplex.com/workitem/11471) +- Fatal error: Call to undefined function mb_substr() in ...Classes\PHPExcel\Reader\Excel5.php on line 2903 - @Erik Tilt [CodePlex #11485](https://phpexcel.codeplex.com/workitem/11485) +- getBold(), getItallic(), getStrikeThrough() not always working with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #11487](https://phpexcel.codeplex.com/workitem/11487) +- AdvancedValueBinder.php not working correctly for $cell->setValue('hh:mm:ss') - @Erik Tilt [CodePlex #11492](https://phpexcel.codeplex.com/workitem/11492) +- Fixed leap year handling for the YEARFRAC() Date/Time function when basis ia 1 (Actual/actual) - @MarkBaker +- Warning messages - @MarkBaker [CodePlex #11490](https://phpexcel.codeplex.com/workitem/11490) + - Calculation Engine code modified to enforce strict standards for pass by reference- PHPExcel_Cell_AdvancedValueBinder doesnt work for dates in far future - @Erik Tilt [CodePlex #11483](https://phpexcel.codeplex.com/workitem/11483) +- MSODRAWING bug with long CONTINUE record in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #11528](https://phpexcel.codeplex.com/workitem/11528) +- PHPExcel_Reader_Excel2007 reads print titles as named range when there is more than one sheet - @Erik Tilt [CodePlex #11571](https://phpexcel.codeplex.com/workitem/11571) +- missing @return in phpdocblock in reader classes - @Erik Tilt [CodePlex #11561](https://phpexcel.codeplex.com/workitem/11561) +- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @Erik Tilt [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576) +- Small numbers escape treatment in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt [CodePlex #11588](https://phpexcel.codeplex.com/workitem/11588) +- Blank styled cells are not blank in output by HTML writer due to   - @Erik Tilt [CodePlex #11590](https://phpexcel.codeplex.com/workitem/11590) +- Calculation engine bug: Existing, blank cell + number gives #NUM - @MarkBaker [CodePlex #11587](https://phpexcel.codeplex.com/workitem/11587) +- AutoSize only measures length of first line in cell with multiple lines (ALT+Enter) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608) +- Fatal error running Tests/12serializedfileformat.php (PHPExcel 1.7.2) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608) +- Fixed various errors in the WORKDAY() and NETWORKDAYS() Date/Time functions (particularly related to holidays) - @MarkBaker +- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/SheetView.php:115 - @Erik Tilt [CodePlex #11660](https://phpexcel.codeplex.com/workitem/11660) +- "Unrecognized token 39 in formula" with PHPExcel_Reader_Excel5 (occuring with add-in functions) - @Erik Tilt [CodePlex #11551](https://phpexcel.codeplex.com/workitem/11551) +- Excel2007 reader not reading PHPExcel_Style_Conditional::CONDITION_EXPRESSION - @Erik Tilt [CodePlex #11668](https://phpexcel.codeplex.com/workitem/11668) +- Fix to the BESSELI(), BESSELJ(), BESSELK(), BESSELY() and COMPLEX() Engineering functions to use correct default values for parameters - @MarkBaker +- DATEVALUE function not working for pure time values + allow DATEVALUE() function to handle partial dates (e.g. "1-Jun" or "12/2010") - @MarkBaker [CodePlex #11525](https://phpexcel.codeplex.com/workitem/11525) +- Fix for empty quoted strings in formulae - @MarkBaker +- Trap for division by zero in Bessel functions - @MarkBaker +- Fix to OOCalc Reader to convert semi-colon (;) argument separator in formulae to a comma (,) - @MarkBaker +- PHPExcel_Writer_Excel5_Parser cannot parse formula like =SUM(C$5:C5) - @Erik Tilt [CodePlex #11693](https://phpexcel.codeplex.com/workitem/11693) +- Fix to OOCalc Reader to handle dates that fall outside 32-bit PHP's date range - @MarkBaker +- File->sys_get_temp_dir() can fail in safe mode - @Erik Tilt [CodePlex #11692](https://phpexcel.codeplex.com/workitem/11692) +- Sheet references in Excel5 writer do not work when referenced sheet title contains non-Latin symbols - @Erik Tilt [CodePlex #11727](https://phpexcel.codeplex.com/workitem/11727) +- Bug in HTML writer can result in missing rows in output - @Erik Tilt [CodePlex #11743](https://phpexcel.codeplex.com/workitem/11743) +- setShowGridLines(true) not working with PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11674](https://phpexcel.codeplex.com/workitem/11674) +- PHPExcel_Worksheet_RowIterator initial position incorrect - @Erik Tilt [CodePlex #11836](https://phpexcel.codeplex.com/workitem/11836) +- PHPExcel_Worksheet_HeaderFooterDrawing Strict Exception thrown (by jshaw86) - @Erik Tilt [CodePlex #11835](https://phpexcel.codeplex.com/workitem/11835) +- Parts of worksheet lost when there are embedded charts (Excel5 reader) - @Erik Tilt [CodePlex #11850](https://phpexcel.codeplex.com/workitem/11850) +- VLOOKUP() function error when lookup value is passed as a cell reference rather than an absolute value - @MarkBaker +- First segment of Rich-Text not read correctly by PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #12041](https://phpexcel.codeplex.com/workitem/12041) +- Fatal Error with getCell('name') when name matches the pattern for a cell reference - @MarkBaker [CodePlex #12048](https://phpexcel.codeplex.com/workitem/12048) +- excel5 writer appears to be swapping image locations - @Erik Tilt [CodePlex #12039](https://phpexcel.codeplex.com/workitem/12039) +- Undefined index: host in ZipStreamWrapper.php, line 94 and line 101 - @Erik Tilt [CodePlex #11954](https://phpexcel.codeplex.com/workitem/11954) +- BIFF8 File Format problem (too short COLINFO record) - @Erik Tilt [CodePlex #11672](https://phpexcel.codeplex.com/workitem/11672) +- Column width sometimes changed after read/write with Excel2007 reader/writer - @Erik Tilt [CodePlex #12121](https://phpexcel.codeplex.com/workitem/12121) +- Worksheet.php throws a fatal error when styling is turned off via setReadDataOnly on the reader - @Erik Tilt [CodePlex #11964](https://phpexcel.codeplex.com/workitem/11964) +- Checking for Circular References in Formulae - @MarkBaker [CodePlex #11851](https://phpexcel.codeplex.com/workitem/11851) + - Calculation Engine code now traps for cyclic references, raising an error or throwing an exception, or allows 1 or more iterations through cyclic references, based on a configuration setting- PNG transparency using Excel2007 writer - @Erik Tilt [CodePlex #12244](https://phpexcel.codeplex.com/workitem/12244) +- Custom readfilter error when cell formulas reference excluded cells (Excel5 reader) - @Erik Tilt [CodePlex #12221](https://phpexcel.codeplex.com/workitem/12221) +- Protection problem in XLS - @Erik Tilt [CodePlex #12288](https://phpexcel.codeplex.com/workitem/12288) +- getColumnDimension()->setAutoSize() incorrect on cells with Number Formatting - @Erik Tilt [CodePlex #12300](https://phpexcel.codeplex.com/workitem/12300) +- Notices reading Excel file with Add-in funcitons (PHPExcel_Reader_Excel5) - @Erik Tilt [CodePlex #12378](https://phpexcel.codeplex.com/workitem/12378) +- Excel5 reader not reading formulas with deleted sheet references - @Erik Tilt [CodePlex #12380](https://phpexcel.codeplex.com/workitem/12380) +- Named range (defined name) scope problems for in PHPExcel - @Erik Tilt [CodePlex #12404](https://phpexcel.codeplex.com/workitem/12404) +- PHP Parse error: syntax error, unexpected T_PUBLIC in PHPExcel/Calculation.php on line 3482 - @Erik Tilt [CodePlex #12423](https://phpexcel.codeplex.com/workitem/12423) +- Named ranges don't appear in name box using Excel5 writer - @Erik Tilt [CodePlex #12505](https://phpexcel.codeplex.com/workitem/12505) +- Many merged cells + autoSize column -> slows down the writer - @Erik Tilt [CodePlex #12509](https://phpexcel.codeplex.com/workitem/12509) +- Incorrect fallback order comment in Shared/Strings.php ConvertEncoding() - @Erik Tilt [CodePlex #12539](https://phpexcel.codeplex.com/workitem/12539) +- IBM AIX iconv() will not work, should revert to mbstring etc. instead - @Erik Tilt [CodePlex #12538](https://phpexcel.codeplex.com/workitem/12538) +- Excel5 writer and mbstring functions overload - @Erik Tilt [CodePlex #12568](https://phpexcel.codeplex.com/workitem/12568) +- OFFSET needs to flattenSingleValue the $rows and $columns args - @MarkBaker [CodePlex #12672](https://phpexcel.codeplex.com/workitem/12672) +- Formula with DMAX(): Notice: Undefined offset: 2 in ...\PHPExcel\Calculation.php on line 2365 - @MarkBaker [CodePlex #12546](https://phpexcel.codeplex.com/workitem/12546) + - Note that the Database functions have not yet been implemented- Call to a member function getParent() on a non-object in Classes\\PHPExcel\\Calculation.php Title is required - @MarkBaker [CodePlex #12839](https://phpexcel.codeplex.com/workitem/12839) +- Cyclic Reference in Formula - @MarkBaker [CodePlex #12935](https://phpexcel.codeplex.com/workitem/12935) +- Memory error...data validation? - @MarkBaker [CodePlex #13025](https://phpexcel.codeplex.com/workitem/13025) + + +## [1.7.2] - 2010-01-11 + +### General + +- Applied patch 4362 - @Erik Tilt +- Applied patch 4363 (modified) - @Erik Tilt +- 1.7.1 Extremely Slow - Refactored PHPExcel_Calculation_Functions::flattenArray() method and set calculation cache timer default to 2.5 seconds - @MarkBaker [CodePlex #10874](https://phpexcel.codeplex.com/workitem/10874) +- Allow formulae to contain line breaks - @MarkBaker +- split() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10910](https://phpexcel.codeplex.com/workitem/10910) +- sys_get_temp_dir() requires PHP 5.2.1, not PHP 5.2 [provide fallback function for PHP 5.2.0] - @Erik Tilt +- Implementation of the ISPMT() Financial function by Matt Groves - @MarkBaker +- Put the example of formula with more arguments in documentation - @MarkBaker [CodePlex #11052](https://phpexcel.codeplex.com/workitem/11052) + +### Features + +- Improved accuracy for the GAMMAINV() Statistical Function - @MarkBaker +- XFEXT record support to fix colors change from Excel5 reader, and copy/paste color change with Excel5 writer - @Erik Tilt [CodePlex #10409](https://phpexcel.codeplex.com/workitem/10409) + - Excel5 reader reads RGB color information in XFEXT records for borders, font color and fill color- Implement more Excel calculation functions - @MarkBaker + - Implemented the FVSCHEDULE(), XNPV(), IRR(), MIRR(), XIRR() and RATE() Financial functions + - Implemented the SUMPRODUCT() Mathematical function + - Implemented the ZTEST() Statistical Function- Multiple print areas in one sheet - @Erik Tilt [CodePlex #10919](https://phpexcel.codeplex.com/workitem/10919) +- Store calculated values in output by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10930](https://phpexcel.codeplex.com/workitem/10930) +- Sheet protection options in Excel5 reader/writer - @Erik Tilt [CodePlex #10939](https://phpexcel.codeplex.com/workitem/10939) +- Modification of the COUNT(), AVERAGE(), AVERAGEA(), DEVSQ, AVEDEV(), STDEV(), STDEVA(), STDEVP(), STDEVPA(), VARA() and VARPA() SKEW() and KURT() functions to correctly handle boolean values depending on whether they're passed in as values, values within a matrix or values within a range of cells. - @MarkBaker +- Cell range selection - @Erik Tilt +- Root-relative path handling - @MarkBaker [CodePlex #10266](https://phpexcel.codeplex.com/workitem/10266) + +### Bugfixes + +- Named Ranges not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #11315](https://phpexcel.codeplex.com/workitem/11315) +- Excel2007 Reader fails to load Apache POI generated Excel - @MarkBaker [CodePlex #11206](https://phpexcel.codeplex.com/workitem/11206) +- Number format is broken when system's thousands separator is empty - @MarkBaker [CodePlex #11154](https://phpexcel.codeplex.com/workitem/11154) +- ReferenceHelper::updateNamedFormulas throws errors if oldName is empty - @MarkBaker [CodePlex #11401](https://phpexcel.codeplex.com/workitem/11401) +- parse_url() fails to parse path to an image in xlsx - @MarkBaker [CodePlex #11296](https://phpexcel.codeplex.com/workitem/11296) +- Workaround for iconv_substr() bug in PHP 5.2.0 - @Erik Tilt [CodePlex #10876](https://phpexcel.codeplex.com/workitem/10876) +- 1 pixel error for image width and height with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10877](https://phpexcel.codeplex.com/workitem/10877) +- Fix to GEOMEAN() Statistical function - @MarkBaker +- setValue('-') and setValue('.') sets numeric 0 instead of 1-character string - @Erik Tilt [CodePlex #10884](https://phpexcel.codeplex.com/workitem/10884) +- Row height sometimes much too low after read with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10885](https://phpexcel.codeplex.com/workitem/10885) +- Diagonal border. Miscellaneous missing support. - @Erik Tilt [CodePlex #10888](https://phpexcel.codeplex.com/workitem/10888) + - Constant PHPExcel_Style_Borders::DIAGONAL_BOTH added to support double-diagonal (cross) + - PHPExcel_Reader_Excel2007 not always reading diagonal borders (only recognizes 'true' and not '1') + - PHPExcel_Reader_Excel5 support for diagonal borders + - PHPExcel_Writer_Excel5 support for diagonal borders- Session bug: Fatal error: Call to a member function bindValue() on a non-object in ...\Classes\PHPExcel\Cell.php on line 217 - @Erik Tilt [CodePlex #10894](https://phpexcel.codeplex.com/workitem/10894) +- Colors messed up saving twice with same instance of PHPExcel_Writer_Excel5 (regression since 1.7.0) - @Erik Tilt [CodePlex #10896](https://phpexcel.codeplex.com/workitem/10896) +- Method PHPExcel_Worksheet::setDefaultStyle is not working - @Erik Tilt [CodePlex #10917](https://phpexcel.codeplex.com/workitem/10917) +- PHPExcel_Reader_CSV::canRead() sometimes says false when it shouldn't - @Erik Tilt [CodePlex #10897](https://phpexcel.codeplex.com/workitem/10897) +- Changes in workbook not picked up between two saves with PHPExcel_Writer_Excel2007 - @Erik Tilt [CodePlex #10922](https://phpexcel.codeplex.com/workitem/10922) +- Decimal and thousands separators missing in HTML and PDF output - @Erik Tilt [CodePlex #10913](https://phpexcel.codeplex.com/workitem/10913) +- Notices with PHPExcel_Reader_Excel5 and named array constants - @Erik Tilt [CodePlex #10936](https://phpexcel.codeplex.com/workitem/10936) +- Calculation engine limitation on 32-bit platform with integers > 2147483647 - @MarkBaker [CodePlex #10938](https://phpexcel.codeplex.com/workitem/10938) +- Shared(?) formulae containing absolute cell references not read correctly using Excel5 Reader - @Erik Tilt [CodePlex #10959](https://phpexcel.codeplex.com/workitem/10959) +- Warning messages with intersection operator involving single cell - @MarkBaker [CodePlex #10962](https://phpexcel.codeplex.com/workitem/10962) +- Infinite loop in Excel5 reader caused by zero-length string in SST - @Erik Tilt [CodePlex #10980](https://phpexcel.codeplex.com/workitem/10980) +- Remove unnecessary cell sorting to improve speed by approx. 18% in HTML and PDF writers - @Erik Tilt [CodePlex #10983](https://phpexcel.codeplex.com/workitem/10983) +- Cannot read A1 cell content - OO_Reader - @MarkBaker [CodePlex #10977](https://phpexcel.codeplex.com/workitem/10977) +- Transliteration failed, invalid encoding - @Erik Tilt [CodePlex #11000](https://phpexcel.codeplex.com/workitem/11000) + + +## [1.7.1] - 2009-11-02 + +### General + +- ereg() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10687](https://phpexcel.codeplex.com/workitem/10687) +- Writer Interface Inconsequence - setTempDir and setUseDiskCaching - @MarkBaker [CodePlex #10739](https://phpexcel.codeplex.com/workitem/10739) + +### Features + +- Upgrade to TCPDF 4.8.009 - @Erik Tilt +- Support for row and column styles (feature request) - @Erik Tilt + - Basic implementation for Excel2007/Excel5 reader/writer- Hyperlink to local file in Excel5 reader/writer - @Erik Tilt [CodePlex #10459](https://phpexcel.codeplex.com/workitem/10459) +- Color Tab (Color Sheet's name) - @MarkBaker [CodePlex #10472](https://phpexcel.codeplex.com/workitem/10472) +- Border style "double" support in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #10488](https://phpexcel.codeplex.com/workitem/10488) +- Multi-section number format support in HTML/PDF/CSV writers - @Erik Tilt [CodePlex #10492](https://phpexcel.codeplex.com/workitem/10492) +- Some additional performance tweaks in the calculation engine - @MarkBaker +- Fix result of DB() and DDB() Financial functions to 2dp when in Gnumeric Compatibility mode - @MarkBaker +- Added AMORDEGRC(), AMORLINC() and COUPNUM() Financial function (no validation of parameters yet) - @MarkBaker +- Improved accuracy of TBILLEQ(), TBILLPRICE() and TBILLYIELD() Financial functions when in Excel or Gnumeric mode - @MarkBaker +- Added INDIRECT() Lookup/Reference function (only supports full addresses at the moment) - @MarkBaker +- PHPExcel_Reader_CSV::canRead() improvements - @MarkBaker [CodePlex #10498](https://phpexcel.codeplex.com/workitem/10498) +- Input encoding option for PHPExcel_Reader_CSV - @Erik Tilt [CodePlex #10500](https://phpexcel.codeplex.com/workitem/10500) +- Colored number format support, e.g. [Red], in HTML/PDF output - @Erik Tilt [CodePlex #10493](https://phpexcel.codeplex.com/workitem/10493) +- Color Tab (Color Sheet's name) [Excel5 reader/writer support] - @Erik Tilt [CodePlex #10559](https://phpexcel.codeplex.com/workitem/10559) +- Initial version of SYLK (slk) and Excel 2003 XML Readers (Cell data and basic cell formatting) - @MarkBaker +- Initial version of Open Office Calc (ods) Reader (Cell data only) - @MarkBaker +- Initial use of "pass by reference" in the calculation engine for ROW() and COLUMN() Lookup/Reference functions - @MarkBaker +- COLUMNS() and ROWS() Lookup/Reference functions, and SUBSTITUTE() Text function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- AdvancedValueBinder(): Re-enable zero-padded string-to-number conversion, e.g '0004' -> 4 - @Erik Tilt [CodePlex #10502](https://phpexcel.codeplex.com/workitem/10502) +- Make PHP type match Excel datatype - @Erik Tilt [CodePlex #10600](https://phpexcel.codeplex.com/workitem/10600) +- Change first page number on header - @MarkBaker [CodePlex #10630](https://phpexcel.codeplex.com/workitem/10630) +- Applied patch 3941 - @MarkBaker +- Hidden sheets - @MB,ET [CodePlex #10745](https://phpexcel.codeplex.com/workitem/10745) +- mbstring fallback when iconv is broken - @Erik Tilt [CodePlex #10761](https://phpexcel.codeplex.com/workitem/10761) +- Note, can't yet handle comparison of two matrices - @MarkBaker +- Improved handling for validation and error trapping in a number of functions - @MarkBaker +- Improved support for fraction number formatting - @MarkBaker +- Support Reading CSV with Byte Order Mark (BOM) - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455) + +### Bugfixes + +- addExternalSheet() at specified index - @Erik Tilt [CodePlex #10860](https://phpexcel.codeplex.com/workitem/10860) +- Named range can no longer be passed to worksheet->getCell() - @MarkBaker [CodePlex #10684](https://phpexcel.codeplex.com/workitem/10684) +- RichText HTML entities no longer working in PHPExcel 1.7.0 - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455) +- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet [+ support for simultaneous scale/fitToPage] - @Erik Tilt +- Performance issue identified by profiling - @MarkBaker [CodePlex #10469](https://phpexcel.codeplex.com/workitem/10469) +- setSelectedCell is wrong - @Erik Tilt [CodePlex #10473](https://phpexcel.codeplex.com/workitem/10473) +- Images get squeezed/stretched with (Mac) Verdana 10 Excel files using Excel5 reader/writer - @Erik Tilt [CodePlex #10481](https://phpexcel.codeplex.com/workitem/10481) +- Error in argument count for DATEDIF() function - @MarkBaker [CodePlex #10482](https://phpexcel.codeplex.com/workitem/10482) +- updateFormulaReferences is buggy - @MarkBaker [CodePlex #10452](https://phpexcel.codeplex.com/workitem/10452) +- CellIterator returns null Cell if onlyExistingCells is set and key() is in use - @MarkBaker [CodePlex #10485](https://phpexcel.codeplex.com/workitem/10485) +- Wrong RegEx for parsing cell references in formulas - @MarkBaker [CodePlex #10453](https://phpexcel.codeplex.com/workitem/10453) +- Optimisation subverted to devastating effect if IterateOnlyExistingCells is clear - @MarkBaker [CodePlex #10486](https://phpexcel.codeplex.com/workitem/10486) +- Fatal error: Uncaught exception 'Exception' with message 'Unrecognized token 6C in formula'... with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10494](https://phpexcel.codeplex.com/workitem/10494) +- Fractions stored as text are not treated as numbers by PHPExcel's calculation engine - @MarkBaker [CodePlex #10490](https://phpexcel.codeplex.com/workitem/10490) +- AutoFit (autosize) row height not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10503](https://phpexcel.codeplex.com/workitem/10503) +- Fixed problem with null values breaking the calculation stack - @MarkBaker +- Date number formats sometimes fail with PHPExcel_Style_NumberFormat::toFormattedString, e.g. [$-40047]mmmm d yyyy - @Erik Tilt [CodePlex #10524](https://phpexcel.codeplex.com/workitem/10524) +- Fixed minor problem with DATEDIFF YM calculation - @MarkBaker +- Applied patch 3695 - @MarkBaker +- setAutosize() and Date cells not working properly - @Erik Tilt [CodePlex #10536](https://phpexcel.codeplex.com/workitem/10536) +- Time value hour offset in output by HTML/PDF/CSV writers (system timezone problem) - @Erik Tilt [CodePlex #10556](https://phpexcel.codeplex.com/workitem/10556) +- Control characters 0x14-0x1F are not treated by PHPExcel - @Erik Tilt [CodePlex #10558](https://phpexcel.codeplex.com/workitem/10558) +- PHPExcel_Writer_Excel5 not working when open_basedir restriction is in effect - @Erik Tilt [CodePlex #10560](https://phpexcel.codeplex.com/workitem/10560) +- IF formula calculation problem in PHPExcel 1.7.0 (string comparisons) - @MarkBaker [CodePlex #10563](https://phpexcel.codeplex.com/workitem/10563) +- Improved CODE() Text function result for UTF-8 characters - @MarkBaker +- Empty rows are collapsed with HTML/PDF writer - @Erik Tilt [CodePlex #10568](https://phpexcel.codeplex.com/workitem/10568) +- Gaps between rows in output by PHPExcel_Writer_PDF (Upgrading to TCPDF 4.7.003) - @Erik Tilt [CodePlex #10569](https://phpexcel.codeplex.com/workitem/10569) +- Problem reading formulas (Excel5 reader problem with "fake" shared formulas) - @Erik Tilt [CodePlex #10575](https://phpexcel.codeplex.com/workitem/10575) +- Error type in formula: "_raiseFormulaError message is Formula Error: An unexpected error occured" - @MarkBaker [CodePlex #10588](https://phpexcel.codeplex.com/workitem/10588) +- Miscellaneous column width problems in Excel5/Excel2007 writer - @Erik Tilt [CodePlex #10599](https://phpexcel.codeplex.com/workitem/10599) +- Reader/Excel5 'Unrecognized token 2D in formula' in latest version - @Erik Tilt [CodePlex #10615](https://phpexcel.codeplex.com/workitem/10615) +- on php 5.3 PHPExcel 1.7 Excel 5 reader fails in _getNextToken, token = 2C, throws exception - @Erik Tilt [CodePlex #10623](https://phpexcel.codeplex.com/workitem/10623) +- Fatal error when altering styles after workbook has been saved - @Erik Tilt [CodePlex #10617](https://phpexcel.codeplex.com/workitem/10617) +- Images vertically stretched or squeezed when default font size is changed (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10661](https://phpexcel.codeplex.com/workitem/10661) +- Styles not read in "manipulated" Excel2007 workbook - @Erik Tilt [CodePlex #10676](https://phpexcel.codeplex.com/workitem/10676) +- Windows 7 says corrupt file by PHPExcel_Writer_Excel5 when opening in Excel - @Erik Tilt [CodePlex #10059](https://phpexcel.codeplex.com/workitem/10059) +- Calculations sometimes not working with cell references to other sheets - @MarkBaker [CodePlex #10708](https://phpexcel.codeplex.com/workitem/10708) +- Problem with merged cells after insertNewRowBefore() - @Erik Tilt [CodePlex #10706](https://phpexcel.codeplex.com/workitem/10706) +- Applied patch 4023 - @MarkBaker +- Fix to SUMIF() and COUNTIF() Statistical functions for when condition is a match against a string value - @MarkBaker +- PHPExcel_Cell::coordinateFromString should throw exception for bad string parameter - @Erik Tilt [CodePlex #10721](https://phpexcel.codeplex.com/workitem/10721) +- EucrosiaUPC (Thai font) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10723](https://phpexcel.codeplex.com/workitem/10723) +- Improved the return of calculated results when the result value is an array - @MarkBaker +- Allow calculation engine to support Functions prefixed with @ within formulae - @MarkBaker +- Intersection operator (space operator) fatal error with calculation engine - @MarkBaker [CodePlex #10632](https://phpexcel.codeplex.com/workitem/10632) +- Chinese, Japanese, Korean characters show as squares in PDF - @Erik Tilt [CodePlex #10742](https://phpexcel.codeplex.com/workitem/10742) +- sheet title allows invalid characters - @Erik Tilt [CodePlex #10756](https://phpexcel.codeplex.com/workitem/10756) +- Sheet!$A$1 as function argument in formula causes infinite loop in Excel5 writer - @Erik Tilt [CodePlex #10757](https://phpexcel.codeplex.com/workitem/10757) +- Cell range involving name not working with calculation engine - Modified calculation parser to handle range operator (:), but doesn't currently handle worksheet references with spaces or other non-alphameric characters, or trap erroneous references - @MarkBaker [CodePlex #10740](https://phpexcel.codeplex.com/workitem/10740) +- DATE function problem with calculation engine (says too few arguments given) - @MarkBaker [CodePlex #10798](https://phpexcel.codeplex.com/workitem/10798) +- Blank cell can cause wrong calculated value - @MarkBaker [CodePlex #10799](https://phpexcel.codeplex.com/workitem/10799) +- Modified ROW() and COLUMN() Lookup/Reference Functions to return an array when passed a cell range, plus some additional work on INDEX() - @MarkBaker +- Images not showing in Excel 97 using PHPExcel_Writer_Excel5 (patch by Jordi Gutiérrez Hermoso) - @Erik Tilt [CodePlex #10817](https://phpexcel.codeplex.com/workitem/10817) +- When figures are contained in the excel sheet, Reader was stopped - @Erik Tilt [CodePlex #10785](https://phpexcel.codeplex.com/workitem/10785) +- Formulas changed after insertNewRowBefore() - @MarkBaker [CodePlex #10818](https://phpexcel.codeplex.com/workitem/10818) +- Cell range row offset problem with shared formulas using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10825](https://phpexcel.codeplex.com/workitem/10825) +- Warning: Call-time pass-by-reference has been deprecated - @MarkBaker [CodePlex #10832](https://phpexcel.codeplex.com/workitem/10832) +- Image should "Move but don't size with cells" instead of "Move and size with cells" with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10849](https://phpexcel.codeplex.com/workitem/10849) +- Opening a Excel5 generated XLS in Excel 2007 results in header/footer entry not showing on input - @Erik Tilt [CodePlex #10856](https://phpexcel.codeplex.com/workitem/10856) +- addExternalSheet() not returning worksheet - @Erik Tilt [CodePlex #10859](https://phpexcel.codeplex.com/workitem/10859) +- Invalid results in formulas with named ranges - @MarkBaker [CodePlex #10629](https://phpexcel.codeplex.com/workitem/10629) + + +## [1.7.0] - 2009-08-10 + +### General + +- Expand documentation: Number formats - @Erik Tilt +- Class 'PHPExcel_Cell_AdvancedValueBinder' not found - @Erik Tilt + +### Features + +- Change return type of date functions to PHPExcel_Calculation_Functions::RETURNDATE_EXCEL - @MarkBaker +- New RPN and stack-based calculation engine for improved performance of formula calculation - @MarkBaker + - Faster (anything between 2 and 12 times faster than the old parser, depending on the complexity and nature of the formula) + - Significantly more memory efficient when formulae reference cells across worksheets + - Correct behaviour when referencing Named Ranges that exist on several worksheets + - Support for Excel ^ (Exponential) and % (Percentage) operators + - Support for matrices within basic arithmetic formulae (e.g. ={1,2,3;4,5,6;7,8,9}/2) + - Better trapping/handling of NaN and infinity results (return #NUM! error) + - Improved handling of empty parameters for Excel functions + - Optional logging of calculation steps- New calculation engine can be accessed independently of workbooks (for use as a standalone calculator) - @MarkBaker +- Implement more Excel calculation functions - @MarkBaker + - Initial implementation of the COUNTIF() and SUMIF() Statistical functions + - Added ACCRINT() Financial function- Modifications to number format handling for dddd and ddd masks in dates, use of thousand separators even when locale only implements it for money, and basic fraction masks (0 ?/? and ?/?) - @MarkBaker +- Support arbitrary fixed number of decimals in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt +- Improving performance and memory on data dumps - @Erik Tilt + - Various style optimizations (merging from branch wi6857-memory) + - Moving hyperlink and dataValidation properties from cell to worksheet for lower PHP memory usage- Provide fluent interfaces where possible - @MarkBaker +- Make easy way to apply a border to a rectangular selection - @Erik Tilt +- Support for system window colors in PHPExcel_Reader_Excel5 - @Erik Tilt +- Horizontal center across selection - @Erik Tilt +- Merged cells record, write to full record size in PHPExcel_Writer_Excel5 - @Erik Tilt +- Add page break between sheets in exported PDF - @MarkBaker +- Sanitization of UTF-8 input for cell values - @Erik Tilt +- Read cached calculated value with PHPExcel_Reader_Excel5 - @Erik Tilt +- Miscellaneous CSS improvements for PHPExcel_Writer_HTML - @Erik Tilt +- getProperties: setCompany feature request - @Erik Tilt +- Insert worksheet at a specified index - @MarkBaker +- Change worksheet index - @MarkBaker +- Readfilter for CSV reader - @MarkBaker +- Check value of mbstring.func_overload when saving with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10172](https://phpexcel.codeplex.com/workitem/10172) +- Eliminate dependency of an include path pointing to class directory - @Erik Tilt [CodePlex #10251](https://phpexcel.codeplex.com/workitem/10251) +- Method for getting the correct reader for a certain file (contribution) - @Erik Tilt [CodePlex #10292](https://phpexcel.codeplex.com/workitem/10292) +- Choosing specific row in fromArray method - @Erik Tilt [CodePlex #10287](https://phpexcel.codeplex.com/workitem/10287) +- Shared formula support in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10319](https://phpexcel.codeplex.com/workitem/10319) + +### Bugfixes + +- Right-to-left column direction in worksheet - @MB,ET [CodePlex #10345](https://phpexcel.codeplex.com/workitem/10345) +- PHPExcel_Reader_Excel5 not reading PHPExcel_Style_NumberFormat::FORMAT_NUMBER ('0') - @Erik Tilt +- Fractional row height in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt +- Fractional (decimal) numbers not inserted correctly when locale is other than English - @Erik Tilt +- Fractional calculated value in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt +- Locale aware decimal and thousands separator in exported formats HTML, CSV, PDF - @Erik Tilt +- Cannot Add Image with Space on its Name - @MarkBaker +- Black line at top of every page in output by PHPExcel_Writer_PDF - @Erik Tilt +- Border styles and border colors not showing in HTML output (regression since 1.6.4) - @Erik Tilt +- Hidden screen gridlines setting in worksheet not read by PHPExcel_Reader_Excel2007 - @Erik Tilt +- Some valid sheet names causes corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker +- More than 32,767 characters in a cell gives corrupt Excel file - @Erik Tilt +- Images not getting copyied with the ->copy() function - @Erik Tilt +- Bad calculation of column width setAutoSize(true) function - @Erik Tilt +- Dates are sometimes offset by 1 day in output by HTML and PDF writers depending on system timezone setting - @Erik Tilt +- Wingdings symbol fonts not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10003](https://phpexcel.codeplex.com/workitem/10003) +- White space string prefix stripped by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #10010](https://phpexcel.codeplex.com/workitem/10010) +- The name of the Workbook stream MUST be "Workbook", not "Book" - @Erik Tilt [CodePlex #10023](https://phpexcel.codeplex.com/workitem/10023) +- Avoid message "Microsoft Excel recalculates formulas..." when closing xls file from Excel - @Erik Tilt [CodePlex #10030](https://phpexcel.codeplex.com/workitem/10030) +- Non-unique newline representation causes problems with LEN formula - @Erik Tilt [CodePlex #10031](https://phpexcel.codeplex.com/workitem/10031) +- Newline in cell not showing with PHPExcel_Writer_HTML and PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10033](https://phpexcel.codeplex.com/workitem/10033) +- Rich-Text strings get prefixed by   when output by HTML writer - @Erik Tilt [CodePlex #10046](https://phpexcel.codeplex.com/workitem/10046) +- Leading spaces do not appear in output by HTML/PDF writers - @Erik Tilt [CodePlex #10052](https://phpexcel.codeplex.com/workitem/10052) +- Empty Apache POI-generated file can not be read - @MarkBaker [CodePlex #10061](https://phpexcel.codeplex.com/workitem/10061) +- Column width not scaling correctly with font size in HTML and PDF writers - @Erik Tilt [CodePlex #10068](https://phpexcel.codeplex.com/workitem/10068) +- Inaccurate row heights with HTML writer - @Erik Tilt [CodePlex #10069](https://phpexcel.codeplex.com/workitem/10069) +- Reference helper - @MarkBaker +- Excel 5 Named ranges should not be local to the worksheet, but accessible from all worksheets - @MarkBaker +- Row heights are ignored by PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10088](https://phpexcel.codeplex.com/workitem/10088) +- Write raw XML - @MarkBaker +- removeRow(), removeColumn() not always clearing cell values - @Erik Tilt [CodePlex #10098](https://phpexcel.codeplex.com/workitem/10098) +- Problem reading certain hyperlink records with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10142](https://phpexcel.codeplex.com/workitem/10142) +- Hyperlink cell range read failure with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #10143](https://phpexcel.codeplex.com/workitem/10143) +- 'Column string index can not be empty.' - @MarkBaker [CodePlex #10149](https://phpexcel.codeplex.com/workitem/10149) +- getHighestColumn() sometimes says there are 256 columns with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10204](https://phpexcel.codeplex.com/workitem/10204) +- extractSheetTitle fails when sheet title contains exclamation mark (!) - @Erik Tilt [CodePlex #10220](https://phpexcel.codeplex.com/workitem/10220) +- setTitle() sometimes erroneously appends integer to sheet name - @Erik Tilt [CodePlex #10221](https://phpexcel.codeplex.com/workitem/10221) +- Mac BIFF5 Excel file read failure (missing support for Mac OS Roman character set) - @Erik Tilt [CodePlex #10229](https://phpexcel.codeplex.com/workitem/10229) +- BIFF5 header and footer incorrectly read by PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10230](https://phpexcel.codeplex.com/workitem/10230) +- iconv notices when reading hyperlinks with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10259](https://phpexcel.codeplex.com/workitem/10259) +- Excel5 reader OLE read failure with small Mac BIFF5 Excel files - @Erik Tilt [CodePlex #10252](https://phpexcel.codeplex.com/workitem/10252) +- Problem in reading formula : IF( IF ) with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10272](https://phpexcel.codeplex.com/workitem/10272) +- Error reading formulas referencing external sheets with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10274](https://phpexcel.codeplex.com/workitem/10274) +- Image horizontally stretched when default font size is increased (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10291](https://phpexcel.codeplex.com/workitem/10291) +- Undefined offset in Reader\Excel5.php on line 3572 - @Erik Tilt [CodePlex #10333](https://phpexcel.codeplex.com/workitem/10333) +- PDF output different then XLS (copied data) - @MarkBaker [CodePlex #10340](https://phpexcel.codeplex.com/workitem/10340) +- Internal hyperlinks with UTF-8 sheet names not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10352](https://phpexcel.codeplex.com/workitem/10352) +- String shared formula result read error with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10361](https://phpexcel.codeplex.com/workitem/10361) +- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/PageSetup.php:338 - @Erik Tilt [CodePlex #10363](https://phpexcel.codeplex.com/workitem/10363) +- Using setLoadSheetsOnly fails if you do not use setReadDataOnly(true) and sheet is not the first sheet - @Erik Tilt [CodePlex #10355](https://phpexcel.codeplex.com/workitem/10355) +- getCalculatedValue() sometimes incorrect with IF formula and 0-values - @MarkBaker [CodePlex #10362](https://phpexcel.codeplex.com/workitem/10362) +- Excel Reader 2007 problem with "shared" formulae when "master" is an error - @MarkBaker +- Named Range Bug, using the same range name on different worksheets - @MarkBaker +- Java code in JAMA classes - @MarkBaker +- getCalculatedValue() not working with some formulas involving error types - @MarkBaker +- evaluation of both return values in an IF() statement returning an error if either result was an error, irrespective of the IF evaluation - @MarkBaker +- Power in formulas: new calculation engine no longer treats ^ as a bitwise XOR operator - @MarkBaker +- Bugfixes and improvements to many of the Excel functions in PHPExcel - @MarkBaker + - Added optional "places" parameter in the BIN2HEX(), BIN2OCT, DEC2BIN(), DEC2OCT(), DEC2HEX(), HEX2BIN(), HEX2OCT(), OCT2BIN() and OCT2HEX() Engineering Functions + - Trap for unbalanced matrix sizes in MDETERM() and MINVERSE() Mathematic and Trigonometric functions + - Fix for default characters parameter value for LEFT() and RIGHT() Text functions + - Fix for GCD() and LCB() Mathematical functions when the parameters include a zero (0) value + - Fix for BIN2OCT() Engineering Function for 2s complement values (which were returning hex values) + - Fix for BESSELK() and BESSELY() Engineering functions + - Fix for IMDIV() Engineering Function when result imaginary component is positive (wasn't setting the sign) + - Fix for ERF() Engineering Function when called with an upper limit value for the integration + - Fix to DATE() Date/Time Function for year value of 0 + - Set ISPMT() function as category FINANCIAL + - Fix for DOLLARDE() and DOLLARFR() Financial functions + - Fix to EFFECT() Financial function (treating $nominal_rate value as a variable name rather than a value) + - Fix to CRITBINOM() Statistical function (CurrentValue and EssentiallyZero treated as constants rather than variables) + - Note that an Error in the function logic can still lead to a permanent loop + - Fix to MOD() Mathematical function to work with floating point results + - Fix for QUOTIENT() Mathematical function + - Fix to HOUR(), MINUTE() and SECOND() Date/Time functions to return an error when passing in a floating point value of 1.0 or greater, or less than 0 + - LOG() Function now correctly returns base-10 log when called with only one parameter, rather than the natural log as the default base + - Modified text functions to handle multibyte character set (UTF-8). + +## [1.6.7] - 2009-04-22 + +### BREAKING CHANGE + +In previous versions of PHPExcel up to and including 1.6.6, +when a cell had a date-like number format code, it was possible to enter a date +directly using an integer PHP-time without converting to Excel date format. +Starting with PHPExcel 1.6.7 this is no longer supported. Refer to the developer +documentation for more information on entering dates into a cell. + +### General + +- Deprecate misspelled setStriketrough() and getStriketrough() methods - @MarkBaker [CodePlex #9416](https://phpexcel.codeplex.com/workitem/9416) + +### Features + +- Performance improvement when saving file - @MarkBaker [CodePlex #9526](https://phpexcel.codeplex.com/workitem/9526) +- Check that sheet title has maximum 31 characters - @MarkBaker [CodePlex #9598](https://phpexcel.codeplex.com/workitem/9598) +- True support for Excel built-in number format codes - @MB, ET [CodePlex #9631](https://phpexcel.codeplex.com/workitem/9631) +- Ability to read defect BIFF5 Excel file without CODEPAGE record - @Erik Tilt [CodePlex #9683](https://phpexcel.codeplex.com/workitem/9683) +- Auto-detect which reader to invoke - @MarkBaker [CodePlex #9701](https://phpexcel.codeplex.com/workitem/9701) +- Deprecate insertion of dates using PHP-time (Unix time) [request for removal of feature] - @Erik Tilt [CodePlex #9214](https://phpexcel.codeplex.com/workitem/9214) +- Support for entering time values like '9:45', '09:45' using AdvancedValueBinder - @Erik Tilt [CodePlex #9747](https://phpexcel.codeplex.com/workitem/9747) + +### Bugfixes + +- DataType dependent horizontal alignment in HTML and PDF writer - @Erik Tilt [CodePlex #9797](https://phpexcel.codeplex.com/workitem/9797) +- Cloning data validation object causes script to stop - @MarkBaker [CodePlex #9375](https://phpexcel.codeplex.com/workitem/9375) +- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9400](https://phpexcel.codeplex.com/workitem/9400) +- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9399](https://phpexcel.codeplex.com/workitem/9399) +- Row outline level not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9437](https://phpexcel.codeplex.com/workitem/9437) +- Occasional notices with PHPExcel_Reader_Excel5 when Excel file contains drawing elements - @Erik Tilt [CodePlex #9452](https://phpexcel.codeplex.com/workitem/9452) +- PHPExcel_Reader_Excel5 fails as a whole when workbook contains images other than JPEG/PNG - @Erik Tilt [CodePlex #9453](https://phpexcel.codeplex.com/workitem/9453) +- Excel5 writer checks for iconv but does not necessarily use it - @Erik Tilt [CodePlex #9444](https://phpexcel.codeplex.com/workitem/9444) +- Altering a style on copied worksheet alters also the original - @Erik Tilt [CodePlex #9463](https://phpexcel.codeplex.com/workitem/9463) +- Formulas are incorrectly updated when a sheet is renamed - @MarkBaker [CodePlex #9480](https://phpexcel.codeplex.com/workitem/9480) +- PHPExcel_Worksheet::extractSheetTitle not treating single quotes correctly - @MarkBaker [CodePlex #9513](https://phpexcel.codeplex.com/workitem/9513) +- PHP Warning raised in function array_key_exists - @MarkBaker [CodePlex #9477](https://phpexcel.codeplex.com/workitem/9477) +- getAlignWithMargins() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9599](https://phpexcel.codeplex.com/workitem/9599) +- getScaleWithDocument() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9600](https://phpexcel.codeplex.com/workitem/9600) +- PHPExcel_Reader_Excel2007 not reading the first user-defined number format - @MarkBaker [CodePlex #9630](https://phpexcel.codeplex.com/workitem/9630) +- Print area converted to uppercase after read with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9647](https://phpexcel.codeplex.com/workitem/9647) +- Incorrect reading of scope for named range using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9661](https://phpexcel.codeplex.com/workitem/9661) +- Error with pattern (getFillType) and rbg (getRGB) - @MarkBaker [CodePlex #9690](https://phpexcel.codeplex.com/workitem/9690) +- AdvancedValueBinder affected by system timezone setting when inserting date values - @Erik Tilt [CodePlex #9712](https://phpexcel.codeplex.com/workitem/9712) +- PHPExcel_Reader_Excel2007 not reading value of active sheet index - @Erik Tilt [CodePlex #9743](https://phpexcel.codeplex.com/workitem/9743) +- getARGB() sometimes returns SimpleXMLElement object instead of string with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #9742](https://phpexcel.codeplex.com/workitem/9742) +- Negative image offset causes defects in 14excel5.xls and 20readexcel5.xlsx - @Erik Tilt [CodePlex #9731](https://phpexcel.codeplex.com/workitem/9731) +- HTML & PDF Writer not working with mergeCells (regression since 1.6.5) - @Erik Tilt [CodePlex #9758](https://phpexcel.codeplex.com/workitem/9758) +- Too wide columns with HTML and PDF writer - @Erik Tilt [CodePlex #9774](https://phpexcel.codeplex.com/workitem/9774) +- PDF and cyrillic fonts - @MarkBaker [CodePlex #9775](https://phpexcel.codeplex.com/workitem/9775) +- Percentages not working correctly with HTML and PDF writers (shows 0.25% instead of 25%) - @Erik Tilt [CodePlex #9793](https://phpexcel.codeplex.com/workitem/9793) +- PHPExcel_Writer_HTML creates extra borders around cell contents using setUseInlineCss(true) - @Erik Tilt [CodePlex #9791](https://phpexcel.codeplex.com/workitem/9791) +- Problem with text wrap + merged cells in HTML and PDF writer - @Erik Tilt [CodePlex #9784](https://phpexcel.codeplex.com/workitem/9784) +- Adjacent path separators in include_path causing IOFactory to violate open_basedir restriction - @Erik Tilt [CodePlex #9814](https://phpexcel.codeplex.com/workitem/9814) + + +## [1.6.6] - 2009-03-02 + +### General + +- Improve support for built-in number formats in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9102](https://phpexcel.codeplex.com/workitem/9102) +- Source files are in both UNIX and DOS formats - changed to UNIX - @Erik Tilt [CodePlex #9281](https://phpexcel.codeplex.com/workitem/9281) + +### Features + +- Update documentation: Which language to write formulas in? - @MarkBaker [CodePlex #9338](https://phpexcel.codeplex.com/workitem/9338) +- Ignore DEFCOLWIDTH records with value 8 in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8817](https://phpexcel.codeplex.com/workitem/8817) +- Support for width, height, offsetX, offsetY for images in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8847](https://phpexcel.codeplex.com/workitem/8847) +- Disk Caching in specific folder - @MarkBaker [CodePlex #8870](https://phpexcel.codeplex.com/workitem/8870) +- Added SUMX2MY2, SUMX2PY2, SUMXMY2, MDETERM and MINVERSE Mathematical and Trigonometric Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added CONVERT Engineering Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added DB, DDB, DISC, DOLLARDE, DOLLARFR, INTRATE, IPMT, PPMT, PRICEDISC, PRICEMAT and RECEIVED Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added ACCRINTM, CUMIPMT, CUMPRINC, TBILLEQ, TBILLPRICE, TBILLYIELD, YIELDDISC and YIELDMAT Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added DOLLAR Text Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added CORREL, COVAR, FORECAST, INTERCEPT, RSQ, SLOPE and STEYX Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added PEARSON Statistical Functions as a synonym for CORREL - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added LINEST, LOGEST (currently only valid for stats = false), TREND and GROWTH Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added RANK and PERCENTRANK Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Added ROMAN Mathematical Function (Classic form only) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Update documentation to show example of getCellByColumnAndRow($col, $row) - @MarkBaker [CodePlex #8931](https://phpexcel.codeplex.com/workitem/8931) +- Implement worksheet, row and cell iterators - @MarkBaker [CodePlex #8770](https://phpexcel.codeplex.com/workitem/8770) +- Support for arbitrary defined names (named range) - @MarkBaker [CodePlex #9001](https://phpexcel.codeplex.com/workitem/9001) +- Update formulas when sheet title / named range title changes - @MB, ET [CodePlex #9016](https://phpexcel.codeplex.com/workitem/9016) +- Ability to read cached calculated value - @MarkBaker [CodePlex #9103](https://phpexcel.codeplex.com/workitem/9103) +- Support for Excel 1904 calendar date mode (Mac) - @MBaker, ET [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483) +- PHPExcel_Writer_Excel5 improvements writing shared strings table - @Erik Tilt [CodePlex #9194](https://phpexcel.codeplex.com/workitem/9194) +- PHPExcel_Writer_Excel5 iconv fallback when mbstring extension is not enabled - @Erik Tilt [CodePlex #9248](https://phpexcel.codeplex.com/workitem/9248) +- UTF-8 support in font names in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9253](https://phpexcel.codeplex.com/workitem/9253) +- Implement value binding architecture - @MarkBaker [CodePlex #9215](https://phpexcel.codeplex.com/workitem/9215) +- PDF writer not working with UTF-8 - @MarkBaker [CodePlex #6742](https://phpexcel.codeplex.com/workitem/6742) + +### Bugfixes + +- Eliminate duplicate style entries in multisheet workbook written by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9355](https://phpexcel.codeplex.com/workitem/9355) +- Redirect to client browser fails due to trailing white space in class definitions - @Erik Tilt [CodePlex #8810](https://phpexcel.codeplex.com/workitem/8810) +- Spurious column dimension element introduced in blank worksheet after using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8816](https://phpexcel.codeplex.com/workitem/8816) +- Image gets slightly narrower than expected when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8830](https://phpexcel.codeplex.com/workitem/8830) +- Image laid over non-visible row gets squeezed in height when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8831](https://phpexcel.codeplex.com/workitem/8831) +- PHPExcel_Reader_Excel5 fails when there are 10 or more images in the workbook - @Erik Tilt [CodePlex #8860](https://phpexcel.codeplex.com/workitem/8860) +- Different header/footer images in different sheets not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8909](https://phpexcel.codeplex.com/workitem/8909) +- Fractional seconds disappear when using PHPExcel_Reader_Excel2007 and PHPExcel_Reader_Excel5 - @MB, ET [CodePlex #8924](https://phpexcel.codeplex.com/workitem/8924) +- Images not showing in OpenOffice when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7994](https://phpexcel.codeplex.com/workitem/7994) +- Images not showing on print using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9047](https://phpexcel.codeplex.com/workitem/9047) +- PHPExcel_Writer_Excel5 maximum allowed record size 4 bytes too short - @Erik Tilt [CodePlex #9085](https://phpexcel.codeplex.com/workitem/9085) +- Not numeric strings are formatted as dates and numbers using worksheet's toArray method - @MarkBaker [CodePlex #9119](https://phpexcel.codeplex.com/workitem/9119) +- Excel5 simple formula parsing error - @Erik Tilt [CodePlex #9132](https://phpexcel.codeplex.com/workitem/9132) +- Problems writing dates with CSV - @Erik Tilt [CodePlex #9206](https://phpexcel.codeplex.com/workitem/9206) +- PHPExcel_Reader_Excel5 reader fails with fatal error when reading group shapes - @Erik Tilt [CodePlex #9203](https://phpexcel.codeplex.com/workitem/9203) +- PHPExcel_Writer_Excel5 fails completely when workbook contains more than 57 colors - @Erik Tilt [CodePlex #9231](https://phpexcel.codeplex.com/workitem/9231) +- PHPExcel_Writer_PDF not compatible with autoload - @Erik Tilt [CodePlex #9244](https://phpexcel.codeplex.com/workitem/9244) +- Fatal error: Call to a member function getNestingLevel() on a non-object in PHPExcel/Reader/Excel5.php on line 690 - @Erik Tilt [CodePlex #9250](https://phpexcel.codeplex.com/workitem/9250) +- Notices when running test 04printing.php on PHP 5.2.8 - @MarkBaker [CodePlex #9246](https://phpexcel.codeplex.com/workitem/9246) +- insertColumn() spawns creation of spurious RowDimension - @MarkBaker [CodePlex #9294](https://phpexcel.codeplex.com/workitem/9294) +- Fix declarations for methods in extended Trend classes - @MarkBaker [CodePlex #9296](https://phpexcel.codeplex.com/workitem/9296) +- Fix to parameters for the FORECAST Statistical Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- PDF writer problems with cell height and text wrapping - @MarkBaker [CodePlex #7083](https://phpexcel.codeplex.com/workitem/7083) +- Fix test for calculated value in case the returned result is an array - @MarkBaker +- Column greater than 256 results in corrupt Excel file using PHPExcel_Writer_Excel5 - @Erik Tilt +- Excel Numberformat 0.00 results in non internal decimal places values in toArray() Method - @MarkBaker [CodePlex #9351](https://phpexcel.codeplex.com/workitem/9351) +- setAutoSize not taking into account text rotation - @MB,ET [CodePlex #9356](https://phpexcel.codeplex.com/workitem/9356) +- Call to undefined method PHPExcel_Worksheet_MemoryDrawing::getPath() in PHPExcel/Writer/HTML.php - @Erik Tilt [CodePlex #9372](https://phpexcel.codeplex.com/workitem/9372) + + +## [1.6.5] - 2009-01-05 + +### General + +- Applied patch 2063 - @MarkBaker +- Optimise Shared Strings - @MarkBaker +- Optimise Cell Sorting - @MarkBaker +- Optimise Style Hashing - @MarkBaker +- UTF-8 enhancements - @Erik Tilt +- PHPExcel_Writer_HTML validation errors against strict HTML 4.01 / CSS 2.1 - @Erik Tilt +- Documented work items 6203 and 8110 in manual - @MarkBaker +- Restructure package hierachy so classes can be found more easily in auto-generated API (from work item 8468) - @Erik Tilt + +### Features + +- Redirect output to a client's browser: Update recommendation in documentation - @MarkBaker [CodePlex #8806](https://phpexcel.codeplex.com/workitem/8806) +- PHPExcel_Reader_Excel5 support for print gridlines - @Erik Tilt [CodePlex #7897](https://phpexcel.codeplex.com/workitem/7897) +- Screen gridlines support in Excel5 reader/writer - @Erik Tilt [CodePlex #7899](https://phpexcel.codeplex.com/workitem/7899) +- Option for adding image to spreadsheet from image resource in memory - @MB, ET [CodePlex #7552](https://phpexcel.codeplex.com/workitem/7552) +- PHPExcel_Reader_Excel5 style support for BIFF5 files (Excel 5.0 - Excel 95) - @Erik Tilt [CodePlex #7862](https://phpexcel.codeplex.com/workitem/7862) +- PHPExcel_Reader_Excel5 support for user-defined colors and special built-in colors - @Erik Tilt [CodePlex #7918](https://phpexcel.codeplex.com/workitem/7918) +- Support for freeze panes in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7992](https://phpexcel.codeplex.com/workitem/7992) +- Support for header and footer margins in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7996](https://phpexcel.codeplex.com/workitem/7996) +- Support for active sheet index in Excel5 reader/writer - @Erik Tilt [CodePlex #7997](https://phpexcel.codeplex.com/workitem/7997) +- Freeze panes not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7991](https://phpexcel.codeplex.com/workitem/7991) +- Support for screen zoom level (feature request) - @MB, ET [CodePlex #7993](https://phpexcel.codeplex.com/workitem/7993) +- Support for default style in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8012](https://phpexcel.codeplex.com/workitem/8012) +- Apple iWork / Numbers.app incompatibility - @MarkBaker [CodePlex #8094](https://phpexcel.codeplex.com/workitem/8094) +- Support "between rule" in conditional formatting - @MarkBaker [CodePlex #7931](https://phpexcel.codeplex.com/workitem/7931) +- Comment size, width and height control (feature request) - @MarkBaker [CodePlex #8308](https://phpexcel.codeplex.com/workitem/8308) +- Improve method for storing MERGEDCELLS records in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8418](https://phpexcel.codeplex.com/workitem/8418) +- Support for protectCells() in Excel5 reader/writer - @Erik Tilt [CodePlex #8435](https://phpexcel.codeplex.com/workitem/8435) +- Support for fitToWidth and fitToHeight pagesetup properties in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8472](https://phpexcel.codeplex.com/workitem/8472) +- Support for setShowSummaryBelow() and setShowSummaryRight() in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8489](https://phpexcel.codeplex.com/workitem/8489) +- Support for Excel 1904 calendar date mode (Mac) - @MarkBaker [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483) +- Excel5 reader: Support for reading images (bitmaps) - @Erik Tilt [CodePlex #7538](https://phpexcel.codeplex.com/workitem/7538) +- Support for default style in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8787](https://phpexcel.codeplex.com/workitem/8787) +- Modified calculate() method to return either an array or the first value from the array for those functions that return arrays rather than single values (e.g the MMULT and TRANSPOSE function). This performance can be modified based on the $returnArrayAsType which can be set/retrieved by calling the setArrayReturnType() and getArrayReturnType() methods of the PHPExcel_Calculation class. - @MarkBaker + +### Bugfixes + +- Added ERROR.TYPE Information Function, MMULT Mathematical and Trigonometry Function, and TRANSPOSE Lookup and Reference Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- setPrintGridlines(true) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7896](https://phpexcel.codeplex.com/workitem/7896) +- Incorrect mapping of fill patterns in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7907](https://phpexcel.codeplex.com/workitem/7907) +- setShowGridlines(false) not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7898](https://phpexcel.codeplex.com/workitem/7898) +- getShowGridlines() gives inverted value when reading sheet with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7905](https://phpexcel.codeplex.com/workitem/7905) +- User-defined column width becomes slightly larger after read/write with Excel5 - @Erik Tilt [CodePlex #7944](https://phpexcel.codeplex.com/workitem/7944) +- Incomplete border style support in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7949](https://phpexcel.codeplex.com/workitem/7949) +- Conditional formatting "containsText" read/write results in MS Office Excel 2007 crash - @MarkBaker [CodePlex #7928](https://phpexcel.codeplex.com/workitem/7928) +- All sheets are always selected in output when using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7995](https://phpexcel.codeplex.com/workitem/7995) +- COLUMN function warning message during plain read/write - @MarkBaker [CodePlex #8013](https://phpexcel.codeplex.com/workitem/8013) +- setValue(0) results in string data type '0' - @MarkBaker [CodePlex #8155](https://phpexcel.codeplex.com/workitem/8155) +- Styles not removed when removing rows from sheet - @MarkBaker [CodePlex #8226](https://phpexcel.codeplex.com/workitem/8226) +- =IF formula causes fatal error during $objWriter->save() in Excel2007 format - @MarkBaker [CodePlex #8301](https://phpexcel.codeplex.com/workitem/8301) +- Exception thrown reading valid xls file: "Excel file is corrupt. Didn't find CONTINUE record while reading shared strings" - @Erik Tilt [CodePlex #8333](https://phpexcel.codeplex.com/workitem/8333) +- MS Outlook corrupts files generated by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8320](https://phpexcel.codeplex.com/workitem/8320) +- Undefined method PHPExcel_Worksheet::setFreezePane() in ReferenceHelper.php on line 271 - @MarkBaker [CodePlex #8351](https://phpexcel.codeplex.com/workitem/8351) +- Ampersands (&), left and right angles (<, >) in Rich-Text strings leads to corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8401](https://phpexcel.codeplex.com/workitem/8401) +- Print header and footer not supporting UTF-8 in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8408](https://phpexcel.codeplex.com/workitem/8408) +- Vertical page breaks not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8463](https://phpexcel.codeplex.com/workitem/8463) +- Missing support for accounting underline types in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8476](https://phpexcel.codeplex.com/workitem/8476) +- Infinite loops when reading corrupt xls file using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8482](https://phpexcel.codeplex.com/workitem/8482) +- Sheet protection password not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8566](https://phpexcel.codeplex.com/workitem/8566) +- PHPExcel_Style_NumberFormat::FORMAT_NUMBER ignored by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8596](https://phpexcel.codeplex.com/workitem/8596) +- PHPExcel_Reader_Excel5 fails a whole when workbook contains a chart - @Erik Tilt [CodePlex #8781](https://phpexcel.codeplex.com/workitem/8781) +- Occasional loss of column widths using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8788](https://phpexcel.codeplex.com/workitem/8788) +- Notices while reading formulas with deleted sheet references using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8795](https://phpexcel.codeplex.com/workitem/8795) +- Default style not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #8807](https://phpexcel.codeplex.com/workitem/8807) +- Blank rows occupy too much space in file generated by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9341](https://phpexcel.codeplex.com/workitem/9341) + + +## [1.6.4] - 2008-10-27 + +### Features + +- RK record number error in MS developer documentation: 0x007E should be 0x027E - @Erik Tilt [CodePlex #7882](https://phpexcel.codeplex.com/workitem/7882) +- getHighestColumn() returning "@" for blank worksheet causes corrupt output - @MarkBaker [CodePlex #7878](https://phpexcel.codeplex.com/workitem/7878) +- Implement ROW and COLUMN Lookup/Reference Functions (when specified with a parameter) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Implement initial work on OFFSET Lookup/Reference Function (returning address rather than value at address) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Excel5 reader: Page margins - @Erik Tilt [CodePlex #7416](https://phpexcel.codeplex.com/workitem/7416) +- Excel5 reader: Header & Footer - @Erik Tilt [CodePlex #7417](https://phpexcel.codeplex.com/workitem/7417) +- Excel5 reader support for page setup (paper size etc.) - @Erik Tilt [CodePlex #7449](https://phpexcel.codeplex.com/workitem/7449) +- Improve speed and memory consumption of PHPExcel_Writer_CSV - @MarkBaker [CodePlex #7445](https://phpexcel.codeplex.com/workitem/7445) +- Better recognition of number format in HTML, CSV, and PDF writer - @MarkBaker [CodePlex #7432](https://phpexcel.codeplex.com/workitem/7432) +- Font support: Superscript and Subscript - @MarkBaker [CodePlex #7485](https://phpexcel.codeplex.com/workitem/7485) +- Excel5 reader font support: Super- and subscript - @Erik Tilt [CodePlex #7509](https://phpexcel.codeplex.com/workitem/7509) +- Excel5 reader style support: Text rotation and stacked text - @Erik Tilt [CodePlex #7521](https://phpexcel.codeplex.com/workitem/7521) +- Excel5 reader: Support for hyperlinks - @Erik Tilt [CodePlex #7530](https://phpexcel.codeplex.com/workitem/7530) +- Import sheet by request - @MB, ET [CodePlex #7557](https://phpexcel.codeplex.com/workitem/7557) +- PHPExcel_Reader_Excel5 support for page breaks - @Erik Tilt [CodePlex #7607](https://phpexcel.codeplex.com/workitem/7607) +- PHPExcel_Reader_Excel5 support for shrink-to-fit - @Erik Tilt [CodePlex #7622](https://phpexcel.codeplex.com/workitem/7622) +- Support for error types - @MB, ET [CodePlex #7675](https://phpexcel.codeplex.com/workitem/7675) +- Excel5 reader true formula support - @Erik Tilt [CodePlex #7388](https://phpexcel.codeplex.com/workitem/7388) +- Support for named ranges (defined names) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7701](https://phpexcel.codeplex.com/workitem/7701) +- Support for repeating rows and repeating columns (print titles) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7781](https://phpexcel.codeplex.com/workitem/7781) +- Support for print area in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7783](https://phpexcel.codeplex.com/workitem/7783) +- Excel5 reader and writer support for horizontal and vertical centering of page - @Erik Tilt [CodePlex #7795](https://phpexcel.codeplex.com/workitem/7795) +- Applied patch 1962 - @MarkBaker +- Excel5 reader and writer support for hidden cells (formulas) - @Erik Tilt [CodePlex #7866](https://phpexcel.codeplex.com/workitem/7866) +- Support for indentation in cells (feature request) - @MB, ET [CodePlex #7612](https://phpexcel.codeplex.com/workitem/7612) + +### Bugfixes + +- Option for reading only specified interval of rows in a sheet - @MB, ET [CodePlex #7828](https://phpexcel.codeplex.com/workitem/7828) +- PHPExcel_Calculation_Functions::DATETIMENOW() and PHPExcel_Calculation_Functions::DATENOW() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367) +- Modified PHPExcel_Shared_Date::FormattedPHPToExcel() and PHPExcel_Shared_Date::ExcelToPHP to force datatype for return values - @MarkBaker [CodePlex #7395](https://phpexcel.codeplex.com/workitem/7395) +- Excel5 reader not producing UTF-8 strings with BIFF5 files - @Erik Tilt [CodePlex #7450](https://phpexcel.codeplex.com/workitem/7450) +- Array constant in formula gives run-time notice with Excel2007 writer - @MarkBaker [CodePlex #7470](https://phpexcel.codeplex.com/workitem/7470) +- PHPExcel_Reader_Excel2007 setReadDataOnly(true) returns Rich-Text - @MarkBaker [CodePlex #7494](https://phpexcel.codeplex.com/workitem/7494) +- PHPExcel_Reader_Excel5 setReadDataOnly(true) returns Rich-Text - @Erik Tilt [CodePlex #7496](https://phpexcel.codeplex.com/workitem/7496) +- Characters before superscript or subscript losing style - @MarkBaker [CodePlex #7497](https://phpexcel.codeplex.com/workitem/7497) +- Subscript not working with HTML writer - @MarkBaker [CodePlex #7507](https://phpexcel.codeplex.com/workitem/7507) +- DefaultColumnDimension not working on first column (A) - @MarkBaker [CodePlex #7508](https://phpexcel.codeplex.com/workitem/7508) +- Negative numbers are stored as text in PHPExcel_Writer_2007 - @MarkBaker [CodePlex #7527](https://phpexcel.codeplex.com/workitem/7527) +- Text rotation and stacked text not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7531](https://phpexcel.codeplex.com/workitem/7531) +- PHPExcel_Shared_Date::isDateTimeFormatCode erroneously says true - @MarkBaker [CodePlex #7536](https://phpexcel.codeplex.com/workitem/7536) +- Different images with same filename in separate directories become duplicates - @MarkBaker [CodePlex #7559](https://phpexcel.codeplex.com/workitem/7559) +- PHPExcel_Reader_Excel5 not returning sheet names as UTF-8 using for Excel 95 files - @Erik Tilt [CodePlex #7568](https://phpexcel.codeplex.com/workitem/7568) +- setAutoSize(true) on empty column gives column width of 10 using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7575](https://phpexcel.codeplex.com/workitem/7575) +- setAutoSize(true) on empty column gives column width of 255 using PHPExcel_Writer_Excel5 - @MB, ET [CodePlex #7573](https://phpexcel.codeplex.com/workitem/7573) +- Worksheet_Drawing bug - @MarkBaker [CodePlex #7514](https://phpexcel.codeplex.com/workitem/7514) +- getCalculatedValue() with REPT function causes script to stop - @MarkBaker [CodePlex #7593](https://phpexcel.codeplex.com/workitem/7593) +- getCalculatedValue() with LEN function causes script to stop - @MarkBaker [CodePlex #7594](https://phpexcel.codeplex.com/workitem/7594) +- Explicit fit-to-width (page setup) results in fit-to-height becoming 1 - @MarkBaker [CodePlex #7600](https://phpexcel.codeplex.com/workitem/7600) +- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet - @MarkBaker [CodePlex #7610](https://phpexcel.codeplex.com/workitem/7610) +- Conditional styles not read properly using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7516](https://phpexcel.codeplex.com/workitem/7516) +- PHPExcel_Writer_2007: Default worksheet style works only for first sheet - @MarkBaker [CodePlex #7611](https://phpexcel.codeplex.com/workitem/7611) +- Cannot Lock Cells using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #6940](https://phpexcel.codeplex.com/workitem/6940) +- Incorrect cell protection values found when using Excel5 reader - @Erik Tilt [CodePlex #7621](https://phpexcel.codeplex.com/workitem/7621) +- Default row height not working above highest row using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7623](https://phpexcel.codeplex.com/workitem/7623) +- Default column width does not get applied when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7637](https://phpexcel.codeplex.com/workitem/7637) +- Broken support for UTF-8 string formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7642](https://phpexcel.codeplex.com/workitem/7642) +- UTF-8 sheet names not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7643](https://phpexcel.codeplex.com/workitem/7643) +- getCalculatedValue() with ISNONTEXT function causes script to stop - @MarkBaker [CodePlex #7631](https://phpexcel.codeplex.com/workitem/7631) +- Missing BIFF3 functions in PHPExcel_Writer_Excel5: USDOLLAR (YEN), FINDB, SEARCHB, REPLACEB, LEFTB, RIGHTB, MIDB, LENB, ASC, DBCS (JIS) - @Erik Tilt [CodePlex #7652](https://phpexcel.codeplex.com/workitem/7652) +- Excel5 reader doesn't read numbers correctly in 64-bit systems - @Erik Tilt [CodePlex #7663](https://phpexcel.codeplex.com/workitem/7663) +- Missing BIFF5 functions in PHPExcel_Writer_Excel5: ISPMT, DATEDIF, DATESTRING, NUMBERSTRING - @Erik Tilt [CodePlex #7667](https://phpexcel.codeplex.com/workitem/7667) +- Missing BIFF8 functions in PHPExcel_Writer_Excel5: GETPIVOTDATA, HYPERLINK, PHONETIC, AVERAGEA, MAXA, MINA, STDEVPA, VARPA, STDEVA, VARA - @Erik Tilt [CodePlex #7668](https://phpexcel.codeplex.com/workitem/7668) +- Wrong host value in PHPExcel_Shared_ZipStreamWrapper::stream_open() - @MarkBaker [CodePlex #7657](https://phpexcel.codeplex.com/workitem/7657) +- PHPExcel_Reader_Excel5 not reading explicitly entered error types in cells - @Erik Tilt [CodePlex #7676](https://phpexcel.codeplex.com/workitem/7676) +- Boolean and error data types not preserved for formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7678](https://phpexcel.codeplex.com/workitem/7678) +- PHPExcel_Reader_Excel2007 ignores cell data type - @MarkBaker [CodePlex #7695](https://phpexcel.codeplex.com/workitem/7695) +- PHPExcel_Reader_Excel5 ignores cell data type - @Erik Tilt [CodePlex #7712](https://phpexcel.codeplex.com/workitem/7712) +- PHPExcel_Writer_Excel5 not aware of data type - @Erik Tilt [CodePlex #7587](https://phpexcel.codeplex.com/workitem/7587) +- Long strings sometimes truncated when using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7713](https://phpexcel.codeplex.com/workitem/7713) +- Direct entry of boolean or error type in cell not supported by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7727](https://phpexcel.codeplex.com/workitem/7727) +- PHPExcel_Reader_Excel2007: Error reading cell with data type string, date number format, and numeric-like cell value - @MarkBaker [CodePlex #7714](https://phpexcel.codeplex.com/workitem/7714) +- Row and column outlines (group indent level) not showing after using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7735](https://phpexcel.codeplex.com/workitem/7735) +- Missing UTF-8 support in number format codes for PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7737](https://phpexcel.codeplex.com/workitem/7737) +- Missing UTF-8 support with PHPExcel_Writer_Excel5 for explicit string in formula - @Erik Tilt [CodePlex #7750](https://phpexcel.codeplex.com/workitem/7750) +- Problem with class constants in PHPExcel_Style_NumberFormat - @MarkBaker [CodePlex #7726](https://phpexcel.codeplex.com/workitem/7726) +- Sometimes errors with PHPExcel_Reader_Excel5 reading hyperlinks - @Erik Tilt [CodePlex #7758](https://phpexcel.codeplex.com/workitem/7758) +- Hyperlink in cell always results in string data type when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7759](https://phpexcel.codeplex.com/workitem/7759) +- Excel file with blank sheet seen as broken in MS Office Excel 2007 when created by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7771](https://phpexcel.codeplex.com/workitem/7771) +- PHPExcel_Reader_Excel5: Incorrect reading of formula with explicit string containing (escaped) double-quote - @Erik Tilt [CodePlex #7785](https://phpexcel.codeplex.com/workitem/7785) +- getCalculatedValue() fails on formula with sheet name containing (escaped) single-quote - @MarkBaker [CodePlex #7787](https://phpexcel.codeplex.com/workitem/7787) +- getCalculatedValue() fails on formula with explicit string containing (escaped) double-quote - @MarkBaker [CodePlex #7786](https://phpexcel.codeplex.com/workitem/7786) +- Problems with simultaneous repeatRowsAtTop and repeatColumnsAtLeft using Excel2007 reader and writer - @MarkBaker [CodePlex #7780](https://phpexcel.codeplex.com/workitem/7780) +- PHPExcel_Reader_Excel5: Error reading formulas with sheet reference containing special characters - @Erik Tilt [CodePlex #7802](https://phpexcel.codeplex.com/workitem/7802) +- Off-sheet references sheet!A1 not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7831](https://phpexcel.codeplex.com/workitem/7831) +- Repeating rows/columns (print titles), print area not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7834](https://phpexcel.codeplex.com/workitem/7834) +- Formula having datetime number format shows as text when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7849](https://phpexcel.codeplex.com/workitem/7849) +- Cannot set formula to hidden using applyFromArray() - @MarkBaker [CodePlex #7863](https://phpexcel.codeplex.com/workitem/7863) +- HTML/PDF Writers limited to 26 columns by calculateWorksheetDimension (erroneous comparison in getHighestColumn() method) - @MarkBaker [CodePlex #7805](https://phpexcel.codeplex.com/workitem/7805) +- Formula returning error type is lost when read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7873](https://phpexcel.codeplex.com/workitem/7873) +- PHPExcel_Reader_Excel5: Cell style lost for last column in group of blank cells - @Erik Tilt [CodePlex #7883](https://phpexcel.codeplex.com/workitem/7883) +- Column width sometimes collapses to auto size using Excel2007 reader/writer - @MarkBaker [CodePlex #7886](https://phpexcel.codeplex.com/workitem/7886) +- Data Validation Formula = 0 crashes Excel - @MarkBaker [CodePlex #9343](https://phpexcel.codeplex.com/workitem/9343) + + +## [1.6.3] - 2008-08-25 + +### General + +- Modified PHPExcel_Shared_Date::PHPToExcel() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367) +- Applied patch 1629 - @MarkBaker +- Applied patch 1644 - @MarkBaker +- Implement repeatRow and repeatColumn in Excel5 writer - @MarkBaker [CodePlex #6485](https://phpexcel.codeplex.com/workitem/6485) + +### Features + +- Remove scene3d filter in Excel2007 drawing - @MarkBaker [CodePlex #6838](https://phpexcel.codeplex.com/workitem/6838) +- Implement CHOOSE and INDEX Lookup/Reference Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Implement CLEAN Text Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Implement YEARFRAC Date/Time Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Implement 2 options for print/show gridlines - @MarkBaker [CodePlex #6508](https://phpexcel.codeplex.com/workitem/6508) +- Add VLOOKUP function (contribution) - @MarkBaker [CodePlex #7270](https://phpexcel.codeplex.com/workitem/7270) +- Implemented: ShrinkToFit - @MarkBaker [CodePlex #7182](https://phpexcel.codeplex.com/workitem/7182) +- Row heights not updated correctly when inserting new rows - @MarkBaker [CodePlex #7218](https://phpexcel.codeplex.com/workitem/7218) +- Copy worksheets within the same workbook - @MarkBaker [CodePlex #7157](https://phpexcel.codeplex.com/workitem/7157) +- Excel5 reader style support: horizontal and vertical alignment plus text wrap - @Erik Tilt [CodePlex #7290](https://phpexcel.codeplex.com/workitem/7290) +- Excel5 reader support for merged cells - @Erik Tilt [CodePlex #7294](https://phpexcel.codeplex.com/workitem/7294) +- Excel5 reader: Sheet Protection - @Erik Tilt [CodePlex #7296](https://phpexcel.codeplex.com/workitem/7296) +- Excel5 reader: Password for sheet protection - @Erik Tilt [CodePlex #7297](https://phpexcel.codeplex.com/workitem/7297) +- Excel5 reader: Column width - @Erik Tilt [CodePlex #7299](https://phpexcel.codeplex.com/workitem/7299) +- Excel5 reader: Row height - @Erik Tilt [CodePlex #7301](https://phpexcel.codeplex.com/workitem/7301) +- Excel5 reader: Font support - @Erik Tilt [CodePlex #7304](https://phpexcel.codeplex.com/workitem/7304) +- Excel5 reader: support for locked cells - @Erik Tilt [CodePlex #7324](https://phpexcel.codeplex.com/workitem/7324) +- Excel5 reader style support: Fill (background colors and patterns) - @Erik Tilt [CodePlex #7330](https://phpexcel.codeplex.com/workitem/7330) +- Excel5 reader style support: Borders (style and color) - @Erik Tilt [CodePlex #7332](https://phpexcel.codeplex.com/workitem/7332) +- Excel5 reader: Rich-Text support - @Erik Tilt [CodePlex #7346](https://phpexcel.codeplex.com/workitem/7346) +- Read Excel built-in number formats with Excel 2007 reader - @MarkBaker [CodePlex #7313](https://phpexcel.codeplex.com/workitem/7313) +- Excel5 reader: Number format support - @Erik Tilt [CodePlex #7317](https://phpexcel.codeplex.com/workitem/7317) +- Creating a copy of PHPExcel object - @MarkBaker [CodePlex #7362](https://phpexcel.codeplex.com/workitem/7362) +- Excel5 reader: support for row / column outline (group) - @Erik Tilt [CodePlex #7373](https://phpexcel.codeplex.com/workitem/7373) +- Implement default row/column sizes - @MarkBaker [CodePlex #7380](https://phpexcel.codeplex.com/workitem/7380) +- Writer HTML - option to return styles and table separately - @MarkBaker [CodePlex #7364](https://phpexcel.codeplex.com/workitem/7364) + +### Bugfixes + +- Excel5 reader: Support for remaining built-in number formats - @Erik Tilt [CodePlex #7393](https://phpexcel.codeplex.com/workitem/7393) +- Fixed rounding in HOUR MINUTE and SECOND Time functions, and improved performance for these - @MarkBaker +- Fix to TRIM function - @MarkBaker +- Fixed range validation in TIME Functions.php - @MarkBaker +- EDATE and EOMONTH functions now return date values based on the returnDateType flag - @MarkBaker +- Write date values that are the result of a calculation function correctly as Excel serialized dates rather than PHP serialized date values - @MarkBaker +- Excel2007 reader not always reading boolean correctly - @MarkBaker [CodePlex #6690](https://phpexcel.codeplex.com/workitem/6690) +- Columns above IZ - @MarkBaker [CodePlex #6275](https://phpexcel.codeplex.com/workitem/6275) +- Other locale than English causes Excel2007 writer to produce broken xlsx - @MarkBaker [CodePlex #6853](https://phpexcel.codeplex.com/workitem/6853) +- Typo: Number_fromat in NumberFormat.php - @MarkBaker [CodePlex #7061](https://phpexcel.codeplex.com/workitem/7061) +- Bug in Worksheet_BaseDrawing setWidth() - @MarkBaker [CodePlex #6865](https://phpexcel.codeplex.com/workitem/6865) +- PDF writer collapses column width for merged cells - @MarkBaker [CodePlex #6891](https://phpexcel.codeplex.com/workitem/6891) +- Issues with drawings filenames - @MarkBaker [CodePlex #6867](https://phpexcel.codeplex.com/workitem/6867) +- fromArray() local variable isn't defined - @MarkBaker [CodePlex #7073](https://phpexcel.codeplex.com/workitem/7073) +- PHPExcel_Writer_Excel5->setTempDir() not passed to all classes involved in writing to a file - @MarkBaker [CodePlex #7276](https://phpexcel.codeplex.com/workitem/7276) +- Excel5 reader not handling UTF-8 properly - @MarkBaker [CodePlex #7277](https://phpexcel.codeplex.com/workitem/7277) +- If you write a 0 value in cell, cell shows as empty - @MarkBaker [CodePlex #7327](https://phpexcel.codeplex.com/workitem/7327) +- Excel2007 writer: Row height ignored for empty rows - @MarkBaker [CodePlex #7302](https://phpexcel.codeplex.com/workitem/7302) +- Excel2007 (comments related error) - @MarkBaker [CodePlex #7281](https://phpexcel.codeplex.com/workitem/7281) +- Column width in other locale - @MarkBaker [CodePlex #7345](https://phpexcel.codeplex.com/workitem/7345) +- Excel2007 reader not reading underlined Rich-Text - @MarkBaker [CodePlex #7347](https://phpexcel.codeplex.com/workitem/7347) +- Excel5 reader converting booleans to strings - @Erik Tilt [CodePlex #7357](https://phpexcel.codeplex.com/workitem/7357) +- Recursive Object Memory Leak - @MarkBaker [CodePlex #7365](https://phpexcel.codeplex.com/workitem/7365) +- Excel2007 writer ignoring row dimensions without cells - @MarkBaker [CodePlex #7372](https://phpexcel.codeplex.com/workitem/7372) +- Excel5 reader is converting formatted numbers / dates to strings - @Erik Tilt [CodePlex #7382](https://phpexcel.codeplex.com/workitem/7382) + + +## [1.6.2] - 2008-06-23 + +### General + +- Document style array values - @MarkBaker [CodePlex #6088](https://phpexcel.codeplex.com/workitem/6088) +- Applied patch 1195 - @MarkBaker +- Redirecting output to a client’s web browser - http headers - @MarkBaker [CodePlex #6178](https://phpexcel.codeplex.com/workitem/6178) +- Improve worksheet garbage collection - @MarkBaker [CodePlex #6187](https://phpexcel.codeplex.com/workitem/6187) +- Functions that return date values can now be configured to return as Excel serialized date/time, PHP serialized date/time, or a PHP date/time object. - @MarkBaker +- Functions that explicitly accept dates as parameters now permit values as Excel serialized date/time, PHP serialized date/time, a valid date string, or a PHP date/time object. - @MarkBaker +- Implement ACOSH, ASINH and ATANH functions for those operating platforms/PHP versions that don't include these functions - @MarkBaker +- Implement ATAN2 logic reversing the arguments as per Excel - @MarkBaker +- Additional validation of parameters for COMBIN - @MarkBaker + +### Features + +- Fixed validation for CEILING and FLOOR when the value and significance parameters have different signs; and allowed default value of 1 or -1 for significance when in GNUMERIC compatibility mode - @MarkBaker +- Implement ADDRESS, ISLOGICAL, ISTEXT and ISNONTEXT functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Implement COMPLEX, IMAGINARY, IMREAL, IMARGUMENT, IMCONJUGATE, IMABS, IMSUB, IMDIV, IMSUM, IMPRODUCT, IMSQRT, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER IMCOS and IMSIN Engineering functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Implement NETWORKDAYS and WORKDAY Date/Time functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) +- Make cell column AAA available - @MarkBaker [CodePlex #6100](https://phpexcel.codeplex.com/workitem/6100) +- Mark particular cell as selected when opening Excel - @MarkBaker [CodePlex #6095](https://phpexcel.codeplex.com/workitem/6095) +- Multiple sheets in PDF and HTML - @MarkBaker [CodePlex #6120](https://phpexcel.codeplex.com/workitem/6120) +- Implement PHPExcel_ReaderFactory and PHPExcel_WriterFactory - @MarkBaker [CodePlex #6227](https://phpexcel.codeplex.com/workitem/6227) +- Set image root of PHPExcel_Writer_HTML - @MarkBaker [CodePlex #6249](https://phpexcel.codeplex.com/workitem/6249) +- Enable/disable calculation cache - @MarkBaker [CodePlex #6264](https://phpexcel.codeplex.com/workitem/6264) +- PDF writer and multi-line text - @MarkBaker [CodePlex #6259](https://phpexcel.codeplex.com/workitem/6259) +- Feature request - setCacheExpirationTime() - @MarkBaker [CodePlex #6350](https://phpexcel.codeplex.com/workitem/6350) +- Implement late-binding mechanisms to reduce memory footprint - @JB [CodePlex #6370](https://phpexcel.codeplex.com/workitem/6370) +- Implement shared styles - @JB [CodePlex #6430](https://phpexcel.codeplex.com/workitem/6430) +- Copy sheet from external Workbook to active Workbook - @MarkBaker [CodePlex #6391](https://phpexcel.codeplex.com/workitem/6391) + +### Bugfixes + +- Functions in Conditional Formatting - @MarkBaker [CodePlex #6428](https://phpexcel.codeplex.com/workitem/6428) +- Default Style in Excel5 - @MarkBaker [CodePlex #6096](https://phpexcel.codeplex.com/workitem/6096) +- Numbers starting with '+' cause Excel 2007 errors - @MarkBaker [CodePlex #6150](https://phpexcel.codeplex.com/workitem/6150) +- ExcelWriter5 is not PHP5 compatible, using it with E_STRICT results in a bunch of errors (applied patches) - @MarkBaker [CodePlex #6092](https://phpexcel.codeplex.com/workitem/6092) +- Error Reader Excel2007 line 653 foreach ($relsDrawing->Relationship as $ele) - @MarkBaker [CodePlex #6179](https://phpexcel.codeplex.com/workitem/6179) +- Worksheet toArray() screws up DATE - @MarkBaker [CodePlex #6229](https://phpexcel.codeplex.com/workitem/6229) +- References to a Richtext cell in a formula - @MarkBaker [CodePlex #6253](https://phpexcel.codeplex.com/workitem/6253) +- insertNewColumnBefore Bug - @MarkBaker [CodePlex #6285](https://phpexcel.codeplex.com/workitem/6285) +- Error reading Excel2007 file with shapes - @MarkBaker [CodePlex #6319](https://phpexcel.codeplex.com/workitem/6319) +- Determine whether date values need conversion from PHP dates to Excel dates before writing to file, based on the data type (float or integer) - @MarkBaker [CodePlex #6302](https://phpexcel.codeplex.com/workitem/6302) +- Fixes to DATE function when it is given negative input parameters - @MarkBaker +- PHPExcel handles empty cells other than Excel - @MarkBaker [CodePlex #6347](https://phpexcel.codeplex.com/workitem/6347) +- PHPExcel handles 0 and "" as being the same - @MarkBaker [CodePlex #6348](https://phpexcel.codeplex.com/workitem/6348) +- Problem Using Excel2007 Reader for Spreadsheets containing images - @MarkBaker [CodePlex #6357](https://phpexcel.codeplex.com/workitem/6357) +- ShowGridLines ignored when reading/writing Excel 2007 - @MarkBaker [CodePlex #6359](https://phpexcel.codeplex.com/workitem/6359) +- Bug With Word Wrap in Excel 2007 Reader - @MarkBaker [CodePlex #6426](https://phpexcel.codeplex.com/workitem/6426) + + +## [1.6.1] - 2008-04-28 + +### General + +- Fix documentation printing - @MarkBaker [CodePlex #5532](https://phpexcel.codeplex.com/workitem/5532) +- Memory usage improvements - @MarkBaker [CodePlex #5586](https://phpexcel.codeplex.com/workitem/5586) +- Applied patch 990 - @MarkBaker + +### Features + +- Applied patch 991 - @MarkBaker +- Implement PHPExcel_Reader_Excel5 - @BM [CodePlex #2841](https://phpexcel.codeplex.com/workitem/2841) +- Implement "toArray" and "fromArray" method - @MarkBaker [CodePlex #5564](https://phpexcel.codeplex.com/workitem/5564) +- Read shared formula - @MarkBaker [CodePlex #5665](https://phpexcel.codeplex.com/workitem/5665) +- Read image twoCellAnchor - @MarkBaker [CodePlex #5681](https://phpexcel.codeplex.com/workitem/5681) +- &G Image as bg for headerfooter - @MarkBaker [CodePlex #4446](https://phpexcel.codeplex.com/workitem/4446) +- Implement page layout functionality for Excel5 format - @MarkBaker [CodePlex #5834](https://phpexcel.codeplex.com/workitem/5834) + +### Bugfixes + +- Feature request: PHPExcel_Writer_PDF - @MarkBaker [CodePlex #6039](https://phpexcel.codeplex.com/workitem/6039) +- DefinedNames null check - @MarkBaker [CodePlex #5517](https://phpexcel.codeplex.com/workitem/5517) +- Hyperlinks should not always have trailing slash - @MarkBaker [CodePlex #5463](https://phpexcel.codeplex.com/workitem/5463) +- Saving Error - Uncaught exception (#REF! named range) - @MarkBaker [CodePlex #5592](https://phpexcel.codeplex.com/workitem/5592) +- Error when creating Zip file on Linux System (Not Windows) - @MarkBaker [CodePlex #5634](https://phpexcel.codeplex.com/workitem/5634) +- Time incorrecly formated - @MarkBaker [CodePlex #5876](https://phpexcel.codeplex.com/workitem/5876) +- Conditional formatting - second rule not applied - @MarkBaker [CodePlex #5914](https://phpexcel.codeplex.com/workitem/5914) +- PHPExcel_Reader_Excel2007 cannot load PHPExcel_Shared_File - @MarkBaker [CodePlex #5978](https://phpexcel.codeplex.com/workitem/5978) +- Output redirection to web browser - @MarkBaker [CodePlex #6020](https://phpexcel.codeplex.com/workitem/6020) + + +## [1.6.0] - 2008-02-14 + +### Features + +- Use PHPExcel datatypes in formula calculation - @MarkBaker [CodePlex #3156](https://phpexcel.codeplex.com/workitem/3156) +- Center on page when printing - @MarkBaker [CodePlex #5019](https://phpexcel.codeplex.com/workitem/5019) +- Hyperlink to other spreadsheet - @MarkBaker [CodePlex #5099](https://phpexcel.codeplex.com/workitem/5099) +- Set the print area of a worksheet - @MarkBaker [CodePlex #5104](https://phpexcel.codeplex.com/workitem/5104) +- Read "definedNames" property of worksheet - @MarkBaker [CodePlex #5118](https://phpexcel.codeplex.com/workitem/5118) +- Set default style for all cells - @MarkBaker [CodePlex #5338](https://phpexcel.codeplex.com/workitem/5338) +- Named Ranges - @MarkBaker [CodePlex #4216](https://phpexcel.codeplex.com/workitem/4216) + +### Bugfixes + +- Implement worksheet references (Sheet1!A1) - @MarkBaker [CodePlex #5398](https://phpexcel.codeplex.com/workitem/5398) +- Redirect output to a client's web browser - @MarkBaker [CodePlex #4967](https://phpexcel.codeplex.com/workitem/4967) +- "File Error: data may have been lost." seen in Excel 2007 and Excel 2003 SP3 when opening XLS file - @MarkBaker [CodePlex #5008](https://phpexcel.codeplex.com/workitem/5008) +- Bug in style's getHashCode() - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165) +- PHPExcel_Reader not correctly reading numeric values - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165) +- Text rotation is read incorrectly - @MarkBaker [CodePlex #5324](https://phpexcel.codeplex.com/workitem/5324) +- Enclosure " and data " result a bad data : \" instead of "" - @MarkBaker [CodePlex #5326](https://phpexcel.codeplex.com/workitem/5326) +- Formula parser - IF statement returning array instead of scalar - @MarkBaker [CodePlex #5332](https://phpexcel.codeplex.com/workitem/5332) +- setFitToWidth(nbpage) & setFitToWidth(nbpage) work partially - @MarkBaker [CodePlex #5351](https://phpexcel.codeplex.com/workitem/5351) +- Worksheet::setTitle() causes unwanted renaming - @MarkBaker [CodePlex #5361](https://phpexcel.codeplex.com/workitem/5361) +- Hyperlinks not working. Results in broken xlsx file. - @MarkBaker [CodePlex #5407](https://phpexcel.codeplex.com/workitem/5407) + + +## [1.5.5] - 2007-12-24 + +### General + +- Grouping Rows - @MarkBaker [CodePlex #4135](https://phpexcel.codeplex.com/workitem/4135) + +### Features + +- Semi-nightly builds - @MarkBaker [CodePlex #4427](https://phpexcel.codeplex.com/workitem/4427) +- Implement "date" datatype - @MarkBaker [CodePlex #3155](https://phpexcel.codeplex.com/workitem/3155) +- Date format not honored in CSV writer - @MarkBaker [CodePlex #4150](https://phpexcel.codeplex.com/workitem/4150) +- RichText and sharedStrings - @MarkBaker [CodePlex #4199](https://phpexcel.codeplex.com/workitem/4199) +- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) + - Addition of DATE, DATEDIF, DATEVALUE, DAY, DAYS360- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) + - Addition of AVEDEV, HARMEAN and GEOMEAN + - Addition of the BINOMDIST (Non-cumulative only), COUNTBLANK, EXPONDIST, FISHER, FISHERINV, NORMDIST, NORMSDIST, PERMUT, POISSON (Non-cumulative only) and STANDARDIZE Statistical Functions + - Addition of the CEILING, COMBIN, EVEN, FACT, FACTDOUBLE, FLOOR, MULTINOMIAL, ODD, ROUNDDOWN, ROUNDUP, SIGN, SQRTPI and SUMSQ Mathematical Functions + - Addition of the NORMINV, NORMSINV, CONFIDENCE and SKEW Statistical Functions + - Addition of the CRITBINOM, HYPGEOMDIST, KURT, LOGINV, LOGNORMDIST, NEGBINOMDIST and WEIBULL Statistical Functions + - Addition of the LARGE, PERCENTILE, QUARTILE, SMALL and TRIMMEAN Statistical Functions + - Addition of the BIN2HEX, BIN2OCT, DELTA, ERF, ERFC, GESTEP, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN and OCT2HEX Engineering Functions + - Addition of the CHIDIST, GAMMADIST and GAMMALN Statistical Functions + - Addition of the GCD, LCM, MROUND and SUBTOTAL Mathematical Functions + - Addition of the LOWER, PROPER and UPPER Text Functions + - Addition of the BETADIST and BETAINV Statistical Functions + - Addition of the CHIINV and GAMMAINV Statistical Functions + - Addition of the SERIESSUM Mathematical Function + - Addition of the CHAR, CODE, FIND, LEN, REPT, SEARCH, T, TRIM Text Functions + - Addition of the FALSE and TRUE Boolean Functions + - Addition of the TDIST and TINV Statistical Functions + - Addition of the EDATE, EOMONTH, YEAR, MONTH, TIME, TIMEVALUE, HOUR, MINUTE, SECOND, WEEKDAY, WEEKNUM, NOW, TODAY and Date/Time Function + - Addition of the BESSELI, BESSELJ, BESSELK and BESSELY Engineering Functions + - Addition of the SLN and SYD Financial Functions + - reworked MODE calculation to handle floating point numbers + - Improved error trapping for invalid input values + - Fix to SMALL, LARGE, PERCENTILE and TRIMMEAN to eliminate non-numeric values + - Added CDF to BINOMDIST and POISSON + - Fix to a potential endless loop in CRITBINOM, together with other bugfixes to the algorithm + - Fix to SQRTPI so that it will work with a real value parameter rather than just integers + - Trap for passing negative values to FACT + - Improved accuracy of the NORMDIST cumulative function, and of the ERF and ERFC functions + - Replicated Excel data-type and error handling for BIN, DEC, OCT and HEX conversion functions + - Replicated Excel data-type and error handling for AND and OR Boolean functions + - Bugfix to MROUND + - Rework of the DATE, DATEVALUE, DAY, DAYS360 and DATEDIF date/Time functions to use Excel dates rather than straight PHP dates + - Rework of the AND, OR Boolean functions to ignore string values + - Rework of the BIN2DEC, BIN2HEX, BIN2OCT, DEC2BIN, DEC2HEX, DEC2OCT Engineering functions to handle two's complement + - Excel, Gnumeric and OpenOffice Calc compatibility flag for functions + - Note, not all functions have yet been written to work with the Gnumeric and OpenOffice Calc compatibility flags + - 1900 or 1904 Calendar flag for date functions + - Reworked ExcelToPHP date method to handle the Excel 1900 leap year + - Note that this will not correctly return values prior to 13-Dec-1901 20:45:52 as this is the minimum value that PHP date serial values can handle. If you need to work with dates prior to this, then an ExcelToPHPObject method has been added which will work correctly with values between Excel's 1900 calendar base date of 1-Jan-1900, and 13-Dec-1901 + - Addition of ExcelToPHPObject date method to return a PHP DateTime object from an Excel date serial value + - PHPToExcel method modified to accept either PHP date serial numbers or PHP DateTime objects + - Addition of FormattedPHPToExcel which will accept a date and time broken to into year, month, day, hour, minute, second and return an Excel date serial value- Control characters in Excel 2007 - @MarkBaker [CodePlex #4485](https://phpexcel.codeplex.com/workitem/4485) +- BaseDrawing::setWidthAndHeight method request - @MarkBaker [CodePlex #4796](https://phpexcel.codeplex.com/workitem/4796) +- Page Setup -> Print Titles -> Sheet -> 'Rows to repeat at top' - @MarkBaker [CodePlex #4798](https://phpexcel.codeplex.com/workitem/4798) + +### Bugfixes + +- Comment functionality - @MarkBaker [CodePlex #4433](https://phpexcel.codeplex.com/workitem/4433) +- Undefined variable in PHPExcel_Writer_Serialized - @MarkBaker [CodePlex #4124](https://phpexcel.codeplex.com/workitem/4124) +- Notice: Object of class PHPExcel_RichText could not be converted to int - @MarkBaker [CodePlex #4125](https://phpexcel.codeplex.com/workitem/4125) +- Excel5Writer: utf8 string not converted to utf16 - @MarkBaker [CodePlex #4126](https://phpexcel.codeplex.com/workitem/4126) +- PHPExcel_RichText and autosize - @MarkBaker [CodePlex #4180](https://phpexcel.codeplex.com/workitem/4180) +- Excel5Writer produces broken xls files after change mentioned in work item 4126 - @MarkBaker [CodePlex #4574](https://phpexcel.codeplex.com/workitem/4574) +- Small bug in PHPExcel_Reader_Excel2007 function _readStyle - @MarkBaker [CodePlex #4797](https://phpexcel.codeplex.com/workitem/4797) + + +## [1.5.0] - 2007-10-23 + +### Features + +- Refactor PHPExcel Drawing - @MarkBaker [CodePlex #3265](https://phpexcel.codeplex.com/workitem/3265) +- Update Shared/OLE.php to latest version from PEAR - @CS [CodePlex #3079](https://phpexcel.codeplex.com/workitem/3079) +- Excel2007 vs Excel2003 compatibility pack - @MarkBaker [CodePlex #3217](https://phpexcel.codeplex.com/workitem/3217) +- Cell protection (lock/unlock) - @MarkBaker [CodePlex #3234](https://phpexcel.codeplex.com/workitem/3234) +- Create clickable links (hyperlinks) - @MarkBaker [CodePlex #3543](https://phpexcel.codeplex.com/workitem/3543) +- Additional page setup parameters - @MarkBaker [CodePlex #3241](https://phpexcel.codeplex.com/workitem/3241) +- Make temporary file path configurable (Excel5) - @MarkBaker [CodePlex #3300](https://phpexcel.codeplex.com/workitem/3300) +- Small addition to applyFromArray for font - @MarkBaker [CodePlex #3306](https://phpexcel.codeplex.com/workitem/3306) + +### Bugfixes + +- Better feedback when save of file is not possible - @MarkBaker [CodePlex #3373](https://phpexcel.codeplex.com/workitem/3373) +- Text Rotation - @MarkBaker [CodePlex #3181](https://phpexcel.codeplex.com/workitem/3181) +- Small bug in Page Orientation - @MarkBaker [CodePlex #3237](https://phpexcel.codeplex.com/workitem/3237) +- insertNewColumnBeforeByColumn undefined - @MarkBaker [CodePlex #3812](https://phpexcel.codeplex.com/workitem/3812) +- Sheet references not working in formula (Excel5 Writer) - @MarkBaker [CodePlex #3893](https://phpexcel.codeplex.com/workitem/3893) + + +## [1.4.5] - 2007-08-23 + +### General + +- Class file endings - @MarkBaker [CodePlex #3003](https://phpexcel.codeplex.com/workitem/3003) +- Different calculation engine improvements - @MarkBaker [CodePlex #3081](https://phpexcel.codeplex.com/workitem/3081) +- Different improvements in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #3082](https://phpexcel.codeplex.com/workitem/3082) + +### Features + +- Set XML indentation in PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #3146](https://phpexcel.codeplex.com/workitem/3146) +- Optionally store temporary Excel2007 writer data in file instead of memory - @MarkBaker [CodePlex #3159](https://phpexcel.codeplex.com/workitem/3159) +- Implement show/hide gridlines - @MarkBaker [CodePlex #3063](https://phpexcel.codeplex.com/workitem/3063) +- Implement option to read only data - @MarkBaker [CodePlex #3064](https://phpexcel.codeplex.com/workitem/3064) +- Optionally disable formula precalculation - @MarkBaker [CodePlex #3080](https://phpexcel.codeplex.com/workitem/3080) +- Explicitly set cell datatype - @MarkBaker [CodePlex #3154](https://phpexcel.codeplex.com/workitem/3154) + +### Bugfixes + +- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) + - Addition of MINA, MAXA, COUNTA, AVERAGEA, MEDIAN, MODE, DEVSQ, STDEV, STDEVA, STDEVP, STDEVPA, VAR, VARA, VARP and VARPA Excel Functions + - Fix to SUM, PRODUCT, QUOTIENT, MIN, MAX, COUNT and AVERAGE functions when cell contains a numeric value in a string datatype, bringing it in line with MS Excel behaviour- File_exists on ZIP fails on some installations - @MarkBaker [CodePlex #2881](https://phpexcel.codeplex.com/workitem/2881) +- Argument in textRotation should be -90..90 - @MarkBaker [CodePlex #2879](https://phpexcel.codeplex.com/workitem/2879) +- Excel2007 reader/writer not implementing OpenXML/SpreadsheetML styles 100% correct - @MarkBaker [CodePlex #2883](https://phpexcel.codeplex.com/workitem/2883) +- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513) +- Print and print preview of generated XLSX causes Excel2007 to crash - @MarkBaker [CodePlex #2935](https://phpexcel.codeplex.com/workitem/2935) +- Error in Calculations - COUNT() function - @MarkBaker [CodePlex #2952](https://phpexcel.codeplex.com/workitem/2952) +- HTML and CSV writer not writing last row - @MarkBaker [CodePlex #3002](https://phpexcel.codeplex.com/workitem/3002) +- Memory leak in Excel5 writer - @MarkBaker [CodePlex #3017](https://phpexcel.codeplex.com/workitem/3017) +- Printing (PHPExcel_Writer_Excel5) - @MarkBaker [CodePlex #3044](https://phpexcel.codeplex.com/workitem/3044) +- Problems reading zip:// - @MarkBaker [CodePlex #3046](https://phpexcel.codeplex.com/workitem/3046) +- Error reading conditional formatting - @MarkBaker [CodePlex #3047](https://phpexcel.codeplex.com/workitem/3047) +- Bug in Excel5 writer (storePanes) - @MarkBaker [CodePlex #3067](https://phpexcel.codeplex.com/workitem/3067) +- Memory leak in PHPExcel_Style_Color - @MarkBaker [CodePlex #3077](https://phpexcel.codeplex.com/workitem/3077) + + +## [1.4.0] - 2007-07-23 + +### General + +- Coding convention / code cleanup - @MarkBaker [CodePlex #2687](https://phpexcel.codeplex.com/workitem/2687) +- Use set_include_path in tests - @MarkBaker [CodePlex #2717](https://phpexcel.codeplex.com/workitem/2717) + +### Features + +- Move PHPExcel_Writer_Excel5 OLE to PHPExcel_Shared_OLE - @MarkBaker [CodePlex #2812](https://phpexcel.codeplex.com/workitem/2812) +- Hide/Unhide Column or Row - @MarkBaker [CodePlex #2679](https://phpexcel.codeplex.com/workitem/2679) +- Implement multi-cell styling - @MarkBaker [CodePlex #2271](https://phpexcel.codeplex.com/workitem/2271) +- Implement CSV file format (reader/writer) - @MarkBaker [CodePlex #2720](https://phpexcel.codeplex.com/workitem/2720) + +### Bugfixes + +- Implement HTML file format - @MarkBaker [CodePlex #2845](https://phpexcel.codeplex.com/workitem/2845) +- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513) +- Freeze Panes with PHPExcel_Writer_Excel5 - @MarkBaker [CodePlex #2678](https://phpexcel.codeplex.com/workitem/2678) +- OLE.php - @MarkBaker [CodePlex #2680](https://phpexcel.codeplex.com/workitem/2680) +- Copy and pasting multiple drop-down list cells breaks reader - @MarkBaker [CodePlex #2736](https://phpexcel.codeplex.com/workitem/2736) +- Function setAutoFilterByColumnAndRow takes wrong arguments - @MarkBaker [CodePlex #2775](https://phpexcel.codeplex.com/workitem/2775) +- Simplexml_load_file fails on ZipArchive - @MarkBaker [CodePlex #2858](https://phpexcel.codeplex.com/workitem/2858) + + +## [1.3.5] - 2007-06-27 + +### Features + +- Documentation - @MarkBaker [CodePlex #15](https://phpexcel.codeplex.com/workitem/15) +- PHPExcel_Writer_Excel5 - @JV +- PHPExcel_Reader_Excel2007: Image shadows - @JV +- Data validation - @MarkBaker [CodePlex #2385](https://phpexcel.codeplex.com/workitem/2385) + +### Bugfixes + +- Implement richtext strings - @MarkBaker +- Empty relations when adding image to any sheet but the first one - @MarkBaker [CodePlex #2443](https://phpexcel.codeplex.com/workitem/2443) +- Excel2007 crashes on print preview - @MarkBaker [CodePlex #2536](https://phpexcel.codeplex.com/workitem/2536) + + +## [1.3.0] - 2007-06-05 + +### General + +- Create PEAR package - @MarkBaker [CodePlex #1942](https://phpexcel.codeplex.com/workitem/1942) + +### Features + +- Replace *->duplicate() by __clone() - @MarkBaker [CodePlex #2331](https://phpexcel.codeplex.com/workitem/2331) +- PHPExcel_Reader_Excel2007: Column auto-size, Protection, Merged cells, Wrap text, Page breaks, Auto filter, Images - @JV +- Implement "freezing" panes - @MarkBaker [CodePlex #245](https://phpexcel.codeplex.com/workitem/245) +- Cell addressing alternative - @MarkBaker [CodePlex #2273](https://phpexcel.codeplex.com/workitem/2273) +- Implement cell word-wrap attribute - @MarkBaker [CodePlex #2270](https://phpexcel.codeplex.com/workitem/2270) +- Auto-size column - @MarkBaker [CodePlex #2282](https://phpexcel.codeplex.com/workitem/2282) +- Implement formula calculation - @MarkBaker [CodePlex #241](https://phpexcel.codeplex.com/workitem/241) + +### Bugfixes + +- Insert/remove row/column - @MarkBaker [CodePlex #2375](https://phpexcel.codeplex.com/workitem/2375) +- PHPExcel_Worksheet::getCell() should not accept absolute coordinates - @MarkBaker [CodePlex #1931](https://phpexcel.codeplex.com/workitem/1931) +- Cell reference without row number - @MarkBaker [CodePlex #2272](https://phpexcel.codeplex.com/workitem/2272) +- Styles with same coordinate but different worksheet - @MarkBaker [CodePlex #2276](https://phpexcel.codeplex.com/workitem/2276) +- PHPExcel_Worksheet->getCellCollection() usort error - @MarkBaker [CodePlex #2290](https://phpexcel.codeplex.com/workitem/2290) +- Bug in PHPExcel_Cell::stringFromColumnIndex - @SS [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353) +- Reader: numFmts can be missing, use cellStyleXfs instead of cellXfs in styles - @JV [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353) + + +## [1.2.0] - 2007-04-26 + +### General + +- Stringtable attribute "count" not necessary, provides wrong info to Excel sometimes... - @MarkBaker +- Updated tests to address more document properties - @MarkBaker +- Some refactoring in PHPExcel_Writer_Excel2007_Workbook - @MarkBaker +- New package: PHPExcel_Shared - @MarkBaker +- Password hashing algorithm implemented in PHPExcel_Shared_PasswordHasher - @MarkBaker +- Moved pixel conversion functions to PHPExcel_Shared_Drawing - @MarkBaker +- Switch over to LGPL license - @MarkBaker [CodePlex #244](https://phpexcel.codeplex.com/workitem/244) + +### Features + +- Include PHPExcel version in file headers - @MarkBaker [CodePlex #5](https://phpexcel.codeplex.com/workitem/5) +- Autofilter - @MarkBaker [CodePlex #6](https://phpexcel.codeplex.com/workitem/6) +- Extra document property: keywords - @MarkBaker [CodePlex #7](https://phpexcel.codeplex.com/workitem/7) +- Extra document property: category - @MarkBaker [CodePlex #8](https://phpexcel.codeplex.com/workitem/8) +- Document security - @MarkBaker [CodePlex #9](https://phpexcel.codeplex.com/workitem/9) +- PHPExcel_Writer_Serialized and PHPExcel_Reader_Serialized - @MarkBaker [CodePlex #10](https://phpexcel.codeplex.com/workitem/10) +- Alternative syntax: Addressing a cell - @MarkBaker [CodePlex #11](https://phpexcel.codeplex.com/workitem/11) +- Merge cells - @MarkBaker [CodePlex #12](https://phpexcel.codeplex.com/workitem/12) + +### Bugfixes + +- Protect ranges of cells with a password - @MarkBaker [CodePlex #13](https://phpexcel.codeplex.com/workitem/13) +- (style/fill/patternFill/fgColor or bgColor can be empty) - @JV [CodePlex #14](https://phpexcel.codeplex.com/workitem/14) + + +## [1.1.1] - 2007-03-26 + +### General + +- Syntax error in "Classes/PHPExcel/Writer/Excel2007.php" on line 243 - @MarkBaker [CodePlex #1250](https://phpexcel.codeplex.com/workitem/1250) +- Reader should check if file exists and throws an exception when it doesn't - @MarkBaker [CodePlex #1282](https://phpexcel.codeplex.com/workitem/1282) + + +## [1.1.0] - 2007-03-22 + +### Bugfixes + +- Style information lost after passing trough Excel2007_Reader - @MarkBaker [CodePlex #836](https://phpexcel.codeplex.com/workitem/836) + +### General + +- Number of columns > AZ fails fixed in PHPExcel_Cell::columnIndexFromString - @MarkBaker [CodePlex #913](https://phpexcel.codeplex.com/workitem/913) + +### Features + +- Added a brief file with installation instructions - @MarkBaker +- Page breaks (horizontal and vertical) - @MarkBaker +- Image shadows - @MarkBaker + + +## [1.0.0] - 2007-02-22 + +### Bugfixes + +- PHPExcel->removeSheetByIndex now re-orders sheets after deletion, so no array indexes are lost - @JV +- PHPExcel_Writer_Excel2007_Worksheet::_writeCols() used direct assignment to $pSheet->getColumnDimension('A')->Width instead of $pSheet->getColumnDimension('A')->setWidth() - @JV +- DocumentProperties used $this->LastModifiedBy instead of $this->_lastModifiedBy. - @JV + +### General + +- Only first = should be removed when writing formula in PHPExcel_Writer_Excel2007_Worksheet. - @JV +- Consistency of method names to camelCase - @JV +- Updated tests to match consistency changes - @JV +- Detection of mime-types now with image_type_to_mime_type() - @JV +- Constants now hold string value used in Excel 2007 - @JV + +### Features + +- Fixed folder name case (WorkSheet -> Worksheet) - @MarkBaker +- PHPExcel classes (not the Writer classes) can be duplicated, using a duplicate() method. - @MarkBaker +- Cell styles can now be duplicated to a range of cells using PHPExcel_Worksheet->duplicateStyle() - @MarkBaker +- Conditional formatting - @MarkBaker +- Reader for Excel 2007 (not supporting full specification yet!) - @JV + + +## [1.0.0 RC] - 2007-01-31 + +- Project name has been changed to PHPExcel +- Project homepage is now http://www.codeplex.com/PHPExcel +- Started versioning at number: PHPExcel 1.0.0 RC + + +## 2007-01-22 + +- Fixed some performance issues on large-scale worksheets (mainly loops vs. indexed arrays) +- Performance on creating StringTable has been increased +- Performance on writing Excel2007 worksheet has been increased + + +## 2007-01-18 + +- Images can now be rotated +- Fixed bug: When drawings have full path specified, no mime type can be deducted +- Fixed bug: Only one drawing can be added to a worksheet + + +## 2007-01-12 + +- Refactoring of some classes to use ArrayObject instead of array() +- Cell style now has support for number format (i.e. #,##0) +- Implemented embedding images + + +## 2007-01-02 + +- Cell style now has support for fills, including gradient fills +- Cell style now has support for fonts +- Cell style now has support for border colors +- Cell style now has support for font colors +- Cell style now has support for alignment + + +## 2006-12-21 + +- Support for cell style borders +- Support for cell styles +- Refactoring of Excel2007 Writer into multiple classes in package SpreadSheet_Writer_Excel2007 +- Refactoring of all classes, changed public members to public properties using getter/setter +- Worksheet names are now unique. On duplicate worksheet names, a number is appended. +- Worksheet now has parent SpreadSheet object +- Worksheet now has support for page header and footer +- Worksheet now has support for page margins +- Worksheet now has support for page setup (only Paper size and Orientation) +- Worksheet properties now accessible by using getProperties() +- Worksheet now has support for row and column dimensions (height / width) +- Exceptions thrown have a more clear description + + +## Initial version + +- Create a Spreadsheet object +- Add one or more Worksheet objects +- Add cells to Worksheet objects +- Export Spreadsheet object to Excel 2007 OpenXML format +- Each cell supports the following data formats: string, number, formula, boolean. diff --git a/CHANGELOG.md b/CHANGELOG.md index cee1da57..30c28392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Initial implementation of SUMIFS() function - Additional codepages - MemoryDrawing not working in HTML writer [#808](https://github.com/PHPOffice/PHPExcel/issues/808) +- CSV Reader can auto-detect the separator used in file [#141](https://github.com/PHPOffice/PhpSpreadsheet/pull/141) ### Changed @@ -39,1624 +40,6 @@ For a comprehensive list of all class changes, and a semi-automated migration pa - Dropped `PHPExcel_Calculation_Functions::VERSION()`. Composer or git should be used to know the version. - Dropped `PHPExcel_Settings::setPdfRenderer()` and `PHPExcel_Settings::setPdfRenderer()`. Composer should be used to autoload PDF libs. +## Previous versions of PHPExcel -## [1.8.1] - 2015-04-30 - -### Bugfixes - -- Fix for Writing an Open Document cell with non-numeric formula - @goncons [#397](https://github.com/PHPOffice/PHPExcel/issues/397) -- Avoid potential divide by zero in basedrawing - @sarciszewski [#329](https://github.com/PHPOffice/PHPExcel/issues/329) -- XML External Entity (XXE) Processing, different behaviour between simplexml_load_string() and simplexml_load_file(). - @ymaerschalck [#405](https://github.com/PHPOffice/PHPExcel/issues/405) -- Fix to ensure that current cell is maintained when executing formula calculations - @MarkBaker -- Keep/set the value on Reader _loadSheetsOnly as NULL, courtesy of Restless-ET - @MarkBaker [#350](https://github.com/PHPOffice/PHPExcel/issues/350) -- Loading an Excel 2007 spreadsheet throws an "Autofilter must be set on a range of cells" exception - @MarkBaker [CodePlex #18105](https://phpexcel.codeplex.com/workitem/18105) -- Fix to autoloader registration for backward compatibility with PHP 5.2.0 not accepting the prepend flag - @MarkBaker [#388](https://github.com/PHPOffice/PHPExcel/issues/388) -- DOM loadHTMLFile() failing with options flags when using PHP < 5.4.0 - @MarkBaker [#384](https://github.com/PHPOffice/PHPExcel/issues/384) -- Fix for percentage operator in formulae for BIFF Writer - @MarkBaker -- Fix to getStyle() call for cell object - @MarkBaker -- Discard Autofilters in Excel2007 Reader when filter range isn't a valid range - @MarkBaker -- Fix invalid NA return in VLOOKUP - @frozenstupidity [#423](https://github.com/PHPOffice/PHPExcel/issues/423) -- "No Impact" conditional formatting fix for NumberFormat - @wiseloren [CodePlex #21454](https://phpexcel.codeplex.com/workitem/21454) -- Bug in Excel2003XML reader, parsing merged cells - @bobwitlox [#467](https://github.com/PHPOffice/PHPExcel/issues/467) -- Fix for CEIL() and FLOOR() when number argument is zero - @MarkBaker [#302](https://github.com/PHPOffice/PHPExcel/issues/302) - -### General - -- Remove cells cleanly when calling RemoveRow() or RemoveColumn() - @MarkBaker -- Small performance improvement for autosize columns - @MarkBaker -- Change the getter/setter for zeroHeight to camel case - @frost-nzcr4 [#379](https://github.com/PHPOffice/PHPExcel/issues/379) -- DefaultValueBinder is too much aggressive when converting string to numeric - @MarkBaker [#394](https://github.com/PHPOffice/PHPExcel/issues/394) -- Default precalculate formulas to false for writers - @MarkBaker -- Set default Cyclic Reference behaviour to 1 to eliminate exception when using a single cyclic iteration in formulae - @MarkBaker - -### Features - -- Some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE - @MarkBaker [#396](https://github.com/PHPOffice/PHPExcel/issues/396) -- Methods to manage most of the existing options for Chart Axis, Major Grid-lines and Minor Grid-lines - @WiktrzGE [#404](https://github.com/PHPOffice/PHPExcel/issues/404) -- ODS read/write comments in the cell - @frost-nzcr4 [#403](https://github.com/PHPOffice/PHPExcel/issues/403) -- Additional Mac CJK codepage definitions - @CQD [#389](https://github.com/PHPOffice/PHPExcel/issues/389) -- Update Worksheet.php getStyleByColumnAndRow() to allow a range of cells rather than just a single cell - @bolovincev [#269](https://github.com/PHPOffice/PHPExcel/issues/269) -- New methods added for testing cell status within merge groups - @MarkBaker -- Handling merge cells in HTML Reader - @cifren/MBaker [#205](https://github.com/PHPOffice/PHPExcel/issues/205) -- Helper to convert basic HTML markup to a Rich Text object - @MarkBaker -- Improved Iterators - @MarkBaker - - New Column Iterator - - Support for row and column ranges - - Improved handling for next/prev - -### Security - -- XML filescan in XML-based Readers to prevent XML Entity Expansion (XEE) - @MarkBaker - - (see http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion for an explanation of XEE injection) attacks - - Reference CVE-2015-3542 - Identification of problem courtesy of Dawid Golunski (Pentest Ltd.) - -## [1.8.0] - 2014-03-02 - -### Bugfixes - -- Undefined variable: fileHandle in CSV Reader - @MarkBaker [CodePlex #19830](https://phpexcel.codeplex.com/workitem/19830) -- Out of memory in style/supervisor.php - @MarkBaker [CodePlex #19968](https://phpexcel.codeplex.com/workitem/19968) -- Style error with merged cells in PDF Writer - @MarkBaker -- Problem with cloning worksheets - @MarkBaker -- Bug fix reading Open Office files - @tavoarcila [#259](https://github.com/PHPOffice/PHPExcel/issues/259) -- Serious bug in absolute cell reference used in shared formula - @MarkBaker [CodePlex #20397](https://phpexcel.codeplex.com/workitem/20397) - - Would also have affected insert/delete column/row- CHOOSE() returns "#VALUE!" if the 1st entry is chosen - @RomanSyroeshko [#267](https://github.com/PHPOffice/PHPExcel/issues/267) -- When duplicating styles, styles shifted by one column to the right - @Gemorroj [#268](https://github.com/PHPOffice/PHPExcel/issues/268) - - Fix also applied to duplicating conditional styles- Fix for formulae that reference a sheet whose name begins with a digit: - @IndrekHaav [#212](https://github.com/PHPOffice/PHPExcel/issues/212) - - these were erroneously identified as numeric values, causing the parser to throw an undefined variable error.- Fixed undefined variable error due to $styleArray being used before it's initialised - @IndrekHaav [CodePlex #16208](https://phpexcel.codeplex.com/workitem/16208) -- ISTEXT() return wrong result if referencing an empty but formatted cell - @PowerKiKi [#273](https://github.com/PHPOffice/PHPExcel/issues/273) -- Binary comparison of strings are case insensitive - @PowerKiKi [#270](https://github.com/PHPOffice/PHPExcel/issues/270), [#31](https://github.com/PHPOffice/PHPExcel/issues/31) -- Insert New Row/Column Before is not correctly updating formula references - @MarkBaker [#275](https://github.com/PHPOffice/PHPExcel/issues/275) -- Passing an array of cells to _generateRow() in the HTML/PDF Writer causes caching problems with last cell in the range - @MarkBaker [#257](https://github.com/PHPOffice/PHPExcel/issues/257) -- Fix to empty worksheet garbage collection when using cell caching - @MarkBaker [#193](https://github.com/PHPOffice/PHPExcel/issues/193) -- Excel2007 does not correctly mark rows as hidden - @Jazzo [#248](https://github.com/PHPOffice/PHPExcel/issues/248) -- Fixed typo in Chart/Layout set/getYMode() - @Roy Shahbazian [#299](https://github.com/PHPOffice/PHPExcel/issues/299) -- Fatal error: Call to a member function cellExists() line: 3327 in calculation.php if referenced worksheet doesn't exist - @EliuFlorez [#279](https://github.com/PHPOffice/PHPExcel/issues/279) -- AdvancedValueBinder "Division by zero"-error - @MarkBaker [#290](https://github.com/PHPOffice/PHPExcel/issues/290) -- Adding Sheet to Workbook Bug - @MarkBaker [CodePlex #20604](https://phpexcel.codeplex.com/workitem/20604) -- Calculation engine incorrectly evaluates empty cells as #VALUE - @MarkBaker [CodePlex #20703](https://phpexcel.codeplex.com/workitem/20703) -- Formula references to cell on another sheet in ODS files - @MarkBaker [CodePlex #20760](https://phpexcel.codeplex.com/workitem/20760) - -### Features - -- LibreOffice created XLSX files results in an empty file. - @MarkBaker [#321](https://github.com/PHPOffice/PHPExcel/issues/321), [#158](https://github.com/PHPOffice/PHPExcel/issues/158), [CodePlex #17824](https://phpexcel.codeplex.com/workitem/17824) -- Implementation of the Excel HLOOKUP() function - @amerov -- Added "Quote Prefix" to style settings (Excel2007 Reader and Writer only) - @MarkBaker -- Added Horizontal FILL alignment for Excel5 and Excel2007 Readers/Writers, and Horizontal DISTRIBUTED alignment for Excel2007 Reader/Writer - @MarkBaker -- Add support for reading protected (RC4 encrypted) .xls files - @trvrnrth [#261](https://github.com/PHPOffice/PHPExcel/issues/261) - -### General - -- Adding support for macros, Ribbon in Excel 2007 - @LWol [#252](https://github.com/PHPOffice/PHPExcel/issues/252) -- Remove array_shift in ReferenceHelper::insertNewBefore improves column or row delete speed - @cdhutch [CodePlex #20055](https://phpexcel.codeplex.com/workitem/20055) -- Improve stock chart handling and rendering, with help from Swashata Ghosh - @MarkBaker -- Fix to calculation properties for Excel2007 so that the opening application will only recalculate on load if it's actually required - @MarkBaker -- Modified Excel2007 Writer to default preCalculateFormulas to false - @MarkBaker - - Note that autosize columns will still recalculate affected formulae internally- Functionality to getHighestRow() for a specified column, and getHighestColumn() for a specified row - @dresenhista [#242](https://github.com/PHPOffice/PHPExcel/issues/242) -- Modify PHPExcel_Reader_Excel2007 to use zipClass from PHPExcel_Settings::getZipClass() - @adamriyadi [#247](https://github.com/PHPOffice/PHPExcel/issues/247) - - This allows the use of PCLZip when reading for people that don't have access to ZipArchive -### Security - -- Convert properties to string in OOCalc reader - @infojunkie [#276](https://github.com/PHPOffice/PHPExcel/issues/276) -- Disable libxml external entity loading by default. - @maartenba [#322](https://github.com/PHPOffice/PHPExcel/issues/322) - - This is to prevent XML External Entity Processing (XXE) injection attacks (see http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html for an explanation of XXE injection). - - Reference CVE-2014-2054 - -## [1.7.9] - 2013-06-02 - -### Features - -- Include charts option for HTML Writer - @MarkBaker -- Added composer file - @MarkBaker -- cache_in_memory_gzip "eats" last worksheet line, cache_in_memory doesn't - @MarkBaker [CodePlex #18844](https://phpexcel.codeplex.com/workitem/18844) -- echo statements in HTML.php - @MarkBaker [#104](https://github.com/PHPOffice/PHPExcel/issues/104) - -### Bugfixes - -- Added getStyle() method to Cell object - @MarkBaker -- Error in PHPEXCEL/Calculation.php script on line 2976 (stack pop check) - @Asker [CodePlex #18777](https://phpexcel.codeplex.com/workitem/18777) -- CSV files without a file extension being identified as HTML - @MarkBaker [CodePlex #18794](https://phpexcel.codeplex.com/workitem/18794) -- Wrong check for maximum number of rows in Excel5 Writer - @AndreKR [#66](https://github.com/PHPOffice/PHPExcel/issues/66) -- Cache directory for DiscISAM cache storage cannot be set - @MarkBaker [#67](https://github.com/PHPOffice/PHPExcel/issues/67) -- Fix to Excel2007 Reader for hyperlinks with an anchor fragment (following a #), otherwise they were treated as sheet references - @MarkBaker [CodePlex #17976](https://phpexcel.codeplex.com/workitem/17976) -- getSheetNames() fails on numeric (floating point style) names with trailing zeroes - @MarkBaker [CodePlex #18963](https://phpexcel.codeplex.com/workitem/18963) -- Modify cell's getCalculatedValue() method to return the content of RichText objects rather than the RichText object itself - @MarkBaker -- Fixed formula/formatting bug when removing rows - @techhead [#70](https://github.com/PHPOffice/PHPExcel/issues/70) -- Fix to cellExists for non-existent namedRanges - @alexgann [#63](https://github.com/PHPOffice/PHPExcel/issues/63) -- Sheet View in Excel5 Writer - @Progi1984 [#22](https://github.com/PHPOffice/PHPExcel/issues/22) -- PHPExcel_Worksheet::getCellCollection() may not return last cached cell - @amironov [#82](https://github.com/PHPOffice/PHPExcel/issues/82) -- Rich Text containing UTF-8 characters creating unreadable content with Excel5 Writer - @teso [CodePlex #18551](https://phpexcel.codeplex.com/workitem/18551) -- Work item GH-8/CP11704 : Conditional formatting in Excel 5 Writer - @Progi1984 -- canRead() Error for GoogleDocs ODS files: in ODS files from Google Docs there is no mimetype file - @MarkBaker [#113](https://github.com/PHPOffice/PHPExcel/issues/113) -- "Sheet index is out of bounds." Exception - @MarkBaker [#80](https://github.com/PHPOffice/PHPExcel/issues/80) -- Fixed number format fatal error - @ccorliss [#105](https://github.com/PHPOffice/PHPExcel/issues/105) -- Add DROP TABLE in destructor for SQLite and SQLite3 cache controllers - @MarkBaker -- Fix merged-cell borders on HTML/PDF output - @alexgann [#154](https://github.com/PHPOffice/PHPExcel/issues/154) -- Fix: Hyperlinks break when removing rows - @Shanto [#161](https://github.com/PHPOffice/PHPExcel/issues/161) -- Fix Extra Table Row From Images and Charts - @neclimdul [#166](https://github.com/PHPOffice/PHPExcel/issues/166) - -### General - -- Single cell print area - @MarkBaker [#130](https://github.com/PHPOffice/PHPExcel/issues/130) -- Improved AdvancedValueBinder for currency - @kea [#69](https://github.com/PHPOffice/PHPExcel/issues/69) -- Fix for environments where there is no access to /tmp but to upload_tmp_dir - @MarkBaker - - Provided an option to set the sys_get_temp_dir() call to use the upload_tmp_dir; though by default the standard temp directory will still be used- Search style by identity in PHPExcel_Worksheet::duplicateStyle() - @amironov [#84](https://github.com/PHPOffice/PHPExcel/issues/84) -- Fill SheetView IO in Excel5 - @karak [#85](https://github.com/PHPOffice/PHPExcel/issues/85) -- Memory and Speed improvements in PHPExcel_Reader_Excel5 - @cfhay [CodePlex #18958](https://phpexcel.codeplex.com/workitem/18958) -- Modify listWorksheetNames() and listWorksheetInfo to use XMLReader with streamed XML rather than SimpleXML - @MarkBaker [#78](https://github.com/PHPOffice/PHPExcel/issues/78) -- Restructuring of PHPExcel Exceptions - @dbonsch -- Refactor Calculation Engine from singleton to a Multiton - @MarkBaker - - Ensures that calculation cache is maintained independently for different workbooks - -## [1.7.8] - 2012-10-12 - -### Features - -- Phar builder script to add phar file as a distribution option - @kkamkou -- Refactor PDF Writer to allow use with a choice of PDF Rendering library - @MarkBaker - - rather than restricting to tcPDF - - Current options are tcPDF, mPDF, DomPDF - - tcPDF Library has now been removed from the deployment bundle- Initial version of HTML Reader - @MarkBaker -- Implement support for AutoFilter in PHPExcel_Writer_Excel5 - @Progi1984 -- Modified ERF and ERFC Engineering functions to accept Excel 2010's modified acceptance of negative arguments - @MarkBaker -- Support SheetView `view` attribute (Excel2007) - @k1LoW -- Excel compatibility option added for writing CSV files - @MarkBaker - - While Excel 2010 can read CSV files with a simple UTF-8 BOM, Excel2007 and earlier require UTF-16LE encoded tab-separated files. - - The new setExcelCompatibility(TRUE) option for the CSV Writer will generate files with this formatting for easy import into Excel2007 and below.- Language implementations for Turkish (tr) - @MarkBaker -- Added fraction tests to advanced value binder - @MarkBaker - -### General - -- Allow call to font setUnderline() for underline format to specify a simple boolean for UNDERLINE_NONE or UNDERLINE_SINGLE - @MarkBaker -- Add Currency detection to the Advanced Value Binder - @alexgann -- setCellValueExplicitByColumnAndRow() do not return PHPExcel_Worksheet - @MarkBaker [CodePlex #18404](https://phpexcel.codeplex.com/workitem/18404) -- Reader factory doesn't read anymore XLTX and XLT files - @MarkBaker [CodePlex #18324](https://phpexcel.codeplex.com/workitem/18324) -- Magic __toString() method added to Cell object to return raw data value as a string - @MarkBaker -- Add cell indent to html rendering - @alexgann - -### Bugfixes - -- ZeroHeight for rows in sheet format - @Raghav1981 -- OOCalc cells containing inside the tag - @cyberconte -- Fix to listWorksheetInfo() method for OOCalc Reader - @schir1964 -- Support for "e" (epoch) date format mask - @MarkBaker - - Rendered as a 4-digit CE year in non-Excel outputs- Background color cell is always black when editing cell - @MarkBaker -- Allow "no impact" to formats on Conditional Formatting - @MarkBaker -- OOCalc Reader fix for NULL cells - @wackonline -- Fix to excel2007 Chart Writer when a $plotSeriesValues is empty - @seltzlab -- Various fixes to Chart handling - @MarkBaker -- Error loading xlsx file with column breaks - @MarkBaker [CodePlex #18370](https://phpexcel.codeplex.com/workitem/18370) -- OOCalc Reader now handles percentage and currency data types - @MarkBaker -- mb_stripos empty delimiter - @MarkBaker -- getNestingLevel() Error on Excel5 Read - @takaakik -- Fix to Excel5 Reader when cell annotations are defined before their referenced text objects - @MarkBaker -- OOCalc Reader modified to process number-rows-repeated - @MarkBaker -- Chart Title compatibility on Excel 2007 - @MarkBaker [CodePlex #18377](https://phpexcel.codeplex.com/workitem/18377) -- Chart Refresh returning cell reference rather than values - @MarkBaker [CodePlex #18146](https://phpexcel.codeplex.com/workitem/18146) -- Autoshape being identified in twoCellAnchor when includeCharts is TRUE triggering load error - @MarkBaker [CodePlex #18145](https://phpexcel.codeplex.com/workitem/18145) -- v-type texts for series labels now recognised and parsed correctly - @MarkBaker [CodePlex #18325](https://phpexcel.codeplex.com/workitem/18325) -- load file failed if the file has no extensionType - @wolf5x [CodePlex #18492](https://phpexcel.codeplex.com/workitem/18492) -- Pattern fill colours in Excel2007 Style Writer - @dverspui -- Excel2007 Writer order of font style elements to conform with Excel2003 using compatibility pack - @MarkBaker -- Problems with $_activeSheetIndex when decreased below 0. - @MarkBaker [CodePlex #18425](https://phpexcel.codeplex.com/workitem/18425) -- PHPExcel_CachedObjectStorage_SQLite3::cacheMethodIsAvailable() uses class_exists - autoloader throws error - @MarkBaker [CodePlex #18597](https://phpexcel.codeplex.com/workitem/18597) -- Cannot access private property PHPExcel_CachedObjectStorageFactory::$_cacheStorageMethod - @MarkBaker [CodePlex #18598](https://phpexcel.codeplex.com/workitem/18598) -- Data titles for charts - @MarkBaker [CodePlex #18397](https://phpexcel.codeplex.com/workitem/18397) - - PHPExcel_Chart_Layout now has methods for getting/setting switches for displaying/hiding chart data labels- Discard single cell merge ranges when reading (stupid that Excel allows them in the first place) - @MarkBaker -- Discard hidden autoFilter named ranges - @MarkBaker - - -## [1.7.7] - 2012-05-19 - -### Bugfixes - -- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916) -- Change iterators to implement Iterator rather than extend CachingIterator, as a fix for PHP 5.4. changes in SPL - @MarkBaker -- Invalid cell coordinate in Autofilter for Excel2007 Writer - @MarkBaker [CodePlex #15459](https://phpexcel.codeplex.com/workitem/15459) -- PCLZip library issue - @MarkBaker [CodePlex #15518](https://phpexcel.codeplex.com/workitem/15518) -- Excel2007 Reader canRead function bug - @MarkBaker [CodePlex #15537](https://phpexcel.codeplex.com/workitem/15537) -- Support for Excel functions whose return can be used as either a value or as a cell reference depending on its context within a formula - @MarkBaker -- ini_set() call in Calculation class destructor - @gilles06 [CodePlex #15707](https://phpexcel.codeplex.com/workitem/15707) -- RangeToArray strange array keys - @MarkBaker [CodePlex #15786](https://phpexcel.codeplex.com/workitem/15786) -- INDIRECT() function doesn't work with named ranges - @MarkBaker [CodePlex #15762](https://phpexcel.codeplex.com/workitem/15762) -- Locale-specific fix to text functions when passing a boolean argument instead of a string - @MarkBaker -- reader/CSV fails on this file - @MarkBaker [CodePlex #16246](https://phpexcel.codeplex.com/workitem/16246) - - auto_detect_line_endings now set in CSV reader- $arguments improperly used in CachedObjectStorage/PHPTemp.php - @MarkBaker [CodePlex #16212](https://phpexcel.codeplex.com/workitem/16212) -- Bug In Cache System (cell reference when throwing caching errors) - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643) -- PHP Invalid index notice on writing excel file when active sheet has been deleted - @MarkBaker [CodePlex #16895](https://phpexcel.codeplex.com/workitem/16895) -- External links in Excel2010 files cause Fatal error - @MarkBaker [CodePlex #16956](https://phpexcel.codeplex.com/workitem/16956) -- Previous calculation engine error conditions trigger cyclic reference errors - @MarkBaker [CodePlex #16960](https://phpexcel.codeplex.com/workitem/16960) -- PHPExcel_Style::applyFromArray() returns null rather than style object in advanced mode - @mkopinsky [CodePlex #16266](https://phpexcel.codeplex.com/workitem/16266) -- Cell::getFormattedValue returns RichText object instead of string - @fauvel [CodePlex #16958](https://phpexcel.codeplex.com/workitem/16958) -- Indexed colors do not refer to Excel's indexed colors? - @MarkBaker [CodePlex #17166](https://phpexcel.codeplex.com/workitem/17166) -- Indexed colors should be consistent with Excel and start from 1 (current index starts at 0) - @MarkBaker [CodePlex #17199](https://phpexcel.codeplex.com/workitem/17199) -- Named Range definition in .xls when sheet reeference is quote wrapped - @MarkBaker [CodePlex #17262](https://phpexcel.codeplex.com/workitem/17262) -- duplicateStyle() method doesn't duplicate conditional formats - @MarkBaker [CodePlex #17403](https://phpexcel.codeplex.com/workitem/17403) - - Added an equivalent duplicateConditionalStyle() method for duplicating conditional styles- =sumproduct(A,B) <> =sumproduct(B,A) in xlsx - @bnr [CodePlex #17501](https://phpexcel.codeplex.com/workitem/17501) - -### Features - -- OOCalc cells contain same data bug? - @cyberconte [CodePlex #17471](https://phpexcel.codeplex.com/workitem/17471) -- listWorksheetInfo() method added to Readers... courtesy of Christopher Mullins - @schir1964 -- Options for cell caching using Igbinary and SQLite/SQlite3. - @MarkBaker -- Additional row iterator options: allow a start row to be defined in the constructor; seek(), and prev() methods added. - @MarkBaker -- Implement document properties in Excel5 writer - @Progi1984 [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759) - -### General - -- Implement chart functionality (EXPERIMENTAL) - @MarkBaker [CodePlex #16](https://phpexcel.codeplex.com/workitem/16) - - Initial definition of chart objects. - - Reading Chart definitions through the Excel2007 Reader - - Facility to render charts to images using the 3rd-party jpgraph library - - Writing Charts using the Excel2007 Writer- Fix to build to ensure that Examples are included with the documentation - @MarkBaker -- Reduce cell caching overhead using dirty flag to ensure that cells are only rewritten to the cache if they have actually been changed - @MarkBaker -- Improved memory usage in CSV Writer - @MarkBaker -- Improved speed and memory usage in Excel5 Writer - @MarkBaker -- Experimental - @MarkBaker - - Added getHighestDataColumn(), getHighestDataRow(), getHighestRowAndColumn() and calculateWorksheetDataDimension() methods for the worksheet that return the highest row and column that have cell records- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916) -- Two easy to fix Issues concerning PHPExcel_Token_Stack (l10n/UC) - @MarkBaker [CodePlex #15405](https://phpexcel.codeplex.com/workitem/15405) -- Locale file paths not fit for windows - @MarkBaker [CodePlex #15461](https://phpexcel.codeplex.com/workitem/15461) -- Add file directory as a cache option for cache_to_discISAM - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643) -- Datatype.php & constant TYPE_NULL - @MarkBaker [CodePlex #16923](https://phpexcel.codeplex.com/workitem/16923) -- Ensure use of system temp directory for all temporary work files, unless explicitly specified - @MarkBaker -- [Patch] faster stringFromColumnIndex() - @char101 [CodePlex #16359](https://phpexcel.codeplex.com/workitem/16359) -- Fix for projects that still use old autoloaders - @whit1206 [CodePlex #16028](https://phpexcel.codeplex.com/workitem/16028) -- Unknown codepage: 10007 - @atz [CodePlex #17024](https://phpexcel.codeplex.com/workitem/17024) - - Additional Mac codepages - -## [1.7.6] - 2011-02-27 - -### Features - -- Provide option to use PCLZip as an alternative to ZipArchive. - @MarkBaker - - This allows the writing of Excel2007 files, even without ZipArchive enabled (it does require zlib), or when php_zip is one of the buggy PHP 5.2.6 or 5.2.8 versions - - It can be enabled using PHPExcel_Settings::setZipClass(PHPExcel_Settings::PCLZIP); - - Note that it is not yet implemented as an alternative to ZipArchive for those Readers that are extracting from zips- Added listWorksheetNames() method to Readers that support multiple worksheets in a workbook, allowing a user to extract a list of all the worksheet names from a file without parsing/loading the whole file. - @MarkBaker [CodePlex #14979](https://phpexcel.codeplex.com/workitem/14979) -- Speed boost and memory reduction in the Worksheet toArray() method. - @MarkBaker -- Added new rangeToArray() and namedRangeToArray() methods to the PHPExcel_Worksheet object. - @MarkBaker - - Functionally, these are identical to the toArray() method, except that they take an additional first parameter of a Range (e.g. 'B2:C3') or a Named Range name. - - Modified the toArray() method so that it actually uses rangeToArray().- Added support for cell comments in the OOCalc, Gnumeric and Excel2003XML Readers, and in the Excel5 Reader - @MarkBaker -- Improved toFormattedString() handling for Currency and Accounting formats to render currency symbols - @MarkBaker - -### Bugfixes - -- Implement more Excel calculation functions - @MarkBaker - - Implemented the DAVERAGE(), DCOUNT(), DCOUNTA(), DGET(), DMAX(), DMIN(), DPRODUCT(), DSTDEV(), DSTDEVP(), DSUM(), DVAR() and DVARP() Database functions- Simple =IF() formula disappears - @MarkBaker [CodePlex #14888](https://phpexcel.codeplex.com/workitem/14888) -- PHP Warning: preg_match(): Compilation failed: PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X - @MarkBaker [CodePlex #14898](https://phpexcel.codeplex.com/workitem/14898) -- VLOOKUP choking on parameters in PHPExcel.1.7.5/PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #14901](https://phpexcel.codeplex.com/workitem/14901) -- PHPExcel_Cell::isInRange() incorrect results - offset by one column - @MarkBaker [CodePlex #14973](https://phpexcel.codeplex.com/workitem/14973) -- Treat CodePage of 0 as CP1251 (for .xls files written by applications that don't set the CodePage correctly, such as Apple Numbers) - @MarkBaker -- Need method for removing autoFilter - @MarkBaker [CodePlex #11583](https://phpexcel.codeplex.com/workitem/11583) -- coordinateFromString throws exception for rows greater than 99,999 - @MarkBaker [CodePlex #15029](https://phpexcel.codeplex.com/workitem/15029) -- PHPExcel Excel2007 Reader colour problems with solidfill - @MarkBaker [CodePlex #14999](https://phpexcel.codeplex.com/workitem/14999) -- Formatting get lost and edit a template XLSX file - @MarkBaker [CodePlex #13215](https://phpexcel.codeplex.com/workitem/13215) -- Excel 2007 Reader /writer lost fontcolor - @MarkBaker [CodePlex #14029](https://phpexcel.codeplex.com/workitem/14029) -- file that makes cells go black - @MarkBaker [CodePlex #13374](https://phpexcel.codeplex.com/workitem/13374) -- Minor patchfix for Excel2003XML Reader when XML is defined with a charset attribute - @MarkBaker -- PHPExcel_Worksheet->toArray() index problem - @MarkBaker [CodePlex #15089](https://phpexcel.codeplex.com/workitem/15089) -- Merge cells 'un-merge' when using an existing spreadsheet - @MarkBaker [CodePlex #15094](https://phpexcel.codeplex.com/workitem/15094) -- Worksheet fromArray() only working with 2-D arrays - @MarkBaker [CodePlex #15129](https://phpexcel.codeplex.com/workitem/15129) -- rangeToarray function modified for non-existent cells - @xkeshav [CodePlex #15172](https://phpexcel.codeplex.com/workitem/15172) -- Images not getting copyied with the ->clone function - @MarkBaker [CodePlex #14980](https://phpexcel.codeplex.com/workitem/14980) -- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @MarkBaker [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576) -- Fix Excel5 Writer so that it only writes column dimensions for columns that are actually used rather than the full range (A to IV) - @MarkBaker -- FreezePane causing damaged or modified error - @MarkBaker [CodePlex #15198](https://phpexcel.codeplex.com/workitem/15198) - - The freezePaneByColumnAndRow() method row argument should default to 1 rather than 0. - - Default row argument for all __ByColumnAndRow() methods should be 1- Column reference rather than cell reference in Print Area definition - @MarkBaker [CodePlex #15121](https://phpexcel.codeplex.com/workitem/15121) - - Fix Excel2007 Writer to handle print areas that are defined as row or column ranges rather than just as cell ranges- Reduced false positives from isDateTimeFormatCode() method by suppressing testing within quoted strings - @MarkBaker -- Caching and tmp partition exhaustion - @MarkBaker [CodePlex #15312](https://phpexcel.codeplex.com/workitem/15312) -- Writing to Variable No Longer Works. $_tmp_dir Missing in PHPExcel\PHPExcel\Shared\OLE\PPS\Root.php - @MarkBaker [CodePlex #15308](https://phpexcel.codeplex.com/workitem/15308) -- Named ranges with dot don't get parsed properly - @MarkBaker [CodePlex #15379](https://phpexcel.codeplex.com/workitem/15379) -- insertNewRowBefore fails to consistently update references - @MarkBaker [CodePlex #15096](https://phpexcel.codeplex.com/workitem/15096) -- "i" is not a valid character for Excel date format masks (in isDateTimeFormatCode() method) - @MarkBaker -- PHPExcel_ReferenceHelper::insertNewBefore() is missing an 'Update worksheet: comments' section - @MKunert [CodePlex #15421](https://phpexcel.codeplex.com/workitem/15421) - -### General - -- Full column/row references in named ranges not supported by updateCellReference() - @MarkBaker [CodePlex #15409](https://phpexcel.codeplex.com/workitem/15409) -- Improved performance (speed), for building the Shared Strings table in the Excel2007 Writer. - @MarkBaker -- Improved performance (speed), for PHP to Excel date conversions - @MarkBaker -- Enhanced SheetViews element structures in the Excel2007 Writer for frozen panes. - @MarkBaker -- Removed Serialized Reader/Writer as these no longer work. - @MarkBaker - - -## [1.7.5] - 2010-12-10 - -### Features - -- Implement Gnumeric File Format - @MarkBaker [CodePlex #8769](https://phpexcel.codeplex.com/workitem/8769) - - Initial work on Gnumeric Reader (Worksheet Data, Document Properties and basic Formatting)- Support for Extended Workbook Properties in Excel2007, Excel5 and OOCalc Readers; support for User-defined Workbook Properties in Excel2007 and OOCalc Readers - @MarkBaker -- Support for Extended and User-defined Workbook Properties in Excel2007 Writer - @MarkBaker -- Provided a setGenerateSheetNavigationBlock(false); option to suppress generation of the sheet navigation block when writing multiple worksheets to HTML - @MarkBaker -- Advanced Value Binder now recognises TRUE/FALSE strings (locale-specific) and converts to boolean - @MarkBaker -- PHPExcel_Worksheet->toArray() is returning truncated values - @MarkBaker [CodePlex #14301](https://phpexcel.codeplex.com/workitem/14301) -- Configure PDF Writer margins based on Excel Worksheet Margin Settings value - @MarkBaker -- Added Contiguous flag for the CSV Reader, when working with Read Filters - @MarkBaker -- Added getFormattedValue() method for cell object - @MarkBaker -- Added strictNullComparison argument to the worksheet fromArray() method - @MarkBaker - -### Bugfixes - -- Fix to toFormattedString() method in PHPExcel_Style_NumberFormat to handle fractions with a # code for the integer part - @MarkBaker -- NA() doesn't propagate in matrix calc - quick fix in JAMA/Matrix.php - @MarkBaker [CodePlex #14143](https://phpexcel.codeplex.com/workitem/14143) -- Excel5 : Formula : String constant containing double quotation mark - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Percent - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Error constant - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Concatenation operator - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Worksheet clone broken for CachedObjectStorage_Memory - @MarkBaker [CodePlex #14146](https://phpexcel.codeplex.com/workitem/14146) -- PHPExcel_Reader_Excel2007 fails when gradient fill without type is present in a file - @MarkBaker [CodePlex #12998](https://phpexcel.codeplex.com/workitem/12998) -- @ format for numeric strings in XLSX to CSV conversion - @MarkBaker [CodePlex #14176](https://phpexcel.codeplex.com/workitem/14176) -- Advanced Value Binder Not Working? - @MarkBaker [CodePlex #14223](https://phpexcel.codeplex.com/workitem/14223) -- unassigned object variable in PHPExcel->removeCellXfByIndex - @MarkBaker [CodePlex #14226](https://phpexcel.codeplex.com/workitem/14226) -- problem with getting cell values from another worksheet... (if cell doesn't exist) - @MarkBaker [CodePlex #14236](https://phpexcel.codeplex.com/workitem/14236) -- Setting cell values to one char strings & Trouble reading one character string (thanks gorfou) - @MarkBaker -- Worksheet title exception when duplicate worksheet is being renamed but exceeds the 31 character limit - @MarkBaker [CodePlex #14256](https://phpexcel.codeplex.com/workitem/14256) -- Named range with sheet name that contains the $ throws exception when getting the cell - @MarkBaker [CodePlex #14086](https://phpexcel.codeplex.com/workitem/14086) -- Added autoloader to DefaultValueBinder and AdvancedValueBinder - @MarkBaker -- Modified PHPExcel_Shared_Date::isDateTimeFormatCode() to return false if format code begins with "_" or with "0 " to prevent false positives - @MarkBaker - - These leading characters are most commonly associated with number, currency or accounting (or occasionally fraction) formats- BUG : Excel5 and setReadFilter ? - @MarkBaker [CodePlex #14374](https://phpexcel.codeplex.com/workitem/14374) -- Wrong exception message while deleting column - @MarkBaker [CodePlex #14425](https://phpexcel.codeplex.com/workitem/14425) -- Formula evaluation fails with Japanese sheet refs - @MarkBaker [CodePlex #14679](https://phpexcel.codeplex.com/workitem/14679) -- PHPExcel_Writer_PDF does not handle cell borders correctly - @MarkBaker [CodePlex #13559](https://phpexcel.codeplex.com/workitem/13559) -- Style : applyFromArray() for 'allborders' not working - @MarkBaker [CodePlex #14831](https://phpexcel.codeplex.com/workitem/14831) - -### General - -- Using $this when not in object context in Excel5 Reader - @MarkBaker [CodePlex #14837](https://phpexcel.codeplex.com/workitem/14837) -- Removes a unnecessary loop through each cell when applying conditional formatting to a range. - @MarkBaker -- Removed spurious PHP end tags (?>) - @MarkBaker -- Improved performance (speed) and reduced memory overheads, particularly for the Writers, but across the whole library. - @MarkBaker - - -## [1.7.4] - 2010-08-26 - -### Bugfixes - -- Excel5 : Formula : Power - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Unary plus - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Just write the Escher stream if necessary in Worksheet - @Progi1984 -- Syntax errors in memcache.php 1.7.3c - @MarkBaker [CodePlex #13433](https://phpexcel.codeplex.com/workitem/13433) -- Support for row or column ranges in the calculation engine, e.g. =SUM(C:C) or =SUM(1:2) - @MarkBaker - - Also support in the calculation engine for absolute row or column ranges e.g. =SUM($C:$E) or =SUM($3:5)- Picture problem with Excel 2003 - @Erik Tilt [CodePlex #13455](https://phpexcel.codeplex.com/workitem/13455) -- Wrong variable used in addExternalSheet in PHPExcel.php - @MarkBaker [CodePlex #13484](https://phpexcel.codeplex.com/workitem/13484) -- "Invalid cell coordinate" error when formula access data from an other sheet - @MarkBaker [CodePlex #13515](https://phpexcel.codeplex.com/workitem/13515) -- (related to Work item 13515) Calculation engine confusing cell range worksheet when referencing cells in a different worksheet to the formula - @MarkBaker -- Wrong var naming in Worksheet->garbageCollect() - @MarkBaker [CodePlex #13752](https://phpexcel.codeplex.com/workitem/13752) -- PHPExcel_Style_*::__clone() methods cause cloning loops? - @MarkBaker [CodePlex #13764](https://phpexcel.codeplex.com/workitem/13764) -- Recent builds causing problems loading xlsx files? (ZipArchive issue?) - @MarkBaker [CodePlex #11488](https://phpexcel.codeplex.com/workitem/11488) -- cache_to_apc causes fatal error when processing large data sets - @MarkBaker [CodePlex #13856](https://phpexcel.codeplex.com/workitem/13856) -- OOCalc reader misses first line if it's a 'table-header-row' - @MarkBaker [CodePlex #13880](https://phpexcel.codeplex.com/workitem/13880) -- using cache with copy or clone bug? - @MarkBaker [CodePlex #14011](https://phpexcel.codeplex.com/workitem/14011) - - Fixed $worksheet->copy() or clone $worksheet when using cache_in_memory, cache_in_memory_gzip, cache_in_memory_serialized, cache_to_discISAM, cache_to_phpTemp, cache_to_apc and cache_to_memcache; - - Fixed but untested when using cache_to_wincache. -### Features - -- Standard Deviation functions returning DIV/0 Error when Standard Deviation is zero - @MarkBaker [CodePlex #13450](https://phpexcel.codeplex.com/workitem/13450) -- Support for print area with several ranges in the Excel2007 reader, and improved features for editing print area with several ranges - @MarkBaker -- Improved Cell Exception Reporting - @MarkBaker [CodePlex #13769](https://phpexcel.codeplex.com/workitem/13769) - -### General - -- Fixed problems with reading Excel2007 Properties - @MarkBaker -- PHP Strict Standards: Non-static method PHPExcel_Shared_String::utf16_decode() should not be called statically - @MarkBaker -- Array functions were ignored when loading an existing file containing them, and as a result, they would lose their 'cse' status. - @MarkBaker -- Minor memory tweaks to Excel2007 Writer - @MarkBaker -- Modified ReferenceHelper updateFormulaReferences() method to handle updates to row and column cell ranges (including absolute references e.g. =SUM(A:$E) or =SUM($5:5), and range/cell references that reference a worksheet by name), and to provide both performance and memory improvements. - @MarkBaker -- Modified Excel2007 Reader so that ReferenceHelper class is instantiated only once rather than for every shared formula in a workbook. - @MarkBaker -- Correct handling for additional (synonym) formula tokens in Excel5 Reader - @MarkBaker -- Additional reading of some Excel2007 Extended Properties (Company, Manager) - @MarkBaker - - -## [1.7.3c] - 2010-06-01 - -### Bugfixes - -- Fatal error: Class 'ZipArchive' not found... ...Reader/Excel2007.php on line 217 - @MarkBaker [CodePlex #13012](https://phpexcel.codeplex.com/workitem/13012) -- PHPExcel_Writer_Excel2007 error after 1.7.3b - @MarkBaker [CodePlex #13398](https://phpexcel.codeplex.com/workitem/13398) - - -## [1.7.3b] - 2010-05-31 - -### Bugfixes - -- Infinite loop when reading - @MarkBaker [CodePlex #12903](https://phpexcel.codeplex.com/workitem/12903) -- Wrong method chaining on PHPExcel_Worksheet class - @MarkBaker [CodePlex #13381](https://phpexcel.codeplex.com/workitem/13381) - - -## [1.7.3] - 2010-05-17 - -### General - -- Applied patch 4990 (modified) - @Erik Tilt -- Applied patch 5568 (modified) - @MarkBaker -- Applied patch 5943 - @MarkBaker -- Upgrade build script to use Phing - @MarkBaker [CodePlex #13042](https://phpexcel.codeplex.com/workitem/13042) -- Replacing var with public/private - @Erik Tilt [CodePlex #11586](https://phpexcel.codeplex.com/workitem/11586) -- Applied Anthony's Sterling's Class Autoloader to reduce memory overhead by "Lazy Loading" of classes - @MarkBaker -- Modification to functions that accept a date parameter to support string values containing ordinals as per Excel (English language only) - @MarkBaker -- Modify PHPExcel_Style_NumberFormat::toFormattedString() to handle dates that fall outside of PHP's 32-bit date range - @MarkBaker -- Applied patch 5207 - @MarkBaker - -### Features - -- PHPExcel developer documentation: Set page margins - @Erik Tilt [CodePlex #11970](https://phpexcel.codeplex.com/workitem/11970) -- Special characters and accents in SYLK reader - @Erik Tilt [CodePlex #11038](https://phpexcel.codeplex.com/workitem/11038) -- Implement more Excel calculation functions - @MarkBaker - - Implemented the COUPDAYS(), COUPDAYBS(), COUPDAYSNC(), COUPNCD(), COUPPCD() and PRICE() Financial functions - - Implemented the N() and TYPE() Information functions - - Implemented the HYPERLINK() Lookup and Reference function- Horizontal page break support in PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11526](https://phpexcel.codeplex.com/workitem/11526) -- Introduce method setActiveSheetIndexByName() - @Erik Tilt [CodePlex #11529](https://phpexcel.codeplex.com/workitem/11529) -- AdvancedValueBinder.php: Automatically wrap text when there is new line in string (ALT+"Enter") - @Erik Tilt [CodePlex #11550](https://phpexcel.codeplex.com/workitem/11550) -- Data validation support in PHPExcel_Reader_Excel5 and PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10300](https://phpexcel.codeplex.com/workitem/10300) -- Improve autosize calculation - @MarkBaker [CodePlex #11616](https://phpexcel.codeplex.com/workitem/11616) -- Methods to translate locale-specific function names in formulae - @MarkBaker - - Language implementations for Czech (cs), Danish (da), German (de), English (uk), Spanish (es), Finnish (fi), French (fr), Hungarian (hu), Italian (it), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt), Brazilian Portuguese (pt_br), Russian (ru) and Swedish (sv)- Implement document properties in Excel5 reader/writer - @Erik Tilt [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759) - - Fixed so far for PHPExcel_Reader_Excel5- Show/hide row and column headers in worksheet - @Erik Tilt [CodePlex #11849](https://phpexcel.codeplex.com/workitem/11849) -- Can't set font on writing PDF (by key) - @Erik Tilt [CodePlex #11919](https://phpexcel.codeplex.com/workitem/11919) -- Thousands scale (1000^n) support in PHPExcel_Style_NumberFormat::toFormattedString - @Erik Tilt [CodePlex #12096](https://phpexcel.codeplex.com/workitem/12096) -- Implement repeating rows in PDF and HTML writer - @Erik Tilt -- Sheet tabs in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #12289](https://phpexcel.codeplex.com/workitem/12289) -- Add Wincache CachedObjectProvider - @MarkBaker [CodePlex #13041](https://phpexcel.codeplex.com/workitem/13041) -- Configure PDF Writer paper size based on Excel Page Settings value, and provided methods to override paper size and page orientation with the writer - @MarkBaker - - Note PHPExcel defaults to Letter size, while the previous PDF writer enforced A4 size, so PDF writer will now default to Letter- Initial implementation of cell caching: allowing larger workbooks to be managed, but at a cost in speed - @MarkBaker - -### Bugfixes - -- Added an identify() method to the IO Factory that identifies the reader which will be used to load a particular file without actually loading it. - @MarkBaker -- Warning messages with INDEX function having 2 arguments - @MarkBaker [CodePlex #10979](https://phpexcel.codeplex.com/workitem/10979) -- setValue('=') should result in string instead of formula - @Erik Tilt [CodePlex #11473](https://phpexcel.codeplex.com/workitem/11473) -- method _raiseFormulaError should no be private - @MarkBaker [CodePlex #11471](https://phpexcel.codeplex.com/workitem/11471) -- Fatal error: Call to undefined function mb_substr() in ...Classes\PHPExcel\Reader\Excel5.php on line 2903 - @Erik Tilt [CodePlex #11485](https://phpexcel.codeplex.com/workitem/11485) -- getBold(), getItallic(), getStrikeThrough() not always working with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #11487](https://phpexcel.codeplex.com/workitem/11487) -- AdvancedValueBinder.php not working correctly for $cell->setValue('hh:mm:ss') - @Erik Tilt [CodePlex #11492](https://phpexcel.codeplex.com/workitem/11492) -- Fixed leap year handling for the YEARFRAC() Date/Time function when basis ia 1 (Actual/actual) - @MarkBaker -- Warning messages - @MarkBaker [CodePlex #11490](https://phpexcel.codeplex.com/workitem/11490) - - Calculation Engine code modified to enforce strict standards for pass by reference- PHPExcel_Cell_AdvancedValueBinder doesnt work for dates in far future - @Erik Tilt [CodePlex #11483](https://phpexcel.codeplex.com/workitem/11483) -- MSODRAWING bug with long CONTINUE record in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #11528](https://phpexcel.codeplex.com/workitem/11528) -- PHPExcel_Reader_Excel2007 reads print titles as named range when there is more than one sheet - @Erik Tilt [CodePlex #11571](https://phpexcel.codeplex.com/workitem/11571) -- missing @return in phpdocblock in reader classes - @Erik Tilt [CodePlex #11561](https://phpexcel.codeplex.com/workitem/11561) -- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @Erik Tilt [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576) -- Small numbers escape treatment in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt [CodePlex #11588](https://phpexcel.codeplex.com/workitem/11588) -- Blank styled cells are not blank in output by HTML writer due to   - @Erik Tilt [CodePlex #11590](https://phpexcel.codeplex.com/workitem/11590) -- Calculation engine bug: Existing, blank cell + number gives #NUM - @MarkBaker [CodePlex #11587](https://phpexcel.codeplex.com/workitem/11587) -- AutoSize only measures length of first line in cell with multiple lines (ALT+Enter) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608) -- Fatal error running Tests/12serializedfileformat.php (PHPExcel 1.7.2) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608) -- Fixed various errors in the WORKDAY() and NETWORKDAYS() Date/Time functions (particularly related to holidays) - @MarkBaker -- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/SheetView.php:115 - @Erik Tilt [CodePlex #11660](https://phpexcel.codeplex.com/workitem/11660) -- "Unrecognized token 39 in formula" with PHPExcel_Reader_Excel5 (occuring with add-in functions) - @Erik Tilt [CodePlex #11551](https://phpexcel.codeplex.com/workitem/11551) -- Excel2007 reader not reading PHPExcel_Style_Conditional::CONDITION_EXPRESSION - @Erik Tilt [CodePlex #11668](https://phpexcel.codeplex.com/workitem/11668) -- Fix to the BESSELI(), BESSELJ(), BESSELK(), BESSELY() and COMPLEX() Engineering functions to use correct default values for parameters - @MarkBaker -- DATEVALUE function not working for pure time values + allow DATEVALUE() function to handle partial dates (e.g. "1-Jun" or "12/2010") - @MarkBaker [CodePlex #11525](https://phpexcel.codeplex.com/workitem/11525) -- Fix for empty quoted strings in formulae - @MarkBaker -- Trap for division by zero in Bessel functions - @MarkBaker -- Fix to OOCalc Reader to convert semi-colon (;) argument separator in formulae to a comma (,) - @MarkBaker -- PHPExcel_Writer_Excel5_Parser cannot parse formula like =SUM(C$5:C5) - @Erik Tilt [CodePlex #11693](https://phpexcel.codeplex.com/workitem/11693) -- Fix to OOCalc Reader to handle dates that fall outside 32-bit PHP's date range - @MarkBaker -- File->sys_get_temp_dir() can fail in safe mode - @Erik Tilt [CodePlex #11692](https://phpexcel.codeplex.com/workitem/11692) -- Sheet references in Excel5 writer do not work when referenced sheet title contains non-Latin symbols - @Erik Tilt [CodePlex #11727](https://phpexcel.codeplex.com/workitem/11727) -- Bug in HTML writer can result in missing rows in output - @Erik Tilt [CodePlex #11743](https://phpexcel.codeplex.com/workitem/11743) -- setShowGridLines(true) not working with PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11674](https://phpexcel.codeplex.com/workitem/11674) -- PHPExcel_Worksheet_RowIterator initial position incorrect - @Erik Tilt [CodePlex #11836](https://phpexcel.codeplex.com/workitem/11836) -- PHPExcel_Worksheet_HeaderFooterDrawing Strict Exception thrown (by jshaw86) - @Erik Tilt [CodePlex #11835](https://phpexcel.codeplex.com/workitem/11835) -- Parts of worksheet lost when there are embedded charts (Excel5 reader) - @Erik Tilt [CodePlex #11850](https://phpexcel.codeplex.com/workitem/11850) -- VLOOKUP() function error when lookup value is passed as a cell reference rather than an absolute value - @MarkBaker -- First segment of Rich-Text not read correctly by PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #12041](https://phpexcel.codeplex.com/workitem/12041) -- Fatal Error with getCell('name') when name matches the pattern for a cell reference - @MarkBaker [CodePlex #12048](https://phpexcel.codeplex.com/workitem/12048) -- excel5 writer appears to be swapping image locations - @Erik Tilt [CodePlex #12039](https://phpexcel.codeplex.com/workitem/12039) -- Undefined index: host in ZipStreamWrapper.php, line 94 and line 101 - @Erik Tilt [CodePlex #11954](https://phpexcel.codeplex.com/workitem/11954) -- BIFF8 File Format problem (too short COLINFO record) - @Erik Tilt [CodePlex #11672](https://phpexcel.codeplex.com/workitem/11672) -- Column width sometimes changed after read/write with Excel2007 reader/writer - @Erik Tilt [CodePlex #12121](https://phpexcel.codeplex.com/workitem/12121) -- Worksheet.php throws a fatal error when styling is turned off via setReadDataOnly on the reader - @Erik Tilt [CodePlex #11964](https://phpexcel.codeplex.com/workitem/11964) -- Checking for Circular References in Formulae - @MarkBaker [CodePlex #11851](https://phpexcel.codeplex.com/workitem/11851) - - Calculation Engine code now traps for cyclic references, raising an error or throwing an exception, or allows 1 or more iterations through cyclic references, based on a configuration setting- PNG transparency using Excel2007 writer - @Erik Tilt [CodePlex #12244](https://phpexcel.codeplex.com/workitem/12244) -- Custom readfilter error when cell formulas reference excluded cells (Excel5 reader) - @Erik Tilt [CodePlex #12221](https://phpexcel.codeplex.com/workitem/12221) -- Protection problem in XLS - @Erik Tilt [CodePlex #12288](https://phpexcel.codeplex.com/workitem/12288) -- getColumnDimension()->setAutoSize() incorrect on cells with Number Formatting - @Erik Tilt [CodePlex #12300](https://phpexcel.codeplex.com/workitem/12300) -- Notices reading Excel file with Add-in funcitons (PHPExcel_Reader_Excel5) - @Erik Tilt [CodePlex #12378](https://phpexcel.codeplex.com/workitem/12378) -- Excel5 reader not reading formulas with deleted sheet references - @Erik Tilt [CodePlex #12380](https://phpexcel.codeplex.com/workitem/12380) -- Named range (defined name) scope problems for in PHPExcel - @Erik Tilt [CodePlex #12404](https://phpexcel.codeplex.com/workitem/12404) -- PHP Parse error: syntax error, unexpected T_PUBLIC in PHPExcel/Calculation.php on line 3482 - @Erik Tilt [CodePlex #12423](https://phpexcel.codeplex.com/workitem/12423) -- Named ranges don't appear in name box using Excel5 writer - @Erik Tilt [CodePlex #12505](https://phpexcel.codeplex.com/workitem/12505) -- Many merged cells + autoSize column -> slows down the writer - @Erik Tilt [CodePlex #12509](https://phpexcel.codeplex.com/workitem/12509) -- Incorrect fallback order comment in Shared/Strings.php ConvertEncoding() - @Erik Tilt [CodePlex #12539](https://phpexcel.codeplex.com/workitem/12539) -- IBM AIX iconv() will not work, should revert to mbstring etc. instead - @Erik Tilt [CodePlex #12538](https://phpexcel.codeplex.com/workitem/12538) -- Excel5 writer and mbstring functions overload - @Erik Tilt [CodePlex #12568](https://phpexcel.codeplex.com/workitem/12568) -- OFFSET needs to flattenSingleValue the $rows and $columns args - @MarkBaker [CodePlex #12672](https://phpexcel.codeplex.com/workitem/12672) -- Formula with DMAX(): Notice: Undefined offset: 2 in ...\PHPExcel\Calculation.php on line 2365 - @MarkBaker [CodePlex #12546](https://phpexcel.codeplex.com/workitem/12546) - - Note that the Database functions have not yet been implemented- Call to a member function getParent() on a non-object in Classes\\PHPExcel\\Calculation.php Title is required - @MarkBaker [CodePlex #12839](https://phpexcel.codeplex.com/workitem/12839) -- Cyclic Reference in Formula - @MarkBaker [CodePlex #12935](https://phpexcel.codeplex.com/workitem/12935) -- Memory error...data validation? - @MarkBaker [CodePlex #13025](https://phpexcel.codeplex.com/workitem/13025) - - -## [1.7.2] - 2010-01-11 - -### General - -- Applied patch 4362 - @Erik Tilt -- Applied patch 4363 (modified) - @Erik Tilt -- 1.7.1 Extremely Slow - Refactored PHPExcel_Calculation_Functions::flattenArray() method and set calculation cache timer default to 2.5 seconds - @MarkBaker [CodePlex #10874](https://phpexcel.codeplex.com/workitem/10874) -- Allow formulae to contain line breaks - @MarkBaker -- split() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10910](https://phpexcel.codeplex.com/workitem/10910) -- sys_get_temp_dir() requires PHP 5.2.1, not PHP 5.2 [provide fallback function for PHP 5.2.0] - @Erik Tilt -- Implementation of the ISPMT() Financial function by Matt Groves - @MarkBaker -- Put the example of formula with more arguments in documentation - @MarkBaker [CodePlex #11052](https://phpexcel.codeplex.com/workitem/11052) - -### Features - -- Improved accuracy for the GAMMAINV() Statistical Function - @MarkBaker -- XFEXT record support to fix colors change from Excel5 reader, and copy/paste color change with Excel5 writer - @Erik Tilt [CodePlex #10409](https://phpexcel.codeplex.com/workitem/10409) - - Excel5 reader reads RGB color information in XFEXT records for borders, font color and fill color- Implement more Excel calculation functions - @MarkBaker - - Implemented the FVSCHEDULE(), XNPV(), IRR(), MIRR(), XIRR() and RATE() Financial functions - - Implemented the SUMPRODUCT() Mathematical function - - Implemented the ZTEST() Statistical Function- Multiple print areas in one sheet - @Erik Tilt [CodePlex #10919](https://phpexcel.codeplex.com/workitem/10919) -- Store calculated values in output by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10930](https://phpexcel.codeplex.com/workitem/10930) -- Sheet protection options in Excel5 reader/writer - @Erik Tilt [CodePlex #10939](https://phpexcel.codeplex.com/workitem/10939) -- Modification of the COUNT(), AVERAGE(), AVERAGEA(), DEVSQ, AVEDEV(), STDEV(), STDEVA(), STDEVP(), STDEVPA(), VARA() and VARPA() SKEW() and KURT() functions to correctly handle boolean values depending on whether they're passed in as values, values within a matrix or values within a range of cells. - @MarkBaker -- Cell range selection - @Erik Tilt -- Root-relative path handling - @MarkBaker [CodePlex #10266](https://phpexcel.codeplex.com/workitem/10266) - -### Bugfixes - -- Named Ranges not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #11315](https://phpexcel.codeplex.com/workitem/11315) -- Excel2007 Reader fails to load Apache POI generated Excel - @MarkBaker [CodePlex #11206](https://phpexcel.codeplex.com/workitem/11206) -- Number format is broken when system's thousands separator is empty - @MarkBaker [CodePlex #11154](https://phpexcel.codeplex.com/workitem/11154) -- ReferenceHelper::updateNamedFormulas throws errors if oldName is empty - @MarkBaker [CodePlex #11401](https://phpexcel.codeplex.com/workitem/11401) -- parse_url() fails to parse path to an image in xlsx - @MarkBaker [CodePlex #11296](https://phpexcel.codeplex.com/workitem/11296) -- Workaround for iconv_substr() bug in PHP 5.2.0 - @Erik Tilt [CodePlex #10876](https://phpexcel.codeplex.com/workitem/10876) -- 1 pixel error for image width and height with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10877](https://phpexcel.codeplex.com/workitem/10877) -- Fix to GEOMEAN() Statistical function - @MarkBaker -- setValue('-') and setValue('.') sets numeric 0 instead of 1-character string - @Erik Tilt [CodePlex #10884](https://phpexcel.codeplex.com/workitem/10884) -- Row height sometimes much too low after read with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10885](https://phpexcel.codeplex.com/workitem/10885) -- Diagonal border. Miscellaneous missing support. - @Erik Tilt [CodePlex #10888](https://phpexcel.codeplex.com/workitem/10888) - - Constant PHPExcel_Style_Borders::DIAGONAL_BOTH added to support double-diagonal (cross) - - PHPExcel_Reader_Excel2007 not always reading diagonal borders (only recognizes 'true' and not '1') - - PHPExcel_Reader_Excel5 support for diagonal borders - - PHPExcel_Writer_Excel5 support for diagonal borders- Session bug: Fatal error: Call to a member function bindValue() on a non-object in ...\Classes\PHPExcel\Cell.php on line 217 - @Erik Tilt [CodePlex #10894](https://phpexcel.codeplex.com/workitem/10894) -- Colors messed up saving twice with same instance of PHPExcel_Writer_Excel5 (regression since 1.7.0) - @Erik Tilt [CodePlex #10896](https://phpexcel.codeplex.com/workitem/10896) -- Method PHPExcel_Worksheet::setDefaultStyle is not working - @Erik Tilt [CodePlex #10917](https://phpexcel.codeplex.com/workitem/10917) -- PHPExcel_Reader_CSV::canRead() sometimes says false when it shouldn't - @Erik Tilt [CodePlex #10897](https://phpexcel.codeplex.com/workitem/10897) -- Changes in workbook not picked up between two saves with PHPExcel_Writer_Excel2007 - @Erik Tilt [CodePlex #10922](https://phpexcel.codeplex.com/workitem/10922) -- Decimal and thousands separators missing in HTML and PDF output - @Erik Tilt [CodePlex #10913](https://phpexcel.codeplex.com/workitem/10913) -- Notices with PHPExcel_Reader_Excel5 and named array constants - @Erik Tilt [CodePlex #10936](https://phpexcel.codeplex.com/workitem/10936) -- Calculation engine limitation on 32-bit platform with integers > 2147483647 - @MarkBaker [CodePlex #10938](https://phpexcel.codeplex.com/workitem/10938) -- Shared(?) formulae containing absolute cell references not read correctly using Excel5 Reader - @Erik Tilt [CodePlex #10959](https://phpexcel.codeplex.com/workitem/10959) -- Warning messages with intersection operator involving single cell - @MarkBaker [CodePlex #10962](https://phpexcel.codeplex.com/workitem/10962) -- Infinite loop in Excel5 reader caused by zero-length string in SST - @Erik Tilt [CodePlex #10980](https://phpexcel.codeplex.com/workitem/10980) -- Remove unnecessary cell sorting to improve speed by approx. 18% in HTML and PDF writers - @Erik Tilt [CodePlex #10983](https://phpexcel.codeplex.com/workitem/10983) -- Cannot read A1 cell content - OO_Reader - @MarkBaker [CodePlex #10977](https://phpexcel.codeplex.com/workitem/10977) -- Transliteration failed, invalid encoding - @Erik Tilt [CodePlex #11000](https://phpexcel.codeplex.com/workitem/11000) - - -## [1.7.1] - 2009-11-02 - -### General - -- ereg() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10687](https://phpexcel.codeplex.com/workitem/10687) -- Writer Interface Inconsequence - setTempDir and setUseDiskCaching - @MarkBaker [CodePlex #10739](https://phpexcel.codeplex.com/workitem/10739) - -### Features - -- Upgrade to TCPDF 4.8.009 - @Erik Tilt -- Support for row and column styles (feature request) - @Erik Tilt - - Basic implementation for Excel2007/Excel5 reader/writer- Hyperlink to local file in Excel5 reader/writer - @Erik Tilt [CodePlex #10459](https://phpexcel.codeplex.com/workitem/10459) -- Color Tab (Color Sheet's name) - @MarkBaker [CodePlex #10472](https://phpexcel.codeplex.com/workitem/10472) -- Border style "double" support in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #10488](https://phpexcel.codeplex.com/workitem/10488) -- Multi-section number format support in HTML/PDF/CSV writers - @Erik Tilt [CodePlex #10492](https://phpexcel.codeplex.com/workitem/10492) -- Some additional performance tweaks in the calculation engine - @MarkBaker -- Fix result of DB() and DDB() Financial functions to 2dp when in Gnumeric Compatibility mode - @MarkBaker -- Added AMORDEGRC(), AMORLINC() and COUPNUM() Financial function (no validation of parameters yet) - @MarkBaker -- Improved accuracy of TBILLEQ(), TBILLPRICE() and TBILLYIELD() Financial functions when in Excel or Gnumeric mode - @MarkBaker -- Added INDIRECT() Lookup/Reference function (only supports full addresses at the moment) - @MarkBaker -- PHPExcel_Reader_CSV::canRead() improvements - @MarkBaker [CodePlex #10498](https://phpexcel.codeplex.com/workitem/10498) -- Input encoding option for PHPExcel_Reader_CSV - @Erik Tilt [CodePlex #10500](https://phpexcel.codeplex.com/workitem/10500) -- Colored number format support, e.g. [Red], in HTML/PDF output - @Erik Tilt [CodePlex #10493](https://phpexcel.codeplex.com/workitem/10493) -- Color Tab (Color Sheet's name) [Excel5 reader/writer support] - @Erik Tilt [CodePlex #10559](https://phpexcel.codeplex.com/workitem/10559) -- Initial version of SYLK (slk) and Excel 2003 XML Readers (Cell data and basic cell formatting) - @MarkBaker -- Initial version of Open Office Calc (ods) Reader (Cell data only) - @MarkBaker -- Initial use of "pass by reference" in the calculation engine for ROW() and COLUMN() Lookup/Reference functions - @MarkBaker -- COLUMNS() and ROWS() Lookup/Reference functions, and SUBSTITUTE() Text function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- AdvancedValueBinder(): Re-enable zero-padded string-to-number conversion, e.g '0004' -> 4 - @Erik Tilt [CodePlex #10502](https://phpexcel.codeplex.com/workitem/10502) -- Make PHP type match Excel datatype - @Erik Tilt [CodePlex #10600](https://phpexcel.codeplex.com/workitem/10600) -- Change first page number on header - @MarkBaker [CodePlex #10630](https://phpexcel.codeplex.com/workitem/10630) -- Applied patch 3941 - @MarkBaker -- Hidden sheets - @MB,ET [CodePlex #10745](https://phpexcel.codeplex.com/workitem/10745) -- mbstring fallback when iconv is broken - @Erik Tilt [CodePlex #10761](https://phpexcel.codeplex.com/workitem/10761) -- Note, can't yet handle comparison of two matrices - @MarkBaker -- Improved handling for validation and error trapping in a number of functions - @MarkBaker -- Improved support for fraction number formatting - @MarkBaker -- Support Reading CSV with Byte Order Mark (BOM) - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455) - -### Bugfixes - -- addExternalSheet() at specified index - @Erik Tilt [CodePlex #10860](https://phpexcel.codeplex.com/workitem/10860) -- Named range can no longer be passed to worksheet->getCell() - @MarkBaker [CodePlex #10684](https://phpexcel.codeplex.com/workitem/10684) -- RichText HTML entities no longer working in PHPExcel 1.7.0 - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455) -- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet [+ support for simultaneous scale/fitToPage] - @Erik Tilt -- Performance issue identified by profiling - @MarkBaker [CodePlex #10469](https://phpexcel.codeplex.com/workitem/10469) -- setSelectedCell is wrong - @Erik Tilt [CodePlex #10473](https://phpexcel.codeplex.com/workitem/10473) -- Images get squeezed/stretched with (Mac) Verdana 10 Excel files using Excel5 reader/writer - @Erik Tilt [CodePlex #10481](https://phpexcel.codeplex.com/workitem/10481) -- Error in argument count for DATEDIF() function - @MarkBaker [CodePlex #10482](https://phpexcel.codeplex.com/workitem/10482) -- updateFormulaReferences is buggy - @MarkBaker [CodePlex #10452](https://phpexcel.codeplex.com/workitem/10452) -- CellIterator returns null Cell if onlyExistingCells is set and key() is in use - @MarkBaker [CodePlex #10485](https://phpexcel.codeplex.com/workitem/10485) -- Wrong RegEx for parsing cell references in formulas - @MarkBaker [CodePlex #10453](https://phpexcel.codeplex.com/workitem/10453) -- Optimisation subverted to devastating effect if IterateOnlyExistingCells is clear - @MarkBaker [CodePlex #10486](https://phpexcel.codeplex.com/workitem/10486) -- Fatal error: Uncaught exception 'Exception' with message 'Unrecognized token 6C in formula'... with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10494](https://phpexcel.codeplex.com/workitem/10494) -- Fractions stored as text are not treated as numbers by PHPExcel's calculation engine - @MarkBaker [CodePlex #10490](https://phpexcel.codeplex.com/workitem/10490) -- AutoFit (autosize) row height not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10503](https://phpexcel.codeplex.com/workitem/10503) -- Fixed problem with null values breaking the calculation stack - @MarkBaker -- Date number formats sometimes fail with PHPExcel_Style_NumberFormat::toFormattedString, e.g. [$-40047]mmmm d yyyy - @Erik Tilt [CodePlex #10524](https://phpexcel.codeplex.com/workitem/10524) -- Fixed minor problem with DATEDIFF YM calculation - @MarkBaker -- Applied patch 3695 - @MarkBaker -- setAutosize() and Date cells not working properly - @Erik Tilt [CodePlex #10536](https://phpexcel.codeplex.com/workitem/10536) -- Time value hour offset in output by HTML/PDF/CSV writers (system timezone problem) - @Erik Tilt [CodePlex #10556](https://phpexcel.codeplex.com/workitem/10556) -- Control characters 0x14-0x1F are not treated by PHPExcel - @Erik Tilt [CodePlex #10558](https://phpexcel.codeplex.com/workitem/10558) -- PHPExcel_Writer_Excel5 not working when open_basedir restriction is in effect - @Erik Tilt [CodePlex #10560](https://phpexcel.codeplex.com/workitem/10560) -- IF formula calculation problem in PHPExcel 1.7.0 (string comparisons) - @MarkBaker [CodePlex #10563](https://phpexcel.codeplex.com/workitem/10563) -- Improved CODE() Text function result for UTF-8 characters - @MarkBaker -- Empty rows are collapsed with HTML/PDF writer - @Erik Tilt [CodePlex #10568](https://phpexcel.codeplex.com/workitem/10568) -- Gaps between rows in output by PHPExcel_Writer_PDF (Upgrading to TCPDF 4.7.003) - @Erik Tilt [CodePlex #10569](https://phpexcel.codeplex.com/workitem/10569) -- Problem reading formulas (Excel5 reader problem with "fake" shared formulas) - @Erik Tilt [CodePlex #10575](https://phpexcel.codeplex.com/workitem/10575) -- Error type in formula: "_raiseFormulaError message is Formula Error: An unexpected error occured" - @MarkBaker [CodePlex #10588](https://phpexcel.codeplex.com/workitem/10588) -- Miscellaneous column width problems in Excel5/Excel2007 writer - @Erik Tilt [CodePlex #10599](https://phpexcel.codeplex.com/workitem/10599) -- Reader/Excel5 'Unrecognized token 2D in formula' in latest version - @Erik Tilt [CodePlex #10615](https://phpexcel.codeplex.com/workitem/10615) -- on php 5.3 PHPExcel 1.7 Excel 5 reader fails in _getNextToken, token = 2C, throws exception - @Erik Tilt [CodePlex #10623](https://phpexcel.codeplex.com/workitem/10623) -- Fatal error when altering styles after workbook has been saved - @Erik Tilt [CodePlex #10617](https://phpexcel.codeplex.com/workitem/10617) -- Images vertically stretched or squeezed when default font size is changed (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10661](https://phpexcel.codeplex.com/workitem/10661) -- Styles not read in "manipulated" Excel2007 workbook - @Erik Tilt [CodePlex #10676](https://phpexcel.codeplex.com/workitem/10676) -- Windows 7 says corrupt file by PHPExcel_Writer_Excel5 when opening in Excel - @Erik Tilt [CodePlex #10059](https://phpexcel.codeplex.com/workitem/10059) -- Calculations sometimes not working with cell references to other sheets - @MarkBaker [CodePlex #10708](https://phpexcel.codeplex.com/workitem/10708) -- Problem with merged cells after insertNewRowBefore() - @Erik Tilt [CodePlex #10706](https://phpexcel.codeplex.com/workitem/10706) -- Applied patch 4023 - @MarkBaker -- Fix to SUMIF() and COUNTIF() Statistical functions for when condition is a match against a string value - @MarkBaker -- PHPExcel_Cell::coordinateFromString should throw exception for bad string parameter - @Erik Tilt [CodePlex #10721](https://phpexcel.codeplex.com/workitem/10721) -- EucrosiaUPC (Thai font) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10723](https://phpexcel.codeplex.com/workitem/10723) -- Improved the return of calculated results when the result value is an array - @MarkBaker -- Allow calculation engine to support Functions prefixed with @ within formulae - @MarkBaker -- Intersection operator (space operator) fatal error with calculation engine - @MarkBaker [CodePlex #10632](https://phpexcel.codeplex.com/workitem/10632) -- Chinese, Japanese, Korean characters show as squares in PDF - @Erik Tilt [CodePlex #10742](https://phpexcel.codeplex.com/workitem/10742) -- sheet title allows invalid characters - @Erik Tilt [CodePlex #10756](https://phpexcel.codeplex.com/workitem/10756) -- Sheet!$A$1 as function argument in formula causes infinite loop in Excel5 writer - @Erik Tilt [CodePlex #10757](https://phpexcel.codeplex.com/workitem/10757) -- Cell range involving name not working with calculation engine - Modified calculation parser to handle range operator (:), but doesn't currently handle worksheet references with spaces or other non-alphameric characters, or trap erroneous references - @MarkBaker [CodePlex #10740](https://phpexcel.codeplex.com/workitem/10740) -- DATE function problem with calculation engine (says too few arguments given) - @MarkBaker [CodePlex #10798](https://phpexcel.codeplex.com/workitem/10798) -- Blank cell can cause wrong calculated value - @MarkBaker [CodePlex #10799](https://phpexcel.codeplex.com/workitem/10799) -- Modified ROW() and COLUMN() Lookup/Reference Functions to return an array when passed a cell range, plus some additional work on INDEX() - @MarkBaker -- Images not showing in Excel 97 using PHPExcel_Writer_Excel5 (patch by Jordi Gutiérrez Hermoso) - @Erik Tilt [CodePlex #10817](https://phpexcel.codeplex.com/workitem/10817) -- When figures are contained in the excel sheet, Reader was stopped - @Erik Tilt [CodePlex #10785](https://phpexcel.codeplex.com/workitem/10785) -- Formulas changed after insertNewRowBefore() - @MarkBaker [CodePlex #10818](https://phpexcel.codeplex.com/workitem/10818) -- Cell range row offset problem with shared formulas using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10825](https://phpexcel.codeplex.com/workitem/10825) -- Warning: Call-time pass-by-reference has been deprecated - @MarkBaker [CodePlex #10832](https://phpexcel.codeplex.com/workitem/10832) -- Image should "Move but don't size with cells" instead of "Move and size with cells" with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10849](https://phpexcel.codeplex.com/workitem/10849) -- Opening a Excel5 generated XLS in Excel 2007 results in header/footer entry not showing on input - @Erik Tilt [CodePlex #10856](https://phpexcel.codeplex.com/workitem/10856) -- addExternalSheet() not returning worksheet - @Erik Tilt [CodePlex #10859](https://phpexcel.codeplex.com/workitem/10859) -- Invalid results in formulas with named ranges - @MarkBaker [CodePlex #10629](https://phpexcel.codeplex.com/workitem/10629) - - -## [1.7.0] - 2009-08-10 - -### General - -- Expand documentation: Number formats - @Erik Tilt -- Class 'PHPExcel_Cell_AdvancedValueBinder' not found - @Erik Tilt - -### Features - -- Change return type of date functions to PHPExcel_Calculation_Functions::RETURNDATE_EXCEL - @MarkBaker -- New RPN and stack-based calculation engine for improved performance of formula calculation - @MarkBaker - - Faster (anything between 2 and 12 times faster than the old parser, depending on the complexity and nature of the formula) - - Significantly more memory efficient when formulae reference cells across worksheets - - Correct behaviour when referencing Named Ranges that exist on several worksheets - - Support for Excel ^ (Exponential) and % (Percentage) operators - - Support for matrices within basic arithmetic formulae (e.g. ={1,2,3;4,5,6;7,8,9}/2) - - Better trapping/handling of NaN and infinity results (return #NUM! error) - - Improved handling of empty parameters for Excel functions - - Optional logging of calculation steps- New calculation engine can be accessed independently of workbooks (for use as a standalone calculator) - @MarkBaker -- Implement more Excel calculation functions - @MarkBaker - - Initial implementation of the COUNTIF() and SUMIF() Statistical functions - - Added ACCRINT() Financial function- Modifications to number format handling for dddd and ddd masks in dates, use of thousand separators even when locale only implements it for money, and basic fraction masks (0 ?/? and ?/?) - @MarkBaker -- Support arbitrary fixed number of decimals in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt -- Improving performance and memory on data dumps - @Erik Tilt - - Various style optimizations (merging from branch wi6857-memory) - - Moving hyperlink and dataValidation properties from cell to worksheet for lower PHP memory usage- Provide fluent interfaces where possible - @MarkBaker -- Make easy way to apply a border to a rectangular selection - @Erik Tilt -- Support for system window colors in PHPExcel_Reader_Excel5 - @Erik Tilt -- Horizontal center across selection - @Erik Tilt -- Merged cells record, write to full record size in PHPExcel_Writer_Excel5 - @Erik Tilt -- Add page break between sheets in exported PDF - @MarkBaker -- Sanitization of UTF-8 input for cell values - @Erik Tilt -- Read cached calculated value with PHPExcel_Reader_Excel5 - @Erik Tilt -- Miscellaneous CSS improvements for PHPExcel_Writer_HTML - @Erik Tilt -- getProperties: setCompany feature request - @Erik Tilt -- Insert worksheet at a specified index - @MarkBaker -- Change worksheet index - @MarkBaker -- Readfilter for CSV reader - @MarkBaker -- Check value of mbstring.func_overload when saving with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10172](https://phpexcel.codeplex.com/workitem/10172) -- Eliminate dependency of an include path pointing to class directory - @Erik Tilt [CodePlex #10251](https://phpexcel.codeplex.com/workitem/10251) -- Method for getting the correct reader for a certain file (contribution) - @Erik Tilt [CodePlex #10292](https://phpexcel.codeplex.com/workitem/10292) -- Choosing specific row in fromArray method - @Erik Tilt [CodePlex #10287](https://phpexcel.codeplex.com/workitem/10287) -- Shared formula support in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10319](https://phpexcel.codeplex.com/workitem/10319) - -### Bugfixes - -- Right-to-left column direction in worksheet - @MB,ET [CodePlex #10345](https://phpexcel.codeplex.com/workitem/10345) -- PHPExcel_Reader_Excel5 not reading PHPExcel_Style_NumberFormat::FORMAT_NUMBER ('0') - @Erik Tilt -- Fractional row height in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt -- Fractional (decimal) numbers not inserted correctly when locale is other than English - @Erik Tilt -- Fractional calculated value in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt -- Locale aware decimal and thousands separator in exported formats HTML, CSV, PDF - @Erik Tilt -- Cannot Add Image with Space on its Name - @MarkBaker -- Black line at top of every page in output by PHPExcel_Writer_PDF - @Erik Tilt -- Border styles and border colors not showing in HTML output (regression since 1.6.4) - @Erik Tilt -- Hidden screen gridlines setting in worksheet not read by PHPExcel_Reader_Excel2007 - @Erik Tilt -- Some valid sheet names causes corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker -- More than 32,767 characters in a cell gives corrupt Excel file - @Erik Tilt -- Images not getting copyied with the ->copy() function - @Erik Tilt -- Bad calculation of column width setAutoSize(true) function - @Erik Tilt -- Dates are sometimes offset by 1 day in output by HTML and PDF writers depending on system timezone setting - @Erik Tilt -- Wingdings symbol fonts not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10003](https://phpexcel.codeplex.com/workitem/10003) -- White space string prefix stripped by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #10010](https://phpexcel.codeplex.com/workitem/10010) -- The name of the Workbook stream MUST be "Workbook", not "Book" - @Erik Tilt [CodePlex #10023](https://phpexcel.codeplex.com/workitem/10023) -- Avoid message "Microsoft Excel recalculates formulas..." when closing xls file from Excel - @Erik Tilt [CodePlex #10030](https://phpexcel.codeplex.com/workitem/10030) -- Non-unique newline representation causes problems with LEN formula - @Erik Tilt [CodePlex #10031](https://phpexcel.codeplex.com/workitem/10031) -- Newline in cell not showing with PHPExcel_Writer_HTML and PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10033](https://phpexcel.codeplex.com/workitem/10033) -- Rich-Text strings get prefixed by   when output by HTML writer - @Erik Tilt [CodePlex #10046](https://phpexcel.codeplex.com/workitem/10046) -- Leading spaces do not appear in output by HTML/PDF writers - @Erik Tilt [CodePlex #10052](https://phpexcel.codeplex.com/workitem/10052) -- Empty Apache POI-generated file can not be read - @MarkBaker [CodePlex #10061](https://phpexcel.codeplex.com/workitem/10061) -- Column width not scaling correctly with font size in HTML and PDF writers - @Erik Tilt [CodePlex #10068](https://phpexcel.codeplex.com/workitem/10068) -- Inaccurate row heights with HTML writer - @Erik Tilt [CodePlex #10069](https://phpexcel.codeplex.com/workitem/10069) -- Reference helper - @MarkBaker -- Excel 5 Named ranges should not be local to the worksheet, but accessible from all worksheets - @MarkBaker -- Row heights are ignored by PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10088](https://phpexcel.codeplex.com/workitem/10088) -- Write raw XML - @MarkBaker -- removeRow(), removeColumn() not always clearing cell values - @Erik Tilt [CodePlex #10098](https://phpexcel.codeplex.com/workitem/10098) -- Problem reading certain hyperlink records with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10142](https://phpexcel.codeplex.com/workitem/10142) -- Hyperlink cell range read failure with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #10143](https://phpexcel.codeplex.com/workitem/10143) -- 'Column string index can not be empty.' - @MarkBaker [CodePlex #10149](https://phpexcel.codeplex.com/workitem/10149) -- getHighestColumn() sometimes says there are 256 columns with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10204](https://phpexcel.codeplex.com/workitem/10204) -- extractSheetTitle fails when sheet title contains exclamation mark (!) - @Erik Tilt [CodePlex #10220](https://phpexcel.codeplex.com/workitem/10220) -- setTitle() sometimes erroneously appends integer to sheet name - @Erik Tilt [CodePlex #10221](https://phpexcel.codeplex.com/workitem/10221) -- Mac BIFF5 Excel file read failure (missing support for Mac OS Roman character set) - @Erik Tilt [CodePlex #10229](https://phpexcel.codeplex.com/workitem/10229) -- BIFF5 header and footer incorrectly read by PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10230](https://phpexcel.codeplex.com/workitem/10230) -- iconv notices when reading hyperlinks with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10259](https://phpexcel.codeplex.com/workitem/10259) -- Excel5 reader OLE read failure with small Mac BIFF5 Excel files - @Erik Tilt [CodePlex #10252](https://phpexcel.codeplex.com/workitem/10252) -- Problem in reading formula : IF( IF ) with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10272](https://phpexcel.codeplex.com/workitem/10272) -- Error reading formulas referencing external sheets with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10274](https://phpexcel.codeplex.com/workitem/10274) -- Image horizontally stretched when default font size is increased (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10291](https://phpexcel.codeplex.com/workitem/10291) -- Undefined offset in Reader\Excel5.php on line 3572 - @Erik Tilt [CodePlex #10333](https://phpexcel.codeplex.com/workitem/10333) -- PDF output different then XLS (copied data) - @MarkBaker [CodePlex #10340](https://phpexcel.codeplex.com/workitem/10340) -- Internal hyperlinks with UTF-8 sheet names not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10352](https://phpexcel.codeplex.com/workitem/10352) -- String shared formula result read error with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10361](https://phpexcel.codeplex.com/workitem/10361) -- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/PageSetup.php:338 - @Erik Tilt [CodePlex #10363](https://phpexcel.codeplex.com/workitem/10363) -- Using setLoadSheetsOnly fails if you do not use setReadDataOnly(true) and sheet is not the first sheet - @Erik Tilt [CodePlex #10355](https://phpexcel.codeplex.com/workitem/10355) -- getCalculatedValue() sometimes incorrect with IF formula and 0-values - @MarkBaker [CodePlex #10362](https://phpexcel.codeplex.com/workitem/10362) -- Excel Reader 2007 problem with "shared" formulae when "master" is an error - @MarkBaker -- Named Range Bug, using the same range name on different worksheets - @MarkBaker -- Java code in JAMA classes - @MarkBaker -- getCalculatedValue() not working with some formulas involving error types - @MarkBaker -- evaluation of both return values in an IF() statement returning an error if either result was an error, irrespective of the IF evaluation - @MarkBaker -- Power in formulas: new calculation engine no longer treats ^ as a bitwise XOR operator - @MarkBaker -- Bugfixes and improvements to many of the Excel functions in PHPExcel - @MarkBaker - - Added optional "places" parameter in the BIN2HEX(), BIN2OCT, DEC2BIN(), DEC2OCT(), DEC2HEX(), HEX2BIN(), HEX2OCT(), OCT2BIN() and OCT2HEX() Engineering Functions - - Trap for unbalanced matrix sizes in MDETERM() and MINVERSE() Mathematic and Trigonometric functions - - Fix for default characters parameter value for LEFT() and RIGHT() Text functions - - Fix for GCD() and LCB() Mathematical functions when the parameters include a zero (0) value - - Fix for BIN2OCT() Engineering Function for 2s complement values (which were returning hex values) - - Fix for BESSELK() and BESSELY() Engineering functions - - Fix for IMDIV() Engineering Function when result imaginary component is positive (wasn't setting the sign) - - Fix for ERF() Engineering Function when called with an upper limit value for the integration - - Fix to DATE() Date/Time Function for year value of 0 - - Set ISPMT() function as category FINANCIAL - - Fix for DOLLARDE() and DOLLARFR() Financial functions - - Fix to EFFECT() Financial function (treating $nominal_rate value as a variable name rather than a value) - - Fix to CRITBINOM() Statistical function (CurrentValue and EssentiallyZero treated as constants rather than variables) - - Note that an Error in the function logic can still lead to a permanent loop - - Fix to MOD() Mathematical function to work with floating point results - - Fix for QUOTIENT() Mathematical function - - Fix to HOUR(), MINUTE() and SECOND() Date/Time functions to return an error when passing in a floating point value of 1.0 or greater, or less than 0 - - LOG() Function now correctly returns base-10 log when called with only one parameter, rather than the natural log as the default base - - Modified text functions to handle multibyte character set (UTF-8). - -## [1.6.7] - 2009-04-22 - -### BREAKING CHANGE - -In previous versions of PHPExcel up to and including 1.6.6, -when a cell had a date-like number format code, it was possible to enter a date -directly using an integer PHP-time without converting to Excel date format. -Starting with PHPExcel 1.6.7 this is no longer supported. Refer to the developer -documentation for more information on entering dates into a cell. - -### General - -- Deprecate misspelled setStriketrough() and getStriketrough() methods - @MarkBaker [CodePlex #9416](https://phpexcel.codeplex.com/workitem/9416) - -### Features - -- Performance improvement when saving file - @MarkBaker [CodePlex #9526](https://phpexcel.codeplex.com/workitem/9526) -- Check that sheet title has maximum 31 characters - @MarkBaker [CodePlex #9598](https://phpexcel.codeplex.com/workitem/9598) -- True support for Excel built-in number format codes - @MB, ET [CodePlex #9631](https://phpexcel.codeplex.com/workitem/9631) -- Ability to read defect BIFF5 Excel file without CODEPAGE record - @Erik Tilt [CodePlex #9683](https://phpexcel.codeplex.com/workitem/9683) -- Auto-detect which reader to invoke - @MarkBaker [CodePlex #9701](https://phpexcel.codeplex.com/workitem/9701) -- Deprecate insertion of dates using PHP-time (Unix time) [request for removal of feature] - @Erik Tilt [CodePlex #9214](https://phpexcel.codeplex.com/workitem/9214) -- Support for entering time values like '9:45', '09:45' using AdvancedValueBinder - @Erik Tilt [CodePlex #9747](https://phpexcel.codeplex.com/workitem/9747) - -### Bugfixes - -- DataType dependent horizontal alignment in HTML and PDF writer - @Erik Tilt [CodePlex #9797](https://phpexcel.codeplex.com/workitem/9797) -- Cloning data validation object causes script to stop - @MarkBaker [CodePlex #9375](https://phpexcel.codeplex.com/workitem/9375) -- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9400](https://phpexcel.codeplex.com/workitem/9400) -- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9399](https://phpexcel.codeplex.com/workitem/9399) -- Row outline level not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9437](https://phpexcel.codeplex.com/workitem/9437) -- Occasional notices with PHPExcel_Reader_Excel5 when Excel file contains drawing elements - @Erik Tilt [CodePlex #9452](https://phpexcel.codeplex.com/workitem/9452) -- PHPExcel_Reader_Excel5 fails as a whole when workbook contains images other than JPEG/PNG - @Erik Tilt [CodePlex #9453](https://phpexcel.codeplex.com/workitem/9453) -- Excel5 writer checks for iconv but does not necessarily use it - @Erik Tilt [CodePlex #9444](https://phpexcel.codeplex.com/workitem/9444) -- Altering a style on copied worksheet alters also the original - @Erik Tilt [CodePlex #9463](https://phpexcel.codeplex.com/workitem/9463) -- Formulas are incorrectly updated when a sheet is renamed - @MarkBaker [CodePlex #9480](https://phpexcel.codeplex.com/workitem/9480) -- PHPExcel_Worksheet::extractSheetTitle not treating single quotes correctly - @MarkBaker [CodePlex #9513](https://phpexcel.codeplex.com/workitem/9513) -- PHP Warning raised in function array_key_exists - @MarkBaker [CodePlex #9477](https://phpexcel.codeplex.com/workitem/9477) -- getAlignWithMargins() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9599](https://phpexcel.codeplex.com/workitem/9599) -- getScaleWithDocument() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9600](https://phpexcel.codeplex.com/workitem/9600) -- PHPExcel_Reader_Excel2007 not reading the first user-defined number format - @MarkBaker [CodePlex #9630](https://phpexcel.codeplex.com/workitem/9630) -- Print area converted to uppercase after read with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9647](https://phpexcel.codeplex.com/workitem/9647) -- Incorrect reading of scope for named range using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9661](https://phpexcel.codeplex.com/workitem/9661) -- Error with pattern (getFillType) and rbg (getRGB) - @MarkBaker [CodePlex #9690](https://phpexcel.codeplex.com/workitem/9690) -- AdvancedValueBinder affected by system timezone setting when inserting date values - @Erik Tilt [CodePlex #9712](https://phpexcel.codeplex.com/workitem/9712) -- PHPExcel_Reader_Excel2007 not reading value of active sheet index - @Erik Tilt [CodePlex #9743](https://phpexcel.codeplex.com/workitem/9743) -- getARGB() sometimes returns SimpleXMLElement object instead of string with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #9742](https://phpexcel.codeplex.com/workitem/9742) -- Negative image offset causes defects in 14excel5.xls and 20readexcel5.xlsx - @Erik Tilt [CodePlex #9731](https://phpexcel.codeplex.com/workitem/9731) -- HTML & PDF Writer not working with mergeCells (regression since 1.6.5) - @Erik Tilt [CodePlex #9758](https://phpexcel.codeplex.com/workitem/9758) -- Too wide columns with HTML and PDF writer - @Erik Tilt [CodePlex #9774](https://phpexcel.codeplex.com/workitem/9774) -- PDF and cyrillic fonts - @MarkBaker [CodePlex #9775](https://phpexcel.codeplex.com/workitem/9775) -- Percentages not working correctly with HTML and PDF writers (shows 0.25% instead of 25%) - @Erik Tilt [CodePlex #9793](https://phpexcel.codeplex.com/workitem/9793) -- PHPExcel_Writer_HTML creates extra borders around cell contents using setUseInlineCss(true) - @Erik Tilt [CodePlex #9791](https://phpexcel.codeplex.com/workitem/9791) -- Problem with text wrap + merged cells in HTML and PDF writer - @Erik Tilt [CodePlex #9784](https://phpexcel.codeplex.com/workitem/9784) -- Adjacent path separators in include_path causing IOFactory to violate open_basedir restriction - @Erik Tilt [CodePlex #9814](https://phpexcel.codeplex.com/workitem/9814) - - -## [1.6.6] - 2009-03-02 - -### General - -- Improve support for built-in number formats in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9102](https://phpexcel.codeplex.com/workitem/9102) -- Source files are in both UNIX and DOS formats - changed to UNIX - @Erik Tilt [CodePlex #9281](https://phpexcel.codeplex.com/workitem/9281) - -### Features - -- Update documentation: Which language to write formulas in? - @MarkBaker [CodePlex #9338](https://phpexcel.codeplex.com/workitem/9338) -- Ignore DEFCOLWIDTH records with value 8 in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8817](https://phpexcel.codeplex.com/workitem/8817) -- Support for width, height, offsetX, offsetY for images in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8847](https://phpexcel.codeplex.com/workitem/8847) -- Disk Caching in specific folder - @MarkBaker [CodePlex #8870](https://phpexcel.codeplex.com/workitem/8870) -- Added SUMX2MY2, SUMX2PY2, SUMXMY2, MDETERM and MINVERSE Mathematical and Trigonometric Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added CONVERT Engineering Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added DB, DDB, DISC, DOLLARDE, DOLLARFR, INTRATE, IPMT, PPMT, PRICEDISC, PRICEMAT and RECEIVED Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added ACCRINTM, CUMIPMT, CUMPRINC, TBILLEQ, TBILLPRICE, TBILLYIELD, YIELDDISC and YIELDMAT Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added DOLLAR Text Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added CORREL, COVAR, FORECAST, INTERCEPT, RSQ, SLOPE and STEYX Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added PEARSON Statistical Functions as a synonym for CORREL - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added LINEST, LOGEST (currently only valid for stats = false), TREND and GROWTH Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added RANK and PERCENTRANK Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added ROMAN Mathematical Function (Classic form only) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Update documentation to show example of getCellByColumnAndRow($col, $row) - @MarkBaker [CodePlex #8931](https://phpexcel.codeplex.com/workitem/8931) -- Implement worksheet, row and cell iterators - @MarkBaker [CodePlex #8770](https://phpexcel.codeplex.com/workitem/8770) -- Support for arbitrary defined names (named range) - @MarkBaker [CodePlex #9001](https://phpexcel.codeplex.com/workitem/9001) -- Update formulas when sheet title / named range title changes - @MB, ET [CodePlex #9016](https://phpexcel.codeplex.com/workitem/9016) -- Ability to read cached calculated value - @MarkBaker [CodePlex #9103](https://phpexcel.codeplex.com/workitem/9103) -- Support for Excel 1904 calendar date mode (Mac) - @MBaker, ET [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483) -- PHPExcel_Writer_Excel5 improvements writing shared strings table - @Erik Tilt [CodePlex #9194](https://phpexcel.codeplex.com/workitem/9194) -- PHPExcel_Writer_Excel5 iconv fallback when mbstring extension is not enabled - @Erik Tilt [CodePlex #9248](https://phpexcel.codeplex.com/workitem/9248) -- UTF-8 support in font names in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9253](https://phpexcel.codeplex.com/workitem/9253) -- Implement value binding architecture - @MarkBaker [CodePlex #9215](https://phpexcel.codeplex.com/workitem/9215) -- PDF writer not working with UTF-8 - @MarkBaker [CodePlex #6742](https://phpexcel.codeplex.com/workitem/6742) - -### Bugfixes - -- Eliminate duplicate style entries in multisheet workbook written by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9355](https://phpexcel.codeplex.com/workitem/9355) -- Redirect to client browser fails due to trailing white space in class definitions - @Erik Tilt [CodePlex #8810](https://phpexcel.codeplex.com/workitem/8810) -- Spurious column dimension element introduced in blank worksheet after using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8816](https://phpexcel.codeplex.com/workitem/8816) -- Image gets slightly narrower than expected when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8830](https://phpexcel.codeplex.com/workitem/8830) -- Image laid over non-visible row gets squeezed in height when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8831](https://phpexcel.codeplex.com/workitem/8831) -- PHPExcel_Reader_Excel5 fails when there are 10 or more images in the workbook - @Erik Tilt [CodePlex #8860](https://phpexcel.codeplex.com/workitem/8860) -- Different header/footer images in different sheets not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8909](https://phpexcel.codeplex.com/workitem/8909) -- Fractional seconds disappear when using PHPExcel_Reader_Excel2007 and PHPExcel_Reader_Excel5 - @MB, ET [CodePlex #8924](https://phpexcel.codeplex.com/workitem/8924) -- Images not showing in OpenOffice when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7994](https://phpexcel.codeplex.com/workitem/7994) -- Images not showing on print using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9047](https://phpexcel.codeplex.com/workitem/9047) -- PHPExcel_Writer_Excel5 maximum allowed record size 4 bytes too short - @Erik Tilt [CodePlex #9085](https://phpexcel.codeplex.com/workitem/9085) -- Not numeric strings are formatted as dates and numbers using worksheet's toArray method - @MarkBaker [CodePlex #9119](https://phpexcel.codeplex.com/workitem/9119) -- Excel5 simple formula parsing error - @Erik Tilt [CodePlex #9132](https://phpexcel.codeplex.com/workitem/9132) -- Problems writing dates with CSV - @Erik Tilt [CodePlex #9206](https://phpexcel.codeplex.com/workitem/9206) -- PHPExcel_Reader_Excel5 reader fails with fatal error when reading group shapes - @Erik Tilt [CodePlex #9203](https://phpexcel.codeplex.com/workitem/9203) -- PHPExcel_Writer_Excel5 fails completely when workbook contains more than 57 colors - @Erik Tilt [CodePlex #9231](https://phpexcel.codeplex.com/workitem/9231) -- PHPExcel_Writer_PDF not compatible with autoload - @Erik Tilt [CodePlex #9244](https://phpexcel.codeplex.com/workitem/9244) -- Fatal error: Call to a member function getNestingLevel() on a non-object in PHPExcel/Reader/Excel5.php on line 690 - @Erik Tilt [CodePlex #9250](https://phpexcel.codeplex.com/workitem/9250) -- Notices when running test 04printing.php on PHP 5.2.8 - @MarkBaker [CodePlex #9246](https://phpexcel.codeplex.com/workitem/9246) -- insertColumn() spawns creation of spurious RowDimension - @MarkBaker [CodePlex #9294](https://phpexcel.codeplex.com/workitem/9294) -- Fix declarations for methods in extended Trend classes - @MarkBaker [CodePlex #9296](https://phpexcel.codeplex.com/workitem/9296) -- Fix to parameters for the FORECAST Statistical Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- PDF writer problems with cell height and text wrapping - @MarkBaker [CodePlex #7083](https://phpexcel.codeplex.com/workitem/7083) -- Fix test for calculated value in case the returned result is an array - @MarkBaker -- Column greater than 256 results in corrupt Excel file using PHPExcel_Writer_Excel5 - @Erik Tilt -- Excel Numberformat 0.00 results in non internal decimal places values in toArray() Method - @MarkBaker [CodePlex #9351](https://phpexcel.codeplex.com/workitem/9351) -- setAutoSize not taking into account text rotation - @MB,ET [CodePlex #9356](https://phpexcel.codeplex.com/workitem/9356) -- Call to undefined method PHPExcel_Worksheet_MemoryDrawing::getPath() in PHPExcel/Writer/HTML.php - @Erik Tilt [CodePlex #9372](https://phpexcel.codeplex.com/workitem/9372) - - -## [1.6.5] - 2009-01-05 - -### General - -- Applied patch 2063 - @MarkBaker -- Optimise Shared Strings - @MarkBaker -- Optimise Cell Sorting - @MarkBaker -- Optimise Style Hashing - @MarkBaker -- UTF-8 enhancements - @Erik Tilt -- PHPExcel_Writer_HTML validation errors against strict HTML 4.01 / CSS 2.1 - @Erik Tilt -- Documented work items 6203 and 8110 in manual - @MarkBaker -- Restructure package hierachy so classes can be found more easily in auto-generated API (from work item 8468) - @Erik Tilt - -### Features - -- Redirect output to a client's browser: Update recommendation in documentation - @MarkBaker [CodePlex #8806](https://phpexcel.codeplex.com/workitem/8806) -- PHPExcel_Reader_Excel5 support for print gridlines - @Erik Tilt [CodePlex #7897](https://phpexcel.codeplex.com/workitem/7897) -- Screen gridlines support in Excel5 reader/writer - @Erik Tilt [CodePlex #7899](https://phpexcel.codeplex.com/workitem/7899) -- Option for adding image to spreadsheet from image resource in memory - @MB, ET [CodePlex #7552](https://phpexcel.codeplex.com/workitem/7552) -- PHPExcel_Reader_Excel5 style support for BIFF5 files (Excel 5.0 - Excel 95) - @Erik Tilt [CodePlex #7862](https://phpexcel.codeplex.com/workitem/7862) -- PHPExcel_Reader_Excel5 support for user-defined colors and special built-in colors - @Erik Tilt [CodePlex #7918](https://phpexcel.codeplex.com/workitem/7918) -- Support for freeze panes in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7992](https://phpexcel.codeplex.com/workitem/7992) -- Support for header and footer margins in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7996](https://phpexcel.codeplex.com/workitem/7996) -- Support for active sheet index in Excel5 reader/writer - @Erik Tilt [CodePlex #7997](https://phpexcel.codeplex.com/workitem/7997) -- Freeze panes not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7991](https://phpexcel.codeplex.com/workitem/7991) -- Support for screen zoom level (feature request) - @MB, ET [CodePlex #7993](https://phpexcel.codeplex.com/workitem/7993) -- Support for default style in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8012](https://phpexcel.codeplex.com/workitem/8012) -- Apple iWork / Numbers.app incompatibility - @MarkBaker [CodePlex #8094](https://phpexcel.codeplex.com/workitem/8094) -- Support "between rule" in conditional formatting - @MarkBaker [CodePlex #7931](https://phpexcel.codeplex.com/workitem/7931) -- Comment size, width and height control (feature request) - @MarkBaker [CodePlex #8308](https://phpexcel.codeplex.com/workitem/8308) -- Improve method for storing MERGEDCELLS records in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8418](https://phpexcel.codeplex.com/workitem/8418) -- Support for protectCells() in Excel5 reader/writer - @Erik Tilt [CodePlex #8435](https://phpexcel.codeplex.com/workitem/8435) -- Support for fitToWidth and fitToHeight pagesetup properties in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8472](https://phpexcel.codeplex.com/workitem/8472) -- Support for setShowSummaryBelow() and setShowSummaryRight() in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8489](https://phpexcel.codeplex.com/workitem/8489) -- Support for Excel 1904 calendar date mode (Mac) - @MarkBaker [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483) -- Excel5 reader: Support for reading images (bitmaps) - @Erik Tilt [CodePlex #7538](https://phpexcel.codeplex.com/workitem/7538) -- Support for default style in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8787](https://phpexcel.codeplex.com/workitem/8787) -- Modified calculate() method to return either an array or the first value from the array for those functions that return arrays rather than single values (e.g the MMULT and TRANSPOSE function). This performance can be modified based on the $returnArrayAsType which can be set/retrieved by calling the setArrayReturnType() and getArrayReturnType() methods of the PHPExcel_Calculation class. - @MarkBaker - -### Bugfixes - -- Added ERROR.TYPE Information Function, MMULT Mathematical and Trigonometry Function, and TRANSPOSE Lookup and Reference Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- setPrintGridlines(true) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7896](https://phpexcel.codeplex.com/workitem/7896) -- Incorrect mapping of fill patterns in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7907](https://phpexcel.codeplex.com/workitem/7907) -- setShowGridlines(false) not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7898](https://phpexcel.codeplex.com/workitem/7898) -- getShowGridlines() gives inverted value when reading sheet with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7905](https://phpexcel.codeplex.com/workitem/7905) -- User-defined column width becomes slightly larger after read/write with Excel5 - @Erik Tilt [CodePlex #7944](https://phpexcel.codeplex.com/workitem/7944) -- Incomplete border style support in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7949](https://phpexcel.codeplex.com/workitem/7949) -- Conditional formatting "containsText" read/write results in MS Office Excel 2007 crash - @MarkBaker [CodePlex #7928](https://phpexcel.codeplex.com/workitem/7928) -- All sheets are always selected in output when using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7995](https://phpexcel.codeplex.com/workitem/7995) -- COLUMN function warning message during plain read/write - @MarkBaker [CodePlex #8013](https://phpexcel.codeplex.com/workitem/8013) -- setValue(0) results in string data type '0' - @MarkBaker [CodePlex #8155](https://phpexcel.codeplex.com/workitem/8155) -- Styles not removed when removing rows from sheet - @MarkBaker [CodePlex #8226](https://phpexcel.codeplex.com/workitem/8226) -- =IF formula causes fatal error during $objWriter->save() in Excel2007 format - @MarkBaker [CodePlex #8301](https://phpexcel.codeplex.com/workitem/8301) -- Exception thrown reading valid xls file: "Excel file is corrupt. Didn't find CONTINUE record while reading shared strings" - @Erik Tilt [CodePlex #8333](https://phpexcel.codeplex.com/workitem/8333) -- MS Outlook corrupts files generated by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8320](https://phpexcel.codeplex.com/workitem/8320) -- Undefined method PHPExcel_Worksheet::setFreezePane() in ReferenceHelper.php on line 271 - @MarkBaker [CodePlex #8351](https://phpexcel.codeplex.com/workitem/8351) -- Ampersands (&), left and right angles (<, >) in Rich-Text strings leads to corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8401](https://phpexcel.codeplex.com/workitem/8401) -- Print header and footer not supporting UTF-8 in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8408](https://phpexcel.codeplex.com/workitem/8408) -- Vertical page breaks not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8463](https://phpexcel.codeplex.com/workitem/8463) -- Missing support for accounting underline types in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8476](https://phpexcel.codeplex.com/workitem/8476) -- Infinite loops when reading corrupt xls file using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8482](https://phpexcel.codeplex.com/workitem/8482) -- Sheet protection password not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8566](https://phpexcel.codeplex.com/workitem/8566) -- PHPExcel_Style_NumberFormat::FORMAT_NUMBER ignored by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8596](https://phpexcel.codeplex.com/workitem/8596) -- PHPExcel_Reader_Excel5 fails a whole when workbook contains a chart - @Erik Tilt [CodePlex #8781](https://phpexcel.codeplex.com/workitem/8781) -- Occasional loss of column widths using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8788](https://phpexcel.codeplex.com/workitem/8788) -- Notices while reading formulas with deleted sheet references using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8795](https://phpexcel.codeplex.com/workitem/8795) -- Default style not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #8807](https://phpexcel.codeplex.com/workitem/8807) -- Blank rows occupy too much space in file generated by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9341](https://phpexcel.codeplex.com/workitem/9341) - - -## [1.6.4] - 2008-10-27 - -### Features - -- RK record number error in MS developer documentation: 0x007E should be 0x027E - @Erik Tilt [CodePlex #7882](https://phpexcel.codeplex.com/workitem/7882) -- getHighestColumn() returning "@" for blank worksheet causes corrupt output - @MarkBaker [CodePlex #7878](https://phpexcel.codeplex.com/workitem/7878) -- Implement ROW and COLUMN Lookup/Reference Functions (when specified with a parameter) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement initial work on OFFSET Lookup/Reference Function (returning address rather than value at address) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Excel5 reader: Page margins - @Erik Tilt [CodePlex #7416](https://phpexcel.codeplex.com/workitem/7416) -- Excel5 reader: Header & Footer - @Erik Tilt [CodePlex #7417](https://phpexcel.codeplex.com/workitem/7417) -- Excel5 reader support for page setup (paper size etc.) - @Erik Tilt [CodePlex #7449](https://phpexcel.codeplex.com/workitem/7449) -- Improve speed and memory consumption of PHPExcel_Writer_CSV - @MarkBaker [CodePlex #7445](https://phpexcel.codeplex.com/workitem/7445) -- Better recognition of number format in HTML, CSV, and PDF writer - @MarkBaker [CodePlex #7432](https://phpexcel.codeplex.com/workitem/7432) -- Font support: Superscript and Subscript - @MarkBaker [CodePlex #7485](https://phpexcel.codeplex.com/workitem/7485) -- Excel5 reader font support: Super- and subscript - @Erik Tilt [CodePlex #7509](https://phpexcel.codeplex.com/workitem/7509) -- Excel5 reader style support: Text rotation and stacked text - @Erik Tilt [CodePlex #7521](https://phpexcel.codeplex.com/workitem/7521) -- Excel5 reader: Support for hyperlinks - @Erik Tilt [CodePlex #7530](https://phpexcel.codeplex.com/workitem/7530) -- Import sheet by request - @MB, ET [CodePlex #7557](https://phpexcel.codeplex.com/workitem/7557) -- PHPExcel_Reader_Excel5 support for page breaks - @Erik Tilt [CodePlex #7607](https://phpexcel.codeplex.com/workitem/7607) -- PHPExcel_Reader_Excel5 support for shrink-to-fit - @Erik Tilt [CodePlex #7622](https://phpexcel.codeplex.com/workitem/7622) -- Support for error types - @MB, ET [CodePlex #7675](https://phpexcel.codeplex.com/workitem/7675) -- Excel5 reader true formula support - @Erik Tilt [CodePlex #7388](https://phpexcel.codeplex.com/workitem/7388) -- Support for named ranges (defined names) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7701](https://phpexcel.codeplex.com/workitem/7701) -- Support for repeating rows and repeating columns (print titles) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7781](https://phpexcel.codeplex.com/workitem/7781) -- Support for print area in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7783](https://phpexcel.codeplex.com/workitem/7783) -- Excel5 reader and writer support for horizontal and vertical centering of page - @Erik Tilt [CodePlex #7795](https://phpexcel.codeplex.com/workitem/7795) -- Applied patch 1962 - @MarkBaker -- Excel5 reader and writer support for hidden cells (formulas) - @Erik Tilt [CodePlex #7866](https://phpexcel.codeplex.com/workitem/7866) -- Support for indentation in cells (feature request) - @MB, ET [CodePlex #7612](https://phpexcel.codeplex.com/workitem/7612) - -### Bugfixes - -- Option for reading only specified interval of rows in a sheet - @MB, ET [CodePlex #7828](https://phpexcel.codeplex.com/workitem/7828) -- PHPExcel_Calculation_Functions::DATETIMENOW() and PHPExcel_Calculation_Functions::DATENOW() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367) -- Modified PHPExcel_Shared_Date::FormattedPHPToExcel() and PHPExcel_Shared_Date::ExcelToPHP to force datatype for return values - @MarkBaker [CodePlex #7395](https://phpexcel.codeplex.com/workitem/7395) -- Excel5 reader not producing UTF-8 strings with BIFF5 files - @Erik Tilt [CodePlex #7450](https://phpexcel.codeplex.com/workitem/7450) -- Array constant in formula gives run-time notice with Excel2007 writer - @MarkBaker [CodePlex #7470](https://phpexcel.codeplex.com/workitem/7470) -- PHPExcel_Reader_Excel2007 setReadDataOnly(true) returns Rich-Text - @MarkBaker [CodePlex #7494](https://phpexcel.codeplex.com/workitem/7494) -- PHPExcel_Reader_Excel5 setReadDataOnly(true) returns Rich-Text - @Erik Tilt [CodePlex #7496](https://phpexcel.codeplex.com/workitem/7496) -- Characters before superscript or subscript losing style - @MarkBaker [CodePlex #7497](https://phpexcel.codeplex.com/workitem/7497) -- Subscript not working with HTML writer - @MarkBaker [CodePlex #7507](https://phpexcel.codeplex.com/workitem/7507) -- DefaultColumnDimension not working on first column (A) - @MarkBaker [CodePlex #7508](https://phpexcel.codeplex.com/workitem/7508) -- Negative numbers are stored as text in PHPExcel_Writer_2007 - @MarkBaker [CodePlex #7527](https://phpexcel.codeplex.com/workitem/7527) -- Text rotation and stacked text not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7531](https://phpexcel.codeplex.com/workitem/7531) -- PHPExcel_Shared_Date::isDateTimeFormatCode erroneously says true - @MarkBaker [CodePlex #7536](https://phpexcel.codeplex.com/workitem/7536) -- Different images with same filename in separate directories become duplicates - @MarkBaker [CodePlex #7559](https://phpexcel.codeplex.com/workitem/7559) -- PHPExcel_Reader_Excel5 not returning sheet names as UTF-8 using for Excel 95 files - @Erik Tilt [CodePlex #7568](https://phpexcel.codeplex.com/workitem/7568) -- setAutoSize(true) on empty column gives column width of 10 using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7575](https://phpexcel.codeplex.com/workitem/7575) -- setAutoSize(true) on empty column gives column width of 255 using PHPExcel_Writer_Excel5 - @MB, ET [CodePlex #7573](https://phpexcel.codeplex.com/workitem/7573) -- Worksheet_Drawing bug - @MarkBaker [CodePlex #7514](https://phpexcel.codeplex.com/workitem/7514) -- getCalculatedValue() with REPT function causes script to stop - @MarkBaker [CodePlex #7593](https://phpexcel.codeplex.com/workitem/7593) -- getCalculatedValue() with LEN function causes script to stop - @MarkBaker [CodePlex #7594](https://phpexcel.codeplex.com/workitem/7594) -- Explicit fit-to-width (page setup) results in fit-to-height becoming 1 - @MarkBaker [CodePlex #7600](https://phpexcel.codeplex.com/workitem/7600) -- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet - @MarkBaker [CodePlex #7610](https://phpexcel.codeplex.com/workitem/7610) -- Conditional styles not read properly using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7516](https://phpexcel.codeplex.com/workitem/7516) -- PHPExcel_Writer_2007: Default worksheet style works only for first sheet - @MarkBaker [CodePlex #7611](https://phpexcel.codeplex.com/workitem/7611) -- Cannot Lock Cells using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #6940](https://phpexcel.codeplex.com/workitem/6940) -- Incorrect cell protection values found when using Excel5 reader - @Erik Tilt [CodePlex #7621](https://phpexcel.codeplex.com/workitem/7621) -- Default row height not working above highest row using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7623](https://phpexcel.codeplex.com/workitem/7623) -- Default column width does not get applied when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7637](https://phpexcel.codeplex.com/workitem/7637) -- Broken support for UTF-8 string formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7642](https://phpexcel.codeplex.com/workitem/7642) -- UTF-8 sheet names not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7643](https://phpexcel.codeplex.com/workitem/7643) -- getCalculatedValue() with ISNONTEXT function causes script to stop - @MarkBaker [CodePlex #7631](https://phpexcel.codeplex.com/workitem/7631) -- Missing BIFF3 functions in PHPExcel_Writer_Excel5: USDOLLAR (YEN), FINDB, SEARCHB, REPLACEB, LEFTB, RIGHTB, MIDB, LENB, ASC, DBCS (JIS) - @Erik Tilt [CodePlex #7652](https://phpexcel.codeplex.com/workitem/7652) -- Excel5 reader doesn't read numbers correctly in 64-bit systems - @Erik Tilt [CodePlex #7663](https://phpexcel.codeplex.com/workitem/7663) -- Missing BIFF5 functions in PHPExcel_Writer_Excel5: ISPMT, DATEDIF, DATESTRING, NUMBERSTRING - @Erik Tilt [CodePlex #7667](https://phpexcel.codeplex.com/workitem/7667) -- Missing BIFF8 functions in PHPExcel_Writer_Excel5: GETPIVOTDATA, HYPERLINK, PHONETIC, AVERAGEA, MAXA, MINA, STDEVPA, VARPA, STDEVA, VARA - @Erik Tilt [CodePlex #7668](https://phpexcel.codeplex.com/workitem/7668) -- Wrong host value in PHPExcel_Shared_ZipStreamWrapper::stream_open() - @MarkBaker [CodePlex #7657](https://phpexcel.codeplex.com/workitem/7657) -- PHPExcel_Reader_Excel5 not reading explicitly entered error types in cells - @Erik Tilt [CodePlex #7676](https://phpexcel.codeplex.com/workitem/7676) -- Boolean and error data types not preserved for formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7678](https://phpexcel.codeplex.com/workitem/7678) -- PHPExcel_Reader_Excel2007 ignores cell data type - @MarkBaker [CodePlex #7695](https://phpexcel.codeplex.com/workitem/7695) -- PHPExcel_Reader_Excel5 ignores cell data type - @Erik Tilt [CodePlex #7712](https://phpexcel.codeplex.com/workitem/7712) -- PHPExcel_Writer_Excel5 not aware of data type - @Erik Tilt [CodePlex #7587](https://phpexcel.codeplex.com/workitem/7587) -- Long strings sometimes truncated when using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7713](https://phpexcel.codeplex.com/workitem/7713) -- Direct entry of boolean or error type in cell not supported by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7727](https://phpexcel.codeplex.com/workitem/7727) -- PHPExcel_Reader_Excel2007: Error reading cell with data type string, date number format, and numeric-like cell value - @MarkBaker [CodePlex #7714](https://phpexcel.codeplex.com/workitem/7714) -- Row and column outlines (group indent level) not showing after using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7735](https://phpexcel.codeplex.com/workitem/7735) -- Missing UTF-8 support in number format codes for PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7737](https://phpexcel.codeplex.com/workitem/7737) -- Missing UTF-8 support with PHPExcel_Writer_Excel5 for explicit string in formula - @Erik Tilt [CodePlex #7750](https://phpexcel.codeplex.com/workitem/7750) -- Problem with class constants in PHPExcel_Style_NumberFormat - @MarkBaker [CodePlex #7726](https://phpexcel.codeplex.com/workitem/7726) -- Sometimes errors with PHPExcel_Reader_Excel5 reading hyperlinks - @Erik Tilt [CodePlex #7758](https://phpexcel.codeplex.com/workitem/7758) -- Hyperlink in cell always results in string data type when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7759](https://phpexcel.codeplex.com/workitem/7759) -- Excel file with blank sheet seen as broken in MS Office Excel 2007 when created by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7771](https://phpexcel.codeplex.com/workitem/7771) -- PHPExcel_Reader_Excel5: Incorrect reading of formula with explicit string containing (escaped) double-quote - @Erik Tilt [CodePlex #7785](https://phpexcel.codeplex.com/workitem/7785) -- getCalculatedValue() fails on formula with sheet name containing (escaped) single-quote - @MarkBaker [CodePlex #7787](https://phpexcel.codeplex.com/workitem/7787) -- getCalculatedValue() fails on formula with explicit string containing (escaped) double-quote - @MarkBaker [CodePlex #7786](https://phpexcel.codeplex.com/workitem/7786) -- Problems with simultaneous repeatRowsAtTop and repeatColumnsAtLeft using Excel2007 reader and writer - @MarkBaker [CodePlex #7780](https://phpexcel.codeplex.com/workitem/7780) -- PHPExcel_Reader_Excel5: Error reading formulas with sheet reference containing special characters - @Erik Tilt [CodePlex #7802](https://phpexcel.codeplex.com/workitem/7802) -- Off-sheet references sheet!A1 not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7831](https://phpexcel.codeplex.com/workitem/7831) -- Repeating rows/columns (print titles), print area not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7834](https://phpexcel.codeplex.com/workitem/7834) -- Formula having datetime number format shows as text when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7849](https://phpexcel.codeplex.com/workitem/7849) -- Cannot set formula to hidden using applyFromArray() - @MarkBaker [CodePlex #7863](https://phpexcel.codeplex.com/workitem/7863) -- HTML/PDF Writers limited to 26 columns by calculateWorksheetDimension (erroneous comparison in getHighestColumn() method) - @MarkBaker [CodePlex #7805](https://phpexcel.codeplex.com/workitem/7805) -- Formula returning error type is lost when read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7873](https://phpexcel.codeplex.com/workitem/7873) -- PHPExcel_Reader_Excel5: Cell style lost for last column in group of blank cells - @Erik Tilt [CodePlex #7883](https://phpexcel.codeplex.com/workitem/7883) -- Column width sometimes collapses to auto size using Excel2007 reader/writer - @MarkBaker [CodePlex #7886](https://phpexcel.codeplex.com/workitem/7886) -- Data Validation Formula = 0 crashes Excel - @MarkBaker [CodePlex #9343](https://phpexcel.codeplex.com/workitem/9343) - - -## [1.6.3] - 2008-08-25 - -### General - -- Modified PHPExcel_Shared_Date::PHPToExcel() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367) -- Applied patch 1629 - @MarkBaker -- Applied patch 1644 - @MarkBaker -- Implement repeatRow and repeatColumn in Excel5 writer - @MarkBaker [CodePlex #6485](https://phpexcel.codeplex.com/workitem/6485) - -### Features - -- Remove scene3d filter in Excel2007 drawing - @MarkBaker [CodePlex #6838](https://phpexcel.codeplex.com/workitem/6838) -- Implement CHOOSE and INDEX Lookup/Reference Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement CLEAN Text Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement YEARFRAC Date/Time Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement 2 options for print/show gridlines - @MarkBaker [CodePlex #6508](https://phpexcel.codeplex.com/workitem/6508) -- Add VLOOKUP function (contribution) - @MarkBaker [CodePlex #7270](https://phpexcel.codeplex.com/workitem/7270) -- Implemented: ShrinkToFit - @MarkBaker [CodePlex #7182](https://phpexcel.codeplex.com/workitem/7182) -- Row heights not updated correctly when inserting new rows - @MarkBaker [CodePlex #7218](https://phpexcel.codeplex.com/workitem/7218) -- Copy worksheets within the same workbook - @MarkBaker [CodePlex #7157](https://phpexcel.codeplex.com/workitem/7157) -- Excel5 reader style support: horizontal and vertical alignment plus text wrap - @Erik Tilt [CodePlex #7290](https://phpexcel.codeplex.com/workitem/7290) -- Excel5 reader support for merged cells - @Erik Tilt [CodePlex #7294](https://phpexcel.codeplex.com/workitem/7294) -- Excel5 reader: Sheet Protection - @Erik Tilt [CodePlex #7296](https://phpexcel.codeplex.com/workitem/7296) -- Excel5 reader: Password for sheet protection - @Erik Tilt [CodePlex #7297](https://phpexcel.codeplex.com/workitem/7297) -- Excel5 reader: Column width - @Erik Tilt [CodePlex #7299](https://phpexcel.codeplex.com/workitem/7299) -- Excel5 reader: Row height - @Erik Tilt [CodePlex #7301](https://phpexcel.codeplex.com/workitem/7301) -- Excel5 reader: Font support - @Erik Tilt [CodePlex #7304](https://phpexcel.codeplex.com/workitem/7304) -- Excel5 reader: support for locked cells - @Erik Tilt [CodePlex #7324](https://phpexcel.codeplex.com/workitem/7324) -- Excel5 reader style support: Fill (background colors and patterns) - @Erik Tilt [CodePlex #7330](https://phpexcel.codeplex.com/workitem/7330) -- Excel5 reader style support: Borders (style and color) - @Erik Tilt [CodePlex #7332](https://phpexcel.codeplex.com/workitem/7332) -- Excel5 reader: Rich-Text support - @Erik Tilt [CodePlex #7346](https://phpexcel.codeplex.com/workitem/7346) -- Read Excel built-in number formats with Excel 2007 reader - @MarkBaker [CodePlex #7313](https://phpexcel.codeplex.com/workitem/7313) -- Excel5 reader: Number format support - @Erik Tilt [CodePlex #7317](https://phpexcel.codeplex.com/workitem/7317) -- Creating a copy of PHPExcel object - @MarkBaker [CodePlex #7362](https://phpexcel.codeplex.com/workitem/7362) -- Excel5 reader: support for row / column outline (group) - @Erik Tilt [CodePlex #7373](https://phpexcel.codeplex.com/workitem/7373) -- Implement default row/column sizes - @MarkBaker [CodePlex #7380](https://phpexcel.codeplex.com/workitem/7380) -- Writer HTML - option to return styles and table separately - @MarkBaker [CodePlex #7364](https://phpexcel.codeplex.com/workitem/7364) - -### Bugfixes - -- Excel5 reader: Support for remaining built-in number formats - @Erik Tilt [CodePlex #7393](https://phpexcel.codeplex.com/workitem/7393) -- Fixed rounding in HOUR MINUTE and SECOND Time functions, and improved performance for these - @MarkBaker -- Fix to TRIM function - @MarkBaker -- Fixed range validation in TIME Functions.php - @MarkBaker -- EDATE and EOMONTH functions now return date values based on the returnDateType flag - @MarkBaker -- Write date values that are the result of a calculation function correctly as Excel serialized dates rather than PHP serialized date values - @MarkBaker -- Excel2007 reader not always reading boolean correctly - @MarkBaker [CodePlex #6690](https://phpexcel.codeplex.com/workitem/6690) -- Columns above IZ - @MarkBaker [CodePlex #6275](https://phpexcel.codeplex.com/workitem/6275) -- Other locale than English causes Excel2007 writer to produce broken xlsx - @MarkBaker [CodePlex #6853](https://phpexcel.codeplex.com/workitem/6853) -- Typo: Number_fromat in NumberFormat.php - @MarkBaker [CodePlex #7061](https://phpexcel.codeplex.com/workitem/7061) -- Bug in Worksheet_BaseDrawing setWidth() - @MarkBaker [CodePlex #6865](https://phpexcel.codeplex.com/workitem/6865) -- PDF writer collapses column width for merged cells - @MarkBaker [CodePlex #6891](https://phpexcel.codeplex.com/workitem/6891) -- Issues with drawings filenames - @MarkBaker [CodePlex #6867](https://phpexcel.codeplex.com/workitem/6867) -- fromArray() local variable isn't defined - @MarkBaker [CodePlex #7073](https://phpexcel.codeplex.com/workitem/7073) -- PHPExcel_Writer_Excel5->setTempDir() not passed to all classes involved in writing to a file - @MarkBaker [CodePlex #7276](https://phpexcel.codeplex.com/workitem/7276) -- Excel5 reader not handling UTF-8 properly - @MarkBaker [CodePlex #7277](https://phpexcel.codeplex.com/workitem/7277) -- If you write a 0 value in cell, cell shows as empty - @MarkBaker [CodePlex #7327](https://phpexcel.codeplex.com/workitem/7327) -- Excel2007 writer: Row height ignored for empty rows - @MarkBaker [CodePlex #7302](https://phpexcel.codeplex.com/workitem/7302) -- Excel2007 (comments related error) - @MarkBaker [CodePlex #7281](https://phpexcel.codeplex.com/workitem/7281) -- Column width in other locale - @MarkBaker [CodePlex #7345](https://phpexcel.codeplex.com/workitem/7345) -- Excel2007 reader not reading underlined Rich-Text - @MarkBaker [CodePlex #7347](https://phpexcel.codeplex.com/workitem/7347) -- Excel5 reader converting booleans to strings - @Erik Tilt [CodePlex #7357](https://phpexcel.codeplex.com/workitem/7357) -- Recursive Object Memory Leak - @MarkBaker [CodePlex #7365](https://phpexcel.codeplex.com/workitem/7365) -- Excel2007 writer ignoring row dimensions without cells - @MarkBaker [CodePlex #7372](https://phpexcel.codeplex.com/workitem/7372) -- Excel5 reader is converting formatted numbers / dates to strings - @Erik Tilt [CodePlex #7382](https://phpexcel.codeplex.com/workitem/7382) - - -## [1.6.2] - 2008-06-23 - -### General - -- Document style array values - @MarkBaker [CodePlex #6088](https://phpexcel.codeplex.com/workitem/6088) -- Applied patch 1195 - @MarkBaker -- Redirecting output to a client’s web browser - http headers - @MarkBaker [CodePlex #6178](https://phpexcel.codeplex.com/workitem/6178) -- Improve worksheet garbage collection - @MarkBaker [CodePlex #6187](https://phpexcel.codeplex.com/workitem/6187) -- Functions that return date values can now be configured to return as Excel serialized date/time, PHP serialized date/time, or a PHP date/time object. - @MarkBaker -- Functions that explicitly accept dates as parameters now permit values as Excel serialized date/time, PHP serialized date/time, a valid date string, or a PHP date/time object. - @MarkBaker -- Implement ACOSH, ASINH and ATANH functions for those operating platforms/PHP versions that don't include these functions - @MarkBaker -- Implement ATAN2 logic reversing the arguments as per Excel - @MarkBaker -- Additional validation of parameters for COMBIN - @MarkBaker - -### Features - -- Fixed validation for CEILING and FLOOR when the value and significance parameters have different signs; and allowed default value of 1 or -1 for significance when in GNUMERIC compatibility mode - @MarkBaker -- Implement ADDRESS, ISLOGICAL, ISTEXT and ISNONTEXT functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement COMPLEX, IMAGINARY, IMREAL, IMARGUMENT, IMCONJUGATE, IMABS, IMSUB, IMDIV, IMSUM, IMPRODUCT, IMSQRT, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER IMCOS and IMSIN Engineering functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement NETWORKDAYS and WORKDAY Date/Time functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Make cell column AAA available - @MarkBaker [CodePlex #6100](https://phpexcel.codeplex.com/workitem/6100) -- Mark particular cell as selected when opening Excel - @MarkBaker [CodePlex #6095](https://phpexcel.codeplex.com/workitem/6095) -- Multiple sheets in PDF and HTML - @MarkBaker [CodePlex #6120](https://phpexcel.codeplex.com/workitem/6120) -- Implement PHPExcel_ReaderFactory and PHPExcel_WriterFactory - @MarkBaker [CodePlex #6227](https://phpexcel.codeplex.com/workitem/6227) -- Set image root of PHPExcel_Writer_HTML - @MarkBaker [CodePlex #6249](https://phpexcel.codeplex.com/workitem/6249) -- Enable/disable calculation cache - @MarkBaker [CodePlex #6264](https://phpexcel.codeplex.com/workitem/6264) -- PDF writer and multi-line text - @MarkBaker [CodePlex #6259](https://phpexcel.codeplex.com/workitem/6259) -- Feature request - setCacheExpirationTime() - @MarkBaker [CodePlex #6350](https://phpexcel.codeplex.com/workitem/6350) -- Implement late-binding mechanisms to reduce memory footprint - @JB [CodePlex #6370](https://phpexcel.codeplex.com/workitem/6370) -- Implement shared styles - @JB [CodePlex #6430](https://phpexcel.codeplex.com/workitem/6430) -- Copy sheet from external Workbook to active Workbook - @MarkBaker [CodePlex #6391](https://phpexcel.codeplex.com/workitem/6391) - -### Bugfixes - -- Functions in Conditional Formatting - @MarkBaker [CodePlex #6428](https://phpexcel.codeplex.com/workitem/6428) -- Default Style in Excel5 - @MarkBaker [CodePlex #6096](https://phpexcel.codeplex.com/workitem/6096) -- Numbers starting with '+' cause Excel 2007 errors - @MarkBaker [CodePlex #6150](https://phpexcel.codeplex.com/workitem/6150) -- ExcelWriter5 is not PHP5 compatible, using it with E_STRICT results in a bunch of errors (applied patches) - @MarkBaker [CodePlex #6092](https://phpexcel.codeplex.com/workitem/6092) -- Error Reader Excel2007 line 653 foreach ($relsDrawing->Relationship as $ele) - @MarkBaker [CodePlex #6179](https://phpexcel.codeplex.com/workitem/6179) -- Worksheet toArray() screws up DATE - @MarkBaker [CodePlex #6229](https://phpexcel.codeplex.com/workitem/6229) -- References to a Richtext cell in a formula - @MarkBaker [CodePlex #6253](https://phpexcel.codeplex.com/workitem/6253) -- insertNewColumnBefore Bug - @MarkBaker [CodePlex #6285](https://phpexcel.codeplex.com/workitem/6285) -- Error reading Excel2007 file with shapes - @MarkBaker [CodePlex #6319](https://phpexcel.codeplex.com/workitem/6319) -- Determine whether date values need conversion from PHP dates to Excel dates before writing to file, based on the data type (float or integer) - @MarkBaker [CodePlex #6302](https://phpexcel.codeplex.com/workitem/6302) -- Fixes to DATE function when it is given negative input parameters - @MarkBaker -- PHPExcel handles empty cells other than Excel - @MarkBaker [CodePlex #6347](https://phpexcel.codeplex.com/workitem/6347) -- PHPExcel handles 0 and "" as being the same - @MarkBaker [CodePlex #6348](https://phpexcel.codeplex.com/workitem/6348) -- Problem Using Excel2007 Reader for Spreadsheets containing images - @MarkBaker [CodePlex #6357](https://phpexcel.codeplex.com/workitem/6357) -- ShowGridLines ignored when reading/writing Excel 2007 - @MarkBaker [CodePlex #6359](https://phpexcel.codeplex.com/workitem/6359) -- Bug With Word Wrap in Excel 2007 Reader - @MarkBaker [CodePlex #6426](https://phpexcel.codeplex.com/workitem/6426) - - -## [1.6.1] - 2008-04-28 - -### General - -- Fix documentation printing - @MarkBaker [CodePlex #5532](https://phpexcel.codeplex.com/workitem/5532) -- Memory usage improvements - @MarkBaker [CodePlex #5586](https://phpexcel.codeplex.com/workitem/5586) -- Applied patch 990 - @MarkBaker - -### Features - -- Applied patch 991 - @MarkBaker -- Implement PHPExcel_Reader_Excel5 - @BM [CodePlex #2841](https://phpexcel.codeplex.com/workitem/2841) -- Implement "toArray" and "fromArray" method - @MarkBaker [CodePlex #5564](https://phpexcel.codeplex.com/workitem/5564) -- Read shared formula - @MarkBaker [CodePlex #5665](https://phpexcel.codeplex.com/workitem/5665) -- Read image twoCellAnchor - @MarkBaker [CodePlex #5681](https://phpexcel.codeplex.com/workitem/5681) -- &G Image as bg for headerfooter - @MarkBaker [CodePlex #4446](https://phpexcel.codeplex.com/workitem/4446) -- Implement page layout functionality for Excel5 format - @MarkBaker [CodePlex #5834](https://phpexcel.codeplex.com/workitem/5834) - -### Bugfixes - -- Feature request: PHPExcel_Writer_PDF - @MarkBaker [CodePlex #6039](https://phpexcel.codeplex.com/workitem/6039) -- DefinedNames null check - @MarkBaker [CodePlex #5517](https://phpexcel.codeplex.com/workitem/5517) -- Hyperlinks should not always have trailing slash - @MarkBaker [CodePlex #5463](https://phpexcel.codeplex.com/workitem/5463) -- Saving Error - Uncaught exception (#REF! named range) - @MarkBaker [CodePlex #5592](https://phpexcel.codeplex.com/workitem/5592) -- Error when creating Zip file on Linux System (Not Windows) - @MarkBaker [CodePlex #5634](https://phpexcel.codeplex.com/workitem/5634) -- Time incorrecly formated - @MarkBaker [CodePlex #5876](https://phpexcel.codeplex.com/workitem/5876) -- Conditional formatting - second rule not applied - @MarkBaker [CodePlex #5914](https://phpexcel.codeplex.com/workitem/5914) -- PHPExcel_Reader_Excel2007 cannot load PHPExcel_Shared_File - @MarkBaker [CodePlex #5978](https://phpexcel.codeplex.com/workitem/5978) -- Output redirection to web browser - @MarkBaker [CodePlex #6020](https://phpexcel.codeplex.com/workitem/6020) - - -## [1.6.0] - 2008-02-14 - -### Features - -- Use PHPExcel datatypes in formula calculation - @MarkBaker [CodePlex #3156](https://phpexcel.codeplex.com/workitem/3156) -- Center on page when printing - @MarkBaker [CodePlex #5019](https://phpexcel.codeplex.com/workitem/5019) -- Hyperlink to other spreadsheet - @MarkBaker [CodePlex #5099](https://phpexcel.codeplex.com/workitem/5099) -- Set the print area of a worksheet - @MarkBaker [CodePlex #5104](https://phpexcel.codeplex.com/workitem/5104) -- Read "definedNames" property of worksheet - @MarkBaker [CodePlex #5118](https://phpexcel.codeplex.com/workitem/5118) -- Set default style for all cells - @MarkBaker [CodePlex #5338](https://phpexcel.codeplex.com/workitem/5338) -- Named Ranges - @MarkBaker [CodePlex #4216](https://phpexcel.codeplex.com/workitem/4216) - -### Bugfixes - -- Implement worksheet references (Sheet1!A1) - @MarkBaker [CodePlex #5398](https://phpexcel.codeplex.com/workitem/5398) -- Redirect output to a client's web browser - @MarkBaker [CodePlex #4967](https://phpexcel.codeplex.com/workitem/4967) -- "File Error: data may have been lost." seen in Excel 2007 and Excel 2003 SP3 when opening XLS file - @MarkBaker [CodePlex #5008](https://phpexcel.codeplex.com/workitem/5008) -- Bug in style's getHashCode() - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165) -- PHPExcel_Reader not correctly reading numeric values - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165) -- Text rotation is read incorrectly - @MarkBaker [CodePlex #5324](https://phpexcel.codeplex.com/workitem/5324) -- Enclosure " and data " result a bad data : \" instead of "" - @MarkBaker [CodePlex #5326](https://phpexcel.codeplex.com/workitem/5326) -- Formula parser - IF statement returning array instead of scalar - @MarkBaker [CodePlex #5332](https://phpexcel.codeplex.com/workitem/5332) -- setFitToWidth(nbpage) & setFitToWidth(nbpage) work partially - @MarkBaker [CodePlex #5351](https://phpexcel.codeplex.com/workitem/5351) -- Worksheet::setTitle() causes unwanted renaming - @MarkBaker [CodePlex #5361](https://phpexcel.codeplex.com/workitem/5361) -- Hyperlinks not working. Results in broken xlsx file. - @MarkBaker [CodePlex #5407](https://phpexcel.codeplex.com/workitem/5407) - - -## [1.5.5] - 2007-12-24 - -### General - -- Grouping Rows - @MarkBaker [CodePlex #4135](https://phpexcel.codeplex.com/workitem/4135) - -### Features - -- Semi-nightly builds - @MarkBaker [CodePlex #4427](https://phpexcel.codeplex.com/workitem/4427) -- Implement "date" datatype - @MarkBaker [CodePlex #3155](https://phpexcel.codeplex.com/workitem/3155) -- Date format not honored in CSV writer - @MarkBaker [CodePlex #4150](https://phpexcel.codeplex.com/workitem/4150) -- RichText and sharedStrings - @MarkBaker [CodePlex #4199](https://phpexcel.codeplex.com/workitem/4199) -- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) - - Addition of DATE, DATEDIF, DATEVALUE, DAY, DAYS360- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) - - Addition of AVEDEV, HARMEAN and GEOMEAN - - Addition of the BINOMDIST (Non-cumulative only), COUNTBLANK, EXPONDIST, FISHER, FISHERINV, NORMDIST, NORMSDIST, PERMUT, POISSON (Non-cumulative only) and STANDARDIZE Statistical Functions - - Addition of the CEILING, COMBIN, EVEN, FACT, FACTDOUBLE, FLOOR, MULTINOMIAL, ODD, ROUNDDOWN, ROUNDUP, SIGN, SQRTPI and SUMSQ Mathematical Functions - - Addition of the NORMINV, NORMSINV, CONFIDENCE and SKEW Statistical Functions - - Addition of the CRITBINOM, HYPGEOMDIST, KURT, LOGINV, LOGNORMDIST, NEGBINOMDIST and WEIBULL Statistical Functions - - Addition of the LARGE, PERCENTILE, QUARTILE, SMALL and TRIMMEAN Statistical Functions - - Addition of the BIN2HEX, BIN2OCT, DELTA, ERF, ERFC, GESTEP, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN and OCT2HEX Engineering Functions - - Addition of the CHIDIST, GAMMADIST and GAMMALN Statistical Functions - - Addition of the GCD, LCM, MROUND and SUBTOTAL Mathematical Functions - - Addition of the LOWER, PROPER and UPPER Text Functions - - Addition of the BETADIST and BETAINV Statistical Functions - - Addition of the CHIINV and GAMMAINV Statistical Functions - - Addition of the SERIESSUM Mathematical Function - - Addition of the CHAR, CODE, FIND, LEN, REPT, SEARCH, T, TRIM Text Functions - - Addition of the FALSE and TRUE Boolean Functions - - Addition of the TDIST and TINV Statistical Functions - - Addition of the EDATE, EOMONTH, YEAR, MONTH, TIME, TIMEVALUE, HOUR, MINUTE, SECOND, WEEKDAY, WEEKNUM, NOW, TODAY and Date/Time Function - - Addition of the BESSELI, BESSELJ, BESSELK and BESSELY Engineering Functions - - Addition of the SLN and SYD Financial Functions - - reworked MODE calculation to handle floating point numbers - - Improved error trapping for invalid input values - - Fix to SMALL, LARGE, PERCENTILE and TRIMMEAN to eliminate non-numeric values - - Added CDF to BINOMDIST and POISSON - - Fix to a potential endless loop in CRITBINOM, together with other bugfixes to the algorithm - - Fix to SQRTPI so that it will work with a real value parameter rather than just integers - - Trap for passing negative values to FACT - - Improved accuracy of the NORMDIST cumulative function, and of the ERF and ERFC functions - - Replicated Excel data-type and error handling for BIN, DEC, OCT and HEX conversion functions - - Replicated Excel data-type and error handling for AND and OR Boolean functions - - Bugfix to MROUND - - Rework of the DATE, DATEVALUE, DAY, DAYS360 and DATEDIF date/Time functions to use Excel dates rather than straight PHP dates - - Rework of the AND, OR Boolean functions to ignore string values - - Rework of the BIN2DEC, BIN2HEX, BIN2OCT, DEC2BIN, DEC2HEX, DEC2OCT Engineering functions to handle two's complement - - Excel, Gnumeric and OpenOffice Calc compatibility flag for functions - - Note, not all functions have yet been written to work with the Gnumeric and OpenOffice Calc compatibility flags - - 1900 or 1904 Calendar flag for date functions - - Reworked ExcelToPHP date method to handle the Excel 1900 leap year - - Note that this will not correctly return values prior to 13-Dec-1901 20:45:52 as this is the minimum value that PHP date serial values can handle. If you need to work with dates prior to this, then an ExcelToPHPObject method has been added which will work correctly with values between Excel's 1900 calendar base date of 1-Jan-1900, and 13-Dec-1901 - - Addition of ExcelToPHPObject date method to return a PHP DateTime object from an Excel date serial value - - PHPToExcel method modified to accept either PHP date serial numbers or PHP DateTime objects - - Addition of FormattedPHPToExcel which will accept a date and time broken to into year, month, day, hour, minute, second and return an Excel date serial value- Control characters in Excel 2007 - @MarkBaker [CodePlex #4485](https://phpexcel.codeplex.com/workitem/4485) -- BaseDrawing::setWidthAndHeight method request - @MarkBaker [CodePlex #4796](https://phpexcel.codeplex.com/workitem/4796) -- Page Setup -> Print Titles -> Sheet -> 'Rows to repeat at top' - @MarkBaker [CodePlex #4798](https://phpexcel.codeplex.com/workitem/4798) - -### Bugfixes - -- Comment functionality - @MarkBaker [CodePlex #4433](https://phpexcel.codeplex.com/workitem/4433) -- Undefined variable in PHPExcel_Writer_Serialized - @MarkBaker [CodePlex #4124](https://phpexcel.codeplex.com/workitem/4124) -- Notice: Object of class PHPExcel_RichText could not be converted to int - @MarkBaker [CodePlex #4125](https://phpexcel.codeplex.com/workitem/4125) -- Excel5Writer: utf8 string not converted to utf16 - @MarkBaker [CodePlex #4126](https://phpexcel.codeplex.com/workitem/4126) -- PHPExcel_RichText and autosize - @MarkBaker [CodePlex #4180](https://phpexcel.codeplex.com/workitem/4180) -- Excel5Writer produces broken xls files after change mentioned in work item 4126 - @MarkBaker [CodePlex #4574](https://phpexcel.codeplex.com/workitem/4574) -- Small bug in PHPExcel_Reader_Excel2007 function _readStyle - @MarkBaker [CodePlex #4797](https://phpexcel.codeplex.com/workitem/4797) - - -## [1.5.0] - 2007-10-23 - -### Features - -- Refactor PHPExcel Drawing - @MarkBaker [CodePlex #3265](https://phpexcel.codeplex.com/workitem/3265) -- Update Shared/OLE.php to latest version from PEAR - @CS [CodePlex #3079](https://phpexcel.codeplex.com/workitem/3079) -- Excel2007 vs Excel2003 compatibility pack - @MarkBaker [CodePlex #3217](https://phpexcel.codeplex.com/workitem/3217) -- Cell protection (lock/unlock) - @MarkBaker [CodePlex #3234](https://phpexcel.codeplex.com/workitem/3234) -- Create clickable links (hyperlinks) - @MarkBaker [CodePlex #3543](https://phpexcel.codeplex.com/workitem/3543) -- Additional page setup parameters - @MarkBaker [CodePlex #3241](https://phpexcel.codeplex.com/workitem/3241) -- Make temporary file path configurable (Excel5) - @MarkBaker [CodePlex #3300](https://phpexcel.codeplex.com/workitem/3300) -- Small addition to applyFromArray for font - @MarkBaker [CodePlex #3306](https://phpexcel.codeplex.com/workitem/3306) - -### Bugfixes - -- Better feedback when save of file is not possible - @MarkBaker [CodePlex #3373](https://phpexcel.codeplex.com/workitem/3373) -- Text Rotation - @MarkBaker [CodePlex #3181](https://phpexcel.codeplex.com/workitem/3181) -- Small bug in Page Orientation - @MarkBaker [CodePlex #3237](https://phpexcel.codeplex.com/workitem/3237) -- insertNewColumnBeforeByColumn undefined - @MarkBaker [CodePlex #3812](https://phpexcel.codeplex.com/workitem/3812) -- Sheet references not working in formula (Excel5 Writer) - @MarkBaker [CodePlex #3893](https://phpexcel.codeplex.com/workitem/3893) - - -## [1.4.5] - 2007-08-23 - -### General - -- Class file endings - @MarkBaker [CodePlex #3003](https://phpexcel.codeplex.com/workitem/3003) -- Different calculation engine improvements - @MarkBaker [CodePlex #3081](https://phpexcel.codeplex.com/workitem/3081) -- Different improvements in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #3082](https://phpexcel.codeplex.com/workitem/3082) - -### Features - -- Set XML indentation in PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #3146](https://phpexcel.codeplex.com/workitem/3146) -- Optionally store temporary Excel2007 writer data in file instead of memory - @MarkBaker [CodePlex #3159](https://phpexcel.codeplex.com/workitem/3159) -- Implement show/hide gridlines - @MarkBaker [CodePlex #3063](https://phpexcel.codeplex.com/workitem/3063) -- Implement option to read only data - @MarkBaker [CodePlex #3064](https://phpexcel.codeplex.com/workitem/3064) -- Optionally disable formula precalculation - @MarkBaker [CodePlex #3080](https://phpexcel.codeplex.com/workitem/3080) -- Explicitly set cell datatype - @MarkBaker [CodePlex #3154](https://phpexcel.codeplex.com/workitem/3154) - -### Bugfixes - -- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) - - Addition of MINA, MAXA, COUNTA, AVERAGEA, MEDIAN, MODE, DEVSQ, STDEV, STDEVA, STDEVP, STDEVPA, VAR, VARA, VARP and VARPA Excel Functions - - Fix to SUM, PRODUCT, QUOTIENT, MIN, MAX, COUNT and AVERAGE functions when cell contains a numeric value in a string datatype, bringing it in line with MS Excel behaviour- File_exists on ZIP fails on some installations - @MarkBaker [CodePlex #2881](https://phpexcel.codeplex.com/workitem/2881) -- Argument in textRotation should be -90..90 - @MarkBaker [CodePlex #2879](https://phpexcel.codeplex.com/workitem/2879) -- Excel2007 reader/writer not implementing OpenXML/SpreadsheetML styles 100% correct - @MarkBaker [CodePlex #2883](https://phpexcel.codeplex.com/workitem/2883) -- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513) -- Print and print preview of generated XLSX causes Excel2007 to crash - @MarkBaker [CodePlex #2935](https://phpexcel.codeplex.com/workitem/2935) -- Error in Calculations - COUNT() function - @MarkBaker [CodePlex #2952](https://phpexcel.codeplex.com/workitem/2952) -- HTML and CSV writer not writing last row - @MarkBaker [CodePlex #3002](https://phpexcel.codeplex.com/workitem/3002) -- Memory leak in Excel5 writer - @MarkBaker [CodePlex #3017](https://phpexcel.codeplex.com/workitem/3017) -- Printing (PHPExcel_Writer_Excel5) - @MarkBaker [CodePlex #3044](https://phpexcel.codeplex.com/workitem/3044) -- Problems reading zip:// - @MarkBaker [CodePlex #3046](https://phpexcel.codeplex.com/workitem/3046) -- Error reading conditional formatting - @MarkBaker [CodePlex #3047](https://phpexcel.codeplex.com/workitem/3047) -- Bug in Excel5 writer (storePanes) - @MarkBaker [CodePlex #3067](https://phpexcel.codeplex.com/workitem/3067) -- Memory leak in PHPExcel_Style_Color - @MarkBaker [CodePlex #3077](https://phpexcel.codeplex.com/workitem/3077) - - -## [1.4.0] - 2007-07-23 - -### General - -- Coding convention / code cleanup - @MarkBaker [CodePlex #2687](https://phpexcel.codeplex.com/workitem/2687) -- Use set_include_path in tests - @MarkBaker [CodePlex #2717](https://phpexcel.codeplex.com/workitem/2717) - -### Features - -- Move PHPExcel_Writer_Excel5 OLE to PHPExcel_Shared_OLE - @MarkBaker [CodePlex #2812](https://phpexcel.codeplex.com/workitem/2812) -- Hide/Unhide Column or Row - @MarkBaker [CodePlex #2679](https://phpexcel.codeplex.com/workitem/2679) -- Implement multi-cell styling - @MarkBaker [CodePlex #2271](https://phpexcel.codeplex.com/workitem/2271) -- Implement CSV file format (reader/writer) - @MarkBaker [CodePlex #2720](https://phpexcel.codeplex.com/workitem/2720) - -### Bugfixes - -- Implement HTML file format - @MarkBaker [CodePlex #2845](https://phpexcel.codeplex.com/workitem/2845) -- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513) -- Freeze Panes with PHPExcel_Writer_Excel5 - @MarkBaker [CodePlex #2678](https://phpexcel.codeplex.com/workitem/2678) -- OLE.php - @MarkBaker [CodePlex #2680](https://phpexcel.codeplex.com/workitem/2680) -- Copy and pasting multiple drop-down list cells breaks reader - @MarkBaker [CodePlex #2736](https://phpexcel.codeplex.com/workitem/2736) -- Function setAutoFilterByColumnAndRow takes wrong arguments - @MarkBaker [CodePlex #2775](https://phpexcel.codeplex.com/workitem/2775) -- Simplexml_load_file fails on ZipArchive - @MarkBaker [CodePlex #2858](https://phpexcel.codeplex.com/workitem/2858) - - -## [1.3.5] - 2007-06-27 - -### Features - -- Documentation - @MarkBaker [CodePlex #15](https://phpexcel.codeplex.com/workitem/15) -- PHPExcel_Writer_Excel5 - @JV -- PHPExcel_Reader_Excel2007: Image shadows - @JV -- Data validation - @MarkBaker [CodePlex #2385](https://phpexcel.codeplex.com/workitem/2385) - -### Bugfixes - -- Implement richtext strings - @MarkBaker -- Empty relations when adding image to any sheet but the first one - @MarkBaker [CodePlex #2443](https://phpexcel.codeplex.com/workitem/2443) -- Excel2007 crashes on print preview - @MarkBaker [CodePlex #2536](https://phpexcel.codeplex.com/workitem/2536) - - -## [1.3.0] - 2007-06-05 - -### General - -- Create PEAR package - @MarkBaker [CodePlex #1942](https://phpexcel.codeplex.com/workitem/1942) - -### Features - -- Replace *->duplicate() by __clone() - @MarkBaker [CodePlex #2331](https://phpexcel.codeplex.com/workitem/2331) -- PHPExcel_Reader_Excel2007: Column auto-size, Protection, Merged cells, Wrap text, Page breaks, Auto filter, Images - @JV -- Implement "freezing" panes - @MarkBaker [CodePlex #245](https://phpexcel.codeplex.com/workitem/245) -- Cell addressing alternative - @MarkBaker [CodePlex #2273](https://phpexcel.codeplex.com/workitem/2273) -- Implement cell word-wrap attribute - @MarkBaker [CodePlex #2270](https://phpexcel.codeplex.com/workitem/2270) -- Auto-size column - @MarkBaker [CodePlex #2282](https://phpexcel.codeplex.com/workitem/2282) -- Implement formula calculation - @MarkBaker [CodePlex #241](https://phpexcel.codeplex.com/workitem/241) - -### Bugfixes - -- Insert/remove row/column - @MarkBaker [CodePlex #2375](https://phpexcel.codeplex.com/workitem/2375) -- PHPExcel_Worksheet::getCell() should not accept absolute coordinates - @MarkBaker [CodePlex #1931](https://phpexcel.codeplex.com/workitem/1931) -- Cell reference without row number - @MarkBaker [CodePlex #2272](https://phpexcel.codeplex.com/workitem/2272) -- Styles with same coordinate but different worksheet - @MarkBaker [CodePlex #2276](https://phpexcel.codeplex.com/workitem/2276) -- PHPExcel_Worksheet->getCellCollection() usort error - @MarkBaker [CodePlex #2290](https://phpexcel.codeplex.com/workitem/2290) -- Bug in PHPExcel_Cell::stringFromColumnIndex - @SS [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353) -- Reader: numFmts can be missing, use cellStyleXfs instead of cellXfs in styles - @JV [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353) - - -## [1.2.0] - 2007-04-26 - -### General - -- Stringtable attribute "count" not necessary, provides wrong info to Excel sometimes... - @MarkBaker -- Updated tests to address more document properties - @MarkBaker -- Some refactoring in PHPExcel_Writer_Excel2007_Workbook - @MarkBaker -- New package: PHPExcel_Shared - @MarkBaker -- Password hashing algorithm implemented in PHPExcel_Shared_PasswordHasher - @MarkBaker -- Moved pixel conversion functions to PHPExcel_Shared_Drawing - @MarkBaker -- Switch over to LGPL license - @MarkBaker [CodePlex #244](https://phpexcel.codeplex.com/workitem/244) - -### Features - -- Include PHPExcel version in file headers - @MarkBaker [CodePlex #5](https://phpexcel.codeplex.com/workitem/5) -- Autofilter - @MarkBaker [CodePlex #6](https://phpexcel.codeplex.com/workitem/6) -- Extra document property: keywords - @MarkBaker [CodePlex #7](https://phpexcel.codeplex.com/workitem/7) -- Extra document property: category - @MarkBaker [CodePlex #8](https://phpexcel.codeplex.com/workitem/8) -- Document security - @MarkBaker [CodePlex #9](https://phpexcel.codeplex.com/workitem/9) -- PHPExcel_Writer_Serialized and PHPExcel_Reader_Serialized - @MarkBaker [CodePlex #10](https://phpexcel.codeplex.com/workitem/10) -- Alternative syntax: Addressing a cell - @MarkBaker [CodePlex #11](https://phpexcel.codeplex.com/workitem/11) -- Merge cells - @MarkBaker [CodePlex #12](https://phpexcel.codeplex.com/workitem/12) - -### Bugfixes - -- Protect ranges of cells with a password - @MarkBaker [CodePlex #13](https://phpexcel.codeplex.com/workitem/13) -- (style/fill/patternFill/fgColor or bgColor can be empty) - @JV [CodePlex #14](https://phpexcel.codeplex.com/workitem/14) - - -## [1.1.1] - 2007-03-26 - -### General - -- Syntax error in "Classes/PHPExcel/Writer/Excel2007.php" on line 243 - @MarkBaker [CodePlex #1250](https://phpexcel.codeplex.com/workitem/1250) -- Reader should check if file exists and throws an exception when it doesn't - @MarkBaker [CodePlex #1282](https://phpexcel.codeplex.com/workitem/1282) - - -## [1.1.0] - 2007-03-22 - -### Bugfixes - -- Style information lost after passing trough Excel2007_Reader - @MarkBaker [CodePlex #836](https://phpexcel.codeplex.com/workitem/836) - -### General - -- Number of columns > AZ fails fixed in PHPExcel_Cell::columnIndexFromString - @MarkBaker [CodePlex #913](https://phpexcel.codeplex.com/workitem/913) - -### Features - -- Added a brief file with installation instructions - @MarkBaker -- Page breaks (horizontal and vertical) - @MarkBaker -- Image shadows - @MarkBaker - - -## [1.0.0] - 2007-02-22 - -### Bugfixes - -- PHPExcel->removeSheetByIndex now re-orders sheets after deletion, so no array indexes are lost - @JV -- PHPExcel_Writer_Excel2007_Worksheet::_writeCols() used direct assignment to $pSheet->getColumnDimension('A')->Width instead of $pSheet->getColumnDimension('A')->setWidth() - @JV -- DocumentProperties used $this->LastModifiedBy instead of $this->_lastModifiedBy. - @JV - -### General - -- Only first = should be removed when writing formula in PHPExcel_Writer_Excel2007_Worksheet. - @JV -- Consistency of method names to camelCase - @JV -- Updated tests to match consistency changes - @JV -- Detection of mime-types now with image_type_to_mime_type() - @JV -- Constants now hold string value used in Excel 2007 - @JV - -### Features - -- Fixed folder name case (WorkSheet -> Worksheet) - @MarkBaker -- PHPExcel classes (not the Writer classes) can be duplicated, using a duplicate() method. - @MarkBaker -- Cell styles can now be duplicated to a range of cells using PHPExcel_Worksheet->duplicateStyle() - @MarkBaker -- Conditional formatting - @MarkBaker -- Reader for Excel 2007 (not supporting full specification yet!) - @JV - - -## [1.0.0 RC] - 2007-01-31 - -- Project name has been changed to PHPExcel -- Project homepage is now http://www.codeplex.com/PHPExcel -- Started versioning at number: PHPExcel 1.0.0 RC - - -## 2007-01-22 - -- Fixed some performance issues on large-scale worksheets (mainly loops vs. indexed arrays) -- Performance on creating StringTable has been increased -- Performance on writing Excel2007 worksheet has been increased - - -## 2007-01-18 - -- Images can now be rotated -- Fixed bug: When drawings have full path specified, no mime type can be deducted -- Fixed bug: Only one drawing can be added to a worksheet - - -## 2007-01-12 - -- Refactoring of some classes to use ArrayObject instead of array() -- Cell style now has support for number format (i.e. #,##0) -- Implemented embedding images - - -## 2007-01-02 - -- Cell style now has support for fills, including gradient fills -- Cell style now has support for fonts -- Cell style now has support for border colors -- Cell style now has support for font colors -- Cell style now has support for alignment - - -## 2006-12-21 - -- Support for cell style borders -- Support for cell styles -- Refactoring of Excel2007 Writer into multiple classes in package SpreadSheet_Writer_Excel2007 -- Refactoring of all classes, changed public members to public properties using getter/setter -- Worksheet names are now unique. On duplicate worksheet names, a number is appended. -- Worksheet now has parent SpreadSheet object -- Worksheet now has support for page header and footer -- Worksheet now has support for page margins -- Worksheet now has support for page setup (only Paper size and Orientation) -- Worksheet properties now accessible by using getProperties() -- Worksheet now has support for row and column dimensions (height / width) -- Exceptions thrown have a more clear description - - -## Initial version - -- Create a Spreadsheet object -- Add one or more Worksheet objects -- Add cells to Worksheet objects -- Export Spreadsheet object to Excel 2007 OpenXML format -- Each cell supports the following data formats: string, number, formula, boolean. +The changelog for the project when it was called PHPExcel is [still available](./CHANGELOG.PHPExcel.md). diff --git a/README.md b/README.md index a36a25e2..e4a978c7 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,10 @@ PhpSpreadsheet is a library written in pure PHP and providing a set of classes t ## Requirements * PHP version 5.6 or higher - * PHP extension php_zip enabled (required if you need PhpSpreadsheet to handle .xlsx .ods or .gnumeric files) + * PHP extension php_zip enabled * PHP extension php_xml enabled * PHP extension php_gd2 enabled (optional, but required for exact column width autocalculation) -*Note:* PHP 5.6.29 has [a bug](https://bugs.php.net/bug.php?id=735300) that -prevents SQLite3 caching to work correctly. Use a newer (or older) versions of -PHP if you need SQLite3 caching. - ## PHP version support Support for PHP versions will only be maintained for a period of six months beyond the end-of-life of that PHP version diff --git a/composer.json b/composer.json index e4f10545..05bb1806 100644 --- a/composer.json +++ b/composer.json @@ -27,18 +27,19 @@ "ext-mbstring": "*", "ext-iconv": "*", "ext-xml": "*", - "ext-xmlwriter": "*" + "ext-xmlwriter": "*", + "ext-zip": "*", + "psr/simple-cache": "^1.0" }, "require-dev": { - "dompdf/dompdf": "^0.7.0", "mpdf/mpdf": "^6.1", "tecnickcom/tcpdf": "^6.2", "squizlabs/php_codesniffer": "^2.7", "phpunit/phpunit": "^5.7", - "friendsofphp/php-cs-fixer": "^2.0" + "friendsofphp/php-cs-fixer": "^2.0", + "dompdf/dompdf": "^0.8.0" }, "suggest": { - "ext-zip": "Required to handle .xlsx .ods or .gnumeric files", "ext-gd": "Required for exact column width autocalculation", "ext-dom": "Option to read and write HTML files", "mpdf/mpdf": "Option for rendering PDF with PDF Writer", diff --git a/composer.lock b/composer.lock index a245e8df..b2bf65ba 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,57 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "97eae2a53946fe7524d2f8840bcc791e", - "packages": [], + "content-hash": "81b6f3fcd32849e29ea13dd37c9af17f", + "packages": [ + { + "name": "psr/simple-cache", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-01-02T13:31:39+00:00" + } + ], "packages-dev": [ { "name": "doctrine/instantiator", @@ -63,28 +112,29 @@ }, { "name": "dompdf/dompdf", - "version": "v0.7.0", + "version": "v0.8.0", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "5c98652b1a5beb7e3cc8ec35419b2828dd63ab14" + "reference": "0f418c6b58fdeafc2a0e80eb1fa5e644e185089c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/5c98652b1a5beb7e3cc8ec35419b2828dd63ab14", - "reference": "5c98652b1a5beb7e3cc8ec35419b2828dd63ab14", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/0f418c6b58fdeafc2a0e80eb1fa5e644e185089c", + "reference": "0f418c6b58fdeafc2a0e80eb1fa5e644e185089c", "shasum": "" }, "require": { "ext-dom": "*", "ext-gd": "*", "ext-mbstring": "*", - "phenx/php-font-lib": "0.4.*", - "phenx/php-svg-lib": "0.1.*", + "phenx/php-font-lib": "0.5.*", + "phenx/php-svg-lib": "0.2.*", "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*" + "phpunit/phpunit": "4.8.*", + "squizlabs/php_codesniffer": "2.*" }, "type": "library", "extra": { @@ -120,20 +170,20 @@ ], "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", "homepage": "https://github.com/dompdf/dompdf", - "time": "2016-05-11T00:36:29+00:00" + "time": "2017-02-16T02:40:40+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.0.0", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "f3baf72eb2f58bf275b372540f5b47d25aed910f" + "reference": "c7de769d7b44f2c9de68e1f678b65efd8126f60b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/f3baf72eb2f58bf275b372540f5b47d25aed910f", - "reference": "f3baf72eb2f58bf275b372540f5b47d25aed910f", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c7de769d7b44f2c9de68e1f678b65efd8126f60b", + "reference": "c7de769d7b44f2c9de68e1f678b65efd8126f60b", "shasum": "" }, "require": { @@ -145,6 +195,8 @@ "symfony/filesystem": "^2.4 || ^3.0", "symfony/finder": "^2.2 || ^3.0", "symfony/polyfill-php54": "^1.0", + "symfony/polyfill-php55": "^1.3", + "symfony/polyfill-xml": "^1.3", "symfony/process": "^2.3 || ^3.0", "symfony/stopwatch": "^2.5 || ^3.0" }, @@ -153,18 +205,18 @@ }, "require-dev": { "gecko-packages/gecko-php-unit": "^2.0", - "phpunit/phpunit": "^4.5|^5", - "satooshi/php-coveralls": "^1.0" + "justinrainbow/json-schema": "^5.0", + "phpunit/phpunit": "^4.5 || ^5.0", + "satooshi/php-coveralls": "^1.0", + "symfony/phpunit-bridge": "^3.2" + }, + "suggest": { + "ext-xml": "For better performance." }, "bin": [ "php-cs-fixer" ], "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { "psr-4": { "PhpCsFixer\\": "src/" @@ -185,7 +237,49 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2016-12-01T06:18:06+00:00" + "time": "2017-03-15T17:13:07+00:00" + }, + { + "name": "ircmaxell/password-compat", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2014-11-20T16:49:30+00:00" }, { "name": "mpdf/mpdf", @@ -240,16 +334,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.5.5", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108" + "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108", - "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe", "shasum": "" }, "require": { @@ -278,26 +372,29 @@ "object", "object graph" ], - "time": "2016-10-31T17:19:45+00:00" + "time": "2017-01-26T22:05:40+00:00" }, { "name": "phenx/php-font-lib", - "version": "0.4", + "version": "0.5", "source": { "type": "git", "url": "https://github.com/PhenX/php-font-lib.git", - "reference": "b8af0cacdc3cbf1e41a586fcb78f506f4121a088" + "reference": "19ad2bebc35be028fcc0221025fcbf3d436a3962" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/b8af0cacdc3cbf1e41a586fcb78f506f4121a088", - "reference": "b8af0cacdc3cbf1e41a586fcb78f506f4121a088", + "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/19ad2bebc35be028fcc0221025fcbf3d436a3962", + "reference": "19ad2bebc35be028fcc0221025fcbf3d436a3962", "shasum": "" }, + "require-dev": { + "phpunit/phpunit": "^4.8" + }, "type": "library", "autoload": { - "psr-0": { - "FontLib\\": "src/" + "psr-4": { + "FontLib\\": "src/FontLib" } }, "notification-url": "https://packagist.org/downloads/", @@ -312,22 +409,25 @@ ], "description": "A library to read, parse, export and make subsets of different types of font files.", "homepage": "https://github.com/PhenX/php-font-lib", - "time": "2015-05-06T20:02:39+00:00" + "time": "2017-02-11T10:58:43+00:00" }, { "name": "phenx/php-svg-lib", - "version": "0.1", + "version": "v0.2", "source": { "type": "git", "url": "https://github.com/PhenX/php-svg-lib.git", - "reference": "b419766515b3426c6da74b0e29e93d71c4f17099" + "reference": "de291bec8449b89acfe85691b5c71434797959dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/b419766515b3426c6da74b0e29e93d71c4f17099", - "reference": "b419766515b3426c6da74b0e29e93d71c4f17099", + "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/de291bec8449b89acfe85691b5c71434797959dc", + "reference": "de291bec8449b89acfe85691b5c71434797959dc", "shasum": "" }, + "require": { + "sabberworm/php-css-parser": "6.0.*" + }, "type": "library", "autoload": { "psr-0": { @@ -346,7 +446,7 @@ ], "description": "A library to read, parse and export to PDF SVG files.", "homepage": "https://github.com/PhenX/php-svg-lib", - "time": "2015-05-06T18:49:49+00:00" + "time": "2016-12-13T20:25:45+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -496,27 +596,27 @@ }, { "name": "phpspec/prophecy", - "version": "v1.6.2", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", + "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", @@ -555,39 +655,39 @@ "spy", "stub" ], - "time": "2016-11-21T14:58:47+00:00" + "time": "2017-03-02T20:05:34+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "4.0.5", + "version": "4.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971" + "reference": "09e2277d14ea467e5a984010f501343ef29ffc69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/09e2277d14ea467e5a984010f501343ef29ffc69", + "reference": "09e2277d14ea467e5a984010f501343ef29ffc69", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "^1.4.2", - "sebastian/code-unit-reverse-lookup": "~1.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "~1.0|~2.0" + "sebastian/version": "^1.0 || ^2.0" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "^5.4" + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.4.0", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.5.1" }, "type": "library", "extra": { @@ -618,7 +718,7 @@ "testing", "xunit" ], - "time": "2017-01-20T15:06:43+00:00" + "time": "2017-03-01T09:12:17+00:00" }, { "name": "phpunit/php-file-iterator", @@ -710,25 +810,30 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.8", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -750,20 +855,20 @@ "keywords": [ "timer" ], - "time": "2016-05-12T18:03:57+00:00" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.9", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", "shasum": "" }, "require": { @@ -799,20 +904,20 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15T14:06:22+00:00" + "time": "2017-02-27T10:12:30+00:00" }, { "name": "phpunit/phpunit", - "version": "5.7.6", + "version": "5.7.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "caf8141b89691498d91aaac6c82e9cd5f685ae86" + "reference": "68752b665d3875f9a38a357e3ecb35c79f8673bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/caf8141b89691498d91aaac6c82e9cd5f685ae86", - "reference": "caf8141b89691498d91aaac6c82e9cd5f685ae86", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/68752b665d3875f9a38a357e3ecb35c79f8673bf", + "reference": "68752b665d3875f9a38a357e3ecb35c79f8673bf", "shasum": "" }, "require": { @@ -829,14 +934,14 @@ "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.2.2", + "sebastian/comparator": "^1.2.4", "sebastian/diff": "~1.2", "sebastian/environment": "^1.3.4 || ^2.0", "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.0 || ^2.0", + "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0|~2.0", + "sebastian/version": "~1.0.3|~2.0", "symfony/yaml": "~2.1|~3.0" }, "conflict": { @@ -881,7 +986,7 @@ "testing", "xunit" ], - "time": "2017-01-22T08:39:59+00:00" + "time": "2017-03-19T16:52:12+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -990,24 +1095,65 @@ "time": "2016-10-10T12:19:37+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.0", + "name": "sabberworm/php-css-parser", + "version": "6.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" + "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", + "reference": "9ea4b00c569b19f731d0c2e0e802055877ff40c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/9ea4b00c569b19f731d0c2e0e802055877ff40c2", + "reference": "9ea4b00c569b19f731d0c2e0e802055877ff40c2", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "Sabberworm\\CSS": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "time": "2015-08-24T08:48:52+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { @@ -1032,20 +1178,20 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13T06:45:14+00:00" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", - "version": "1.2.2", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", "shasum": "" }, "require": { @@ -1096,7 +1242,7 @@ "compare", "equality" ], - "time": "2016-11-19T09:18:40+00:00" + "time": "2017-01-29T09:50:25+00:00" }, { "name": "sebastian/diff", @@ -1320,16 +1466,16 @@ }, { "name": "sebastian/object-enumerator", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", "shasum": "" }, "require": { @@ -1362,7 +1508,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-11-19T07:35:10+00:00" + "time": "2017-02-18T15:18:39+00:00" }, { "name": "sebastian/recursion-context", @@ -1553,16 +1699,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "2.7.1", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "9b324f3a1132459a7274a0ace2e1b766ba80930f" + "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9b324f3a1132459a7274a0ace2e1b766ba80930f", - "reference": "9b324f3a1132459a7274a0ace2e1b766ba80930f", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", + "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", "shasum": "" }, "require": { @@ -1627,20 +1773,20 @@ "phpcs", "standards" ], - "time": "2016-11-30T04:02:31+00:00" + "time": "2017-03-01T22:17:45+00:00" }, { "name": "symfony/console", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd" + "reference": "28fb243a2b5727774ca309ec2d92da240f1af0dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/4f9e449e76996adf310498a8ca955c6deebe29dd", - "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd", + "url": "https://api.github.com/repos/symfony/console/zipball/28fb243a2b5727774ca309ec2d92da240f1af0dd", + "reference": "28fb243a2b5727774ca309ec2d92da240f1af0dd", "shasum": "" }, "require": { @@ -1690,20 +1836,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-01-08T20:47:33+00:00" + "time": "2017-03-06T19:30:27+00:00" }, { "name": "symfony/debug", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05" + "reference": "b90c9f91ad8ac37d9f114e369042d3226b34dc1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/810ba5c1c5352a4ddb15d4719e8936751dff0b05", - "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05", + "url": "https://api.github.com/repos/symfony/debug/zipball/b90c9f91ad8ac37d9f114e369042d3226b34dc1a", + "reference": "b90c9f91ad8ac37d9f114e369042d3226b34dc1a", "shasum": "" }, "require": { @@ -1747,20 +1893,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-18T17:28:00+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6" + "reference": "b7a1b9e0a0f623ce43b4c8d775eb138f190c9d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9137eb3a3328e413212826d63eeeb0217836e2b6", - "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7a1b9e0a0f623ce43b4c8d775eb138f190c9d8d", + "reference": "b7a1b9e0a0f623ce43b4c8d775eb138f190c9d8d", "shasum": "" }, "require": { @@ -1807,20 +1953,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-21T09:12:04+00:00" }, { "name": "symfony/filesystem", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4" + "reference": "bc0f17bed914df2cceb989972c3b996043c4da4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/a0c6ef2dc78d33b58d91d3a49f49797a184d06f4", - "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/bc0f17bed914df2cceb989972c3b996043c4da4a", + "reference": "bc0f17bed914df2cceb989972c3b996043c4da4a", "shasum": "" }, "require": { @@ -1856,20 +2002,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2017-01-08T20:47:33+00:00" + "time": "2017-03-06T19:30:27+00:00" }, { "name": "symfony/finder", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8c71141cae8e2957946b403cc71a67213c0380d6" + "reference": "92d7476d2df60cd851a3e13e078664b1deb8ce10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8c71141cae8e2957946b403cc71a67213c0380d6", - "reference": "8c71141cae8e2957946b403cc71a67213c0380d6", + "url": "https://api.github.com/repos/symfony/finder/zipball/92d7476d2df60cd851a3e13e078664b1deb8ce10", + "reference": "92d7476d2df60cd851a3e13e078664b1deb8ce10", "shasum": "" }, "require": { @@ -1905,7 +2051,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-21T09:12:04+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2025,17 +2171,131 @@ "time": "2016-11-14T01:06:16+00:00" }, { - "name": "symfony/process", - "version": "v3.2.2", + "name": "symfony/polyfill-php55", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893" + "url": "https://github.com/symfony/polyfill-php55.git", + "reference": "03e3f0350bca2220e3623a0e340eef194405fc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/350e810019fc52dd06ae844b6a6d382f8a0e8893", - "reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/03e3f0350bca2220e3623a0e340eef194405fc67", + "reference": "03e3f0350bca2220e3623a0e340eef194405fc67", + "shasum": "" + }, + "require": { + "ircmaxell/password-compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php55\\": "" + }, + "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 backporting some PHP 5.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/polyfill-xml", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-xml.git", + "reference": "64b6a864f18ab4fddad49f5025f805f6781dfabd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-xml/zipball/64b6a864f18ab4fddad49f5025f805f6781dfabd", + "reference": "64b6a864f18ab4fddad49f5025f805f6781dfabd", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-xml": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Xml\\": "" + }, + "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 xml's utf8_encode and utf8_decode functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/process", + "version": "v3.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "68bfa8c83f24c0ac04ea7193bcdcda4519f41892" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/68bfa8c83f24c0ac04ea7193bcdcda4519f41892", + "reference": "68bfa8c83f24c0ac04ea7193bcdcda4519f41892", "shasum": "" }, "require": { @@ -2071,20 +2331,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-03-04T12:23:14+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "9aa0b51889c01bca474853ef76e9394b02264464" + "reference": "c5ee0f8650c84b4d36a5f76b3b504233feaabf75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/9aa0b51889c01bca474853ef76e9394b02264464", - "reference": "9aa0b51889c01bca474853ef76e9394b02264464", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/c5ee0f8650c84b4d36a5f76b3b504233feaabf75", + "reference": "c5ee0f8650c84b4d36a5f76b3b504233feaabf75", "shasum": "" }, "require": { @@ -2120,20 +2380,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-18T17:28:00+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "50eadbd7926e31842893c957eca362b21592a97d" + "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d", - "reference": "50eadbd7926e31842893c957eca362b21592a97d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/093e416ad096355149e265ea2e4cc1f9ee40ab1a", + "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a", "shasum": "" }, "require": { @@ -2175,7 +2435,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-01-03T13:51:32+00:00" + "time": "2017-03-07T16:47:02+00:00" }, { "name": "tecnickcom/tcpdf", @@ -2301,7 +2561,8 @@ "ext-mbstring": "*", "ext-iconv": "*", "ext-xml": "*", - "ext-xmlwriter": "*" + "ext-xmlwriter": "*", + "ext-zip": "*" }, "platform-dev": [] } diff --git a/docs/Examples/Calculations/Database/DAVERAGE.php b/docs/Examples/Calculations/Database/DAVERAGE.php index 650e1324..b54a7d1d 100644 --- a/docs/Examples/Calculations/Database/DAVERAGE.php +++ b/docs/Examples/Calculations/Database/DAVERAGE.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Returns the average of selected database entries.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DCOUNT.php b/docs/Examples/Calculations/Database/DCOUNT.php index 22669b36..2cd0661d 100644 --- a/docs/Examples/Calculations/Database/DCOUNT.php +++ b/docs/Examples/Calculations/Database/DCOUNT.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Counts the cells that contain numbers in a database.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DGET.php b/docs/Examples/Calculations/Database/DGET.php index 6a133426..d64b1d6e 100644 --- a/docs/Examples/Calculations/Database/DGET.php +++ b/docs/Examples/Calculations/Database/DGET.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Extracts a single value from a column of a list or database that matches conditions that you specify.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DMAX.php b/docs/Examples/Calculations/Database/DMAX.php index 18936c5b..58d2f039 100644 --- a/docs/Examples/Calculations/Database/DMAX.php +++ b/docs/Examples/Calculations/Database/DMAX.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Returns the maximum value from selected database entries.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DMIN.php b/docs/Examples/Calculations/Database/DMIN.php index 0184173b..e4fb7658 100644 --- a/docs/Examples/Calculations/Database/DMIN.php +++ b/docs/Examples/Calculations/Database/DMIN.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Returns the minimum value from selected database entries.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DPRODUCT.php b/docs/Examples/Calculations/Database/DPRODUCT.php index 41d60a2f..382227df 100644 --- a/docs/Examples/Calculations/Database/DPRODUCT.php +++ b/docs/Examples/Calculations/Database/DPRODUCT.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Multiplies the values in a column of a list or database that match conditions that you specify.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DSTDEV.php b/docs/Examples/Calculations/Database/DSTDEV.php index 2fbbda20..e1273e5a 100644 --- a/docs/Examples/Calculations/Database/DSTDEV.php +++ b/docs/Examples/Calculations/Database/DSTDEV.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Estimates the standard deviation based on a sample of selected database entries.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DSTDEVP.php b/docs/Examples/Calculations/Database/DSTDEVP.php index 1f0a0fe6..3af791bb 100644 --- a/docs/Examples/Calculations/Database/DSTDEVP.php +++ b/docs/Examples/Calculations/Database/DSTDEVP.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Calculates the standard deviation based on the entire population of selected database entries.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DVAR.php b/docs/Examples/Calculations/Database/DVAR.php index 00bd97aa..44e63994 100644 --- a/docs/Examples/Calculations/Database/DVAR.php +++ b/docs/Examples/Calculations/Database/DVAR.php @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Estimates variance based on a sample from selected database entries.

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/Database/DVARP.php b/docs/Examples/Calculations/Database/DVARP.php index c6cc0677..c630e731 100644 --- a/docs/Examples/Calculations/Database/DVARP.php +++ b/docs/Examples/Calculations/Database/DVARP.php @@ -10,7 +10,7 @@ date_default_timezone_set('Europe/London'); -PHPExcel Calculation Examples +PhpSpreadsheet Calculation Examples @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Calculates variance based on the entire population of selected database entries,

getActiveSheet(); // Add some data diff --git a/docs/Examples/Calculations/DateTime/DATE.php b/docs/Examples/Calculations/DateTime/DATE.php index 0179b7c1..1af05e3e 100644 --- a/docs/Examples/Calculations/DateTime/DATE.php +++ b/docs/Examples/Calculations/DateTime/DATE.php @@ -10,7 +10,7 @@ date_default_timezone_set('Europe/London'); -PHPExcel Calculation Examples +PhpSpreadsheet Calculation Examples @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Returns the serial number of a particular date.

getActiveSheet(); // Add some data @@ -76,4 +72,6 @@ echo '
'; echo ''; } ?> - \ No newline at end of file + + + \ No newline at end of file diff --git a/docs/Examples/Calculations/DateTime/DATEVALUE.php b/docs/Examples/Calculations/DateTime/DATEVALUE.php index 7b0a8556..42996a4e 100644 --- a/docs/Examples/Calculations/DateTime/DATEVALUE.php +++ b/docs/Examples/Calculations/DateTime/DATEVALUE.php @@ -10,7 +10,7 @@ date_default_timezone_set('Europe/London'); -PHPExcel Calculation Examples +PhpSpreadsheet Calculation Examples @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Converts a date in the form of text to a serial number.

getActiveSheet(); // Add some data @@ -51,7 +47,7 @@ echo '
'; // Test the formulae ?> -

Warning: The PHPExcel DATEVALUE() function accepts a wider range of date formats than MS Excel's DATEFORMAT() function.

+

Warning: The PhpSpreadsheet DATEVALUE() function accepts a wider range of date formats than MS Excel's DATEFORMAT() function.

@@ -69,4 +65,6 @@ echo '
'; echo ''; } ?> -
Date String
\ No newline at end of file + + + \ No newline at end of file diff --git a/docs/Examples/Calculations/DateTime/TIME.php b/docs/Examples/Calculations/DateTime/TIME.php index 3fae88c2..9c206304 100644 --- a/docs/Examples/Calculations/DateTime/TIME.php +++ b/docs/Examples/Calculations/DateTime/TIME.php @@ -10,7 +10,7 @@ date_default_timezone_set('Europe/London'); -PHPExcel Calculation Examples +PhpSpreadsheet Calculation Examples @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Returns the serial number of a particular time.

getActiveSheet(); // Add some data @@ -74,4 +70,6 @@ echo '
'; echo ''; } ?> - \ No newline at end of file + + + \ No newline at end of file diff --git a/docs/Examples/Calculations/DateTime/TIMEVALUE.php b/docs/Examples/Calculations/DateTime/TIMEVALUE.php index c482c374..c5745cc2 100644 --- a/docs/Examples/Calculations/DateTime/TIMEVALUE.php +++ b/docs/Examples/Calculations/DateTime/TIMEVALUE.php @@ -10,7 +10,7 @@ date_default_timezone_set('Europe/London'); -PHPExcel Calculation Examples +PhpSpreadsheet Calculation Examples @@ -19,14 +19,10 @@ date_default_timezone_set('Europe/London');

Converts a time in the form of text to a serial number.

getActiveSheet(); // Add some data @@ -65,4 +61,6 @@ echo '
'; echo ''; } ?> - \ No newline at end of file + + + \ No newline at end of file diff --git a/docs/Examples/Calculations/index.php b/docs/Examples/Calculations/index.php index 0c3d8c00..5990ca23 100644 --- a/docs/Examples/Calculations/index.php +++ b/docs/Examples/Calculations/index.php @@ -10,14 +10,14 @@ date_default_timezone_set('Europe/London'); -PHPExcel Calculation Function Examples +PhpSpreadsheet Calculation Function Examples PHPExcel Calculation Function Examples'; +echo '

PhpSpreadsheet Calculation Function Examples

'; $exampleTypeList = glob('./*', GLOB_ONLYDIR); diff --git a/docs/Examples/Reader/exampleReader01.php b/docs/Examples/Reader/exampleReader01.php index 1bad76d8..9ef1f565 100644 --- a/docs/Examples/Reader/exampleReader01.php +++ b/docs/Examples/Reader/exampleReader01.php @@ -10,20 +10,16 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reader Example #01 +PhpSpreadsheet Reader Example #01 -

PHPExcel Reader Example #01

+

PhpSpreadsheet Reader Example #01

Simple File Reader using \PhpOffice\PhpSpreadsheet\IOFactory::load()

'; diff --git a/docs/Examples/Reader/exampleReader02.php b/docs/Examples/Reader/exampleReader02.php index 2eccc24d..b98b860d 100644 --- a/docs/Examples/Reader/exampleReader02.php +++ b/docs/Examples/Reader/exampleReader02.php @@ -5,23 +5,19 @@ set_time_limit(0); date_default_timezone_set('Europe/London'); -/* Include path **/ -set_include_path(get_include_path() . PATH_SEPARATOR . '../../../Classes/'); - -/** \PhpOffice\PhpSpreadsheet\IOFactory */ -include 'PHPExcel/IOFactory.php'; +require_once __DIR__ . '/../../../src/Bootstrap.php'; ?> -PHPExcel Reader Example #02 +PhpSpreadsheet Reader Example #02 -

PHPExcel Reader Example #02

+

PhpSpreadsheet Reader Example #02

Simple File Reader using a Specified Reader

-PHPExcel Reader Example #03 +PhpSpreadsheet Reader Example #03 -

PHPExcel Reader Example #03

+

PhpSpreadsheet Reader Example #03

Simple File Reader using the \PhpOffice\PhpSpreadsheet\IOFactory to Return a Reader

-PHPExcel Reader Example #04 +PhpSpreadsheet Reader Example #04 -

PHPExcel Reader Example #04

+

PhpSpreadsheet Reader Example #04

Simple File Reader using the \PhpOffice\PhpSpreadsheet\IOFactory to Identify a Reader to Use

-PHPExcel Reader Example #05 +PhpSpreadsheet Reader Example #05 -

PHPExcel Reader Example #05

+

PhpSpreadsheet Reader Example #05

Simple File Reader using the "Read Data Only" Option

-PHPExcel Reader Example #06 +PhpSpreadsheet Reader Example #06 -

PHPExcel Reader Example #06

+

PhpSpreadsheet Reader Example #06

Simple File Reader Loading All WorkSheets

-PHPExcel Reader Example #07 +PhpSpreadsheet Reader Example #07 -

PHPExcel Reader Example #07

+

PhpSpreadsheet Reader Example #07

Simple File Reader Loading a Single Named WorkSheet

-PHPExcel Reader Example #08 +PhpSpreadsheet Reader Example #08 -

PHPExcel Reader Example #08

+

PhpSpreadsheet Reader Example #08

Simple File Reader Loading Several Named WorkSheets

-PHPExcel Reader Example #09 +PhpSpreadsheet Reader Example #09 -

PHPExcel Reader Example #09

+

PhpSpreadsheet Reader Example #09

Simple File Reader Using a Read Filter

-PHPExcel Reader Example #10 +PhpSpreadsheet Reader Example #10 -

PHPExcel Reader Example #10

+

PhpSpreadsheet Reader Example #10

Simple File Reader Using a Configurable Read Filter

-PHPExcel Reader Example #11 +PhpSpreadsheet Reader Example #11 -

PHPExcel Reader Example #11

+

PhpSpreadsheet Reader Example #11

Reading a Workbook in "Chunks" Using a Configurable Read Filter (Version 1)

setReadFilter($chunkFilter); - /* Load only the rows that match our filter from $inputFileName to a PHPExcel Object **/ + /* Load only the rows that match our filter from $inputFileName to a PhpSpreadsheet Object **/ $spreadsheet = $reader->load($inputFileName); // Do some processing here diff --git a/docs/Examples/Reader/exampleReader12.php b/docs/Examples/Reader/exampleReader12.php index 8ac4de27..673b2f14 100644 --- a/docs/Examples/Reader/exampleReader12.php +++ b/docs/Examples/Reader/exampleReader12.php @@ -10,20 +10,16 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reader Example #12 +PhpSpreadsheet Reader Example #12 -

PHPExcel Reader Example #12

+

PhpSpreadsheet Reader Example #12

Reading a Workbook in "Chunks" Using a Configurable Read Filter (Version 2)

'; /* Tell the Read Filter, the limits on which rows we want to read this iteration **/ $chunkFilter->setRows($startRow, $chunkSize); - /* Load only the rows that match our filter from $inputFileName to a PHPExcel Object **/ + /* Load only the rows that match our filter from $inputFileName to a PhpSpreadsheet Object **/ $spreadsheet = $reader->load($inputFileName); // Do some processing here diff --git a/docs/Examples/Reader/exampleReader13.php b/docs/Examples/Reader/exampleReader13.php index 45f2c612..9977a83c 100644 --- a/docs/Examples/Reader/exampleReader13.php +++ b/docs/Examples/Reader/exampleReader13.php @@ -10,20 +10,16 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reader Example #13 +PhpSpreadsheet Reader Example #13 -

PHPExcel Reader Example #13

+

PhpSpreadsheet Reader Example #13

Simple File Reader for Multiple CSV Files

-PHPExcel Reader Example #15 +PhpSpreadsheet Reader Example #14 -

PHPExcel Reader Example #14

+

PhpSpreadsheet Reader Example #14

Reading a Large CSV file in "Chunks" to split across multiple Worksheets

setReadFilter($chunkFilter) ->setContiguous(true); -/* Instantiate a new PHPExcel object manually **/ -$spreadsheet = new PHPExcel(); +/* Instantiate a new PhpSpreadsheet object manually **/ +$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); /* Set a sheet index **/ $sheet = 0; @@ -83,7 +84,7 @@ for ($startRow = 2; $startRow <= 240; $startRow += $chunkSize) { /* Increment the worksheet index pointer for the Reader **/ $reader->setSheetIndex($sheet); - /* Load only the rows that match our filter into a new worksheet in the PHPExcel Object **/ + /* Load only the rows that match our filter into a new worksheet in the PhpSpreadsheet Object **/ $reader->loadIntoExisting($inputFileName, $spreadsheet); /* Set the worksheet title (to reference the "sheet" of data that we've loaded) **/ /* and increment the sheet index as well **/ diff --git a/docs/Examples/Reader/exampleReader15.php b/docs/Examples/Reader/exampleReader15.php index 7931a073..a04a1adc 100644 --- a/docs/Examples/Reader/exampleReader15.php +++ b/docs/Examples/Reader/exampleReader15.php @@ -1,24 +1,25 @@ -PHPExcel Reader Example #15 +PhpSpreadsheet Reader Example #15 -

PHPExcel Reader Example #15

+

PhpSpreadsheet Reader Example #15

Simple File Reader for Tab-Separated Value File using the Advanced Value Binder

-PHPExcel Reader Example #16 +PhpSpreadsheet Reader Example #16 -

PHPExcel Reader Example #16

+

PhpSpreadsheet Reader Example #16

Handling Loader Exceptions using Try/Catch

'; try { $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName); -} catch (\PHPExcel\Reader\Exception $e) { +} catch (\InvalidArgumentException $e) { die('Error loading file "' . pathinfo($inputFileName, PATHINFO_BASENAME) . '": ' . $e->getMessage()); } diff --git a/docs/Examples/Reader/exampleReader17.php b/docs/Examples/Reader/exampleReader17.php index fbcae6b1..f76994e9 100644 --- a/docs/Examples/Reader/exampleReader17.php +++ b/docs/Examples/Reader/exampleReader17.php @@ -10,20 +10,16 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reader Example #17 +PhpSpreadsheet Reader Example #17 -

PHPExcel Reader Example #17

+

PhpSpreadsheet Reader Example #17

Simple File Reader Loading Several Named WorkSheets

-PHPExcel Reader Example #18 +PhpSpreadsheet Reader Example #18 -

PHPExcel Reader Example #18

+

PhpSpreadsheet Reader Example #18

Reading list of WorkSheets without loading entire file

-PHPExcel Reader Example #19 +PhpSpreadsheet Reader Example #19 -

PHPExcel Reader Example #19

+

PhpSpreadsheet Reader Example #19

Reading WorkSheet information without loading entire file

-PHPExcel Reading WorkBook Data Example #01 +PhpSpreadsheet Reading WorkBook Data Example #01 -

PHPExcel Reading WorkBook Data Example #01

+

PhpSpreadsheet Reading WorkBook Data Example #01

Read the WorkBook Properties

load($inputFileName); echo '
'; diff --git a/docs/Examples/Reading WorkBook Data/exampleWorkBookReader02.php b/docs/Examples/Reading WorkBook Data/exampleWorkBookReader02.php index 5018ea00..c021728c 100644 --- a/docs/Examples/Reading WorkBook Data/exampleWorkBookReader02.php +++ b/docs/Examples/Reading WorkBook Data/exampleWorkBookReader02.php @@ -10,27 +10,23 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reading WorkBook Data Example #02 +PhpSpreadsheet Reading WorkBook Data Example #02 -

PHPExcel Reading WorkBook Data Example #02

+

PhpSpreadsheet Reading WorkBook Data Example #02

Read a list of Custom Properties for a WorkBook

load($inputFileName); echo '
'; diff --git a/docs/Examples/Reading WorkBook Data/exampleWorkBookReader03.php b/docs/Examples/Reading WorkBook Data/exampleWorkBookReader03.php index 6d56a3fb..c02f51bd 100644 --- a/docs/Examples/Reading WorkBook Data/exampleWorkBookReader03.php +++ b/docs/Examples/Reading WorkBook Data/exampleWorkBookReader03.php @@ -10,27 +10,23 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reading WorkBook Data Example #03 +PhpSpreadsheet Reading WorkBook Data Example #03 -

PHPExcel Reading WorkBook Data Example #03

+

PhpSpreadsheet Reading WorkBook Data Example #03

Read Custom Property Values for a WorkBook

load($inputFileName); echo '
'; diff --git a/docs/Examples/Reading WorkBook Data/exampleWorkBookReader04.php b/docs/Examples/Reading WorkBook Data/exampleWorkBookReader04.php index 307d8ede..88c7f9e0 100644 --- a/docs/Examples/Reading WorkBook Data/exampleWorkBookReader04.php +++ b/docs/Examples/Reading WorkBook Data/exampleWorkBookReader04.php @@ -10,38 +10,34 @@ date_default_timezone_set('Europe/London'); -PHPExcel Reading WorkBook Data Example #04 +PhpSpreadsheet Reading WorkBook Data Example #04 -

PHPExcel Reading WorkBook Data Example #04

+

PhpSpreadsheet Reading WorkBook Data Example #04

Get a List of the Worksheets in a WorkBook

load($inputFileName); echo '
'; echo 'Reading the number of Worksheets in the WorkBook
'; -/* Use the PHPExcel object's getSheetCount() method to get a count of the number of WorkSheets in the WorkBook */ +/* Use the PhpSpreadsheet object's getSheetCount() method to get a count of the number of WorkSheets in the WorkBook */ $sheetCount = $spreadsheet->getSheetCount(); echo 'There ',(($sheetCount == 1) ? 'is' : 'are'),' ',$sheetCount,' WorkSheet',(($sheetCount == 1) ? '' : 's'),' in the WorkBook

'; echo 'Reading the names of Worksheets in the WorkBook
'; -/* Use the PHPExcel object's getSheetNames() method to get an array listing the names/titles of the WorkSheets in the WorkBook */ +/* Use the PhpSpreadsheet object's getSheetNames() method to get an array listing the names/titles of the WorkSheets in the WorkBook */ $sheetNames = $spreadsheet->getSheetNames(); foreach ($sheetNames as $sheetIndex => $sheetName) { echo 'WorkSheet #',$sheetIndex,' is named "',$sheetName,'"
'; diff --git a/docs/Examples/index.php b/docs/Examples/index.php index f776a26a..a2c51879 100644 --- a/docs/Examples/index.php +++ b/docs/Examples/index.php @@ -10,7 +10,7 @@ date_default_timezone_set('Europe/London'); -PHPExcel Examples +PhpSpreadsheet Examples @@ -20,7 +20,7 @@ date_default_timezone_set('Europe/London'); $exampleTypeList = glob('./*', GLOB_ONLYDIR); foreach ($exampleTypeList as $exampleType) { - echo '

PHPExcel ' . pathinfo($exampleType, PATHINFO_BASENAME) . ' Examples

'; + echo '

PhpSpreadsheet ' . pathinfo($exampleType, PATHINFO_BASENAME) . ' Examples

'; $exampleList = glob('./' . $exampleType . '/*.php'); diff --git a/docs/faq.md b/docs/faq.md index 6ef48c87..114c48a8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,48 +1,18 @@ # Frequently asked questions -The up-to-date F.A.Q. page for PHPExcel can be found on -. - ## There seems to be a problem with character encoding... It is necessary to use UTF-8 encoding for all texts in PhpSpreadsheet. If the script uses different encoding then you can convert those texts -with PHP's iconv() or mb\_convert\_encoding() functions. - -## PHP complains about ZipArchive not being found - -Make sure you meet all requirements, especially php\_zip extension -should be enabled. - -The ZipArchive class is only required when reading or writing formats -that use Zip compression (Xlsx and Ods). Since version 1.7.6 the PCLZip -library has been bundled with PhpSpreadsheet as an alternative to the -ZipArchive class. - -This can be enabled by calling: - -``` php -\PhpOffice\PhpSpreadsheet\Settings::setZipClass(\PhpOffice\PhpSpreadsheet\Settings::PCLZIP); -``` - -*before* calling the save method of the Xlsx Writer. - -You can revert to using ZipArchive by calling: - -``` php -\PhpOffice\PhpSpreadsheet\Settings::setZipClass(\PhpOffice\PhpSpreadsheet\Settings::ZIPARCHIVE); -``` - -At present, this only allows you to write Xlsx files without the need -for ZipArchive (not to read Xlsx or Ods) +with PHP's `iconv()` or `mb_convert_encoding()` functions. ## Fatal error: Allowed memory size of xxx bytes exhausted (tried to allocate yyy bytes) in zzz on line aaa PhpSpreadsheet holds an "in memory" representation of a spreadsheet, so it is susceptible to PHP's memory limitations. The memory made available -to PHP can be increased by editing the value of the memory\_limit -directive in your php.ini file, or by using ini\_set('memory\_limit', -'128M') within your code (ISP permitting). +to PHP can be increased by editing the value of the `memory_limit` +directive in your php.ini file, or by using +`ini_set('memory_limit', '128M')` within your code. Some Readers and Writers are faster than others, and they also use differing amounts of memory. You can find some indication of the @@ -83,8 +53,8 @@ reference](./references/features-cross-reference.md). ## Formulas don't seem to be calculated in Excel2003 using compatibility pack? -This is normal behaviour of the compatibility pack, Xlsx displays this -correctly. Use \PhpOffice\PhpSpreadsheet\Writer\Xls if you really need +This is normal behaviour of the compatibility pack, `Xlsx` displays this +correctly. Use `\PhpOffice\PhpSpreadsheet\Writer\Xls` if you really need calculated values, or force recalculation in Excel2003. ## Setting column width is not 100% accurate @@ -93,7 +63,8 @@ Trying to set column width, I experience one problem. When I open the file in Excel, the actual width is 0.71 less than it should be. The short answer is that PhpSpreadsheet uses a measure where padding is -included. See section: "Setting a column's width" for more details. +included. See [how to seta column's width](./topics/recipes.md#setting-a-columns-width) +for more details. ## How do I use PhpSpreadsheet with my framework @@ -108,8 +79,8 @@ included. See section: "Setting a column's width" for more details. within CakePHP - Integrating [PhpSpreadsheet into Kohana 3](http://www.flynsarmy.com/2010/07/phpexcel-module-for-kohana-3/) - and \[Интеграция PHPExcel и Kohana - Framework\]\[http://szpargalki.blogspot.com/2011/02/phpexcel-kohana-framework.html\] + and [Интеграция PHPExcel и Kohana + Framework](http://szpargalki.blogspot.com/2011/02/phpexcel-kohana-framework.html) - Using [PhpSpreadsheet with TYPO3](http://typo3.org/documentation/document-library/extension-manuals/phpexcel_library/1.1.1/view/toc/0/) diff --git a/docs/index.md b/docs/index.md index fb0e1ecd..30043595 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,1643 +13,71 @@ spreadsheet file formats, like Excel and LibreOffice Calc. The following software is required to develop using PhpSpreadsheet: - PHP version 5.6 or newer -- PHP extension php\_zip enabled (see - [FAQ](./faq.md#php-complains-about-ziparchive-not-being-found)) +- PHP extension php\_zip enabled - PHP extension php\_xml enabled - PHP extension php\_gd2 enabled (if not compiled in) -*Note:* PHP 5.6.29 has [a bug](https://bugs.php.net/bug.php?id=735300) that -prevents SQLite3 caching to work correctly. Use a newer (or older) versions of -PHP if you need SQLite3 caching. +## Installation -## Installation instructions +Use [composer](https://getcomposer.org/) to install PhpSpreadsheet into your project: -Installation is quite easy: copy the contents of the Classes folder to -any location within your application source directories. - -*Example:* - -If your web root folder is /var/www/ you may want to create a subfolder -called /var/www/Classes/ and copy the files into that folder so you end -up with files: - - /var/www/Classes/PHPExcel.php - /var/www/Classes/PHPExcel/Calculation.php - /var/www/Classes/PHPExcel/Cell.php - ... - -## Getting started - -A good way to get started is to run some of the tests included in the -download. Copy the "Examples" folder next to your "Classes" folder from -above so you end up with: - - /var/www/Examples/01simple.php - /var/www/Examples/02types.php - ... - -Start running the tests by pointing your browser to the test scripts: - -http://example.com/Tests/01simple.php -http://example.com/Tests/02types.php ... - -**Note:** It may be necessary to modify the include/require statements -at the beginning of each of the test scripts if your "Classes" folder -from above is named differently. - -## Useful links and tools - -There are some links and tools which are very useful when developing -using PhpSpreadsheet. - -### OpenXML / SpreadsheetML - -- [File format - documentation](http://www.ecma-international.org/news/TC45_current_work/TC45_available_docs.htm) -- [OpenXML Explained - e-book](http://openxmldeveloper.org/articles/1970.aspx) -- [Microsoft Office Compatibility Pack for Word, Excel, and PowerPoint - 2007 File - Formats](http://www.microsoft.com/downloads/details.aspx?familyid=941b3470-3ae9-4aee-8f43-c6bb74cd1466&displaylang=en) -- [OpenXML Package Explorer](http://www.codeplex.com/PackageExplorer/) - -# Architecture - -## Schematical - -![01-schematic.png](./images/01-schematic.png "Basic Architecture Schematic") - -## AutoLoader - -PhpSpreadsheet relies on Composer autoloader. So before working with -PhpSpreadsheet in standalone, be sure to run `composer install`. Or add it to a -pre-existing project with `composer require phpoffice/phpspreadsheet`. - -## Spreadsheet in memory - -PhpSpreadsheet's architecture is built in a way that it can serve as an -in-memory spreadsheet. This means that, if one would want to create a -web based view of a spreadsheet which communicates with PhpSpreadsheet's -object model, he would only have to write the front-end code. - -Just like desktop spreadsheet software, PhpSpreadsheet represents a -spreadsheet containing one or more worksheets, which contain cells with -data, formulas, images, ... - -## Readers and writers - -On its own, the `Spreadsheet` class does not provide the functionality -to read from or write to a persisted spreadsheet (on disk or in a -database). To provide that functionality, readers and writers can be -used. - -By default, the PhpSpreadsheet package provides some readers and -writers, including one for the Open XML spreadsheet format (a.k.a. Excel -2007 file format). You are not limited to the default readers and -writers, as you are free to implement the -\PhpOffice\PhpSpreadsheet\Reader\IReader and -\PhpOffice\PhpSpreadsheet\Writer\IWriter interface in a custom class. - -![02-readers-writers.png](./images/02-readers-writers.png "Readers/Writers") - -## Fluent interfaces - -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 -$spreadsheet->getProperties()->setCreator("Maarten Balliauw"); -$spreadsheet->getProperties()->setLastModifiedBy("Maarten Balliauw"); -$spreadsheet->getProperties()->setTitle("Office 2007 XLSX Test Document"); -$spreadsheet->getProperties()->setSubject("Office 2007 XLSX Test Document"); -$spreadsheet->getProperties()->setDescription("Test document for Office 2007 XLSX, generated using PHP classes."); -$spreadsheet->getProperties()->setKeywords("office 2007 openxml php"); -$spreadsheet->getProperties()->setCategory("Test result file"); -``` - -This can be rewritten as: - -``` php -$spreadsheet->getProperties() - ->setCreator("Maarten Balliauw") - ->setLastModifiedBy("Maarten Balliauw") - ->setTitle("Office 2007 XLSX Test Document") - ->setSubject("Office 2007 XLSX Test Document") - ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.") - ->setKeywords("office 2007 openxml php") - ->setCategory("Test result file"); -``` - -> **Using fluent interfaces is not required** Fluent interfaces have -> been implemented to provide a convenient programming API. Use of them -> is not required, but can make your code easier to read and maintain. -> It can also improve performance, as you are reducing the overall -> number of calls to PhpSpreadsheet methods: in the above example, the -> `getProperties()` method is being called only once rather than 7 times -> in the non-fluent version. - -# Creating a spreadsheet - -## The `Spreadsheet` class - -The `Spreadsheet` class is the core of PhpSpreadsheet. It contains -references to the contained worksheets, document security settings and -document meta data. - -To simplify the PhpSpreadsheet concept: the `Spreadsheet` class -represents your workbook. - -Typically, you will create a workbook in one of two ways, either by -loading it from a spreadsheet file, or creating it manually. A third -option, though less commonly used, is cloning an existing workbook that -has been created using one of the previous two methods. - -### Loading a Workbook from a file - -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](../topics/reading-files.md) document. - -``` php -$inputFileName = './sampleData/example1.xls'; - -/** Load $inputFileName to a Spreadsheet object **/ -$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName); -``` - -### Creating a new workbook - -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 -/** Create a new Spreadsheet Object **/ -$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); -``` - -A new workbook will always be created with a single worksheet. - -## Clearing a Workbook from memory - -The PhpSpreadsheet object contains cyclic references (e.g. the workbook -is linked to the worksheets, and the worksheets are linked to their -parent workbook) which cause problems when PHP tries to clear the -objects from memory when they are unset(), or at the end of a function -when they are in local scope. The result of this is "memory leaks", -which can easily use a large amount of PHP's limited memory. - -This can only be resolved manually: if you need to unset a workbook, -then you also need to "break" these cyclic references before doing so. -PhpSpreadsheet provides the disconnectWorksheets() method for this -purpose. - -``` php -$spreadsheet->disconnectWorksheets(); -unset($spreadsheet); -``` - -# Worksheets - -A worksheet is a collection of cells, formulae, images, graphs, etc. It -holds all data necessary to represent a spreadsheet worksheet. - -When you load a workbook from a spreadsheet file, it will be loaded with -all its existing worksheets (unless you specified that only certain -sheets should be loaded). When you load from non-spreadsheet files (such -as a CSV or HTML file) or from spreadsheet formats that don't identify -worksheets by name (such as SYLK), then a single worksheet called -"WorkSheet1" will be created containing the data from that file. - -When you instantiate a new workbook, PhpSpreadsheet will create it with -a single worksheet called "WorkSheet1". - -The `getSheetCount()` method will tell you the number of worksheets in -the workbook; while the `getSheetNames()` method will return a list of -all worksheets in the workbook, indexed by the order in which their -"tabs" would appear when opened in MS Excel (or other appropriate -Spreadsheet program). - -Individual worksheets can be accessed by name, or by their index -position in the workbook. The index position represents the order that -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 -// Get the second sheet in the workbook -// Note that sheets are indexed from 0 -$spreadsheet->getSheet(1); -``` - -If you don't specify a sheet index, then the first worksheet will be -returned. - -Methods also exist allowing you to reorder the worksheets in the -workbook. - -To access a sheet by name, use the `getSheetByName()` method, specifying -the name of the worksheet that you want to access. - -``` php -// Retrieve the worksheet called 'Worksheet 1' -$spreadsheet->getSheetByName('Worksheet 1'); -``` - -Alternatively, one worksheet is always the currently active worksheet, -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 -// Retrieve the current active worksheet -$spreadsheet->getActiveSheet(); -``` - -You can change the currently active sheet by index or by name using the -`setActiveSheetIndex()` and `setActiveSheetIndexByName()` methods. - -## Adding a new Worksheet - -You can add a new worksheet to the workbook using the `createSheet()` -method of the `Spreadsheet` object. By default, this will be created as -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 -$spreadsheet->createSheet(); -``` - -A new worksheet created using this method will be called -"Worksheet<n>" where "<n>" is the lowest number possible to -guarantee that the title is unique. - -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 -// Create a new worksheet called "My Data" -$myWorkSheet = new \PhpOffice\PhpSpreadsheet\Worksheet($spreadsheet, 'My Data'); - -// Attach the "My Data" worksheet as the first worksheet in the Spreadsheet object -$spreadsheet->addSheet($myWorkSheet, 0); -``` - -If you don't specify an index position as the second argument, then the -new worksheet will be added after the last existing worksheet. - -## Copying Worksheets - -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 -$clonedWorksheet = clone $spreadsheet->getSheetByName('Worksheet 1'); -$clonedWorksheet->setTitle('Copy of Worksheet 1') -$spreadsheet->addSheet($clonedWorksheet); -``` - -You can also copy worksheets from one workbook to another, though this -is more complex as PhpSpreadsheet also has to replicate the styling -between the two workbooks. The addExternalSheet() method is provided for -this purpose. - - $clonedWorksheet = clone $spreadsheet1->getSheetByName('Worksheet 1'); - $spreadsheet->addExternalSheet($clonedWorksheet); - -In both cases, it is the developer's responsibility to ensure that -worksheet names are not duplicated. PhpSpreadsheet will throw an -exception if you attempt to copy worksheets that will result in a -duplicate name. - -## Removing a Worksheet - -You can delete a worksheet from a workbook, identified by its index -position, using the removeSheetByIndex() method - -``` php -$sheetIndex = $spreadsheet->getIndex( - $spreadsheet->getSheetByName('Worksheet 1') -); -$spreadsheet->removeSheetByIndex($sheetIndex); -``` - -If the currently active worksheet is deleted, then the sheet at the -previous index position will become the currently active sheet. - -# Accessing cells - -Accessing cells in a Spreadsheet should be pretty straightforward. This -topic lists some of the options to access a cell. - -## Setting a cell value by coordinate - -Setting a cell value by coordinate can be done using the worksheet's -`setCellValue()` method. - -``` php -// Set cell A1 with a string value -$spreadsheet->getActiveSheet()->setCellValue('A1', 'PhpSpreadsheet'); - -// Set cell A2 with a numeric value -$spreadsheet->getActiveSheet()->setCellValue('A2', 12345.6789); - -// Set cell A3 with a boolean value -$spreadsheet->getActiveSheet()->setCellValue('A3', TRUE); - -// Set cell A4 with a formula -$spreadsheet->getActiveSheet()->setCellValue( - 'A4', - '=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))' -); -``` - -Alternatively, you can retrieve the cell object, and then call the -cell’s setValue() method: - -``` php -$spreadsheet->getActiveSheet() - ->getCell('B8') - ->setValue('Some value'); -``` - -**Excel DataTypes** - -MS Excel supports 7 basic datatypes - string - number - boolean - null - -formula - error - Inline (or rich text) string - -By default, when you call the worksheet's `setCellValue()` method or the -cell's `setValue()` method, PhpSpreadsheet will use the appropriate -datatype for PHP nulls, booleans, floats or integers; or cast any string -data value that you pass to the method into the most appropriate -datatype, so numeric strings will be cast to numbers, while string -values beginning with “=” will be converted to a formula. Strings that -aren't numeric, or that don't begin with a leading "=" will be treated -as genuine string values. - -This "conversion" is handled by a cell "value binder", and you can write -custom "value binders" to change the behaviour of these "conversions". -The standard PhpSpreadsheet package also provides an "advanced value -binder" that handles a number of more complex conversions, such as -converting strings with a fractional format like "3/4" to a number value -(0.75 in this case) and setting an appropriate "fraction" number format -mask. Similarly, strings like "5%" will be converted to a value of 0.05, -and a percentage number format mask applied, and strings containing -values that look like dates will be converted to Excel serialized -datetimestamp values, and a corresponding mask applied. This is -particularly useful when loading data from csv files, or setting cell -values from a database. - -Formats handled by the advanced value binder include - TRUE or FALSE -(dependent on locale settings) are converted to booleans. - Numeric -strings identified as scientific (exponential) format are converted to -numbers. - Fractions and vulgar fractions are converted to numbers, and -an appropriate number format mask applied. - Percentages are converted -to numbers, divided by 100, and an appropriate number format mask -applied. - Dates and times are converted to Excel timestamp values -(numbers), and an appropriate number format mask applied. - When strings -contain a newline character ("\n"), then the cell styling is set to -wrap. - -You can read more about value binders later in this section of the -documentation. - -### Setting a date and/or time value in a cell - -Date or time values are held as timestamp in Excel (a simple floating -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 -// Get the current date/time and convert to an Excel date/time -$dateTimeNow = time(); -$excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $dateTimeNow ); -// Set cell A6 with the Excel date/time value -$spreadsheet->getActiveSheet()->setCellValue( - 'A6', - $excelDateValue -); -// Set the number format mask so that the excel timestamp will be displayed as a human-readable date/time -$spreadsheet->getActiveSheet()->getStyle('A6') - ->getNumberFormat() - ->setFormatCode( - \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME - ); -``` - -### Setting a number with leading zeroes - -By default, PhpSpreadsheet will automatically detect the value type and -set it to the appropriate Excel numeric datatype. This type conversion -is handled by a value binder, as described in the section of this -document entitled "Using value binders to facilitate data entry". - -Numbers don't have leading zeroes, so if you try to set a numeric value -that does have leading zeroes (such as a telephone number) then these -will be normally be lost as the value is cast to a number, so -"01513789642" will be displayed as 1513789642. - -There are two ways you can force PhpSpreadsheet to override this -behaviour. - -Firstly, you can set the datatype explicitly as a string so that it is -not converted to a number. - -``` php -// Set cell A8 with a numeric value, but tell PhpSpreadsheet it should be treated as a string -$spreadsheet->getActiveSheet()->setCellValueExplicit( - 'A8', - "01513789642", - \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING -); -``` - -Alternatively, you can use a number format mask to display the value -with leading zeroes. - -``` 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 -$spreadsheet->getActiveSheet()->getStyle('A9') - ->getNumberFormat() - ->setFormatCode( - '00000000000' - ); -``` - -With number format masking, you can even break up the digits into groups -to make the value more easily readable. - -``` 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 -$spreadsheet->getActiveSheet()->getStyle('A10') - ->getNumberFormat() - ->setFormatCode( - '0000-000-0000' - ); -``` - -![07-simple-example-1.png](./images/07-simple-example-1.png) - -**Note** that not all complex format masks such as this one will work -when retrieving a formatted value to display "on screen", or for certain -writers such as HTML or PDF, but it will work with the true spreadsheet -writers (Xlsx and Xls). - -## Setting a range of cells from an array - -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 -$arrayData = array( - array(NULL, 2010, 2011, 2012), - array('Q1', 12, 15, 21), - array('Q2', 56, 73, 86), - array('Q3', 52, 61, 69), - array('Q4', 30, 32, 0), -); -$spreadsheet->getActiveSheet() - ->fromArray( - $arrayData, // The data to set - NULL, // Array values with this value will not be set - 'C3' // Top left coordinate of the worksheet range where - // we want to set these values (default is A1) - ); -``` - -![07-simple-example-2.png](./images/07-simple-example-2.png) - -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 -$rowArray = array('Value1', 'Value2', 'Value3', 'Value4'); -$spreadsheet->getActiveSheet() - ->fromArray( - $rowArray, // The data to set - NULL, // Array values with this value will not be set - 'C3' // Top left coordinate of the worksheet range where - // we want to set these values (default is A1) - ); -``` - -![07-simple-example-3.png](./images/07-simple-example-3.png) - -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 -$rowArray = array('Value1', 'Value2', 'Value3', 'Value4'); -$columnArray = array_chunk($rowArray, 1); -$spreadsheet->getActiveSheet() - ->fromArray( - $columnArray, // The data to set - NULL, // Array values with this value will not be set - 'C3' // Top left coordinate of the worksheet range where - // we want to set these values (default is A1) - ); -``` - -![07-simple-example-4.png](./images/07-simple-example-4.png) - -## Retrieving a cell value by coordinate - -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 -// Get the value fom cell A1 -$cellValue = $spreadsheet->getActiveSheet()->getCell('A1') - ->getValue(); -``` - -This will retrieve the raw, unformatted value contained in the cell. - -If a cell contains a formula, and you need to retrieve the calculated -value rather than the formula itself, then use the cell's -`getCalculatedValue()` method. This is further explained in . - -``` php -// Get the value fom cell A4 -$cellValue = $spreadsheet->getActiveSheet()->getCell('A4') - ->getCalculatedValue(); -``` - -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 -// Get the value fom cell A6 -$cellValue = $spreadsheet->getActiveSheet()->getCell('A6') - ->getFormattedValue(); -``` - -## Setting a cell value by column and row - -Setting a cell value by coordinate can be done using the worksheet's -`setCellValueByColumnAndRow()` method. - -``` php -// Set cell B5 with a string value -$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet'); -``` - -**Note** that column references start with '0' for column 'A', rather -than from '1'. - -## Retrieving a cell value by column and row - -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 -// Get the value fom cell B5 -$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(1, 5) - ->getValue(); -``` - -If you need the calculated value of a cell, use the following code. This -is further explained in . - -``` php -// Get the value fom cell A4 -$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(0, 4) - ->getCalculatedValue(); -``` - -## Retrieving a range of cell values to an array - -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 -$dataArray = $spreadsheet->getActiveSheet() - ->rangeToArray( - 'C3:E5', // The worksheet range that we want to retrieve - NULL, // Value that should be returned for empty cells - TRUE, // Should formulas be calculated (the equivalent of getCalculatedValue() for each cell) - TRUE, // Should values be formatted (the equivalent of getFormattedValue() for each cell) - TRUE // Should the array be indexed by cell row and cell column - ); -``` - -These methods will all return a 2-d array of rows and columns. The -`toArray()` method will return the whole worksheet; `rangeToArray()` -will return a specified range or cells; while `namedRangeToArray()` will -return the cells within a defined `named range`. - -## Looping through cells - -### Looping through cells using iterators - -The easiest way to loop cells is by using iterators. Using iterators, -one can use foreach to loop worksheets, rows within a worksheet, and -cells within a row. - -Below is an example where we read all the values in a worksheet and -display them in a table. - -``` php -$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); -$reader->setReadDataOnly(TRUE); -$spreadsheet = $reader->load("test.xlsx"); - -$worksheet = $spreadsheet->getActiveSheet(); - -echo '' . PHP_EOL; -foreach ($worksheet->getRowIterator() as $row) { - echo '' . PHP_EOL; - $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. - foreach ($cellIterator as $cell) { - echo '' . PHP_EOL; - } - echo '' . PHP_EOL; -} -echo '
' . - $cell->getValue() . - '
' . PHP_EOL; -``` - -Note that we have set the cell iterator's -`setIterateOnlyExistingCells()` to FALSE. This makes the iterator loop -all cells within the worksheet range, even if they have not been set. - -The cell iterator will return a **NULL** as the cell value if it is not -set in the worksheet. Setting the cell iterator's -setIterateOnlyExistingCells() to FALSE will loop all cells in the -worksheet that can be available at that moment. This will create new -cells if required and increase memory usage! Only use it if it is -intended to loop all cells that are possibly available. - -### Looping through cells using indexes - -One can use the possibility to access cell values by column and row -index like (0,1) instead of 'A1' for reading and writing cell values in -loops. - -Note: In PhpSpreadsheet column index is 0-based while row index is -1-based. That means 'A1' \~ (0,1) - -Below is an example where we read all the values in a worksheet and -display them in a table. - -``` php -$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); -$reader->setReadDataOnly(TRUE); -$spreadsheet = $reader->load("test.xlsx"); - -$worksheet = $spreadsheet->getActiveSheet(); -// Get the highest row and column numbers referenced in the worksheet -$highestRow = $worksheet->getHighestRow(); // e.g. 10 -$highestColumn = $worksheet->getHighestColumn(); // e.g 'F' -$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($highestColumn); // e.g. 5 - -echo '' . "\n"; -for ($row = 1; $row <= $highestRow; ++$row) { - echo '' . PHP_EOL; - for ($col = 0; $col <= $highestColumnIndex; ++$col) { - echo '' . PHP_EOL; - } - echo '' . PHP_EOL; -} -echo '
' . - $worksheet->getCellByColumnAndRow($col, $row) - ->getValue() . - '
' . PHP_EOL; -``` - -Alternatively, you can take advantage of PHP's "Perl-style" character -incrementors to loop through the cells by coordinate: - -``` php -$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); -$reader->setReadDataOnly(TRUE); -$spreadsheet = $reader->load("test.xlsx"); - -$worksheet = $spreadsheet->getActiveSheet(); -// Get the highest row number and column letter referenced in the worksheet -$highestRow = $worksheet->getHighestRow(); // e.g. 10 -$highestColumn = $worksheet->getHighestColumn(); // e.g 'F' -// Increment the highest column letter -$highestColumn++; - -echo '' . "\n"; -for ($row = 1; $row <= $highestRow; ++$row) { - echo '' . PHP_EOL; - for ($col = 'A'; $col != $highestColumn; ++$col) { - echo '' . PHP_EOL; - } - echo '' . PHP_EOL; -} -echo '
' . - $worksheet->getCell($col . $row) - ->getValue() . - '
' . PHP_EOL; -``` - -Note that we can't use a <= comparison here, because 'AA' would match -as <= 'B', so we increment the highest column letter and then loop -while \$col != the incremented highest column. - -## Using value binders to facilitate data entry - -Internally, PhpSpreadsheet uses a default -\PhpOffice\PhpSpreadsheet\Cell\IValueBinder implementation -(\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder) to determine data -types of entered data using a cell's `setValue()` method (the -`setValueExplicit()` method bypasses this check). - -Optionally, the default behaviour of PhpSpreadsheet can be modified, -allowing easier data entry. For example, a -\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder class is available. -It automatically converts percentages, number in scientific format, and -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 -/** PhpSpreadsheet */ -require_once 'src/Boostrap.php'; - -// Set value binder -\PhpOffice\PhpSpreadsheet\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() ); - -// Create new Spreadsheet object -$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); - -// ... -// Add some data, resembling some different data types -$spreadsheet->getActiveSheet()->setCellValue('A4', 'Percentage value:'); -// Converts the string value to 0.1 and sets percentage cell style -$spreadsheet->getActiveSheet()->setCellValue('B4', '10%'); - -$spreadsheet->getActiveSheet()->setCellValue('A5', 'Date/time value:'); -// Converts the string value to an Excel datestamp and sets the date format cell style -$spreadsheet->getActiveSheet()->setCellValue('B5', '21 December 1983'); -``` - -**Creating your own value binder is easy.** When advanced value binding -is required, you can implement the -\PhpOffice\PhpSpreadsheet\Cell\IValueBinder interface or extend the -\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder or -\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder classes. - -# Reading and writing to file - -As you already know from part REF \_Ref191885438 \w \h 3.3 REF -\_Ref191885438 \h Readers and writers, reading and writing to a -persisted storage is not possible using the base PhpSpreadsheet classes. -For this purpose, PhpSpreadsheet provides readers and writers, which are -implementations of \PhpOffice\PhpSpreadsheet\Reader\IReader and -\PhpOffice\PhpSpreadsheet\Writer\IWriter. - -## \PhpOffice\PhpSpreadsheet\IOFactory - -The PhpSpreadsheet API offers multiple methods to create a -\PhpOffice\PhpSpreadsheet\Reader\IReader or -\PhpOffice\PhpSpreadsheet\Writer\IWriter instance: - -Direct creation via \PhpOffice\PhpSpreadsheet\IOFactory. All examples -underneath demonstrate the direct creation method. Note that you can -also use the \PhpOffice\PhpSpreadsheet\IOFactory class to do this. - -### Creating \PhpOffice\PhpSpreadsheet\Reader\IReader using \PhpOffice\PhpSpreadsheet\IOFactory - -There are 2 methods for reading in a file into PhpSpreadsheet: using -automatic file type resolving or explicitly. - -Automatic file type resolving checks the different -\PhpOffice\PhpSpreadsheet\Reader\IReader distributed with -PhpSpreadsheet. If one of them can load the specified file name, the -file is loaded using that \PhpOffice\PhpSpreadsheet\Reader\IReader. -Explicit mode requires you to specify which -\PhpOffice\PhpSpreadsheet\Reader\IReader should be used. - -You can create a \PhpOffice\PhpSpreadsheet\Reader\IReader instance using -\PhpOffice\PhpSpreadsheet\IOFactory in automatic file type resolving -mode using the following code sample: - -``` php -$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load("05featuredemo.xlsx"); -``` - -A typical use of this feature is when you need to read files uploaded by -your users, and you don’t know whether they are uploading xls or xlsx -files. - -If you need to set some properties on the reader, (e.g. to only read -data, see more about this later), then you may instead want to use this -variant: - -``` php -$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("05featuredemo.xlsx"); -$reader->setReadDataOnly(true); -$reader->load("05featuredemo.xlsx"); -``` - -You can create a \PhpOffice\PhpSpreadsheet\Reader\IReader instance using -\PhpOffice\PhpSpreadsheet\IOFactory in explicit mode using the following -code sample: - -``` php -$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx"); -$spreadsheet = $reader->load("05featuredemo.xlsx"); -``` - -Note that automatic type resolving mode is slightly slower than explicit -mode. - -### Creating \PhpOffice\PhpSpreadsheet\Writer\IWriter using \PhpOffice\PhpSpreadsheet\IOFactory - -You can create a PhpOffice\PhpSpreadsheet\Writer\IWriter instance using -\PhpOffice\PhpSpreadsheet\IOFactory: - -``` php -$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, "Xlsx"); -$writer->save("05featuredemo.xlsx"); -``` - -## Excel 2007 (SpreadsheetML) file format - -Xlsx file format is the main file format of PhpSpreadsheet. It allows -outputting the in-memory spreadsheet to a .xlsx file. - -### \PhpOffice\PhpSpreadsheet\Reader\Xlsx - -#### Reading a spreadsheet - -You can read an .xlsx file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); -$spreadsheet = $reader->load("05featuredemo.xlsx"); -``` - -#### Read data only - -You can set the option setReadDataOnly on the reader, to instruct the -reader to ignore styling, data validation, … and just read cell data: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); -$reader->setReadDataOnly(true); -$spreadsheet = $reader->load("05featuredemo.xlsx"); -``` - -#### Read specific sheets only - -You can set the option setLoadSheetsOnly on the reader, to instruct the -reader to only load the sheets with a given name: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); -$reader->setLoadSheetsOnly( array("Sheet 1", "My special sheet") ); -$spreadsheet = $reader->load("05featuredemo.xlsx"); +```sh +composer require phpoffice/phpspreadsheet ``` -#### Read specific cells only - -You can set the option setReadFilter on the reader, to instruct the -reader to only load the cells which match a given rule. A read filter -can be any class which implements -\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are -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 -class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { - - public function readCell($column, $row, $worksheetName = '') { - // Read title row and rows 20 - 30 - if ($row == 1 || ($row >= 20 && $row <= 30)) { - return true; - } - return false; - } -} - -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); -$reader->setReadFilter( new MyReadFilter() ); -$spreadsheet = $reader->load("06largescale.xlsx"); -``` - -### \PhpOffice\PhpSpreadsheet\Writer\Xlsx - -#### Writing a spreadsheet - -You can write an .xlsx file using the following code: - -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); -$writer->save("05featuredemo.xlsx"); -``` - -#### Formula pre-calculation - -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 -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); -$writer->setPreCalculateFormulas(false); -$writer->save("05featuredemo.xlsx"); -``` - -#### Office 2003 compatibility pack - -Because of a bug in the Office2003 compatibility pack, there can be some -small issues when opening Xlsx spreadsheets (mostly related to formula -calculation). You can enable Office2003 compatibility with the following -code: - - $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); - $writer->setOffice2003Compatibility(true); - $writer->save("05featuredemo.xlsx"); - -**Office2003 compatibility should only be used when needed** Office2003 -compatibility option should only be used when needed. This option -disables several Office2007 file format options, resulting in a -lower-featured Office2007 spreadsheet when this option is used. - -## Excel 5 (BIFF) file format - -Xls file format is the old Excel file format, implemented in -PhpSpreadsheet to provide a uniform manner to create both .xlsx and .xls -files. It is basically a modified version of [PEAR -Spreadsheet\_Excel\_Writer](http://pear.php.net/package/Spreadsheet_Excel_Writer), -although it has been extended and has fewer limitations and more -features than the old PEAR library. This can read all BIFF versions that -use OLE2: BIFF5 (introduced with office 95) through BIFF8, but cannot -read earlier versions. - -Xls file format will not be developed any further, it just provides an -additional file format for PhpSpreadsheet. - -**Excel5 (BIFF) limitations** Please note that BIFF file format has some -limits regarding to styling cells and handling large spreadsheets via -PHP. - -### \PhpOffice\PhpSpreadsheet\Reader\Xls - -#### Reading a spreadsheet - -You can read an .xls file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); -$spreadsheet = $reader->load("05featuredemo.xls"); -``` - -#### Read data only - -You can set the option setReadDataOnly on the reader, to instruct the -reader to ignore styling, data validation, … and just read cell data: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); -$reader->setReadDataOnly(true); -$spreadsheet = $reader->load("05featuredemo.xls"); -``` - -#### Read specific sheets only - -You can set the option setLoadSheetsOnly on the reader, to instruct the -reader to only load the sheets with a given name: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); -$reader->setLoadSheetsOnly( array("Sheet 1", "My special sheet") ); -$spreadsheet = $reader->load("05featuredemo.xls"); -``` - -#### Read specific cells only - -You can set the option setReadFilter on the reader, to instruct the -reader to only load the cells which match a given rule. A read filter -can be any class which implements -\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are -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 -class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { - - public function readCell($column, $row, $worksheetName = '') { - // Read title row and rows 20 - 30 - if ($row == 1 || ($row >= 20 && $row <= 30)) { - return true; - } - return false; - } -} - -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); -$reader->setReadFilter( new MyReadFilter() ); -$spreadsheet = $reader->load("06largescale.xls"); -``` - -### \PhpOffice\PhpSpreadsheet\Writer\Xls - -#### Writing a spreadsheet - -You can write an .xls file using the following code: - -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet); -$writer->save("05featuredemo.xls"); -``` - -## Excel 2003 XML file format - -Excel 2003 XML file format is a file format which can be used in older -versions of Microsoft Excel. - -**Excel 2003 XML limitations** Please note that Excel 2003 XML format -has some limits regarding to styling cells and handling large -spreadsheets via PHP. - -### \PhpOffice\PhpSpreadsheet\Reader\Xml - -#### Reading a spreadsheet - -You can read an Excel 2003 .xml file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml(); -$spreadsheet = $reader->load("05featuredemo.xml"); -``` - -#### Read specific cells only - -You can set the option setReadFilter on the reader, to instruct the -reader to only load the cells which match a given rule. A read filter -can be any class which implements -\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are -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 -class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { - - public function readCell($column, $row, $worksheetName = '') { - // Read title row and rows 20 - 30 - if ($row == 1 || ($row >= 20 && $row <= 30)) { - return true; - } - return false; - } - -} - -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml(); -$reader->setReadFilter( new MyReadFilter() ); -$spreadsheet = $reader->load("06largescale.xml"); -``` - -## Symbolic LinK (SYLK) - -Symbolic Link (SYLK) is a Microsoft file format typically used to -exchange data between applications, specifically spreadsheets. SYLK -files conventionally have a .slk suffix. Composed of only displayable -ANSI characters, it can be easily created and processed by other -applications, such as databases. - -**SYLK limitations** Please note that SYLK file format has some limits -regarding to styling cells and handling large spreadsheets via PHP. - -### \PhpOffice\PhpSpreadsheet\Reader\Slk - -#### Reading a spreadsheet - -You can read an .slk file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Slk(); -$spreadsheet = $reader->load("05featuredemo.slk"); -``` - -#### Read specific cells only - -You can set the option setReadFilter on the reader, to instruct the -reader to only load the cells which match a given rule. A read filter -can be any class which implements -\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are -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 -class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { - - public function readCell($column, $row, $worksheetName = '') { - // Read title row and rows 20 - 30 - if ($row == 1 || ($row >= 20 && $row <= 30)) { - return true; - } - return false; - } - -} - -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Slk(); -$reader->setReadFilter( new MyReadFilter() ); -$spreadsheet = $reader->load("06largescale.slk"); -``` - -## Open/Libre Office (.ods) - -Open Office or Libre Office .ods files are the standard file format for -Open Office or Libre Office Calc files. - -### \PhpOffice\PhpSpreadsheet\Reader\Ods - -#### Reading a spreadsheet - -You can read an .ods file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods(); -$spreadsheet = $reader->load("05featuredemo.ods"); -``` - -#### Read specific cells only - -You can set the option setReadFilter on the reader, to instruct the -reader to only load the cells which match a given rule. A read filter -can be any class which implements -\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are -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 -class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { - - public function readCell($column, $row, $worksheetName = '') { - // Read title row and rows 20 - 30 - if ($row == 1 || ($row >= 20 && $row <= 30)) { - return true; - } - return false; - } - -} - -$reader = new PhpOffice\PhpSpreadsheet\Reader\Ods(); -$reader->setReadFilter( new MyReadFilter() ); -$spreadsheet = $reader->load("06largescale.ods"); -``` - -## CSV (Comma Separated Values) - -CSV (Comma Separated Values) are often used as an import/export file -format with other systems. PhpSpreadsheet allows reading and writing to -CSV files. - -**CSV limitations** Please note that CSV file format has some limits -regarding to styling cells, number formatting, ... - -### \PhpOffice\PhpSpreadsheet\Reader\Csv - -#### Reading a CSV file - -You can read a .csv file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); -$spreadsheet = $reader->load("sample.csv"); -``` - -#### Setting CSV options - -Often, CSV files are not really “comma separated”, or use semicolon (;) -as a separator. You can instruct -\PhpOffice\PhpSpreadsheet\Reader\Csv some options before reading a CSV -file. - -Note that \PhpOffice\PhpSpreadsheet\Reader\Csv by default assumes that -the loaded CSV file is UTF-8 encoded. If you are reading CSV files that -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 -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); -$reader->setInputEncoding('CP1252'); -$reader->setDelimiter(';'); -$reader->setEnclosure(''); -$reader->setLineEnding("\r\n"); -$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 -$reader->setSheetIndex(0); -``` - -#### Read into existing spreadsheet - -When working with CSV files, it might occur that you want to import CSV -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 -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); -$reader->setDelimiter(';'); -$reader->setEnclosure(''); -$reader->setLineEnding("\r\n"); -$reader->setSheetIndex(5); - -$reader->loadIntoExisting("05featuredemo.csv", $spreadsheet); -``` - -### \PhpOffice\PhpSpreadsheet\Writer\Csv - -#### Writing a CSV file - -You can write a .csv file using the following code: - -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); -$writer->save("05featuredemo.csv"); -``` - -#### Setting CSV options - -Often, CSV files are not really “comma separated”, or use semicolon (;) -as a separator. You can instruct -\PhpOffice\PhpSpreadsheet\Writer\Csv some options before writing a CSV -file: - -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); -$writer->setDelimiter(';'); -$writer->setEnclosure(''); -$writer->setLineEnding("\r\n"); -$writer->setSheetIndex(0); - -$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 -$writer->setSheetIndex(0); -``` - -#### Formula pre-calculation - -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 -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); -$writer->setPreCalculateFormulas(false); -$writer->save("05featuredemo.csv"); -``` - -#### Writing UTF-8 CSV files - -A CSV file can be marked as UTF-8 by writing a BOM file header. This can -be enabled by using the following code: - -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); -$writer->setUseBOM(true); -$writer->save("05featuredemo.csv"); -``` - -#### Decimal and thousands separators - -If the worksheet you are exporting contains numbers with decimal or -thousands separators then you should think about what characters you -want to use for those before doing the export. - -By default PhpSpreadsheet looks up in the server's locale settings to -decide what characters to use. But to avoid problems it is recommended -to set the characters explicitly as shown below. - -English users will want to use this before doing the export: - -``` php -\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator('.'); -\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator(','); -``` - -German users will want to use the opposite values. - -``` php -\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator(','); -\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator('.'); -``` - -Note that the above code sets decimal and thousand separators as global -options. This also affects how HTML and PDF is exported. - -## HTML - -PhpSpreadsheet allows you to read or write a spreadsheet as HTML format, -for quick representation of the data in it to anyone who does not have a -spreadsheet application on their PC, or loading files saved by other -scripts that simply create HTML markup and give it a .xls file -extension. - -**HTML limitations** Please note that HTML file format has some limits -regarding to styling cells, number formatting, ... - -### \PhpOffice\PhpSpreadsheet\Reader\Html - -#### Reading a spreadsheet - -You can read an .html or .htm file using the following code: - -``` php -$reader = new \PhpOffice\PhpSpreadsheet\Reader\Html(); - -$spreadsheet = $reader->load("05featuredemo.html"); -``` - -**HTML limitations** Please note that HTML reader is still experimental -and does not yet support merged cells or nested tables cleanly - -### \PhpOffice\PhpSpreadsheet\Writer\Html - -Please note that \PhpOffice\PhpSpreadsheet\Writer\Html only outputs the -first worksheet by default. - -#### Writing a spreadsheet - -You can write a .htm file using the following code: - -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); - -$writer->save("05featuredemo.htm"); -``` - -#### Write all worksheets - -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 -$writer->writeAllSheets(); -``` - -#### Write a specific worksheet - -HTML files can contain one or more worksheets. Therefore, you can -specify which sheet to write to HTML: - -``` php -$writer->setSheetIndex(0); -``` - -#### Setting the images root of the HTML file - -There might be situations where you want to explicitly set the included -images root. For example, one might want to see - -``` html - -``` - -instead of - -``` html -. -``` - -You can use the following code to achieve this result: - -``` php -$writer->setImagesRoot('http://www.example.com'); -``` - -#### Formula pre-calculation - -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 -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); -$writer->setPreCalculateFormulas(false); - -$writer->save("05featuredemo.htm"); -``` - -#### Embedding generated HTML in a web page - -There might be a situation where you want to embed the generated HTML in -an existing website. \PhpOffice\PhpSpreadsheet\Writer\Html provides -support to generate only specific parts of the HTML code, which allows -you to use these parts in your website. - -Supported methods: +**Note:** If you want the unreleased, unstable development version use +`phpoffice/phpspreadsheet:dev-develop` instead. -- generateHTMLHeader() -- generateStyles() -- generateSheetData() -- generateHTMLFooter() +## Hello World -Here's an example which retrieves all parts independently and merges -them into a resulting HTML page: +This would be the simplest way to write a spreadsheet: -``` php +```php generateHTMLHeader(); -?> - -?> +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx; ---> - +$spreadsheet = new Spreadsheet(); +$sheet = $spreadsheet->getActiveSheet(); +$sheet->setCellValue('A1', 'Hello World !'); -generateSheetData(); -echo $writer->generateHTMLFooter(); -?> +$writer = new Xlsx($spreadsheet); +$writer->save('hello world.xlsx'); ``` -#### Writing UTF-8 HTML files +## Learn by example -A HTML file can be marked as UTF-8 by writing a BOM file header. This -can be enabled by using the following code: +A good way to get started is to run some of the samples. Serve the samples via +PHP built-in webserver: -``` php -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); -$writer->setUseBOM(true); - -$writer->save("05featuredemo.htm"); +```sh +php -S localhost:8000 -t vendor/phpoffice/phpspreadsheet/samples ``` -#### Decimal and thousands separators +Then point your browser to: -See section \PhpOffice\PhpSpreadsheet\Writer\Csv how to control the -appearance of these. +> http://localhost:8000/ -## PDF +The samples may also be run directly from the command line, for example: -PhpSpreadsheet allows you to write a spreadsheet into PDF format, for -fast distribution of represented data. - -**PDF limitations** Please note that PDF file format has some limits -regarding to styling cells, number formatting, ... - -### \PhpOffice\PhpSpreadsheet\Writer\Pdf - -PhpSpreadsheet’s PDF Writer is a wrapper for a 3rd-Party PDF Rendering -library such as tcPDF, mPDF or DomPDF. You must now install a PDF -rendering library yourself; but PhpSpreadsheet will work with a number -of different libraries. - -Currently, the following libraries are supported: - -Library | Downloadable from | PhpSpreadsheet Internal Constant ---------|-------------------------------------|--------------------------------- -tcPDF | https://github.com/tecnickcom/tcpdf | PDF_RENDERER_TCPDF -mPDF | https://github.com/mpdf/mpdf | PDF_RENDERER_MPDF -domPDF | https://github.com/dompdf/dompdf | PDF_RENDERER_DOMPDF - -The different libraries have different strengths and weaknesses. Some -generate better formatted output than others, some are faster or use -less memory than others, while some generate smaller .pdf files. It is -the developers choice which one they wish to use, appropriate to their -own circumstances. - -Before instantiating a Writer to generate PDF output, you will need to -indicate which Rendering library you are using. - -``` php -$rendererName = \PhpOffice\PhpSpreadsheet\Settings::PDF_RENDERER_MPDF; -\PhpOffice\PhpSpreadsheet\Settings::setPdfRendererName($rendererName); +```sh +php vendor/phpoffice/phpspreadsheet/samples/01_Simple.php ``` -#### Writing a spreadsheet +## Learn by documentation -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 -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf($spreadsheet); -$writer->save("05featuredemo.pdf"); -``` - -Please note that \PhpOffice\PhpSpreadsheet\Writer\Pdf only outputs the -first worksheet by default. - -#### Write all worksheets - -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 -$writer->writeAllSheets(); -``` - -#### Write a specific worksheet - -PDF files can contain one or more worksheets. Therefore, you can specify -which sheet to write to PDF: - -``` php -$writer->setSheetIndex(0); -``` - -#### Formula pre-calculation - -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 -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf($spreadsheet); -$writer->setPreCalculateFormulas(false); - -$writer->save("05featuredemo.pdf"); -``` - -#### Decimal and thousands separators - -See section \PhpOffice\PhpSpreadsheet\Writer\Csv how to control the -appearance of these. - -## Generating Excel files from templates (read, modify, write) - -Readers and writers are the tools that allow you to generate Excel files -from templates. This requires less coding effort than generating the -Excel file from scratch, especially if your template has many styles, -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 -$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('template.xlsx'); - -$worksheet = $spreadsheet->getActiveSheet(); - -$worksheet->getCell('A1')->setValue('John'); -$worksheet->getCell('A2')->setValue('Smith'); - -$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls'); -$writer->save('write.xls'); -``` - -Notice that it is ok to load an xlsx file and generate an xls file. +For more in-depth documentation, you may read about an [overview of the +architecture](./topics/architecture.md), +[creating a spreadsheet](./topics/creating-spreadsheet.md), +[worksheets](./topics/worksheets.md), +[accessing cells](./topics/accessing-cells.md) and +[reading and writing to files](./topics/reading-and-writing-to-file.md). # Credits -Please refer to the internet page [contributor +Please refer to the [contributor list](https://github.com/PHPOffice/PhpSpreadsheet/graphs/contributors) for up-to-date credits. diff --git a/docs/references/features-cross-reference.md b/docs/references/features-cross-reference.md index a5e579ba..cdca7001 100644 --- a/docs/references/features-cross-reference.md +++ b/docs/references/features-cross-reference.md @@ -1,10 +1,15 @@ # Features cross reference +- Supported +- Partially supported +- Not supported +- N/A Cannot be supported + - - + + @@ -12,12 +17,13 @@ - + + @@ -33,11 +39,12 @@ - - - - - + + + + + + @@ -50,11 +57,12 @@ - - - - - + + + + + + @@ -67,11 +75,12 @@ - - - - - + + + + + + @@ -84,11 +93,12 @@ - - - - - + + + + + + @@ -103,6 +113,7 @@ + @@ -120,6 +131,7 @@ + @@ -137,6 +149,7 @@ + @@ -154,6 +167,7 @@ + @@ -171,6 +185,7 @@ + @@ -188,6 +203,7 @@ + @@ -205,6 +221,7 @@ + @@ -222,6 +239,7 @@ + @@ -239,6 +257,7 @@ + @@ -256,6 +275,7 @@ + @@ -273,6 +293,7 @@ + @@ -290,6 +311,7 @@ + @@ -307,6 +329,7 @@ + @@ -324,7 +347,8 @@ - + + @@ -341,6 +365,7 @@ + @@ -358,6 +383,7 @@ + @@ -375,6 +401,7 @@ + @@ -392,6 +419,7 @@ + @@ -409,6 +437,7 @@ + @@ -448,6 +477,7 @@ + @@ -465,6 +495,7 @@ + @@ -482,6 +513,7 @@ + @@ -499,6 +531,7 @@ + @@ -516,6 +549,7 @@ + @@ -533,6 +567,7 @@ + @@ -550,6 +585,7 @@ + @@ -567,6 +603,7 @@ + @@ -578,6 +615,7 @@ + @@ -601,6 +639,7 @@ + @@ -618,6 +657,7 @@ + @@ -635,6 +675,7 @@ + @@ -652,6 +693,7 @@ + @@ -669,6 +711,7 @@ + @@ -686,6 +729,7 @@ + @@ -703,6 +747,7 @@ + @@ -720,6 +765,7 @@ + @@ -737,6 +783,7 @@ + @@ -754,6 +801,7 @@ + @@ -771,6 +819,7 @@ + @@ -788,6 +837,7 @@ + @@ -805,6 +855,7 @@ + @@ -822,6 +873,7 @@ + @@ -839,6 +891,7 @@ + @@ -856,6 +909,7 @@ + @@ -873,6 +927,7 @@ + @@ -885,6 +940,7 @@ + @@ -902,6 +958,7 @@ + @@ -919,6 +976,7 @@ + @@ -936,6 +994,7 @@ + @@ -953,6 +1012,7 @@ + @@ -970,6 +1030,7 @@ + @@ -987,6 +1048,7 @@ + @@ -1004,6 +1066,7 @@ + @@ -1021,6 +1084,7 @@ + @@ -1043,6 +1107,7 @@ + @@ -1060,6 +1125,7 @@ + @@ -1077,6 +1143,7 @@ + @@ -1094,23 +1161,25 @@ + - - - - + + - - - - - - - - - + + + + + + + + + + + + @@ -1128,6 +1197,7 @@ + @@ -1145,6 +1215,7 @@ + @@ -1157,6 +1228,7 @@ + @@ -1174,6 +1246,7 @@ + @@ -1191,6 +1264,7 @@ + @@ -1199,20 +1273,21 @@ - - - - + + - - - - - - - - - + + + + + + + + + + + + @@ -1228,8 +1303,9 @@ - - + + + @@ -1247,6 +1323,7 @@ + @@ -1264,6 +1341,7 @@ + @@ -1281,6 +1359,7 @@ + @@ -1298,6 +1377,7 @@ + @@ -1315,6 +1395,7 @@ + @@ -1332,6 +1413,7 @@ + @@ -1349,6 +1431,7 @@ + @@ -1366,6 +1449,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WritersWritersReadersWriters Methods
XLS XLSX Excel2003XMLOOCalcOds Gnumeric CSV SYLK XLS XLSXOds CSV HTML PDF N/A N/A-----N/AN/AN/AN/AN/AN/A
N/A N/A-----N/AN/AN/AN/AN/AN/A $reader->getReadDataOnly() $reader->setReadDataOnly()
N/A N/A-----N/AN/AN/AN/AN/AN/A $reader->getLoadSheetsOnly() $reader->setLoadSheetsOnly()
$reader->setLoadAllSheets()
N/A N/A-----N/AN/AN/AN/AN/AN/A $reader->getReadFilter() $reader->setReadFilter()
N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/AN/A N/A $spreadsheet->getProperties()->getManager()N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A
Boolean
Integer
Floating Point
String
Error
Formula
Array
Rich TextN/A N/A
Rows and Column Properties
Row Height/Column Width
Hidden
Worksheet Properties
Frozen Panes
Coloured Tabs
Cell Formatting
Number Format Mask
Alignment
Horizontal
Vertical
Wrapping
Shring-to-Fit
Indent
Background Colour
Patterned
Font Attributes
Line Style
Position
Diagonal
Hyperlinks $cell->getHyperlink()->getUrl($url)$cell->getHyperlink()->setUrl($url)
http
Merged Cells
Cell CommentsN/A N/A N/AN/A N/A N/AN/A N/A N/A
Cell Validation N/AN/AN/AN/AN/A$cell->getDataValidation()$cell->setDataValidation()
AutoFilters $spreadsheet->getActivesheet()->getAutoFilter()$spreadsheet->getActivesheet()->setAutoFilter()$sheet->getAutoFilter()$sheet->setAutoFilter()
AutoFilter Expressions
Filter
Custom Filter
DateGroup Filter
Dynamic Filter
Colour Filter
Icon Filter
Top 10 Filter
Macros$spreadsheet->getMacrosCode();$spreadsheet->setMacrosCode();
XLSXLSXExcel2003XMLOdsGnumericCSVSYLKXLSXLSXOdsCSVHTMLPDFGettersSetters
ReadersWritersMethods
diff --git a/docs/topics/accessing-cells.md b/docs/topics/accessing-cells.md new file mode 100644 index 00000000..f5416ed7 --- /dev/null +++ b/docs/topics/accessing-cells.md @@ -0,0 +1,482 @@ +# Accessing cells + +Accessing cells in a Spreadsheet should be pretty straightforward. This +topic lists some of the options to access a cell. + +## Setting a cell value by coordinate + +Setting a cell value by coordinate can be done using the worksheet's +`setCellValue()` method. + +``` php +// Set cell A1 with a string value +$spreadsheet->getActiveSheet()->setCellValue('A1', 'PhpSpreadsheet'); + +// Set cell A2 with a numeric value +$spreadsheet->getActiveSheet()->setCellValue('A2', 12345.6789); + +// Set cell A3 with a boolean value +$spreadsheet->getActiveSheet()->setCellValue('A3', TRUE); + +// Set cell A4 with a formula +$spreadsheet->getActiveSheet()->setCellValue( + 'A4', + '=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))' +); +``` + +Alternatively, you can retrieve the cell object, and then call the +cell’s `setValue()` method: + +``` php +$spreadsheet->getActiveSheet() + ->getCell('B8') + ->setValue('Some value'); +``` + +## Excel DataTypes + +MS Excel supports 7 basic datatypes: + +- string +- number +- boolean +- null +- formula +- error +- Inline (or rich text) string + +By default, when you call the worksheet's `setCellValue()` method or the +cell's `setValue()` method, PhpSpreadsheet will use the appropriate +datatype for PHP nulls, booleans, floats or integers; or cast any string +data value that you pass to the method into the most appropriate +datatype, so numeric strings will be cast to numbers, while string +values beginning with `=` will be converted to a formula. Strings that +aren't numeric, or that don't begin with a leading `=` will be treated +as genuine string values. + +This "conversion" is handled by a cell "value binder", and you can write +custom "value binders" to change the behaviour of these "conversions". +The standard PhpSpreadsheet package also provides an "advanced value +binder" that handles a number of more complex conversions, such as +converting strings with a fractional format like "3/4" to a number value +(0.75 in this case) and setting an appropriate "fraction" number format +mask. Similarly, strings like "5%" will be converted to a value of 0.05, +and a percentage number format mask applied, and strings containing +values that look like dates will be converted to Excel serialized +datetimestamp values, and a corresponding mask applied. This is +particularly useful when loading data from csv files, or setting cell +values from a database. + +Formats handled by the advanced value binder include: + +- TRUE or FALSE (dependent on locale settings) are converted to booleans. +- Numeric strings identified as scientific (exponential) format are + converted to numbers. +- Fractions and vulgar fractions are converted to numbers, and + an appropriate number format mask applied. +- Percentages are converted + to numbers, divided by 100, and an appropriate number format mask + applied. +- Dates and times are converted to Excel timestamp values + (numbers), and an appropriate number format mask applied. +- When strings contain a newline character (`\n`), then the cell styling is + set to wrap. + +You can read more about value binders later in this section of the +documentation. + +### Setting a date and/or time value in a cell + +Date or time values are held as timestamp in Excel (a simple floating +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 +// Get the current date/time and convert to an Excel date/time +$dateTimeNow = time(); +$excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $dateTimeNow ); +// Set cell A6 with the Excel date/time value +$spreadsheet->getActiveSheet()->setCellValue( + 'A6', + $excelDateValue +); +// Set the number format mask so that the excel timestamp will be displayed as a human-readable date/time +$spreadsheet->getActiveSheet()->getStyle('A6') + ->getNumberFormat() + ->setFormatCode( + \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME + ); +``` + +### Setting a number with leading zeroes + +By default, PhpSpreadsheet will automatically detect the value type and +set it to the appropriate Excel numeric datatype. This type conversion +is handled by a value binder, as described in the section of this +document entitled "Using value binders to facilitate data entry". + +Numbers don't have leading zeroes, so if you try to set a numeric value +that does have leading zeroes (such as a telephone number) then these +will be normally be lost as the value is cast to a number, so +"01513789642" will be displayed as 1513789642. + +There are two ways you can force PhpSpreadsheet to override this +behaviour. + +Firstly, you can set the datatype explicitly as a string so that it is +not converted to a number. + +``` php +// Set cell A8 with a numeric value, but tell PhpSpreadsheet it should be treated as a string +$spreadsheet->getActiveSheet()->setCellValueExplicit( + 'A8', + "01513789642", + \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING +); +``` + +Alternatively, you can use a number format mask to display the value +with leading zeroes. + +``` 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 +$spreadsheet->getActiveSheet()->getStyle('A9') + ->getNumberFormat() + ->setFormatCode( + '00000000000' + ); +``` + +With number format masking, you can even break up the digits into groups +to make the value more easily readable. + +``` 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 +$spreadsheet->getActiveSheet()->getStyle('A10') + ->getNumberFormat() + ->setFormatCode( + '0000-000-0000' + ); +``` + +![07-simple-example-1.png](./images/07-simple-example-1.png) + +**Note** that not all complex format masks such as this one will work +when retrieving a formatted value to display "on screen", or for certain +writers such as HTML or PDF, but it will work with the true spreadsheet +writers (Xlsx and Xls). + +## Setting a range of cells from an array + +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 +$arrayData = array( + array(NULL, 2010, 2011, 2012), + array('Q1', 12, 15, 21), + array('Q2', 56, 73, 86), + array('Q3', 52, 61, 69), + array('Q4', 30, 32, 0), +); +$spreadsheet->getActiveSheet() + ->fromArray( + $arrayData, // The data to set + NULL, // Array values with this value will not be set + 'C3' // Top left coordinate of the worksheet range where + // we want to set these values (default is A1) + ); +``` + +![07-simple-example-2.png](./images/07-simple-example-2.png) + +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 +$rowArray = array('Value1', 'Value2', 'Value3', 'Value4'); +$spreadsheet->getActiveSheet() + ->fromArray( + $rowArray, // The data to set + NULL, // Array values with this value will not be set + 'C3' // Top left coordinate of the worksheet range where + // we want to set these values (default is A1) + ); +``` + +![07-simple-example-3.png](./images/07-simple-example-3.png) + +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 +$rowArray = array('Value1', 'Value2', 'Value3', 'Value4'); +$columnArray = array_chunk($rowArray, 1); +$spreadsheet->getActiveSheet() + ->fromArray( + $columnArray, // The data to set + NULL, // Array values with this value will not be set + 'C3' // Top left coordinate of the worksheet range where + // we want to set these values (default is A1) + ); +``` + +![07-simple-example-4.png](./images/07-simple-example-4.png) + +## Retrieving a cell value by coordinate + +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 +// Get the value fom cell A1 +$cellValue = $spreadsheet->getActiveSheet()->getCell('A1') + ->getValue(); +``` + +This will retrieve the raw, unformatted value contained in the cell. + +If a cell contains a formula, and you need to retrieve the calculated +value rather than the formula itself, then use the cell's +`getCalculatedValue()` method. This is further explained in . + +``` php +// Get the value fom cell A4 +$cellValue = $spreadsheet->getActiveSheet()->getCell('A4') + ->getCalculatedValue(); +``` + +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 +// Get the value fom cell A6 +$cellValue = $spreadsheet->getActiveSheet()->getCell('A6') + ->getFormattedValue(); +``` + +## Setting a cell value by column and row + +Setting a cell value by coordinate can be done using the worksheet's +`setCellValueByColumnAndRow()` method. + +``` php +// Set cell B5 with a string value +$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet'); +``` + +**Note** that column references start with `0` for column `A`, rather +than from `1`. + +## Retrieving a cell value by column and row + +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 +// Get the value fom cell B5 +$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(1, 5) + ->getValue(); +``` + +If you need the calculated value of a cell, use the following code. This +is further explained in . + +``` php +// Get the value fom cell A4 +$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(0, 4) + ->getCalculatedValue(); +``` + +## Retrieving a range of cell values to an array + +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 +$dataArray = $spreadsheet->getActiveSheet() + ->rangeToArray( + 'C3:E5', // The worksheet range that we want to retrieve + NULL, // Value that should be returned for empty cells + TRUE, // Should formulas be calculated (the equivalent of getCalculatedValue() for each cell) + TRUE, // Should values be formatted (the equivalent of getFormattedValue() for each cell) + TRUE // Should the array be indexed by cell row and cell column + ); +``` + +These methods will all return a 2-d array of rows and columns. The +`toArray()` method will return the whole worksheet; `rangeToArray()` +will return a specified range or cells; while `namedRangeToArray()` will +return the cells within a defined `named range`. + +## Looping through cells + +### Looping through cells using iterators + +The easiest way to loop cells is by using iterators. Using iterators, +one can use foreach to loop worksheets, rows within a worksheet, and +cells within a row. + +Below is an example where we read all the values in a worksheet and +display them in a table. + +``` php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(TRUE); +$spreadsheet = $reader->load("test.xlsx"); + +$worksheet = $spreadsheet->getActiveSheet(); + +echo '' . PHP_EOL; +foreach ($worksheet->getRowIterator() as $row) { + echo '' . PHP_EOL; + $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. + foreach ($cellIterator as $cell) { + echo '' . PHP_EOL; + } + echo '' . PHP_EOL; +} +echo '
' . + $cell->getValue() . + '
' . PHP_EOL; +``` + +Note that we have set the cell iterator's +`setIterateOnlyExistingCells()` to FALSE. This makes the iterator loop +all cells within the worksheet range, even if they have not been set. + +The cell iterator will return a **NULL** as the cell value if it is not +set in the worksheet. Setting the cell iterator's +setIterateOnlyExistingCells() to FALSE will loop all cells in the +worksheet that can be available at that moment. This will create new +cells if required and increase memory usage! Only use it if it is +intended to loop all cells that are possibly available. + +### Looping through cells using indexes + +One can use the possibility to access cell values by column and row +index like (0,1) instead of 'A1' for reading and writing cell values in +loops. + +Note: In PhpSpreadsheet column index is 0-based while row index is +1-based. That means 'A1' \~ (0,1) + +Below is an example where we read all the values in a worksheet and +display them in a table. + +``` php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(TRUE); +$spreadsheet = $reader->load("test.xlsx"); + +$worksheet = $spreadsheet->getActiveSheet(); +// Get the highest row and column numbers referenced in the worksheet +$highestRow = $worksheet->getHighestRow(); // e.g. 10 +$highestColumn = $worksheet->getHighestColumn(); // e.g 'F' +$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($highestColumn); // e.g. 5 + +echo '' . "\n"; +for ($row = 1; $row <= $highestRow; ++$row) { + echo '' . PHP_EOL; + for ($col = 0; $col <= $highestColumnIndex; ++$col) { + echo '' . PHP_EOL; + } + echo '' . PHP_EOL; +} +echo '
' . + $worksheet->getCellByColumnAndRow($col, $row) + ->getValue() . + '
' . PHP_EOL; +``` + +Alternatively, you can take advantage of PHP's "Perl-style" character +incrementors to loop through the cells by coordinate: + +``` php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(TRUE); +$spreadsheet = $reader->load("test.xlsx"); + +$worksheet = $spreadsheet->getActiveSheet(); +// Get the highest row number and column letter referenced in the worksheet +$highestRow = $worksheet->getHighestRow(); // e.g. 10 +$highestColumn = $worksheet->getHighestColumn(); // e.g 'F' +// Increment the highest column letter +$highestColumn++; + +echo '' . "\n"; +for ($row = 1; $row <= $highestRow; ++$row) { + echo '' . PHP_EOL; + for ($col = 'A'; $col != $highestColumn; ++$col) { + echo '' . PHP_EOL; + } + echo '' . PHP_EOL; +} +echo '
' . + $worksheet->getCell($col . $row) + ->getValue() . + '
' . PHP_EOL; +``` + +Note that we can't use a <= comparison here, because 'AA' would match +as <= 'B', so we increment the highest column letter and then loop +while \$col != the incremented highest column. + +## Using value binders to facilitate data entry + +Internally, PhpSpreadsheet uses a default +\PhpOffice\PhpSpreadsheet\Cell\IValueBinder implementation +(\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder) to determine data +types of entered data using a cell's `setValue()` method (the +`setValueExplicit()` method bypasses this check). + +Optionally, the default behaviour of PhpSpreadsheet can be modified, +allowing easier data entry. For example, a +\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder class is available. +It automatically converts percentages, number in scientific format, and +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 +/** PhpSpreadsheet */ +require_once 'src/Boostrap.php'; + +// Set value binder +\PhpOffice\PhpSpreadsheet\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() ); + +// Create new Spreadsheet object +$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + +// ... +// Add some data, resembling some different data types +$spreadsheet->getActiveSheet()->setCellValue('A4', 'Percentage value:'); +// Converts the string value to 0.1 and sets percentage cell style +$spreadsheet->getActiveSheet()->setCellValue('B4', '10%'); + +$spreadsheet->getActiveSheet()->setCellValue('A5', 'Date/time value:'); +// Converts the string value to an Excel datestamp and sets the date format cell style +$spreadsheet->getActiveSheet()->setCellValue('B5', '21 December 1983'); +``` + +**Creating your own value binder is easy.** When advanced value binding +is required, you can implement the +\PhpOffice\PhpSpreadsheet\Cell\IValueBinder interface or extend the +\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder or +\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder classes. diff --git a/docs/topics/architecture.md b/docs/topics/architecture.md new file mode 100644 index 00000000..a353353f --- /dev/null +++ b/docs/topics/architecture.md @@ -0,0 +1,75 @@ +# Architecture + +## Schematical + +![01-schematic.png](./images/01-schematic.png "Basic Architecture Schematic") + +## AutoLoader + +PhpSpreadsheet relies on Composer autoloader. So before working with +PhpSpreadsheet in standalone, be sure to run `composer install`. Or add it to a +pre-existing project with `composer require phpoffice/phpspreadsheet`. + +## Spreadsheet in memory + +PhpSpreadsheet's architecture is built in a way that it can serve as an +in-memory spreadsheet. This means that, if one would want to create a +web based view of a spreadsheet which communicates with PhpSpreadsheet's +object model, he would only have to write the front-end code. + +Just like desktop spreadsheet software, PhpSpreadsheet represents a +spreadsheet containing one or more worksheets, which contain cells with +data, formulas, images, ... + +## Readers and writers + +On its own, the `Spreadsheet` class does not provide the functionality +to read from or write to a persisted spreadsheet (on disk or in a +database). To provide that functionality, readers and writers can be +used. + +By default, the PhpSpreadsheet package provides some readers and +writers, including one for the Open XML spreadsheet format (a.k.a. Excel +2007 file format). You are not limited to the default readers and +writers, as you are free to implement the +\PhpOffice\PhpSpreadsheet\Reader\IReader and +\PhpOffice\PhpSpreadsheet\Writer\IWriter interface in a custom class. + +![02-readers-writers.png](./images/02-readers-writers.png "Readers/Writers") + +## Fluent interfaces + +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 +$spreadsheet->getProperties()->setCreator("Maarten Balliauw"); +$spreadsheet->getProperties()->setLastModifiedBy("Maarten Balliauw"); +$spreadsheet->getProperties()->setTitle("Office 2007 XLSX Test Document"); +$spreadsheet->getProperties()->setSubject("Office 2007 XLSX Test Document"); +$spreadsheet->getProperties()->setDescription("Test document for Office 2007 XLSX, generated using PHP classes."); +$spreadsheet->getProperties()->setKeywords("office 2007 openxml php"); +$spreadsheet->getProperties()->setCategory("Test result file"); +``` + +This can be rewritten as: + +``` php +$spreadsheet->getProperties() + ->setCreator("Maarten Balliauw") + ->setLastModifiedBy("Maarten Balliauw") + ->setTitle("Office 2007 XLSX Test Document") + ->setSubject("Office 2007 XLSX Test Document") + ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.") + ->setKeywords("office 2007 openxml php") + ->setCategory("Test result file"); +``` + +> **Using fluent interfaces is not required** Fluent interfaces have +> been implemented to provide a convenient programming API. Use of them +> is not required, but can make your code easier to read and maintain. +> It can also improve performance, as you are reducing the overall +> number of calls to PhpSpreadsheet methods: in the above example, the +> `getProperties()` method is being called only once rather than 7 times +> in the non-fluent version. diff --git a/docs/topics/autofilters.md b/docs/topics/autofilters.md index 679dd87a..f6b0c572 100644 --- a/docs/topics/autofilters.md +++ b/docs/topics/autofilters.md @@ -142,7 +142,7 @@ $columnFilter->createRule() ``` This creates two filter rules: the column will be filtered by values -that match “France” OR “Germany”. For Simple Filters, you can create as +that match "France" OR "Germany". For Simple Filters, you can create as many rules as you want Simple filters are always a comparison match of EQUALS, and multiple @@ -238,7 +238,7 @@ $columnFilter->setFilterType( And then define our rules. The following shows a simple wildcard filter to show all column entries -beginning with the letter 'U'. +beginning with the letter `U`. ``` php $columnFilter->createRule() @@ -487,7 +487,7 @@ saved. ### Applying the Filter 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() +this manually. You can do this using the autofilters `showHideRows()` method. ``` php diff --git a/docs/topics/calculation-engine.md b/docs/topics/calculation-engine.md index 650e0847..1480e13f 100644 --- a/docs/topics/calculation-engine.md +++ b/docs/topics/calculation-engine.md @@ -7,11 +7,11 @@ As PhpSpreadsheet represents an in-memory spreadsheet, it also offers formula calculation capabilities. A cell can be of a value type (containing a number or text), or a formula type (containing a formula -which can be evaluated). For example, the formula "=SUM(A1:A10)" +which can be evaluated). For example, the formula `=SUM(A1:A10)` 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: +method `getCalculatedValue()`, for example: ``` php $spreadsheet->getActiveSheet()->getCell('E11')->getCalculatedValue(); @@ -57,7 +57,7 @@ Not all functions are supported, for a comprehensive list, read the #### Operator precedence -In Excel '+' wins over '&', just like '\*' wins over '+' in ordinary +In Excel `+` wins over `&`, just like `*` wins over `+` in ordinary algebra. The former rule is not what one finds using the calculation engine shipped with PhpSpreadsheet. @@ -69,9 +69,9 @@ Reference for operator precedence in PHP: #### Formulas involving numbers and text Formulas involving numbers and text may produce unexpected results or -even unreadable file contents. For example, the formula '=3+"Hello "' is +even unreadable file contents. For example, the formula `=3+"Hello "` is expected to produce an error in Excel (\#VALUE!). Due to the fact that -PHP converts “Hello” to a numeric value (zero), the result of this +PHP converts `"Hello "` to a numeric value (zero), the result of this formula is evaluated as 3 instead of evaluating as an error. This also causes the Excel document being generated as containing unreadable content. @@ -94,14 +94,14 @@ return either an Excel timestamp or a PHP timestamp or date object. It is possible for scripts to change the data type used for returning date values by calling the -\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType() +`\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType()` method: ``` php \PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType($returnDateType); ``` -where the following constants can be used for \$returnDateType +where the following constants can be used for `$returnDateType`: - `\PhpOffice\PhpSpreadsheet\Calculation\Functions::RETURNDATE_PHP_NUMERIC` - `\PhpOffice\PhpSpreadsheet\Calculation\Functions::RETURNDATE_PHP_OBJECT` @@ -155,7 +155,7 @@ Excel date values by calling the \PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar($baseDate); ``` -where the following constants can be used for \$baseDate +where the following constants can be used for `$baseDate`: - \PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR\_WINDOWS\_1900 - \PhpOffice\PhpSpreadsheet\Shared\Date::CALENDAR\_MAC\_1904 @@ -202,9 +202,9 @@ However, if you pass through a value such as '31/12/2008' that would be considered an error by a US-based server, but which is not ambiguous, then PhpSpreadsheet will attempt to correct this to 31st December 2008. If the content of the string doesn’t match any of the formats recognised -by the php date/time object implementation of strtotime() (which can -handle a wider range of formats than the normal strtotime() function), -then the function will return a '\#VALUE' error. However, Excel +by the php date/time object implementation of `strtotime()` (which can +handle a wider range of formats than the normal `strtotime()` function), +then the function will return a `#VALUE` error. However, Excel recommends that you should always use date/timestamps for your date functions, and the recommendation for PhpSpreadsheet is the same: avoid strings because the result is not predictable. @@ -238,12 +238,12 @@ rather than converted to Excel date timestamp values. ### Helper Methods -In addition to the setExcelCalendar() and getExcelCalendar() methods, a +In addition to the `setExcelCalendar()` and `getExcelCalendar()` methods, a number of other methods are available in the \PhpOffice\PhpSpreadsheet\Shared\Date class that can help when working with dates: -#### \PhpOffice\PhpSpreadsheet\Shared\Date::ExcelToPHP(\$excelDate) +#### \PhpOffice\PhpSpreadsheet\Shared\Date::ExcelToPHP($excelDate) Converts a date/time from an Excel date timestamp to return a PHP serialized date/timestamp. @@ -251,17 +251,17 @@ serialized date/timestamp. Note that this method does not trap for Excel dates that fall outside of the valid range for a PHP date timestamp. -#### \PhpOffice\PhpSpreadsheet\Shared\Date::ExcelToPHPObject(\$excelDate) +#### \PhpOffice\PhpSpreadsheet\Shared\Date::ExcelToPHPObject($excelDate) Converts a date from an Excel date/timestamp to return a PHP DateTime object. -#### \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(\$PHPDate) +#### \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($PHPDate) Converts a PHP serialized date/timestamp or a PHP DateTime object to return an Excel date timestamp. -#### \PhpOffice\PhpSpreadsheet\Shared\Date::FormattedPHPToExcel(\$year, \$month, \$day, \$hours=0, \$minutes=0, \$seconds=0) +#### \PhpOffice\PhpSpreadsheet\Shared\Date::FormattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0) Takes year, month and day values (and optional hour, minute and second values) and returns an Excel date timestamp value. @@ -1114,7 +1114,7 @@ ym | Months Excluding Years | Complete calendar months between the da yd | Days Excluding Years | Complete calendar days between the dates as if they were of the same year. md | Days Excluding Years And Months | Complete calendar days between the dates as if they were of the same month and same year. -The unit value is not case sensitive, and defaults to "d". +The unit value is not case sensitive, and defaults to `d`. ##### Return Value @@ -1290,8 +1290,8 @@ $retVal = call_user_func_array( ##### Notes -DATEVALUE uses the php date/time object implementation of strtotime() -(which can handle a wider range of formats than the normal strtotime() +DATEVALUE uses the php date/time object implementation of `strtotime()` +(which can handle a wider range of formats than the normal `strtotime()` function), and it is also called for any date parameter passed to other date functions (such as DATEDIF) when the parameter value is a string. @@ -1449,7 +1449,7 @@ $retVal = call_user_func_array( when a PHP Boolean is used for the third (optional) parameter (as shown in the example above), and the writer will generate and error. It will work if a numeric 0 or 1 is used for the method parameter; or if the -Excel TRUE() and FALSE() functions are used instead. +Excel `TRUE()` and `FALSE()` functions are used instead. #### EDATE @@ -1824,7 +1824,7 @@ The NOW function returns the current date and time. ##### Parameters -There are now parameters for the NOW() function. +There are no parameters for the `NOW()` function. ##### Return Value diff --git a/docs/topics/creating-spreadsheet.md b/docs/topics/creating-spreadsheet.md new file mode 100644 index 00000000..dceafe4b --- /dev/null +++ b/docs/topics/creating-spreadsheet.md @@ -0,0 +1,59 @@ +# Creating a spreadsheet + +## The `Spreadsheet` class + +The `Spreadsheet` class is the core of PhpSpreadsheet. It contains +references to the contained worksheets, document security settings and +document meta data. + +To simplify the PhpSpreadsheet concept: the `Spreadsheet` class +represents your workbook. + +Typically, you will create a workbook in one of two ways, either by +loading it from a spreadsheet file, or creating it manually. A third +option, though less commonly used, is cloning an existing workbook that +has been created using one of the previous two methods. + +### Loading a Workbook from a file + +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 +$inputFileName = './sampleData/example1.xls'; + +/** Load $inputFileName to a Spreadsheet object **/ +$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName); +``` + +### Creating a new workbook + +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 +/** Create a new Spreadsheet Object **/ +$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); +``` + +A new workbook will always be created with a single worksheet. + +## Clearing a Workbook from memory + +The PhpSpreadsheet object contains cyclic references (e.g. the workbook +is linked to the worksheets, and the worksheets are linked to their +parent workbook) which cause problems when PHP tries to clear the +objects from memory when they are `unset()`, or at the end of a function +when they are in local scope. The result of this is "memory leaks", +which can easily use a large amount of PHP's limited memory. + +This can only be resolved manually: if you need to unset a workbook, +then you also need to "break" these cyclic references before doing so. +PhpSpreadsheet provides the `disconnectWorksheets()` method for this +purpose. + +``` php +$spreadsheet->disconnectWorksheets(); +unset($spreadsheet); +``` diff --git a/docs/topics/file-formats.md b/docs/topics/file-formats.md index 36475be8..41889f41 100644 --- a/docs/topics/file-formats.md +++ b/docs/topics/file-formats.md @@ -78,10 +78,10 @@ library. Comma Separated Value (CSV) file format is a common structuring strategy for text format files. In CSV flies, each line in the file represents a row of data and (within each line of the file) the different data fields -(or columns) are separated from one another using a comma (","). If a +(or columns) are separated from one another using a comma (`,`). If a data field contains a comma, then it should be enclosed (typically in -quotation marks ("). Sometimes tabs "\t", or the pipe symbol ("|"), or a -semi-colon (";") are used as separators instead of a comma, although +quotation marks (`"`). Sometimes tabs `\t`, or the pipe symbol (`|`), or a +semi-colon (`;`) are used as separators instead of a comma, although other symbols can be used. Because CSV is a text-only format, it doesn't support any data formatting options. diff --git a/docs/images/01-schematic.png b/docs/topics/images/01-schematic.png similarity index 100% rename from docs/images/01-schematic.png rename to docs/topics/images/01-schematic.png diff --git a/docs/images/02-readers-writers.png b/docs/topics/images/02-readers-writers.png similarity index 100% rename from docs/images/02-readers-writers.png rename to docs/topics/images/02-readers-writers.png diff --git a/docs/images/07-simple-example-1.png b/docs/topics/images/07-simple-example-1.png similarity index 100% rename from docs/images/07-simple-example-1.png rename to docs/topics/images/07-simple-example-1.png diff --git a/docs/images/07-simple-example-2.png b/docs/topics/images/07-simple-example-2.png similarity index 100% rename from docs/images/07-simple-example-2.png rename to docs/topics/images/07-simple-example-2.png diff --git a/docs/images/07-simple-example-3.png b/docs/topics/images/07-simple-example-3.png similarity index 100% rename from docs/images/07-simple-example-3.png rename to docs/topics/images/07-simple-example-3.png diff --git a/docs/images/07-simple-example-4.png b/docs/topics/images/07-simple-example-4.png similarity index 100% rename from docs/images/07-simple-example-4.png rename to docs/topics/images/07-simple-example-4.png diff --git a/docs/topics/links-and-tools.md b/docs/topics/links-and-tools.md new file mode 100644 index 00000000..dab842d7 --- /dev/null +++ b/docs/topics/links-and-tools.md @@ -0,0 +1,15 @@ +# Useful links and tools + +There are some links and tools which are very useful when developing +using PhpSpreadsheet. + +## OpenXML / SpreadsheetML + +- [File format + documentation](http://www.ecma-international.org/news/TC45_current_work/TC45_available_docs.htm) +- [OpenXML Explained + e-book](http://openxmldeveloper.org/articles/1970.aspx) +- [Microsoft Office Compatibility Pack for Word, Excel, and PowerPoint + 2007 File + Formats](http://www.microsoft.com/downloads/details.aspx?familyid=941b3470-3ae9-4aee-8f43-c6bb74cd1466&displaylang=en) +- [OpenXML Package Explorer](http://www.codeplex.com/PackageExplorer/) diff --git a/docs/topics/memory_saving.md b/docs/topics/memory_saving.md new file mode 100644 index 00000000..f2ff2070 --- /dev/null +++ b/docs/topics/memory_saving.md @@ -0,0 +1,108 @@ +# Memory saving + +PhpSpreadsheet uses an average of about 1k per cell in your worksheets, so +large workbooks can quickly use up available memory. Cell caching +provides a mechanism that allows PhpSpreadsheet to maintain the cell +objects in a smaller size of memory, or off-memory (eg: on disk, in APCu, +memcache or redis). This allows you to reduce the memory usage for large +workbooks, although at a cost of speed to access cell data. + +By default, PhpSpreadsheet holds all cell objects in memory, but +you can specify alternatives by providing your own +[PSR-16](http://www.php-fig.org/psr/psr-16/) implementation. PhpSpreadsheet keys +are automatically namespaced, and cleaned up after use, so a single cache +instance may be shared across several usage of PhpSpreadsheet or even with other +cache usages. + +To enable cell caching, you must provide your own implementation of cache like so: + +``` php +$cache = new MyCustomPsr16Implementation(); + +\PhpOffice\PhpSpreadsheet\Settings::setCache($cache); +``` + +A separate cache is maintained for each individual worksheet, and is +automatically created when the worksheet is instantiated based on the +settings that you have configured. You cannot change +the configuration settings once you have started to read a workbook, or +have created your first worksheet. + +## Beware of TTL + +As opposed to common cache concept, PhpSpreadsheet data cannot be re-generated +from scratch. If some data is stored and later is not retrievable, +PhpSpreadsheet will throw an exception. + +That means that the data stored in cache **must not be deleted** by a +third-party or via TTL mechanism. + +So be sure that TTL is either de-activated or long enough to cover the entire +usage of PhpSpreadsheet. + +## Common use cases + +PhpSpreadsheet does not ship with alternative cache implementation. It is up to +you to select the most appropriate implementation for your environnement. You +can either implement [PSR-16](http://www.php-fig.org/psr/psr-16/) from scratch, +or use [pre-existing libraries](https://packagist.org/search/?q=psr-16). + +One such library is [PHP Cache](http://www.php-cache.com/) which +provides a wide range of alternatives. Refers to their documentation for +details, but here are a few suggestions that should get you started. + + +### APCu + +Require the packages into your project: + +```sh +composer require cache/simple-cache-bridge cache/apcu-adapter +``` + +Configure PhpSpreadsheet with something like: + +```php +$pool = new \Cache\Adapter\Apcu\ApcuCachePool(); +$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool); + +\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache); +``` + +### Redis + +Require the packages into your project: + +```sh +composer require cache/simple-cache-bridge cache/redis-adapter +``` + +Configure PhpSpreadsheet with something like: + +```php +$client = new \Redis(); +$client->connect('127.0.0.1', 6379); +$pool = new \Cache\Adapter\Redis\RedisCachePool($client); +$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool); + +\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache); +``` + +### Memcache + +Require the packages into your project: + +```sh +composer require cache/simple-cache-bridge cache/memcache-adapter +``` + +Configure PhpSpreadsheet with something like: + +```php +$client = new \Memcache(); +$client->connect('localhost', 11211); +$pool = new \Cache\Adapter\Memcache\MemcacheCachePool($client); +$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool); + +\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache); +``` \ No newline at end of file diff --git a/docs/topics/migration-from-PHPExcel.md b/docs/topics/migration-from-PHPExcel.md index bd619252..c34fba7f 100644 --- a/docs/topics/migration-from-PHPExcel.md +++ b/docs/topics/migration-from-PHPExcel.md @@ -132,3 +132,63 @@ to do is to specify a renderer like so: $rendererName = \PhpOffice\PhpSpreadsheet\Settings::PDF_RENDERER_MPDF; \PhpOffice\PhpSpreadsheet\Settings::setPdfRendererName($rendererName); ``` + +## PclZip and ZipArchive + +Support for PclZip were dropped in favor of the more complete and modern +[PHP extension ZipArchive](http://php.net/manual/en/book.zip.php). +So the following were removed: + +- `PclZip` +- `PHPExcel_Settings::setZipClass()` +- `PHPExcel_Settings::getZipClass()` +- `PHPExcel_Shared_ZipArchive` +- `PHPExcel_Shared_ZipStreamWrapper` + + +## Cell caching + +Cell caching was heavily refactored to leverage +[PSR-16](http://www.php-fig.org/psr/psr-16/). That means most classes +related to that feature were removed: + +- `PHPExcel_CachedObjectStorage_APC` +- `PHPExcel_CachedObjectStorage_DiscISAM` +- `PHPExcel_CachedObjectStorage_ICache` +- `PHPExcel_CachedObjectStorage_Igbinary` +- `PHPExcel_CachedObjectStorage_Memcache` +- `PHPExcel_CachedObjectStorage_Memory` +- `PHPExcel_CachedObjectStorage_MemoryGZip` +- `PHPExcel_CachedObjectStorage_MemorySerialized` +- `PHPExcel_CachedObjectStorage_PHPTemp` +- `PHPExcel_CachedObjectStorage_SQLite` +- `PHPExcel_CachedObjectStorage_SQLite3` +- `PHPExcel_CachedObjectStorage_Wincache` + +In addition to that, `\PhpOffice\PhpSpreadsheet::getCellCollection()` was renamed +to `\PhpOffice\PhpSpreadsheet::getCoordinates()` and +`\PhpOffice\PhpSpreadsheet::getCellCacheController()` to +`\PhpOffice\PhpSpreadsheet::getCellCollection()` for clarity. + +Refer to [the new documentation](./memory_saving.md) to see how to migrate. + +## Dropped conditionally returned cell + +For all the following methods, it is no more possible to change the type of +returned value. It always return the Worksheet and never the Cell or Rule: + +- Worksheet::setCellValue() +- Worksheet::setCellValueByColumnAndRow() +- Worksheet::setCellValueExplicit() +- Worksheet::setCellValueExplicitByColumnAndRow() +- Worksheet::addRule() + +Migration would be similar to: + +``` php +// Before +$cell = $worksheet->setCellValue('A1', 'value', true); + +// After +$cell = $worksheet->getCell('A1')->setValue('value'); +``` diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md new file mode 100644 index 00000000..5bc05b74 --- /dev/null +++ b/docs/topics/reading-and-writing-to-file.md @@ -0,0 +1,839 @@ +# Reading and writing to file + +As you already know from the [architecture](./architecture.md#readers-and-writers), +reading and writing to a +persisted storage is not possible using the base PhpSpreadsheet classes. +For this purpose, PhpSpreadsheet provides readers and writers, which are +implementations of \PhpOffice\PhpSpreadsheet\Reader\IReader and +\PhpOffice\PhpSpreadsheet\Writer\IWriter. + +## \PhpOffice\PhpSpreadsheet\IOFactory + +The PhpSpreadsheet API offers multiple methods to create a +\PhpOffice\PhpSpreadsheet\Reader\IReader or +\PhpOffice\PhpSpreadsheet\Writer\IWriter instance: + +Direct creation via \PhpOffice\PhpSpreadsheet\IOFactory. All examples +underneath demonstrate the direct creation method. Note that you can +also use the \PhpOffice\PhpSpreadsheet\IOFactory class to do this. + +### Creating \PhpOffice\PhpSpreadsheet\Reader\IReader using \PhpOffice\PhpSpreadsheet\IOFactory + +There are 2 methods for reading in a file into PhpSpreadsheet: using +automatic file type resolving or explicitly. + +Automatic file type resolving checks the different +\PhpOffice\PhpSpreadsheet\Reader\IReader distributed with +PhpSpreadsheet. If one of them can load the specified file name, the +file is loaded using that \PhpOffice\PhpSpreadsheet\Reader\IReader. +Explicit mode requires you to specify which +\PhpOffice\PhpSpreadsheet\Reader\IReader should be used. + +You can create a \PhpOffice\PhpSpreadsheet\Reader\IReader instance using +\PhpOffice\PhpSpreadsheet\IOFactory in automatic file type resolving +mode using the following code sample: + +``` php +$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load("05featuredemo.xlsx"); +``` + +A typical use of this feature is when you need to read files uploaded by +your users, and you don’t know whether they are uploading xls or xlsx +files. + +If you need to set some properties on the reader, (e.g. to only read +data, see more about this later), then you may instead want to use this +variant: + +``` php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("05featuredemo.xlsx"); +$reader->setReadDataOnly(true); +$reader->load("05featuredemo.xlsx"); +``` + +You can create a \PhpOffice\PhpSpreadsheet\Reader\IReader instance using +\PhpOffice\PhpSpreadsheet\IOFactory in explicit mode using the following +code sample: + +``` php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx"); +$spreadsheet = $reader->load("05featuredemo.xlsx"); +``` + +Note that automatic type resolving mode is slightly slower than explicit +mode. + +### Creating \PhpOffice\PhpSpreadsheet\Writer\IWriter using \PhpOffice\PhpSpreadsheet\IOFactory + +You can create a PhpOffice\PhpSpreadsheet\Writer\IWriter instance using +\PhpOffice\PhpSpreadsheet\IOFactory: + +``` php +$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, "Xlsx"); +$writer->save("05featuredemo.xlsx"); +``` + +## Excel 2007 (SpreadsheetML) file format + +Xlsx file format is the main file format of PhpSpreadsheet. It allows +outputting the in-memory spreadsheet to a .xlsx file. + +### \PhpOffice\PhpSpreadsheet\Reader\Xlsx + +#### Reading a spreadsheet + +You can read an .xlsx file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); +$spreadsheet = $reader->load("05featuredemo.xlsx"); +``` + +#### Read data only + +You can set the option setReadDataOnly on the reader, to instruct the +reader to ignore styling, data validation, … and just read cell data: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); +$reader->setReadDataOnly(true); +$spreadsheet = $reader->load("05featuredemo.xlsx"); +``` + +#### Read specific sheets only + +You can set the option setLoadSheetsOnly on the reader, to instruct the +reader to only load the sheets with a given name: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); +$reader->setLoadSheetsOnly( array("Sheet 1", "My special sheet") ); +$spreadsheet = $reader->load("05featuredemo.xlsx"); +``` + +#### Read specific cells only + +You can set the option setReadFilter on the reader, to instruct the +reader to only load the cells which match a given rule. A read filter +can be any class which implements +\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are +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 +class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { + + public function readCell($column, $row, $worksheetName = '') { + // Read title row and rows 20 - 30 + if ($row == 1 || ($row >= 20 && $row <= 30)) { + return true; + } + return false; + } +} + +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); +$reader->setReadFilter( new MyReadFilter() ); +$spreadsheet = $reader->load("06largescale.xlsx"); +``` + +### \PhpOffice\PhpSpreadsheet\Writer\Xlsx + +#### Writing a spreadsheet + +You can write an .xlsx file using the following code: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); +$writer->save("05featuredemo.xlsx"); +``` + +#### Formula pre-calculation + +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 +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); +$writer->setPreCalculateFormulas(false); +$writer->save("05featuredemo.xlsx"); +``` + +#### Office 2003 compatibility pack + +Because of a bug in the Office2003 compatibility pack, there can be some +small issues when opening Xlsx spreadsheets (mostly related to formula +calculation). You can enable Office2003 compatibility with the following +code: + + $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); + $writer->setOffice2003Compatibility(true); + $writer->save("05featuredemo.xlsx"); + +**Office2003 compatibility should only be used when needed** Office2003 +compatibility option should only be used when needed. This option +disables several Office2007 file format options, resulting in a +lower-featured Office2007 spreadsheet when this option is used. + +## Excel 5 (BIFF) file format + +Xls file format is the old Excel file format, implemented in +PhpSpreadsheet to provide a uniform manner to create both .xlsx and .xls +files. It is basically a modified version of [PEAR +Spreadsheet\_Excel\_Writer](http://pear.php.net/package/Spreadsheet_Excel_Writer), +although it has been extended and has fewer limitations and more +features than the old PEAR library. This can read all BIFF versions that +use OLE2: BIFF5 (introduced with office 95) through BIFF8, but cannot +read earlier versions. + +Xls file format will not be developed any further, it just provides an +additional file format for PhpSpreadsheet. + +**Excel5 (BIFF) limitations** Please note that BIFF file format has some +limits regarding to styling cells and handling large spreadsheets via +PHP. + +### \PhpOffice\PhpSpreadsheet\Reader\Xls + +#### Reading a spreadsheet + +You can read an .xls file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); +$spreadsheet = $reader->load("05featuredemo.xls"); +``` + +#### Read data only + +You can set the option setReadDataOnly on the reader, to instruct the +reader to ignore styling, data validation, … and just read cell data: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); +$reader->setReadDataOnly(true); +$spreadsheet = $reader->load("05featuredemo.xls"); +``` + +#### Read specific sheets only + +You can set the option setLoadSheetsOnly on the reader, to instruct the +reader to only load the sheets with a given name: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); +$reader->setLoadSheetsOnly( array("Sheet 1", "My special sheet") ); +$spreadsheet = $reader->load("05featuredemo.xls"); +``` + +#### Read specific cells only + +You can set the option setReadFilter on the reader, to instruct the +reader to only load the cells which match a given rule. A read filter +can be any class which implements +\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are +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 +class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { + + public function readCell($column, $row, $worksheetName = '') { + // Read title row and rows 20 - 30 + if ($row == 1 || ($row >= 20 && $row <= 30)) { + return true; + } + return false; + } +} + +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls(); +$reader->setReadFilter( new MyReadFilter() ); +$spreadsheet = $reader->load("06largescale.xls"); +``` + +### \PhpOffice\PhpSpreadsheet\Writer\Xls + +#### Writing a spreadsheet + +You can write an .xls file using the following code: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet); +$writer->save("05featuredemo.xls"); +``` + +## Excel 2003 XML file format + +Excel 2003 XML file format is a file format which can be used in older +versions of Microsoft Excel. + +**Excel 2003 XML limitations** Please note that Excel 2003 XML format +has some limits regarding to styling cells and handling large +spreadsheets via PHP. + +### \PhpOffice\PhpSpreadsheet\Reader\Xml + +#### Reading a spreadsheet + +You can read an Excel 2003 .xml file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml(); +$spreadsheet = $reader->load("05featuredemo.xml"); +``` + +#### Read specific cells only + +You can set the option setReadFilter on the reader, to instruct the +reader to only load the cells which match a given rule. A read filter +can be any class which implements +\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are +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 +class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { + + public function readCell($column, $row, $worksheetName = '') { + // Read title row and rows 20 - 30 + if ($row == 1 || ($row >= 20 && $row <= 30)) { + return true; + } + return false; + } + +} + +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml(); +$reader->setReadFilter( new MyReadFilter() ); +$spreadsheet = $reader->load("06largescale.xml"); +``` + +## Symbolic LinK (SYLK) + +Symbolic Link (SYLK) is a Microsoft file format typically used to +exchange data between applications, specifically spreadsheets. SYLK +files conventionally have a .slk suffix. Composed of only displayable +ANSI characters, it can be easily created and processed by other +applications, such as databases. + +**SYLK limitations** Please note that SYLK file format has some limits +regarding to styling cells and handling large spreadsheets via PHP. + +### \PhpOffice\PhpSpreadsheet\Reader\Slk + +#### Reading a spreadsheet + +You can read an .slk file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Slk(); +$spreadsheet = $reader->load("05featuredemo.slk"); +``` + +#### Read specific cells only + +You can set the option setReadFilter on the reader, to instruct the +reader to only load the cells which match a given rule. A read filter +can be any class which implements +\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are +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 +class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { + + public function readCell($column, $row, $worksheetName = '') { + // Read title row and rows 20 - 30 + if ($row == 1 || ($row >= 20 && $row <= 30)) { + return true; + } + return false; + } + +} + +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Slk(); +$reader->setReadFilter( new MyReadFilter() ); +$spreadsheet = $reader->load("06largescale.slk"); +``` + +## Open/Libre Office (.ods) + +Open Office or Libre Office .ods files are the standard file format for +Open Office or Libre Office Calc files. + +### \PhpOffice\PhpSpreadsheet\Reader\Ods + +#### Reading a spreadsheet + +You can read an .ods file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods(); +$spreadsheet = $reader->load("05featuredemo.ods"); +``` + +#### Read specific cells only + +You can set the option setReadFilter on the reader, to instruct the +reader to only load the cells which match a given rule. A read filter +can be any class which implements +\PhpOffice\PhpSpreadsheet\Reader\IReadFilter. By default, all cells are +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 +class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter { + + public function readCell($column, $row, $worksheetName = '') { + // Read title row and rows 20 - 30 + if ($row == 1 || ($row >= 20 && $row <= 30)) { + return true; + } + return false; + } + +} + +$reader = new PhpOffice\PhpSpreadsheet\Reader\Ods(); +$reader->setReadFilter( new MyReadFilter() ); +$spreadsheet = $reader->load("06largescale.ods"); +``` + +## CSV (Comma Separated Values) + +CSV (Comma Separated Values) are often used as an import/export file +format with other systems. PhpSpreadsheet allows reading and writing to +CSV files. + +**CSV limitations** Please note that CSV file format has some limits +regarding to styling cells, number formatting, ... + +### \PhpOffice\PhpSpreadsheet\Reader\Csv + +#### Reading a CSV file + +You can read a .csv file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); +$spreadsheet = $reader->load("sample.csv"); +``` + +#### Setting CSV options + +Often, CSV files are not really "comma separated", or use semicolon (`;`) +as a separator. You can instruct +\PhpOffice\PhpSpreadsheet\Reader\Csv some options before reading a CSV +file. + +The separator will be auto-detected, so in most cases it should not be necessary +to specify it. But in cases where auto-detection does not fit the use-case, then +it can be set manually. + +Note that \PhpOffice\PhpSpreadsheet\Reader\Csv by default assumes that +the loaded CSV file is UTF-8 encoded. If you are reading CSV files that +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 +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); +$reader->setInputEncoding('CP1252'); +$reader->setDelimiter(';'); +$reader->setEnclosure(''); +$reader->setLineEnding("\r\n"); +$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 +$reader->setSheetIndex(0); +``` + +#### Read into existing spreadsheet + +When working with CSV files, it might occur that you want to import CSV +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 +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); +$reader->setDelimiter(';'); +$reader->setEnclosure(''); +$reader->setLineEnding("\r\n"); +$reader->setSheetIndex(5); + +$reader->loadIntoExisting("05featuredemo.csv", $spreadsheet); +``` + +### \PhpOffice\PhpSpreadsheet\Writer\Csv + +#### Writing a CSV file + +You can write a .csv file using the following code: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); +$writer->save("05featuredemo.csv"); +``` + +#### Setting CSV options + +Often, CSV files are not really "comma separated", or use semicolon (`;`) +as a separator. You can instruct +\PhpOffice\PhpSpreadsheet\Writer\Csv some options before writing a CSV +file: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); +$writer->setDelimiter(';'); +$writer->setEnclosure(''); +$writer->setLineEnding("\r\n"); +$writer->setSheetIndex(0); + +$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 +$writer->setSheetIndex(0); +``` + +#### Formula pre-calculation + +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 +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); +$writer->setPreCalculateFormulas(false); +$writer->save("05featuredemo.csv"); +``` + +#### Writing UTF-8 CSV files + +A CSV file can be marked as UTF-8 by writing a BOM file header. This can +be enabled by using the following code: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); +$writer->setUseBOM(true); +$writer->save("05featuredemo.csv"); +``` + +#### Decimal and thousands separators + +If the worksheet you are exporting contains numbers with decimal or +thousands separators then you should think about what characters you +want to use for those before doing the export. + +By default PhpSpreadsheet looks up in the server's locale settings to +decide what characters to use. But to avoid problems it is recommended +to set the characters explicitly as shown below. + +English users will want to use this before doing the export: + +``` php +\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator('.'); +\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator(','); +``` + +German users will want to use the opposite values. + +``` php +\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator(','); +\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator('.'); +``` + +Note that the above code sets decimal and thousand separators as global +options. This also affects how HTML and PDF is exported. + +## HTML + +PhpSpreadsheet allows you to read or write a spreadsheet as HTML format, +for quick representation of the data in it to anyone who does not have a +spreadsheet application on their PC, or loading files saved by other +scripts that simply create HTML markup and give it a .xls file +extension. + +**HTML limitations** Please note that HTML file format has some limits +regarding to styling cells, number formatting, ... + +### \PhpOffice\PhpSpreadsheet\Reader\Html + +#### Reading a spreadsheet + +You can read an .html or .htm file using the following code: + +``` php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Html(); + +$spreadsheet = $reader->load("05featuredemo.html"); +``` + +**HTML limitations** Please note that HTML reader is still experimental +and does not yet support merged cells or nested tables cleanly + +### \PhpOffice\PhpSpreadsheet\Writer\Html + +Please note that \PhpOffice\PhpSpreadsheet\Writer\Html only outputs the +first worksheet by default. + +#### Writing a spreadsheet + +You can write a .htm file using the following code: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); + +$writer->save("05featuredemo.htm"); +``` + +#### Write all worksheets + +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 +$writer->writeAllSheets(); +``` + +#### Write a specific worksheet + +HTML files can contain one or more worksheets. Therefore, you can +specify which sheet to write to HTML: + +``` php +$writer->setSheetIndex(0); +``` + +#### Setting the images root of the HTML file + +There might be situations where you want to explicitly set the included +images root. For example, one might want to see + +``` html + +``` + +instead of + +``` html +. +``` + +You can use the following code to achieve this result: + +``` php +$writer->setImagesRoot('http://www.example.com'); +``` + +#### Formula pre-calculation + +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 +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); +$writer->setPreCalculateFormulas(false); + +$writer->save("05featuredemo.htm"); +``` + +#### Embedding generated HTML in a web page + +There might be a situation where you want to embed the generated HTML in +an existing website. \PhpOffice\PhpSpreadsheet\Writer\Html provides +support to generate only specific parts of the HTML code, which allows +you to use these parts in your website. + +Supported methods: + +- `generateHTMLHeader()` +- `generateStyles()` +- `generateSheetData()` +- `generateHTMLFooter()` + +Here's an example which retrieves all parts independently and merges +them into a resulting HTML page: + +``` php +generateHTMLHeader(); +?> + + +?> + +--> + + +generateSheetData(); +echo $writer->generateHTMLFooter(); +?> +``` + +#### Writing UTF-8 HTML files + +A HTML file can be marked as UTF-8 by writing a BOM file header. This +can be enabled by using the following code: + +``` php +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet); +$writer->setUseBOM(true); + +$writer->save("05featuredemo.htm"); +``` + +#### Decimal and thousands separators + +See section \PhpOffice\PhpSpreadsheet\Writer\Csv how to control the +appearance of these. + +## PDF + +PhpSpreadsheet allows you to write a spreadsheet into PDF format, for +fast distribution of represented data. + +**PDF limitations** Please note that PDF file format has some limits +regarding to styling cells, number formatting, ... + +### \PhpOffice\PhpSpreadsheet\Writer\Pdf + +PhpSpreadsheet’s PDF Writer is a wrapper for a 3rd-Party PDF Rendering +library such as tcPDF, mPDF or DomPDF. You must now install a PDF +rendering library yourself; but PhpSpreadsheet will work with a number +of different libraries. + +Currently, the following libraries are supported: + +Library | Downloadable from | PhpSpreadsheet Internal Constant +--------|-------------------------------------|--------------------------------- +tcPDF | https://github.com/tecnickcom/tcpdf | PDF_RENDERER_TCPDF +mPDF | https://github.com/mpdf/mpdf | PDF_RENDERER_MPDF +domPDF | https://github.com/dompdf/dompdf | PDF_RENDERER_DOMPDF + +The different libraries have different strengths and weaknesses. Some +generate better formatted output than others, some are faster or use +less memory than others, while some generate smaller .pdf files. It is +the developers choice which one they wish to use, appropriate to their +own circumstances. + +Before instantiating a Writer to generate PDF output, you will need to +indicate which Rendering library you are using. + +``` php +$rendererName = \PhpOffice\PhpSpreadsheet\Settings::PDF_RENDERER_MPDF; +\PhpOffice\PhpSpreadsheet\Settings::setPdfRendererName($rendererName); +``` + +#### Writing a spreadsheet + +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 +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf($spreadsheet); +$writer->save("05featuredemo.pdf"); +``` + +Please note that \PhpOffice\PhpSpreadsheet\Writer\Pdf only outputs the +first worksheet by default. + +#### Write all worksheets + +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 +$writer->writeAllSheets(); +``` + +#### Write a specific worksheet + +PDF files can contain one or more worksheets. Therefore, you can specify +which sheet to write to PDF: + +``` php +$writer->setSheetIndex(0); +``` + +#### Formula pre-calculation + +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 +$writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf($spreadsheet); +$writer->setPreCalculateFormulas(false); + +$writer->save("05featuredemo.pdf"); +``` + +#### Decimal and thousands separators + +See section \PhpOffice\PhpSpreadsheet\Writer\Csv how to control the +appearance of these. + +## Generating Excel files from templates (read, modify, write) + +Readers and writers are the tools that allow you to generate Excel files +from templates. This requires less coding effort than generating the +Excel file from scratch, especially if your template has many styles, +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 +$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('template.xlsx'); + +$worksheet = $spreadsheet->getActiveSheet(); + +$worksheet->getCell('A1')->setValue('John'); +$worksheet->getCell('A2')->setValue('Smith'); + +$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls'); +$writer->save('write.xls'); +``` + +Notice that it is ok to load an xlsx file and generate an xls file. diff --git a/docs/topics/reading-files.md b/docs/topics/reading-files.md index a5f744d5..afd3db35 100644 --- a/docs/topics/reading-files.md +++ b/docs/topics/reading-files.md @@ -12,14 +12,13 @@ spreadsheet files. This can lead to: - Server Side Request Forgery - Command Execution (depending on the installed PHP wrappers) -To prevent this, PhpSpreadsheet sets `libxml_disable_entity_loader` to -`true` for the XML-based Readers by default. +To prevent this, by default every XML-based Reader looks for XML entities declared inside the DOCTYPE and if any is found an exception is raised. ## Loading a Spreadsheet File 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. +Factory identify the file type and load it, calling the static `load()` +method of the `\PhpOffice\PhpSpreadsheet\IOFactory` class. ``` php $inputFileName = './sampleData/example1.xls'; @@ -31,7 +30,7 @@ $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName); > See Examples/Reader/exampleReader01.php for a working example of this > code. -The load() method will attempt to identify the file type, and +The `load()` method will attempt to identify the file type, and instantiate a loader for that file type; using it to load the file and store the data and any formatting in a `Spreadsheet` object. @@ -52,7 +51,7 @@ actually reading the file into a `Spreadsheet` object. If you know the file type of the spreadsheet file that you need to load, you can instantiate a new reader object for that file type, then use the -reader's load() method to read the file to a `Spreadsheet` object. It is +reader's `load()` method to read the file to a `Spreadsheet` object. It is possible to instantiate the reader objects for each of the different 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 @@ -76,7 +75,7 @@ $spreadsheet = $reader->load($inputFileName); > See Examples/Reader/exampleReader02.php for a working example of this > code. -Alternatively, you can use the IO Factory's createReader() method to +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. @@ -121,7 +120,7 @@ $spreadsheet = $reader->load($inputFileName); Once you have created a reader object for the workbook that you want to load, you have the opportunity to set additional options before -executing the load() method. +executing the `load()` method. ### Reading Only Data from a Spreadsheet File @@ -173,7 +172,7 @@ setLoadSheetsOnly() method to identify those sheets you are interested in reading. To read a single sheet, you can pass that sheet name as a parameter to -the setLoadSheetsOnly() method. +the `setLoadSheetsOnly()` method. ``` php $inputFileType = 'Xls'; @@ -192,7 +191,7 @@ $spreadsheet = $reader->load($inputFileName); > 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. +of sheet names as an array parameter to the `setLoadSheetsOnly()` method. ``` php $inputFileType = 'Xls'; @@ -210,7 +209,7 @@ $spreadsheet = $reader->load($inputFileName); > See Examples/Reader/exampleReader08.php for a working example of this > code. -To reset this option to the default, you can call the setLoadAllSheets() +To reset this option to the default, you can call the `setLoadAllSheets()` method. ``` php @@ -242,8 +241,8 @@ If you are only interested in reading part of a worksheet, then you can write a filter class that identifies whether or not individual cells should be read by the loader. A read filter must implement the \PhpOffice\PhpSpreadsheet\Reader\IReadFilter interface, and contain a -readCell() method that accepts arguments of \$column, \$row and -\$worksheetName, and return a boolean true or false that indicates +readCell() method that accepts arguments of `$column`, `$row` and +`$worksheetName`, and return a boolean true or false that indicates whether a workbook cell identified by those arguments should be read or not. @@ -320,7 +319,7 @@ $filterSubset = new MyReadFilter(9,15,range('G','K')); > code. This can be particularly useful for conserving memory, by allowing you -to read and process a large workbook in “chunks”: an example of this +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. @@ -387,13 +386,13 @@ CSV | YES | HTML | NO | | | ### Combining Multiple Files into a Single Spreadsheet Object While you can limit the number of worksheets that are read from a -workbook file using the setLoadSheetsOnly() method, certain readers also +workbook file using the `setLoadSheetsOnly()` method, certain readers also allow you to combine several individual "sheets" from different files into a single `Spreadsheet` object, where each individual file is a single worksheet within that workbook. For each file that you read, you need to indicate which worksheet index it should be loaded into using -the setSheetIndex() method of the \$reader, then use the -loadIntoExisting() method rather than the load() method to actually read +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 @@ -451,9 +450,9 @@ Xlsx Microsoft Office Open XML SpreadsheetML .xlsx file is limited to by available disk space. This means that we wouldn’t ordinarily be able to read all the rows from a very large CSV file that exceeded those limits, and save it as an Xls or Xlsx file. However, by using Read -Filters to read the CSV file in “chunks” (using the chunkReadFilter -Class that we defined in section REF \_Ref275604563 \r \p 5.3 above), -and the setSheetIndex() method of the \$reader, we can split the CSV +Filters to read the CSV file in "chunks" (using the chunkReadFilter +Class that we defined in [the above section](#reading-only-specific-columns-and-rows-from-a-file-read-filters), +and the `setSheetIndex()` method of the `$reader`, we can split the CSV file across several individual worksheets. ``` php @@ -504,7 +503,7 @@ for ($startRow = 2; $startRow <= 1000000; $startRow += $chunkSize) { This code will read 65,530 rows at a time from the CSV file that we’re loading, and store each "chunk" in a new worksheet. -The setContiguous() method for the Reader is important here. It is +The `setContiguous()` method for the Reader is important here. It is applicable only when working with a Read Filter, and identifies whether or not the cells should be stored by their position within the CSV file, or their position relative to the filter. @@ -524,15 +523,17 @@ CSV | YES | HTML | NO ### Pipe or Tab Separated Value Files -The CSV loader defaults to loading a file where comma is used as the -separator, but you can modify this to load tab- or pipe-separated value -files using the setDelimiter() method. +The CSV loader will attempt to auto-detect the separator used in the file. If it +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 $inputFileType = 'Csv'; $inputFileName = './sampleData/example1.tsv'; -/** Create a new Reader of the type defined in $inputFileType **/ $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); +/** Create a new Reader of the type defined in $inputFileType **/ +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); /** Set the delimiter to a TAB character **/ $reader->setDelimiter("\t"); // $reader->setDelimiter('|'); @@ -547,8 +548,11 @@ $spreadsheet = $reader->load($inputFileName); In addition to the delimiter, you can also use the following methods to set other attributes for the data load: -setEnclosure() | default is " setLineEnding() | default is PHP\_EOL -setInputEncoding() | default is UTF-8 +Method | Default +-------------------|---------- +setEnclosure() | `"` +setLineEnding() | `PHP_EOL` +setInputEncoding() | `UTF-8` Setting CSV delimiter applies to: diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md index c074be74..db2fa417 100644 --- a/docs/topics/recipes.md +++ b/docs/topics/recipes.md @@ -5,8 +5,8 @@ Please note that these do NOT offer complete documentation on specific PhpSpreadsheet API functions, but just a bump to get you started. If you need specific API functions, please refer to the API documentation. -For example, REF \_Ref191885321 \w \h 4.4.7 REF \_Ref191885321 -\h Setting a worksheet's page orientation and size covers setting a page +For example, [setting a worksheet's page orientation and size +](#setting-a-worksheets-page-orientation-and-size) covers setting a page orientation to A4. Other paper formats, like US Letter, are not covered in this document, but in the PhpSpreadsheet API documentation. @@ -119,9 +119,9 @@ in an English version of Microsoft Office Excel, and PhpSpreadsheet handles all formulae internally in this format. This means that the following rules hold: -- Decimal separator is '.' (period) -- Function argument separator is ',' (comma) -- Matrix row separator is ';' (semicolon) +- Decimal separator is `.` (period) +- Function argument separator is `,` (comma) +- Matrix row separator is `;` (semicolon) - English function names must be used This is regardless of which language version of Microsoft Office Excel @@ -132,16 +132,16 @@ will take care of displaying the formula according the applications language. Translation is taken care of by the application! The following line of code writes the formula -'=IF(C4>500,"profit","loss")' into the cell B8. Note that the -formula must start with "=" to make PhpSpreadsheet recognise this as a +`=IF(C4>500,"profit","loss")` into the cell B8. Note that the +formula must start with `=` to make PhpSpreadsheet recognise this as a formula. ``` 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. +If you want to write a string beginning with an `=` character to a +cell, then you should use the `setCellValueExplicit()` method. ``` php $spreadsheet->getActiveSheet() @@ -159,7 +159,7 @@ $formula = $spreadsheet->getActiveSheet()->getCell('B8')->getValue(); ``` If you need the calculated value of a cell, use the following code. This -is further explained in REF \_Ref191885372 \w \h \* MERGEFORMAT 4.4.35. +is further explained in [the calculation engine](./calculation-engine.md). ``` php $value = $spreadsheet->getActiveSheet()->getCell('B8')->getCalculatedValue(); @@ -239,7 +239,7 @@ $spreadsheet->getActiveSheet()->getStyle('A1')->getAlignment()->setWrapText(true **Tip** -Read more about formatting cells using getStyle() elsewhere. +Read more about formatting cells using `getStyle()` elsewhere. **Tip** @@ -332,7 +332,7 @@ $spreadsheet->getActiveSheet()->getPageSetup()->setFitToHeight(0); As you can see, it is not necessary to call setFitToPage(TRUE) since setFitToWidth(...) and setFitToHeight(...) triggers this. -If you use setFitToWidth() you should in general also specify +If you use `setFitToWidth()` you should in general also specify setFitToHeight() explicitly like in the example. Be careful relying on the initial values. @@ -474,7 +474,9 @@ $spreadsheet->getActiveSheet()->setBreak( 'D10' , \PhpOffice\PhpSpreadsheet\Work To show/hide gridlines when printing, use the following code: -\$spreadsheet->getActiveSheet()->setShowGridlines(true); +```php +$spreadsheet->getActiveSheet()->setShowGridlines(true); +``` ### Setting rows/columns to repeat at top/left @@ -738,7 +740,7 @@ vertical/horizontal, left/right/top/bottom/diagonal. This border hierarchy can be utilized to achieve various effects in an easy manner. -### Valid array keys for style applyFromArray() +### Valid array keys for style `applyFromArray()` The following table lists the valid array keys for \PhpOffice\PhpSpreadsheet\Style applyFromArray() classes. If the "Maps @@ -1001,15 +1003,15 @@ $validation->setFormula1('"Item A,Item B,Item C"'); ``` When using a data validation list like above, make sure you put the list -between " and " and that you split the items with a comma (,). +between `"` and `"` and that you split the items with a comma (`,`). It is important to remember that any string participating in an Excel formula is allowed to be maximum 255 characters (not bytes). This sets a limit on how many items you can have in the string "Item A,Item B,Item C". Therefore it is normally a better idea to type the item values directly in some cell range, say A1:A3, and instead use, say, -$validation->setFormula1('Sheet!$A$1:$A\$3');. Another benefit is that -the item values themselves can contain the comma "," character itself. +`$validation->setFormula1('Sheet!$A$1:$A$3')`. Another benefit is that +the item values themselves can contain the comma `,` character itself. If you need data validation on multiple cells, one can clone the ruleset: @@ -1039,20 +1041,26 @@ $spreadsheet->getActiveSheet()->getColumnDimension('B')->setAutoSize(true); The measure for column width in PhpSpreadsheet does **not** correspond exactly to the measure you may be used to in Microsoft Office Excel. Column widths are difficult to deal with in Excel, and there are several -measures for the column width.1) **Inner width in character units** -(e.g. 8.43 this is probably what you are familiar with in Excel)2) -**Full width in pixels** (e.g. 64 pixels)3) **Full width in character -units** (e.g. 9.140625, value -1 indicates unset width)**PHPExcel always -operates with 3) "Full width in character units"** which is in fact the +measures for the column width. + +1. Inner width in character units +(e.g. 8.43 this is probably what you are familiar with in Excel) +2. Full width in pixels (e.g. 64 pixels) +3. Full width in character units (e.g. 9.140625, value -1 indicates unset width) + +**PhpSpreadsheet always +operates with "3. Full width in character units"** which is in fact the only value that is stored in any Excel file, hence the most reliable measure. Unfortunately, **Microsoft Office Excel does not present you -with this measure**. Instead measures 1) and 2) are computed by the +with this measure**. Instead measures 1 and 2 are computed by the application when the file is opened and these values are presented in -various dialogues and tool tips.The character width unit is the width of -a '0' (zero) glyph in the workbooks default font. Therefore column +various dialogues and tool tips. + +The character width unit is the width of +a `0` (zero) glyph in the workbooks default font. Therefore column widths measured in character units in two different workbooks can only be compared if they have the same default workbook font.If you have some -Excel file and need to know the column widths in measure 3), you can +Excel file and need to know the column widths in measure 3, you can read the Excel file with PhpSpreadsheet and echo the retrieved values. ## Show/hide a column @@ -1487,7 +1495,7 @@ visible via the user interface. ## Right-to-left worksheet -Worksheets can be set individually whether column "A" should start at +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. diff --git a/docs/topics/settings.md b/docs/topics/settings.md index f24adecb..a9aae9f9 100644 --- a/docs/topics/settings.md +++ b/docs/topics/settings.md @@ -5,177 +5,20 @@ before instantiating a `Spreadsheet` object or loading a workbook file, there are a number of configuration options that can be set which will affect the subsequent behaviour of the script. -## Cell Caching +## Cell collection caching -PhpSpreadsheet uses an average of about 1k/cell in your worksheets, so -large workbooks can quickly use up available memory. Cell caching -provides a mechanism that allows PhpSpreadsheet to maintain the cell -objects in a smaller size of memory, on disk, or in APC, memcache or -Wincache, rather than in PHP memory. This allows you to reduce the -memory usage for large workbooks, although at a cost of speed to access -cell data. +By default, PhpSpreadsheet holds all cell objects in memory, but +you can specify alternatives to reduce memory consumption at the cost of speed. +Read more about [memory saving](./memory_saving.md). -By default, PhpSpreadsheet still holds all cell objects in memory, but -you can specify alternatives. To enable cell caching, you must call the -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod() method, -passing in the caching method that you wish to use. +To enable cell caching, you must provide your own implementation of cache like so: ``` php -$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_IN_MEMORY; +$cache = new MyCustomPsr16Implementation(); -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod); +\PhpOffice\PhpSpreadsheet\Settings::setCache($cache); ``` -setCacheStorageMethod() will return a boolean true on success, false on -failure (for example if trying to cache to APC when APC is not enabled). - -A separate cache is maintained for each individual worksheet, and is -automatically created when the worksheet is instantiated based on the -caching method and settings that you have configured. You cannot change -the configuration settings once you have started to read a workbook, or -have created your first worksheet. - -Currently, the following caching methods are available. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IN\_MEMORY - -The default. If you don't initialise any caching method, then this is -the method that PhpSpreadsheet will use. Cell objects are maintained in -PHP memory as at present. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IN\_MEMORY\_SERIALIZED - -Using this caching method, cells are held in PHP memory as an array of -serialized objects, which reduces the memory footprint with minimal -performance overhead. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IN\_MEMORY\_GZIP - -Like cache\_in\_memory\_serialized, this method holds cells in PHP -memory as an array of serialized objects, but gzipped to reduce the -memory usage still further, although access to read or write a cell is -slightly slower. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IGBINARY - -Uses PHPs igbinary extension (if its available) to serialize cell -objects in memory. This is normally faster and uses less memory than -standard PHP serialization, but isnt available in most hosting -environments. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_DISCISAM - -When using CACHE\_TO\_DISCISAM all cells are held in a temporary disk -file, with only an index to their location in that file maintained in -PHP memory. This is slower than any of the CACHE\_IN\_MEMORY methods, -but significantly reduces the memory footprint. By default, -PhpSpreadsheet will use PHP's temp directory for the cache file, but you -can specify a different directory when initialising CACHE\_TO\_DISCISAM. - -``` php -$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_DISCISAM; -$cacheSettings = array( - 'dir' => '/usr/local/tmp' -); -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); -``` - -The temporary disk file is automatically deleted when your script -terminates. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_PHPTEMP - -Like CACHE\_TO\_DISCISAM, when using CACHE\_TO\_PHPTEMP all cells are -held in the php://temp I/O stream, with only an index to their location -maintained in PHP memory. In PHP, the php://memory wrapper stores data -in the memory: php://temp behaves similarly, but uses a temporary file -for storing the data when a certain memory limit is reached. The default -is 1 MB, but you can change this when initialising CACHE\_TO\_PHPTEMP. - -``` php -$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_PHPTEMP; -$cacheSettings = array( - 'memoryCacheSize' => '8MB' -); -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); -``` - -The php://temp file is automatically deleted when your script -terminates. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_APC - -When using CACHE\_TO\_APC, cell objects are maintained in APC with only -an index maintained in PHP memory to identify that the cell exists. By -default, an APC cache timeout of 600 seconds is used, which should be -enough for most applications: although it is possible to change this -when initialising CACHE\_TO\_APC. - -``` php -$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_APC; -$cacheSettings = array( - 'cacheTime' => 600 -); -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); -``` - -When your script terminates all entries will be cleared from APC, -regardless of the cacheTime value, so it cannot be used for persistent -storage using this mechanism. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_MEMCACHE - -When using CACHE\_TO\_MEMCACHE, cell objects are maintained in memcache -with only an index maintained in PHP memory to identify that the cell -exists. - -By default, PhpSpreadsheet looks for a memcache server on localhost at -port 11211. It also sets a memcache timeout limit of 600 seconds. If you -are running memcache on a different server or port, then you can change -these defaults when you initialise CACHE\_TO\_MEMCACHE: - -``` php -$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_MEMCACHE; -$cacheSettings = array( - 'memcacheServer' => 'localhost', - 'memcachePort' => 11211, - 'cacheTime' => 600 -); -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); -``` - -When your script terminates all entries will be cleared from memcache, -regardless of the cacheTime value, so it cannot be used for persistent -storage using this mechanism. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_WINCACHE - -When using CACHE\_TO\_WINCACHE, cell objects are maintained in Wincache -with only an index maintained in PHP memory to identify that the cell -exists. By default, a Wincache cache timeout of 600 seconds is used, -which should be enough for most applications: although it is possible to -change this when initialising CACHE\_TO\_WINCACHE. - -``` php -$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_WINCACHE; -$cacheSettings = array( - 'cacheTime' => 600 -); -\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); -``` - -When your script terminates all entries will be cleared from Wincache, -regardless of the cacheTime value, so it cannot be used for persistent -storage using this mechanism. - -### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_SQLITE3; - -Uses an SQLite 3 "in-memory" database for caching cell data. Unlike -other caching methods, neither cells nor an index are held in PHP memory -- an indexed database table makes it unnecessary to hold any index in -PHP memory, which makes this the most memory-efficient of the cell -caching methods. - ## Language/Locale Some localisation elements have been included in PhpSpreadsheet. You can @@ -190,12 +33,13 @@ if (!$validLocale) { } ``` -If Brazilian Portuguese language files aren't available, then Portuguese -will be enabled instead: if Portuguese language files aren't available, -then the setLocale() method will return an error, and American English +- If Brazilian Portuguese language files aren't available, then Portuguese +will be enabled instead +- If Portuguese language files aren't available, +then the `setLocale()` method will return an error, and American English (en\_us) settings will be used throughout. 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 the section of this document entitled "Locale Settings for -Formulae". +be found in [Locale Settings for +Formulae](./recipes.md#locale-settings-for-formulae). diff --git a/docs/topics/worksheets.md b/docs/topics/worksheets.md new file mode 100644 index 00000000..ccbaa115 --- /dev/null +++ b/docs/topics/worksheets.md @@ -0,0 +1,130 @@ +# Worksheets + +A worksheet is a collection of cells, formulae, images, graphs, etc. It +holds all data necessary to represent a spreadsheet worksheet. + +When you load a workbook from a spreadsheet file, it will be loaded with +all its existing worksheets (unless you specified that only certain +sheets should be loaded). When you load from non-spreadsheet files (such +as a CSV or HTML file) or from spreadsheet formats that don't identify +worksheets by name (such as SYLK), then a single worksheet called +"WorkSheet1" will be created containing the data from that file. + +When you instantiate a new workbook, PhpSpreadsheet will create it with +a single worksheet called "WorkSheet1". + +The `getSheetCount()` method will tell you the number of worksheets in +the workbook; while the `getSheetNames()` method will return a list of +all worksheets in the workbook, indexed by the order in which their +"tabs" would appear when opened in MS Excel (or other appropriate +Spreadsheet program). + +Individual worksheets can be accessed by name, or by their index +position in the workbook. The index position represents the order that +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 +// Get the second sheet in the workbook +// Note that sheets are indexed from 0 +$spreadsheet->getSheet(1); +``` + +If you don't specify a sheet index, then the first worksheet will be +returned. + +Methods also exist allowing you to reorder the worksheets in the +workbook. + +To access a sheet by name, use the `getSheetByName()` method, specifying +the name of the worksheet that you want to access. + +``` php +// Retrieve the worksheet called 'Worksheet 1' +$spreadsheet->getSheetByName('Worksheet 1'); +``` + +Alternatively, one worksheet is always the currently active worksheet, +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 +// Retrieve the current active worksheet +$spreadsheet->getActiveSheet(); +``` + +You can change the currently active sheet by index or by name using the +`setActiveSheetIndex()` and `setActiveSheetIndexByName()` methods. + +## Adding a new Worksheet + +You can add a new worksheet to the workbook using the `createSheet()` +method of the `Spreadsheet` object. By default, this will be created as +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 +$spreadsheet->createSheet(); +``` + +A new worksheet created using this method will be called +"Worksheet<n>" where "<n>" is the lowest number possible to +guarantee that the title is unique. + +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 +// Create a new worksheet called "My Data" +$myWorkSheet = new \PhpOffice\PhpSpreadsheet\Worksheet($spreadsheet, 'My Data'); + +// Attach the "My Data" worksheet as the first worksheet in the Spreadsheet object +$spreadsheet->addSheet($myWorkSheet, 0); +``` + +If you don't specify an index position as the second argument, then the +new worksheet will be added after the last existing worksheet. + +## Copying Worksheets + +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 +$clonedWorksheet = clone $spreadsheet->getSheetByName('Worksheet 1'); +$clonedWorksheet->setTitle('Copy of Worksheet 1') +$spreadsheet->addSheet($clonedWorksheet); +``` + +You can also copy worksheets from one workbook to another, though this +is more complex as PhpSpreadsheet also has to replicate the styling +between the two workbooks. The `addExternalSheet()` method is provided for +this purpose. + + $clonedWorksheet = clone $spreadsheet1->getSheetByName('Worksheet 1'); + $spreadsheet->addExternalSheet($clonedWorksheet); + +In both cases, it is the developer's responsibility to ensure that +worksheet names are not duplicated. PhpSpreadsheet will throw an +exception if you attempt to copy worksheets that will result in a +duplicate name. + +## Removing a Worksheet + +You can delete a worksheet from a workbook, identified by its index +position, using the `removeSheetByIndex()` method + +``` php +$sheetIndex = $spreadsheet->getIndex( + $spreadsheet->getSheetByName('Worksheet 1') +); +$spreadsheet->removeSheetByIndex($sheetIndex); +``` + +If the currently active worksheet is deleted, then the sheet at the +previous index position will become the currently active sheet. diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 52bbf2c7..6880d23e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,7 +14,6 @@ ./src - ./src/PhpSpreadsheet/Shared/PCLZip ./src/PhpSpreadsheet/Shared/JAMA ./src/PhpSpreadsheet/Writer/PDF diff --git a/samples/01_Simple_PCLZip.php b/samples/01_Simple_PCLZip.php deleted file mode 100644 index b1cb73e6..00000000 --- a/samples/01_Simple_PCLZip.php +++ /dev/null @@ -1,50 +0,0 @@ -log('Create new Spreadsheet object'); -$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); - -// Set document properties -$helper->log('Set document properties'); -$spreadsheet->getProperties()->setCreator('Maarten Balliauw') - ->setLastModifiedBy('Maarten Balliauw') - ->setTitle('PhpSpreadsheet Test Document') - ->setSubject('PhpSpreadsheet Test Document') - ->setDescription('Test document for PhpSpreadsheet, generated using PHP classes.') - ->setKeywords('office PhpSpreadsheet php') - ->setCategory('Test result file'); - -// Add some data -$helper->log('Add some data'); -$spreadsheet->setActiveSheetIndex(0) - ->setCellValue('A1', 'Hello') - ->setCellValue('B2', 'world!') - ->setCellValue('C1', 'Hello') - ->setCellValue('D2', 'world!'); - -// Miscellaneous glyphs, UTF-8 -$spreadsheet->setActiveSheetIndex(0) - ->setCellValue('A4', 'Miscellaneous glyphs') - ->setCellValue('A5', 'éàèùâêîôûëïüÿäöüç'); - -$spreadsheet->getActiveSheet()->setCellValue('A8', "Hello\nWorld"); -$spreadsheet->getActiveSheet()->getRowDimension(8)->setRowHeight(-1); -$spreadsheet->getActiveSheet()->getStyle('A8')->getAlignment()->setWrapText(true); - -// Rename worksheet -$helper->log('Rename worksheet'); -$spreadsheet->getActiveSheet()->setTitle('Simple'); - -// Set active sheet index to the first sheet, so Excel opens this as the first sheet -$spreadsheet->setActiveSheetIndex(0); - -// Save Excel 2007 file -$helper->log('Write to Xlsx format'); - -// Use PCLZip rather than ZipArchive to create the Xlsx OfficeOpenXML file -\PhpOffice\PhpSpreadsheet\Settings::setZipClass(\PhpOffice\PhpSpreadsheet\Settings::PCLZIP); - -// Save -$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/01_Simple_download_pdf.php b/samples/01_Simple_download_pdf.php index 67111fb0..dc23daa0 100644 --- a/samples/01_Simple_download_pdf.php +++ b/samples/01_Simple_download_pdf.php @@ -53,6 +53,6 @@ header('Content-Type: application/pdf'); header('Content-Disposition: attachment;filename="01simple.pdf"'); header('Cache-Control: max-age=0'); -$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'PDF'); +$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Pdf'); $writer->save('php://output'); exit; diff --git a/samples/06_Largescale_with_cellcaching.php b/samples/06_Largescale_with_cellcaching.php deleted file mode 100644 index 30e8b9bd..00000000 --- a/samples/06_Largescale_with_cellcaching.php +++ /dev/null @@ -1,15 +0,0 @@ -log('Enable Cell Caching using ' . $cacheMethod . ' method'); -} else { - $helper->log('ERROR: Unable to set Cell Caching using ' . $cacheMethod . ' method, reverting to memory'); -} - -$spreadsheet = require __DIR__ . '/templates/largeSpreadsheet.php'; - -// Save -$helper->write($spreadsheet, __FILE__); diff --git a/samples/06_Largescale_with_cellcaching_sqlite3.php b/samples/06_Largescale_with_cellcaching_sqlite3.php deleted file mode 100644 index 3d781004..00000000 --- a/samples/06_Largescale_with_cellcaching_sqlite3.php +++ /dev/null @@ -1,15 +0,0 @@ -log('Enable Cell Caching using ' . $cacheMethod . ' method'); -} else { - $helper->log('ERROR: Unable to set Cell Caching using ' . $cacheMethod . ' method, reverting to memory'); -} - -$spreadsheet = require __DIR__ . '/templates/largeSpreadsheet.php'; - -// Save -$helper->write($spreadsheet, __FILE__); diff --git a/samples/07_Reader_PCLZip.php b/samples/07_Reader_PCLZip.php deleted file mode 100644 index e9ce8f05..00000000 --- a/samples/07_Reader_PCLZip.php +++ /dev/null @@ -1,20 +0,0 @@ -getTemporaryFilename(); -$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($sampleSpreadsheet); -$writer->save($filename); - -// Use PCLZip rather than ZipArchive to read the Xlsx OfficeOpenXML file -\PhpSpreadsheet\Settings::setZipClass(\PhpOffice\PhpSpreadsheet\Settings::PCLZIP); - -$callStartTime = microtime(true); -$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($filename); -$helper->logRead('Xlsx', $filename, $callStartTime); -$callEndTime = microtime(true); - -// Save -$helper->write($spreadsheet, __FILE__); diff --git a/samples/20_Read_Ods_with_PCLZip.php b/samples/20_Read_Ods_with_PCLZip.php deleted file mode 100644 index 152c91a7..00000000 --- a/samples/20_Read_Ods_with_PCLZip.php +++ /dev/null @@ -1,14 +0,0 @@ -logRead('Ods', $filename, $callStartTime); - -// Save -$helper->write($spreadsheet, __FILE__); diff --git a/samples/32_Chart_read_write_PDF.php b/samples/32_Chart_read_write_PDF.php index 8d895d43..d41f6728 100644 --- a/samples/32_Chart_read_write_PDF.php +++ b/samples/32_Chart_read_write_PDF.php @@ -91,7 +91,7 @@ foreach ($inputFileNames as $inputFileName) { // Save $filename = $helper->getFilename($inputFileName); - $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'PDF'); + $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Pdf'); $writer->setIncludeCharts(true); $callStartTime = microtime(true); $writer->save($filename); diff --git a/samples/45_Quadratic_equation_solver.php b/samples/45_Quadratic_equation_solver.php index 56bf4e7e..f5e11e47 100644 --- a/samples/45_Quadratic_equation_solver.php +++ b/samples/45_Quadratic_equation_solver.php @@ -1,7 +1,7 @@ -
+ Enter the coefficients for the Ax2 + Bx + C = 0 @@ -28,13 +28,13 @@ if (isset($_POST['submit'])) { echo '
Roots:
'; $discriminantFormula = '=POWER(' . $_POST['B'] . ',2) - (4 * ' . $_POST['A'] . ' * ' . $_POST['C'] . ')'; - $discriminant = PhpSpreadsheet_Calculation::getInstance()->calculateFormula($discriminantFormula); + $discriminant = \PhpOffice\PhpSpreadsheet\Calculation::getInstance()->calculateFormula($discriminantFormula); $r1Formula = '=IMDIV(IMSUM(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . ')'; $r2Formula = '=IF(' . $discriminant . '=0,"Only one root",IMDIV(IMSUB(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . '))'; - echo PhpSpreadsheet_Calculation::getInstance()->calculateFormula($r1Formula) . '
'; - echo PhpSpreadsheet_Calculation::getInstance()->calculateFormula($r2Formula) . '
'; + echo \PhpOffice\PhpSpreadsheet\Calculation::getInstance()->calculateFormula($r1Formula) . '
'; + echo \PhpOffice\PhpSpreadsheet\Calculation::getInstance()->calculateFormula($r2Formula) . '
'; $callEndTime = microtime(true); $helper->logEndingNotes(); } diff --git a/samples/index.php b/samples/index.php index 8347ea32..4b7dbe7a 100644 --- a/samples/index.php +++ b/samples/index.php @@ -7,7 +7,7 @@ $requirements = [ 'PHP extension XML' => extension_loaded('xml'), 'PHP extension xmlwriter' => extension_loaded('xmlwriter'), 'PHP extension mbstring' => extension_loaded('mbstring'), - 'PHP extension ZipArchive (optional)' => extension_loaded('zip'), + 'PHP extension ZipArchive' => extension_loaded('zip'), 'PHP extension GD (optional)' => extension_loaded('gd'), 'PHP extension dom (optional)' => extension_loaded('dom'), ]; diff --git a/src/PhpSpreadsheet/CachedObjectStorage/APC.php b/src/PhpSpreadsheet/CachedObjectStorage/APC.php deleted file mode 100644 index 12d6f345..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/APC.php +++ /dev/null @@ -1,287 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - if (!apc_store( - $this->cachePrefix . $this->currentObjectID . '.cache', - serialize($this->currentObject), - $this->cacheTime - )) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $this->currentObjectID . ' in APC'); - } - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - $this->cellCache[$pCoord] = true; - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return bool - */ - public function isDataSet($pCoord) - { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in apc - $success = apc_fetch($this->cachePrefix . $pCoord . '.cache'); - if ($success === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in APC cache'); - } - - return true; - } - - return false; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (parent::isDataSet($pCoord)) { - $obj = apc_fetch($this->cachePrefix . $pCoord . '.cache'); - if ($obj === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in APC cache'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - $this->currentObject = unserialize($obj); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Delete a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to delete - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function deleteCacheData($pCoord) - { - // Delete the entry from APC - apc_delete($this->cachePrefix . $pCoord . '.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::copyCellCollection($parent); - // Get a new id for the new file name - $baseUnique = $this->getUniqueID(); - $newCachePrefix = substr(md5($baseUnique), 0, 8) . '.'; - $cacheList = $this->getCellList(); - foreach ($cacheList as $cellID) { - if ($cellID != $this->currentObjectID) { - $obj = apc_fetch($this->cachePrefix . $cellID . '.cache'); - if ($obj === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($cellID); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $cellID . ' no longer exists in APC'); - } - if (!apc_store($newCachePrefix . $cellID . '.cache', $obj, $this->cacheTime)) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $cellID . ' in APC'); - } - } - } - $this->cachePrefix = $newCachePrefix; - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if ($this->currentObject !== null) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - - // Flush the APC cache - $this->__destruct(); - - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } - - /** - * Initialise this new cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection - * @param array $arguments Additional initialisation arguments - */ - public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments) - { - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if ($this->cachePrefix === null) { - $baseUnique = $this->getUniqueID(); - $this->cachePrefix = substr(md5($baseUnique), 0, 8) . '.'; - $this->cacheTime = $cacheTime; - - parent::__construct($parent); - } - } - - /** - * Destroy this cell collection. - */ - public function __destruct() - { - $cacheList = $this->getCellList(); - foreach ($cacheList as $cellID) { - apc_delete($this->cachePrefix . $cellID . '.cache'); - } - } - - /** - * Identify whether the caching method is currently available - * Some methods are dependent on the availability of certain extensions being enabled in the PHP build. - * - * @return bool - */ - public static function cacheMethodIsAvailable() - { - if (!function_exists('apc_store')) { - return false; - } - if (apc_sma_info() === false) { - return false; - } - - return true; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/CacheBase.php b/src/PhpSpreadsheet/CachedObjectStorage/CacheBase.php deleted file mode 100644 index db224696..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/CacheBase.php +++ /dev/null @@ -1,377 +0,0 @@ -parent = $parent; - } - - /** - * Return the parent worksheet for this cell collection. - * - * @return \PhpOffice\PhpSpreadsheet\Worksheet - */ - public function getParent() - { - return $this->parent; - } - - /** - * Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * - * @return bool - */ - public function isDataSet($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return true; - } - // Check if the requested entry exists in the cache - return isset($this->cellCache[$pCoord]); - } - - /** - * Move a cell object from one address to another. - * - * @param string $fromAddress Current address of the cell to move - * @param string $toAddress Destination address of the cell to move - * - * @return bool - */ - public function moveCell($fromAddress, $toAddress) - { - if ($fromAddress === $this->currentObjectID) { - $this->currentObjectID = $toAddress; - } - $this->currentCellIsDirty = true; - if (isset($this->cellCache[$fromAddress])) { - $this->cellCache[$toAddress] = &$this->cellCache[$fromAddress]; - unset($this->cellCache[$fromAddress]); - } - - return true; - } - - /** - * Add or Update a cell in cache. - * - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function updateCacheData(\PhpOffice\PhpSpreadsheet\Cell $cell) - { - return $this->addCacheData($cell->getCoordinate(), $cell); - } - - /** - * Delete a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to delete - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function deleteCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID && !is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObjectID = $this->currentObject = null; - } - - if (is_object($this->cellCache[$pCoord])) { - $this->cellCache[$pCoord]->detach(); - } - unset($this->cellCache[$pCoord]); - $this->currentCellIsDirty = false; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - return array_keys($this->cellCache); - } - - /** - * Sort the list of all cell addresses currently held in cache by row and column. - * - * @return string[] - */ - public function getSortedCellList() - { - $sortKeys = []; - foreach ($this->getCellList() as $coord) { - sscanf($coord, '%[A-Z]%d', $column, $row); - $sortKeys[sprintf('%09d%3s', $row, $column)] = $coord; - } - ksort($sortKeys); - - return array_values($sortKeys); - } - - /** - * Get highest worksheet column and highest row that have cell records. - * - * @return array Highest column name and highest row number - */ - public function getHighestRowAndColumn() - { - // Lookup highest column and highest row - $col = ['A' => '1A']; - $row = [1]; - foreach ($this->getCellList() as $coord) { - sscanf($coord, '%[A-Z]%d', $c, $r); - $row[$r] = $r; - $col[$c] = strlen($c) . $c; - } - if (!empty($row)) { - // Determine highest column and row - $highestRow = max($row); - $highestColumn = substr(max($col), 1); - } - - return [ - 'row' => $highestRow, - 'column' => $highestColumn, - ]; - } - - /** - * Return the cell address of the currently active cell object. - * - * @return string - */ - public function getCurrentAddress() - { - return $this->currentObjectID; - } - - /** - * Return the column address of the currently active cell object. - * - * @return string - */ - public function getCurrentColumn() - { - sscanf($this->currentObjectID, '%[A-Z]%d', $column, $row); - - return $column; - } - - /** - * Return the row address of the currently active cell object. - * - * @return int - */ - public function getCurrentRow() - { - sscanf($this->currentObjectID, '%[A-Z]%d', $column, $row); - - return (int) $row; - } - - /** - * Get highest worksheet column. - * - * @param string $row Return the highest column for the specified row, - * or the highest column of any row if no row number is passed - * - * @return string Highest column name - */ - public function getHighestColumn($row = null) - { - if ($row == null) { - $colRow = $this->getHighestRowAndColumn(); - - return $colRow['column']; - } - - $columnList = [1]; - foreach ($this->getCellList() as $coord) { - sscanf($coord, '%[A-Z]%d', $c, $r); - if ($r != $row) { - continue; - } - $columnList[] = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($c); - } - - return \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(max($columnList) - 1); - } - - /** - * Get highest worksheet row. - * - * @param string $column Return the highest row for the specified column, - * or the highest row of any column if no column letter is passed - * - * @return int Highest row number - */ - public function getHighestRow($column = null) - { - if ($column == null) { - $colRow = $this->getHighestRowAndColumn(); - - return $colRow['row']; - } - - $rowList = [0]; - foreach ($this->getCellList() as $coord) { - sscanf($coord, '%[A-Z]%d', $c, $r); - if ($c != $column) { - continue; - } - $rowList[] = $r; - } - - return max($rowList); - } - - /** - * Generate a unique ID for cache referencing. - * - * @return string Unique Reference - */ - protected function getUniqueID() - { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - - return uniqid($baseUnique, true); - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - $this->currentCellIsDirty; - $this->storeData(); - - $this->parent = $parent; - if (($this->currentObject !== null) && (is_object($this->currentObject))) { - $this->currentObject->attach($this); - } - } - - /** - * Remove a row, deleting all cells in that row. - * - * @param string $row Row number to remove - */ - public function removeRow($row) - { - foreach ($this->getCellList() as $coord) { - sscanf($coord, '%[A-Z]%d', $c, $r); - if ($r == $row) { - $this->deleteCacheData($coord); - } - } - } - - /** - * Remove a column, deleting all cells in that column. - * - * @param string $column Column ID to remove - */ - public function removeColumn($column) - { - foreach ($this->getCellList() as $coord) { - sscanf($coord, '%[A-Z]%d', $c, $r); - if ($c == $column) { - $this->deleteCacheData($coord); - } - } - } - - /** - * Identify whether the caching method is currently available - * Some methods are dependent on the availability of certain extensions being enabled in the PHP build. - * - * @return bool - */ - public static function cacheMethodIsAvailable() - { - return true; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/DiscISAM.php b/src/PhpSpreadsheet/CachedObjectStorage/DiscISAM.php deleted file mode 100644 index d630742b..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/DiscISAM.php +++ /dev/null @@ -1,209 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - fseek($this->fileHandle, 0, SEEK_END); - - $this->cellCache[$this->currentObjectID] = [ - 'ptr' => ftell($this->fileHandle), - 'sz' => fwrite($this->fileHandle, serialize($this->currentObject)), - ]; - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - fseek($this->fileHandle, $this->cellCache[$pCoord]['ptr']); - $this->currentObject = unserialize(fread($this->fileHandle, $this->cellCache[$pCoord]['sz'])); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::copyCellCollection($parent); - // Get a new id for the new file name - $baseUnique = $this->getUniqueID(); - $newFileName = $this->cacheDirectory . '/PhpSpreadsheet.' . $baseUnique . '.cache'; - // Copy the existing cell cache file - copy($this->fileName, $newFileName); - $this->fileName = $newFileName; - // Open the copied cell cache file - $this->fileHandle = fopen($this->fileName, 'a+'); - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - - // Close down the temporary cache file - $this->__destruct(); - } - - /** - * Initialise this new cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection - * @param array of mixed $arguments Additional initialisation arguments - */ - public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments) - { - $this->cacheDirectory = ((isset($arguments['dir'])) && ($arguments['dir'] !== null)) - ? $arguments['dir'] - : \PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir(); - - parent::__construct($parent); - if (is_null($this->fileHandle)) { - $baseUnique = $this->getUniqueID(); - $this->fileName = $this->cacheDirectory . '/PhpSpreadsheet.' . $baseUnique . '.cache'; - $this->fileHandle = fopen($this->fileName, 'a+'); - } - } - - /** - * Destroy this cell collection. - */ - public function __destruct() - { - if (!is_null($this->fileHandle)) { - fclose($this->fileHandle); - unlink($this->fileName); - } - $this->fileHandle = null; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/ICache.php b/src/PhpSpreadsheet/CachedObjectStorage/ICache.php deleted file mode 100644 index 9c2edefa..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/ICache.php +++ /dev/null @@ -1,109 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - $this->cellCache[$this->currentObjectID] = igbinary_serialize($this->currentObject); - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - // function _storeData() - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - $this->currentObject = igbinary_unserialize($this->cellCache[$pCoord]); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - // function getCacheData() - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } - - // function unsetWorksheetCells() - - /** - * Identify whether the caching method is currently available - * Some methods are dependent on the availability of certain extensions being enabled in the PHP build. - * - * @return bool - */ - public static function cacheMethodIsAvailable() - { - if (!function_exists('igbinary_serialize')) { - return false; - } - - return true; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/Memcache.php b/src/PhpSpreadsheet/CachedObjectStorage/Memcache.php deleted file mode 100644 index a760b38a..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/Memcache.php +++ /dev/null @@ -1,313 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - $obj = serialize($this->currentObject); - if (!$this->memcache->replace($this->cachePrefix . $this->currentObjectID . '.cache', $obj, null, $this->cacheTime)) { - if (!$this->memcache->add($this->cachePrefix . $this->currentObjectID . '.cache', $obj, null, $this->cacheTime)) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception("Failed to store cell {$this->currentObjectID} in MemCache"); - } - } - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - $this->cellCache[$pCoord] = true; - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return bool - */ - public function isDataSet($pCoord) - { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in Memcache - $success = $this->memcache->get($this->cachePrefix . $pCoord . '.cache'); - if ($success === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in MemCache'); - } - - return true; - } - - return false; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (parent::isDataSet($pCoord)) { - $obj = $this->memcache->get($this->cachePrefix . $pCoord . '.cache'); - if ($obj === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new \PhpOffice\PhpSpreadsheet\Exception("Cell entry {$pCoord} no longer exists in MemCache"); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - $this->currentObject = unserialize($obj); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Delete a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to delete - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function deleteCacheData($pCoord) - { - // Delete the entry from Memcache - $this->memcache->delete($this->cachePrefix . $pCoord . '.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::copyCellCollection($parent); - // Get a new id for the new file name - $baseUnique = $this->getUniqueID(); - $newCachePrefix = substr(md5($baseUnique), 0, 8) . '.'; - $cacheList = $this->getCellList(); - foreach ($cacheList as $cellID) { - if ($cellID != $this->currentObjectID) { - $obj = $this->memcache->get($this->cachePrefix . $cellID . '.cache'); - if ($obj === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($cellID); - throw new \PhpOffice\PhpSpreadsheet\Exception("Cell entry {$cellID} no longer exists in MemCache"); - } - if (!$this->memcache->add($newCachePrefix . $cellID . '.cache', $obj, null, $this->cacheTime)) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception("Failed to store cell {$cellID} in MemCache"); - } - } - } - $this->cachePrefix = $newCachePrefix; - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - - // Flush the Memcache cache - $this->__destruct(); - - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } - - /** - * Initialise this new cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection - * @param mixed[] $arguments Additional initialisation arguments - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments) - { - $memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost'; - $memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211; - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->cachePrefix)) { - $baseUnique = $this->getUniqueID(); - $this->cachePrefix = substr(md5($baseUnique), 0, 8) . '.'; - - // Set a new Memcache object and connect to the Memcache server - $this->memcache = new \Memcache(); - if (!$this->memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, [$this, 'failureCallback'])) { - throw new \PhpOffice\PhpSpreadsheet\Exception("Could not connect to MemCache server at {$memcacheServer}:{$memcachePort}"); - } - $this->cacheTime = $cacheTime; - - parent::__construct($parent); - } - } - - /** - * Memcache error handler. - * - * @param string $host Memcache server - * @param int $port Memcache port - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function failureCallback($host, $port) - { - throw new \PhpOffice\PhpSpreadsheet\Exception("memcache {$host}:{$port} failed"); - } - - /** - * Destroy this cell collection. - */ - public function __destruct() - { - $cacheList = $this->getCellList(); - foreach ($cacheList as $cellID) { - $this->memcache->delete($this->cachePrefix . $cellID . '.cache'); - } - } - - /** - * Identify whether the caching method is currently available - * Some methods are dependent on the availability of certain extensions being enabled in the PHP build. - * - * @return bool - */ - public static function cacheMethodIsAvailable() - { - if (!function_exists('memcache_add')) { - return false; - } - - return true; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/Memory.php b/src/PhpSpreadsheet/CachedObjectStorage/Memory.php deleted file mode 100644 index 57d0c496..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/Memory.php +++ /dev/null @@ -1,116 +0,0 @@ -cellCache[$pCoord] = $cell; - - // Set current entry to the new/updated entry - $this->currentObjectID = $pCoord; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - // Check if the entry that has been requested actually exists - if (!isset($this->cellCache[$pCoord])) { - $this->currentObjectID = null; - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - - // Return requested entry - return $this->cellCache[$pCoord]; - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::copyCellCollection($parent); - - $newCollection = []; - foreach ($this->cellCache as $k => &$cell) { - $newCollection[$k] = clone $cell; - $newCollection[$k]->attach($this); - } - - $this->cellCache = $newCollection; - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - // Because cells are all stored as intact objects in memory, we need to detach each one from the parent - foreach ($this->cellCache as $k => &$cell) { - $cell->detach(); - $this->cellCache[$k] = null; - } - unset($cell); - - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/MemoryGZip.php b/src/PhpSpreadsheet/CachedObjectStorage/MemoryGZip.php deleted file mode 100644 index 8483fbb6..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/MemoryGZip.php +++ /dev/null @@ -1,129 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - $this->cellCache[$this->currentObjectID] = gzdeflate(serialize($this->currentObject), 9); - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - $this->currentObject = unserialize(gzinflate($this->cellCache[$pCoord])); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/MemorySerialized.php b/src/PhpSpreadsheet/CachedObjectStorage/MemorySerialized.php deleted file mode 100644 index 2c3069c9..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/MemorySerialized.php +++ /dev/null @@ -1,129 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - $this->cellCache[$this->currentObjectID] = serialize($this->currentObject); - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - $this->currentObject = unserialize($this->cellCache[$pCoord]); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/PHPTemp.php b/src/PhpSpreadsheet/CachedObjectStorage/PHPTemp.php deleted file mode 100644 index e2497436..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/PHPTemp.php +++ /dev/null @@ -1,197 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - fseek($this->fileHandle, 0, SEEK_END); - - $this->cellCache[$this->currentObjectID] = [ - 'ptr' => ftell($this->fileHandle), - 'sz' => fwrite($this->fileHandle, serialize($this->currentObject)), - ]; - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - fseek($this->fileHandle, $this->cellCache[$pCoord]['ptr']); - $this->currentObject = unserialize(fread($this->fileHandle, $this->cellCache[$pCoord]['sz'])); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::copyCellCollection($parent); - // Open a new stream for the cell cache data - $newFileHandle = fopen('php://temp/maxmemory:' . $this->memoryCacheSize, 'a+'); - // Copy the existing cell cache data to the new stream - fseek($this->fileHandle, 0); - while (!feof($this->fileHandle)) { - fwrite($newFileHandle, fread($this->fileHandle, 1024)); - } - $this->fileHandle = $newFileHandle; - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - - // Close down the php://temp file - $this->__destruct(); - } - - /** - * Initialise this new cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection - * @param mixed[] $arguments Additional initialisation arguments - */ - public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments) - { - $this->memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : 1 * 1024 * 1024; - - parent::__construct($parent); - if (is_null($this->fileHandle)) { - $this->fileHandle = fopen('php://temp/maxmemory:' . $this->memoryCacheSize, 'a+'); - } - } - - /** - * Destroy this cell collection. - */ - public function __destruct() - { - if (!is_null($this->fileHandle)) { - fclose($this->fileHandle); - } - $this->fileHandle = null; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/SQLite3.php b/src/PhpSpreadsheet/CachedObjectStorage/SQLite3.php deleted file mode 100644 index 3f78e911..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/SQLite3.php +++ /dev/null @@ -1,359 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - $this->insertQuery->bindValue('id', $this->currentObjectID, SQLITE3_TEXT); - $this->insertQuery->bindValue('data', serialize($this->currentObject), SQLITE3_BLOB); - $result = $this->insertQuery->execute(); - if ($result === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - $this->currentCellIsDirty = false; - } - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - $this->selectQuery->bindValue('id', $pCoord, SQLITE3_TEXT); - $cellResult = $this->selectQuery->execute(); - if ($cellResult === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - $cellData = $cellResult->fetchArray(SQLITE3_ASSOC); - if ($cellData === false) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - - $this->currentObject = unserialize($cellData['value']); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Is a value set for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return bool - */ - public function isDataSet($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return true; - } - - // Check if the requested entry exists in the cache - $this->selectQuery->bindValue('id', $pCoord, SQLITE3_TEXT); - $cellResult = $this->selectQuery->execute(); - if ($cellResult === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - $cellData = $cellResult->fetchArray(SQLITE3_ASSOC); - - return ($cellData === false) ? false : true; - } - - /** - * Delete a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to delete - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function deleteCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - $this->currentObject->detach(); - $this->currentObjectID = $this->currentObject = null; - } - - // Check if the requested entry exists in the cache - $this->deleteQuery->bindValue('id', $pCoord, SQLITE3_TEXT); - $result = $this->deleteQuery->execute(); - if ($result === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - - $this->currentCellIsDirty = false; - } - - /** - * Move a cell object from one address to another. - * - * @param string $fromAddress Current address of the cell to move - * @param string $toAddress Destination address of the cell to move - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return bool - */ - public function moveCell($fromAddress, $toAddress) - { - if ($fromAddress === $this->currentObjectID) { - $this->currentObjectID = $toAddress; - } - - $this->deleteQuery->bindValue('id', $toAddress, SQLITE3_TEXT); - $result = $this->deleteQuery->execute(); - if ($result === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - - $this->updateQuery->bindValue('toid', $toAddress, SQLITE3_TEXT); - $this->updateQuery->bindValue('fromid', $fromAddress, SQLITE3_TEXT); - $result = $this->updateQuery->execute(); - if ($result === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - - return true; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - $query = 'SELECT id FROM kvp_' . $this->TableName; - $cellIdsResult = $this->DBHandle->query($query); - if ($cellIdsResult === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - - $cellKeys = []; - while ($row = $cellIdsResult->fetchArray(SQLITE3_ASSOC)) { - $cellKeys[] = $row['id']; - } - - return $cellKeys; - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - $this->currentCellIsDirty; - $this->storeData(); - - // Get a new id for the new table name - $tableName = str_replace('.', '_', $this->getUniqueID()); - if (!$this->DBHandle->exec('CREATE TABLE kvp_' . $tableName . ' (id VARCHAR(12) PRIMARY KEY, value BLOB) - AS SELECT * FROM kvp_' . $this->TableName) - ) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - - // Copy the existing cell cache file - $this->TableName = $tableName; - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - - // Close down the temporary cache file - $this->__destruct(); - } - - /** - * Initialise this new cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::__construct($parent); - if (is_null($this->DBHandle)) { - $this->TableName = str_replace('.', '_', $this->getUniqueID()); - $_DBName = ':memory:'; - - $this->DBHandle = new \SQLite3($_DBName); - if ($this->DBHandle === false) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - if (!$this->DBHandle->exec('CREATE TABLE kvp_' . $this->TableName . ' (id VARCHAR(12) PRIMARY KEY, value BLOB)')) { - throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg()); - } - } - - $this->selectQuery = $this->DBHandle->prepare('SELECT value FROM kvp_' . $this->TableName . ' WHERE id = :id'); - $this->insertQuery = $this->DBHandle->prepare('INSERT OR REPLACE INTO kvp_' . $this->TableName . ' VALUES(:id,:data)'); - $this->updateQuery = $this->DBHandle->prepare('UPDATE kvp_' . $this->TableName . ' SET id=:toId WHERE id=:fromId'); - $this->deleteQuery = $this->DBHandle->prepare('DELETE FROM kvp_' . $this->TableName . ' WHERE id = :id'); - } - - /** - * Destroy this cell collection. - */ - public function __destruct() - { - if (!is_null($this->DBHandle)) { - $this->DBHandle->exec('DROP TABLE kvp_' . $this->TableName); - $this->DBHandle->close(); - } - $this->DBHandle = null; - } - - /** - * Identify whether the caching method is currently available - * Some methods are dependent on the availability of certain extensions being enabled in the PHP build. - * - * @return bool - */ - public static function cacheMethodIsAvailable() - { - if (!class_exists('SQLite3', false)) { - return false; - } - - return true; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorage/Wincache.php b/src/PhpSpreadsheet/CachedObjectStorage/Wincache.php deleted file mode 100644 index 8dc656f7..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorage/Wincache.php +++ /dev/null @@ -1,292 +0,0 @@ -currentCellIsDirty && !empty($this->currentObjectID)) { - $this->currentObject->detach(); - - $obj = serialize($this->currentObject); - if (wincache_ucache_exists($this->cachePrefix . $this->currentObjectID . '.cache')) { - if (!wincache_ucache_set($this->cachePrefix . $this->currentObjectID . '.cache', $obj, $this->cacheTime)) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $this->currentObjectID . ' in WinCache'); - } - } else { - if (!wincache_ucache_add($this->cachePrefix . $this->currentObjectID . '.cache', $obj, $this->cacheTime)) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $this->currentObjectID . ' in WinCache'); - } - } - $this->currentCellIsDirty = false; - } - - $this->currentObjectID = $this->currentObject = null; - } - - /** - * Add or Update a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to update - * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell - */ - public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell) - { - if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { - $this->storeData(); - } - $this->cellCache[$pCoord] = true; - - $this->currentObjectID = $pCoord; - $this->currentObject = $cell; - $this->currentCellIsDirty = true; - - return $cell; - } - - /** - * Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return bool - */ - public function isDataSet($pCoord) - { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in cache - $success = wincache_ucache_exists($this->cachePrefix . $pCoord . '.cache'); - if ($success === false) { - // Entry no longer exists in Wincache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in WinCache'); - } - - return true; - } - - return false; - } - - /** - * Get cell at a specific coordinate. - * - * @param string $pCoord Coordinate of the cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) - { - if ($pCoord === $this->currentObjectID) { - return $this->currentObject; - } - $this->storeData(); - - // Check if the entry that has been requested actually exists - $obj = null; - if (parent::isDataSet($pCoord)) { - $success = false; - $obj = wincache_ucache_get($this->cachePrefix . $pCoord . '.cache', $success); - if ($success === false) { - // Entry no longer exists in WinCache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in WinCache'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->currentObjectID = $pCoord; - $this->currentObject = unserialize($obj); - // Re-attach this as the cell's parent - $this->currentObject->attach($this); - - // Return requested entry - return $this->currentObject; - } - - /** - * Get a list of all cell addresses currently held in cache. - * - * @return string[] - */ - public function getCellList() - { - if ($this->currentObjectID !== null) { - $this->storeData(); - } - - return parent::getCellList(); - } - - /** - * Delete a cell in cache identified by coordinate address. - * - * @param string $pCoord Coordinate address of the cell to delete - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function deleteCacheData($pCoord) - { - // Delete the entry from Wincache - wincache_ucache_delete($this->cachePrefix . $pCoord . '.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } - - /** - * Clone the cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - */ - public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent) - { - parent::copyCellCollection($parent); - // Get a new id for the new file name - $baseUnique = $this->getUniqueID(); - $newCachePrefix = substr(md5($baseUnique), 0, 8) . '.'; - $cacheList = $this->getCellList(); - foreach ($cacheList as $cellID) { - if ($cellID != $this->currentObjectID) { - $success = false; - $obj = wincache_ucache_get($this->cachePrefix . $cellID . '.cache', $success); - if ($success === false) { - // Entry no longer exists in WinCache, so clear it from the cache array - parent::deleteCacheData($cellID); - throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $cellID . ' no longer exists in Wincache'); - } - if (!wincache_ucache_add($newCachePrefix . $cellID . '.cache', $obj, $this->cacheTime)) { - $this->__destruct(); - throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $cellID . ' in Wincache'); - } - } - } - $this->cachePrefix = $newCachePrefix; - } - - /** - * Clear the cell collection and disconnect from our parent. - */ - public function unsetWorksheetCells() - { - if (!is_null($this->currentObject)) { - $this->currentObject->detach(); - $this->currentObject = $this->currentObjectID = null; - } - - // Flush the WinCache cache - $this->__destruct(); - - $this->cellCache = []; - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->parent = null; - } - - /** - * Initialise this new cell collection. - * - * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection - * @param mixed[] $arguments Additional initialisation arguments - */ - public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments) - { - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->cachePrefix)) { - $baseUnique = $this->getUniqueID(); - $this->cachePrefix = substr(md5($baseUnique), 0, 8) . '.'; - $this->cacheTime = $cacheTime; - - parent::__construct($parent); - } - } - - /** - * Destroy this cell collection. - */ - public function __destruct() - { - $cacheList = $this->getCellList(); - foreach ($cacheList as $cellID) { - wincache_ucache_delete($this->cachePrefix . $cellID . '.cache'); - } - } - - /** - * Identify whether the caching method is currently available - * Some methods are dependent on the availability of certain extensions being enabled in the PHP build. - * - * @return bool - */ - public static function cacheMethodIsAvailable() - { - if (!function_exists('wincache_ucache_add')) { - return false; - } - - return true; - } -} diff --git a/src/PhpSpreadsheet/CachedObjectStorageFactory.php b/src/PhpSpreadsheet/CachedObjectStorageFactory.php deleted file mode 100644 index 6b81eaf5..00000000 --- a/src/PhpSpreadsheet/CachedObjectStorageFactory.php +++ /dev/null @@ -1,228 +0,0 @@ - [], - self::CACHE_IN_MEMORY_GZIP => [], - self::CACHE_IN_MEMORY_SERIALIZED => [], - self::CACHE_IGBINARY => [], - self::CACHE_TO_PHPTEMP => [ - 'memoryCacheSize' => '1MB', - ], - self::CACHE_TO_DISCISAM => [ - 'dir' => null, - ], - self::CACHE_TO_APC => [ - 'cacheTime' => 600, - ], - self::CACHE_TO_MEMCACHE => [ - 'memcacheServer' => 'localhost', - 'memcachePort' => 11211, - 'cacheTime' => 600, - ], - self::CACHE_TO_WINCACHE => [ - 'cacheTime' => 600, - ], - self::CACHE_TO_SQLITE3 => [], - ]; - - /** - * Arguments for the active cache storage method. - * - * @var mixed[] - */ - private static $storageMethodParameters = []; - - /** - * Return the current cache storage method. - * - * @return string|null - **/ - public static function getCacheStorageMethod() - { - return self::$cacheStorageMethod; - } - - /** - * Return the current cache storage class. - * - * @return string - **/ - public static function getCacheStorageClass() - { - return self::$cacheStorageClass; - } - - /** - * Return the list of all possible cache storage methods. - * - * @return string[] - **/ - public static function getAllCacheStorageMethods() - { - return self::$storageMethods; - } - - /** - * Return the list of all available cache storage methods. - * - * @return string[] - **/ - public static function getCacheStorageMethods() - { - $activeMethods = []; - foreach (self::$storageMethods as $storageMethod) { - $cacheStorageClass = '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\' . $storageMethod; - if (call_user_func([$cacheStorageClass, 'cacheMethodIsAvailable'])) { - $activeMethods[] = $storageMethod; - } - } - - return $activeMethods; - } - - /** - * Identify the cache storage method to use. - * - * @param string $method Name of the method to use for cell cacheing - * @param mixed[] $arguments Additional arguments to pass to the cell caching class - * when instantiating - * - * @return bool - **/ - public static function initialize($method = self::CACHE_IN_MEMORY, $arguments = []) - { - if (!in_array($method, self::$storageMethods)) { - return false; - } - - $cacheStorageClass = '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\' . $method; - if (!call_user_func([$cacheStorageClass, 'cacheMethodIsAvailable'])) { - return false; - } - - self::$storageMethodParameters[$method] = self::$storageMethodDefaultParameters[$method]; - foreach ($arguments as $argument => $value) { - if (isset(self::$storageMethodParameters[$method][$argument])) { - self::$storageMethodParameters[$method][$argument] = $value; - } - } - - if (self::$cacheStorageMethod === null) { - self::$cacheStorageClass = '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\' . $method; - self::$cacheStorageMethod = $method; - } - - return true; - } - - /** - * Initialise the cache storage. - * - * @param Worksheet $parent Enable cell caching for this worksheet - * - * @return CachedObjectStorage\ICache - **/ - public static function getInstance(Worksheet $parent) - { - $cacheMethodIsAvailable = true; - if (self::$cacheStorageMethod === null) { - $cacheMethodIsAvailable = self::initialize(); - } - - if ($cacheMethodIsAvailable) { - $instance = new self::$cacheStorageClass( - $parent, - self::$storageMethodParameters[self::$cacheStorageMethod] - ); - if ($instance !== null) { - return $instance; - } - } - - return false; - } - - /** - * Clear the cache storage. - **/ - public static function finalize() - { - self::$cacheStorageMethod = null; - self::$cacheStorageClass = null; - self::$storageMethodParameters = []; - } -} diff --git a/src/PhpSpreadsheet/CalcEngine/Logger.php b/src/PhpSpreadsheet/CalcEngine/Logger.php index 9bc97179..ba4b2971 100644 --- a/src/PhpSpreadsheet/CalcEngine/Logger.php +++ b/src/PhpSpreadsheet/CalcEngine/Logger.php @@ -74,7 +74,7 @@ class Logger * * @param bool $pValue */ - public function setWriteDebugLog($pValue = false) + public function setWriteDebugLog($pValue) { $this->writeDebugLog = $pValue; } @@ -94,7 +94,7 @@ class Logger * * @param bool $pValue */ - public function setEchoDebugLog($pValue = false) + public function setEchoDebugLog($pValue) { $this->echoDebugLog = $pValue; } diff --git a/src/PhpSpreadsheet/Calculation.php b/src/PhpSpreadsheet/Calculation.php index 9383c37c..d3554ceb 100644 --- a/src/PhpSpreadsheet/Calculation.php +++ b/src/PhpSpreadsheet/Calculation.php @@ -2177,7 +2177,7 @@ class Calculation * * @param bool $pValue */ - public function setCalculationCacheEnabled($pValue = true) + public function setCalculationCacheEnabled($pValue) { $this->calculationCacheEnabled = $pValue; $this->clearCalculationCache(); @@ -2246,11 +2246,11 @@ class Calculation /** * Set the locale code. * - * @param string $locale The locale to use for formula translation + * @param string $locale The locale to use for formula translation, eg: 'en_us' * * @return bool */ - public function setLocale($locale = 'en_us') + public function setLocale($locale) { // Identify our locale and language $language = $locale = strtolower($locale); @@ -3664,7 +3664,7 @@ class Calculation $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue)); } else { $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet'); - if ($pCellParent->isDataSet($cellRef)) { + if ($pCellParent->has($cellRef)) { $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); $pCell->attach($pCellParent); } else { @@ -3765,6 +3765,11 @@ class Calculation } elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) { $namedRange = $matches[6]; $this->debugLog->writeDebugLog('Evaluating Named Range ', $namedRange); + + if (substr($namedRange, 0, 6) === '_xlfn.') { + return $this->raiseFormulaError("undefined named range / function '$token'"); + } + $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false); $pCell->attach($pCellParent); $this->debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue)); diff --git a/src/PhpSpreadsheet/Calculation/FormulaToken.php b/src/PhpSpreadsheet/Calculation/FormulaToken.php index ab46f5b1..ff3c8585 100644 --- a/src/PhpSpreadsheet/Calculation/FormulaToken.php +++ b/src/PhpSpreadsheet/Calculation/FormulaToken.php @@ -142,11 +142,11 @@ class FormulaToken } /** - * Set Token Type. + * Set Token Type (represented by TOKEN_TYPE_*). * * @param string $value */ - public function setTokenType($value = self::TOKEN_TYPE_UNKNOWN) + public function setTokenType($value) { $this->tokenType = $value; } @@ -162,11 +162,11 @@ class FormulaToken } /** - * Set Token SubType. + * Set Token SubType (represented by TOKEN_SUBTYPE_*). * * @param string $value */ - public function setTokenSubType($value = self::TOKEN_SUBTYPE_NOTHING) + public function setTokenSubType($value) { $this->tokenSubType = $value; } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef.php b/src/PhpSpreadsheet/Calculation/LookupRef.php index 7373f832..8359b0ba 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef.php @@ -497,93 +497,107 @@ class LookupRef * Excel Function: * =MATCH(lookup_value, lookup_array, [match_type]) * - * @param lookup_value The value that you want to match in lookup_array - * @param lookup_array The range of cells being searched - * @param match_type The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered. - * @param mixed $lookup_value - * @param mixed $lookup_array - * @param mixed $match_type + * @param mixed $lookupValue The value that you want to match in lookup_array + * @param mixed $lookupArray The range of cells being searched + * @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered. * * @return int The relative position of the found item */ - public static function MATCH($lookup_value, $lookup_array, $match_type = 1) + public static function MATCH($lookupValue, $lookupArray, $matchType = 1) { - $lookup_array = Functions::flattenArray($lookup_array); - $lookup_value = Functions::flattenSingleValue($lookup_value); - $match_type = (is_null($match_type)) ? 1 : (int) Functions::flattenSingleValue($match_type); - // MATCH is not case sensitive - $lookup_value = strtolower($lookup_value); + $lookupArray = Functions::flattenArray($lookupArray); + $lookupValue = Functions::flattenSingleValue($lookupValue); + $matchType = (is_null($matchType)) ? 1 : (int) Functions::flattenSingleValue($matchType); - // lookup_value type has to be number, text, or logical values - if ((!is_numeric($lookup_value)) && (!is_string($lookup_value)) && (!is_bool($lookup_value))) { + // MATCH is not case sensitive + $lookupValue = strtolower($lookupValue); + + // Lookup_value type has to be number, text, or logical values + if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) { return Functions::NA(); } - // match_type is 0, 1 or -1 - if (($match_type !== 0) && ($match_type !== -1) && ($match_type !== 1)) { + // Match_type is 0, 1 or -1 + if (($matchType !== 0) && ($matchType !== -1) && ($matchType !== 1)) { return Functions::NA(); } - // lookup_array should not be empty - $lookupArraySize = count($lookup_array); + // Lookup_array should not be empty + $lookupArraySize = count($lookupArray); if ($lookupArraySize <= 0) { return Functions::NA(); } - // lookup_array should contain only number, text, or logical values, or empty (null) cells - foreach ($lookup_array as $i => $lookupArrayValue) { + // Lookup_array should contain only number, text, or logical values, or empty (null) cells + foreach ($lookupArray as $i => $lookupArrayValue) { // check the type of the value if ((!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) && - (!is_bool($lookupArrayValue)) && (!is_null($lookupArrayValue))) { + (!is_bool($lookupArrayValue)) && (!is_null($lookupArrayValue)) + ) { return Functions::NA(); } - // convert strings to lowercase for case-insensitive testing + // Convert strings to lowercase for case-insensitive testing if (is_string($lookupArrayValue)) { - $lookup_array[$i] = strtolower($lookupArrayValue); + $lookupArray[$i] = strtolower($lookupArrayValue); } - if ((is_null($lookupArrayValue)) && (($match_type == 1) || ($match_type == -1))) { - $lookup_array = array_slice($lookup_array, 0, $i - 1); + if ((is_null($lookupArrayValue)) && (($matchType == 1) || ($matchType == -1))) { + $lookupArray = array_slice($lookupArray, 0, $i - 1); } } - // if match_type is 1 or -1, the list has to be ordered - if ($match_type == 1) { - asort($lookup_array); - $keySet = array_keys($lookup_array); - } elseif ($match_type == -1) { - arsort($lookup_array); - $keySet = array_keys($lookup_array); + if ($matchType == 1) { + // If match_type is 1 the list has to be processed from last to first + + $lookupArray = array_reverse($lookupArray); + $keySet = array_reverse(array_keys($lookupArray)); } // ** // find the match // ** - foreach ($lookup_array as $i => $lookupArrayValue) { - if (($match_type == 0) && ($lookupArrayValue == $lookup_value)) { - // exact match - return ++$i; - } elseif (($match_type == -1) && ($lookupArrayValue <= $lookup_value)) { - $i = array_search($i, $keySet); - // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value - if ($i < 1) { - // 1st cell was already smaller than the lookup_value - break; + + if ($matchType == 0 || $matchType == 1) { + foreach ($lookupArray as $i => $lookupArrayValue) { + if (($matchType == 0) && ($lookupArrayValue == $lookupValue)) { + // exact match + return ++$i; + } elseif (($matchType == 1) && ($lookupArrayValue <= $lookupValue)) { + $i = array_search($i, $keySet); + + // The current value is the (first) match + return $i + 1; } - // the previous cell was the match - return $keySet[$i - 1] + 1; - } elseif (($match_type == 1) && ($lookupArrayValue >= $lookup_value)) { - $i = array_search($i, $keySet); - // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value - if ($i < 1) { - // 1st cell was already bigger than the lookup_value - break; + } + } else { + // matchType = -1 + + // "Special" case: since the array it's supposed to be ordered in descending order, the + // Excel algorithm gives up immediately if the first element is smaller than the searched value + if ($lookupArray[0] < $lookupValue) { + return Functions::NA(); + } + + $maxValueKey = null; + + // The basic algorithm is: + // Iterate and keep the highest match until the next element is smaller than the searched value. + // Return immediately if perfect match is found + foreach ($lookupArray as $i => $lookupArrayValue) { + if ($lookupArrayValue == $lookupValue) { + // Another "special" case. If a perfect match is found, + // the algorithm gives up immediately + return $i + 1; + } elseif ($lookupArrayValue >= $lookupValue) { + $maxValueKey = $i + 1; } - // the previous cell was the match - return $keySet[$i - 1] + 1; + } + + if ($maxValueKey !== null) { + return $maxValueKey; } } - // unsuccessful in finding a match, return #N/A error value + // Unsuccessful in finding a match, return #N/A error value return Functions::NA(); } diff --git a/src/PhpSpreadsheet/Calculation/TextData.php b/src/PhpSpreadsheet/Calculation/TextData.php index abf05d95..fd9cd7a9 100644 --- a/src/PhpSpreadsheet/Calculation/TextData.php +++ b/src/PhpSpreadsheet/Calculation/TextData.php @@ -493,7 +493,7 @@ class TextData $instance = floor(Functions::flattenSingleValue($instance)); if ($instance == 0) { - return \PhpOffice\PhpSpreadsheet\Shared\StringHelper::mbStrReplace($fromText, $toText, $text); + return str_replace($fromText, $toText, $text); } $pos = -1; diff --git a/src/PhpSpreadsheet/Cell.php b/src/PhpSpreadsheet/Cell.php index 889739a3..0263853d 100644 --- a/src/PhpSpreadsheet/Cell.php +++ b/src/PhpSpreadsheet/Cell.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet; +use PhpOffice\PhpSpreadsheet\Collection\Cells; + /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. * @@ -67,9 +69,9 @@ class Cell private $dataType; /** - * Parent worksheet. + * Collection of cells. * - * @var CachedObjectStorage\CacheBase + * @var Cells */ private $parent; @@ -86,11 +88,13 @@ class Cell private $formulaAttributes; /** - * Send notification to the cache controller. - **/ - public function notifyCacheController() + * Update the cell into the cell collection. + * + * @return self + */ + public function updateInCollection() { - $this->parent->updateCacheData($this); + $this->parent->update($this); return $this; } @@ -100,7 +104,7 @@ class Cell $this->parent = null; } - public function attach(CachedObjectStorage\CacheBase $parent) + public function attach(Cells $parent) { $this->parent = $parent; } @@ -114,13 +118,13 @@ class Cell * * @throws Exception */ - public function __construct($pValue = null, $pDataType = null, Worksheet $pSheet = null) + public function __construct($pValue, $pDataType, Worksheet $pSheet) { // Initialise cell value $this->value = $pValue; // Set worksheet cache - $this->parent = $pSheet->getCellCacheController(); + $this->parent = $pSheet->getCellCollection(); // Set datatype? if ($pDataType !== null) { @@ -160,7 +164,7 @@ class Cell */ public function getCoordinate() { - return $this->parent->getCurrentAddress(); + return $this->parent->getCurrentCoordinate(); } /** @@ -198,7 +202,7 @@ class Cell * * @return Cell */ - public function setValue($pValue = null) + public function setValue($pValue) { if (!self::getValueBinder()->bindValue($this, $pValue)) { throw new Exception('Value could not be bound to cell.'); @@ -211,13 +215,13 @@ class Cell * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder). * * @param mixed $pValue Value - * @param string $pDataType Explicit data type + * @param string $pDataType Explicit data type, see Cell\DataType::TYPE_* * * @throws Exception * * @return Cell */ - public function setValueExplicit($pValue = null, $pDataType = Cell\DataType::TYPE_STRING) + public function setValueExplicit($pValue, $pDataType) { // set the value according to data type switch ($pDataType) { @@ -253,14 +257,12 @@ class Cell // set the datatype $this->dataType = $pDataType; - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** * Get calculated cell value. * - * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling - * * @param bool $resetLog Whether the calculation engine logger should be reset or not * * @throws Exception @@ -309,13 +311,13 @@ class Cell * * @return Cell */ - public function setCalculatedValue($pValue = null) + public function setCalculatedValue($pValue) { if ($pValue !== null) { $this->calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue; } - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** @@ -346,18 +348,18 @@ class Cell /** * Set cell data type. * - * @param string $pDataType + * @param string $pDataType see Cell\DataType::TYPE_* * * @return Cell */ - public function setDataType($pDataType = Cell\DataType::TYPE_STRING) + public function setDataType($pDataType) { if ($pDataType == Cell\DataType::TYPE_STRING2) { $pDataType = Cell\DataType::TYPE_STRING; } $this->dataType = $pDataType; - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** @@ -419,7 +421,7 @@ class Cell $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation); - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** @@ -471,13 +473,13 @@ class Cell $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink); - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** - * Get parent worksheet. + * Get cell collection. * - * @return CachedObjectStorage\CacheBase + * @return Cells */ public function getParent() { @@ -557,9 +559,9 @@ class Cell */ public function rebindParent(Worksheet $parent) { - $this->parent = $parent->getCellCacheController(); + $this->parent = $parent->getCellCollection(); - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** @@ -569,7 +571,7 @@ class Cell * * @return bool */ - public function isInRange($pRange = 'A1:A1') + public function isInRange($pRange) { list($rangeStart, $rangeEnd) = self::rangeBoundaries($pRange); @@ -585,13 +587,13 @@ class Cell /** * Coordinate from string. * - * @param string $pCoordinateString + * @param string $pCoordinateString eg: 'A1' * * @throws Exception * * @return string[] Array containing column and row (indexes 0 and 1) */ - public static function coordinateFromString($pCoordinateString = 'A1') + public static function coordinateFromString($pCoordinateString) { if (preg_match("/^([$]?[A-Z]{1,3})([$]?\d{1,7})$/", $pCoordinateString, $matches)) { return [$matches[1], $matches[2]]; @@ -614,7 +616,7 @@ class Cell * * @return string Absolute coordinate e.g. '$A' or '$1' or '$A$1' */ - public static function absoluteReference($pCoordinateString = 'A1') + public static function absoluteReference($pCoordinateString) { if (strpos($pCoordinateString, ':') === false && strpos($pCoordinateString, ',') === false) { // Split out any worksheet name from the reference @@ -649,7 +651,7 @@ class Cell * * @return string Absolute coordinate e.g. '$A$1' */ - public static function absoluteCoordinate($pCoordinateString = 'A1') + public static function absoluteCoordinate($pCoordinateString) { if (strpos($pCoordinateString, ':') === false && strpos($pCoordinateString, ',') === false) { // Split out any worksheet name from the coordinate @@ -682,7 +684,7 @@ class Cell * e.g. array('B4','D9') or array(array('B4','D9'),array('H2','O11')) * or array('B4') */ - public static function splitRange($pRange = 'A1:A1') + public static function splitRange($pRange) { // Ensure $pRange is a valid range if (empty($pRange)) { @@ -707,10 +709,10 @@ class Cell * * @return string String representation of $pRange */ - public static function buildRange($pRange) + public static function buildRange(array $pRange) { // Verify range - if (!is_array($pRange) || empty($pRange) || !is_array($pRange[0])) { + if (empty($pRange) || !is_array($pRange[0])) { throw new Exception('Range does not contain any information'); } @@ -733,7 +735,7 @@ class Cell * @return array Range coordinates array(Start Cell, End Cell) * where Start Cell and End Cell are arrays (Column Number, Row Number) */ - public static function rangeBoundaries($pRange = 'A1:A1') + public static function rangeBoundaries($pRange) { // Ensure $pRange is a valid range if (empty($pRange)) { @@ -768,7 +770,7 @@ class Cell * * @return array Range dimension (width, height) */ - public static function rangeDimension($pRange = 'A1:A1') + public static function rangeDimension($pRange) { // Calculate range outer borders list($rangeStart, $rangeEnd) = self::rangeBoundaries($pRange); @@ -784,7 +786,7 @@ class Cell * @return array Range coordinates array(Start Cell, End Cell) * where Start Cell and End Cell are arrays (Column ID, Row Number) */ - public static function getRangeBoundaries($pRange = 'A1:A1') + public static function getRangeBoundaries($pRange) { // Ensure $pRange is a valid range if (empty($pRange)) { @@ -807,11 +809,11 @@ class Cell /** * Column index from string. * - * @param string $pString + * @param string $pString eg 'A' * * @return int Column index (base 1 !!!) */ - public static function columnIndexFromString($pString = 'A') + public static function columnIndexFromString($pString) { // Using a lookup cache adds a slight memory overhead, but boosts speed // caching using a static within the method is faster than a class static, @@ -854,11 +856,11 @@ class Cell /** * String from columnindex. * - * @param int $pColumnIndex Column index (base 0 !!!) + * @param int $pColumnIndex Column index (A = 0) * * @return string */ - public static function stringFromColumnIndex($pColumnIndex = 0) + public static function stringFromColumnIndex($pColumnIndex) { // Using a lookup cache adds a slight memory overhead, but boosts speed // caching using a static within the method is faster than a class static, @@ -889,7 +891,7 @@ class Cell * * @return array Array containing single cell references */ - public static function extractAllCellReferencesInRange($pRange = 'A1') + public static function extractAllCellReferencesInRange($pRange) { // Returnvalue $returnValue = []; @@ -988,12 +990,8 @@ class Cell * * @throws Exception */ - public static function setValueBinder(Cell\IValueBinder $binder = null) + public static function setValueBinder(Cell\IValueBinder $binder) { - if ($binder === null) { - throw new Exception('A \\PhpOffice\\PhpSpreadsheet\\Cell\\IValueBinder is required for PhpSpreadsheet to function correctly.'); - } - self::$valueBinder = $binder; } @@ -1029,15 +1027,15 @@ class Cell * * @return Cell */ - public function setXfIndex($pValue = 0) + public function setXfIndex($pValue) { $this->xfIndex = $pValue; - return $this->notifyCacheController(); + return $this->updateInCollection(); } /** - * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling + * Set the formula attributes. * * @param mixed $pAttributes */ @@ -1049,7 +1047,7 @@ class Cell } /** - * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling + * Get the formula attributes. */ public function getFormulaAttributes() { diff --git a/src/PhpSpreadsheet/Cell/DataType.php b/src/PhpSpreadsheet/Cell/DataType.php index ee2fa6b6..2f41b66c 100644 --- a/src/PhpSpreadsheet/Cell/DataType.php +++ b/src/PhpSpreadsheet/Cell/DataType.php @@ -69,7 +69,7 @@ class DataType * * @return mixed Sanitized value */ - public static function checkString($pValue = null) + public static function checkString($pValue) { if ($pValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { // TODO: Sanitize Rich-Text string (max. character count is 32,767) @@ -93,7 +93,7 @@ class DataType * * @return string Sanitized value */ - public static function checkErrorCode($pValue = null) + public static function checkErrorCode($pValue) { $pValue = (string) $pValue; diff --git a/src/PhpSpreadsheet/Cell/DataValidation.php b/src/PhpSpreadsheet/Cell/DataValidation.php index c7e7681d..ae138bde 100644 --- a/src/PhpSpreadsheet/Cell/DataValidation.php +++ b/src/PhpSpreadsheet/Cell/DataValidation.php @@ -84,7 +84,7 @@ class DataValidation * * @var string */ - private $operator = ''; + private $operator = self::OPERATOR_BETWEEN; /** * Allow Blank. @@ -166,7 +166,7 @@ class DataValidation * * @return DataValidation */ - public function setFormula1($value = '') + public function setFormula1($value) { $this->formula1 = $value; @@ -190,7 +190,7 @@ class DataValidation * * @return DataValidation */ - public function setFormula2($value = '') + public function setFormula2($value) { $this->formula2 = $value; @@ -214,7 +214,7 @@ class DataValidation * * @return DataValidation */ - public function setType($value = self::TYPE_NONE) + public function setType($value) { $this->type = $value; @@ -234,11 +234,11 @@ class DataValidation /** * Set Error style. * - * @param string $value + * @param string $value see self::STYLE_* * * @return DataValidation */ - public function setErrorStyle($value = self::STYLE_STOP) + public function setErrorStyle($value) { $this->errorStyle = $value; @@ -262,7 +262,7 @@ class DataValidation * * @return DataValidation */ - public function setOperator($value = '') + public function setOperator($value) { $this->operator = $value; @@ -286,7 +286,7 @@ class DataValidation * * @return DataValidation */ - public function setAllowBlank($value = false) + public function setAllowBlank($value) { $this->allowBlank = $value; @@ -310,7 +310,7 @@ class DataValidation * * @return DataValidation */ - public function setShowDropDown($value = false) + public function setShowDropDown($value) { $this->showDropDown = $value; @@ -334,7 +334,7 @@ class DataValidation * * @return DataValidation */ - public function setShowInputMessage($value = false) + public function setShowInputMessage($value) { $this->showInputMessage = $value; @@ -358,7 +358,7 @@ class DataValidation * * @return DataValidation */ - public function setShowErrorMessage($value = false) + public function setShowErrorMessage($value) { $this->showErrorMessage = $value; @@ -382,7 +382,7 @@ class DataValidation * * @return DataValidation */ - public function setErrorTitle($value = '') + public function setErrorTitle($value) { $this->errorTitle = $value; @@ -406,7 +406,7 @@ class DataValidation * * @return DataValidation */ - public function setError($value = '') + public function setError($value) { $this->error = $value; @@ -430,7 +430,7 @@ class DataValidation * * @return DataValidation */ - public function setPromptTitle($value = '') + public function setPromptTitle($value) { $this->promptTitle = $value; @@ -454,7 +454,7 @@ class DataValidation * * @return DataValidation */ - public function setPrompt($value = '') + public function setPrompt($value) { $this->prompt = $value; diff --git a/src/PhpSpreadsheet/Cell/DefaultValueBinder.php b/src/PhpSpreadsheet/Cell/DefaultValueBinder.php index 461c96e6..f9e7729f 100644 --- a/src/PhpSpreadsheet/Cell/DefaultValueBinder.php +++ b/src/PhpSpreadsheet/Cell/DefaultValueBinder.php @@ -62,7 +62,7 @@ class DefaultValueBinder implements IValueBinder * * @return string */ - public static function dataTypeForValue($pValue = null) + public static function dataTypeForValue($pValue) { // Match the value against a few data types if ($pValue === null) { diff --git a/src/PhpSpreadsheet/Cell/Hyperlink.php b/src/PhpSpreadsheet/Cell/Hyperlink.php index 0ec811b7..6065357d 100644 --- a/src/PhpSpreadsheet/Cell/Hyperlink.php +++ b/src/PhpSpreadsheet/Cell/Hyperlink.php @@ -70,7 +70,7 @@ class Hyperlink * * @return Hyperlink */ - public function setUrl($value = '') + public function setUrl($value) { $this->url = $value; @@ -94,7 +94,7 @@ class Hyperlink * * @return Hyperlink */ - public function setTooltip($value = '') + public function setTooltip($value) { $this->tooltip = $value; diff --git a/src/PhpSpreadsheet/Chart.php b/src/PhpSpreadsheet/Chart.php index 17530463..ea31112a 100644 --- a/src/PhpSpreadsheet/Chart.php +++ b/src/PhpSpreadsheet/Chart.php @@ -341,7 +341,7 @@ class Chart * * @return Chart */ - public function setPlotVisibleOnly($plotVisibleOnly = true) + public function setPlotVisibleOnly($plotVisibleOnly) { $this->plotVisibleOnly = $plotVisibleOnly; @@ -365,7 +365,7 @@ class Chart * * @return Chart */ - public function setDisplayBlanksAs($displayBlanksAs = '0') + public function setDisplayBlanksAs($displayBlanksAs) { $this->displayBlanksAs = $displayBlanksAs; } @@ -494,11 +494,12 @@ class Chart * * @return Chart */ - public function setTopLeftOffset($xOffset = null, $yOffset = null) + public function setTopLeftOffset($xOffset, $yOffset) { if (!is_null($xOffset)) { $this->setTopLeftXOffset($xOffset); } + if (!is_null($yOffset)) { $this->setTopLeftYOffset($yOffset); } @@ -604,11 +605,12 @@ class Chart * * @return Chart */ - public function setBottomRightOffset($xOffset = null, $yOffset = null) + public function setBottomRightOffset($xOffset, $yOffset) { if (!is_null($xOffset)) { $this->setBottomRightXOffset($xOffset); } + if (!is_null($yOffset)) { $this->setBottomRightYOffset($yOffset); } diff --git a/src/PhpSpreadsheet/Chart/DataSeries.php b/src/PhpSpreadsheet/Chart/DataSeries.php index 6dc90760..092736a6 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -178,7 +178,7 @@ class DataSeries * * @return DataSeries */ - public function setPlotType($plotType = '') + public function setPlotType($plotType) { $this->plotType = $plotType; @@ -202,7 +202,7 @@ class DataSeries * * @return DataSeries */ - public function setPlotGrouping($groupingType = null) + public function setPlotGrouping($groupingType) { $this->plotGrouping = $groupingType; @@ -226,7 +226,7 @@ class DataSeries * * @return DataSeries */ - public function setPlotDirection($plotDirection = null) + public function setPlotDirection($plotDirection) { $this->plotDirection = $plotDirection; @@ -318,7 +318,7 @@ class DataSeries * * @return DataSeries */ - public function setPlotStyle($plotStyle = null) + public function setPlotStyle($plotStyle) { $this->plotStyle = $plotStyle; @@ -381,7 +381,7 @@ class DataSeries * * @return DataSeries */ - public function setSmoothLine($smoothLine = true) + public function setSmoothLine($smoothLine) { $this->smoothLine = $smoothLine; diff --git a/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/src/PhpSpreadsheet/Chart/DataSeriesValues.php index d1e92df1..82813400 100644 --- a/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -120,7 +120,7 @@ class DataSeriesValues * * @return DataSeriesValues */ - public function setDataType($dataType = self::DATASERIES_TYPE_NUMBER) + public function setDataType($dataType) { if (!in_array($dataType, self::$dataTypeValues)) { throw new Exception('Invalid datatype for chart data series values'); @@ -144,18 +144,13 @@ class DataSeriesValues * Set Series Data Source (formula). * * @param string $dataSource - * @param mixed $refreshDataValues * * @return DataSeriesValues */ - public function setDataSource($dataSource = null, $refreshDataValues = true) + public function setDataSource($dataSource) { $this->dataSource = $dataSource; - if ($refreshDataValues) { - // TO DO - } - return $this; } @@ -176,7 +171,7 @@ class DataSeriesValues * * @return DataSeriesValues */ - public function setPointMarker($marker = null) + public function setPointMarker($marker) { $this->pointMarker = $marker; @@ -200,7 +195,7 @@ class DataSeriesValues * * @return DataSeriesValues */ - public function setFormatCode($formatCode = null) + public function setFormatCode($formatCode) { $this->formatCode = $formatCode; @@ -277,29 +272,17 @@ class DataSeriesValues * Set Series Data Values. * * @param array $dataValues - * @param bool $refreshDataSource - * TRUE - refresh the value of dataSource based on the values of $dataValues - * FALSE - don't change the value of dataSource * * @return DataSeriesValues */ - public function setDataValues($dataValues = [], $refreshDataSource = true) + public function setDataValues($dataValues) { $this->dataValues = \PhpOffice\PhpSpreadsheet\Calculation\Functions::flattenArray($dataValues); $this->pointCount = count($dataValues); - if ($refreshDataSource) { - // TO DO - } - return $this; } - private function stripNulls($var) - { - return $var !== null; - } - public function refresh(\PhpOffice\PhpSpreadsheet\Worksheet $worksheet, $flatten = true) { if ($this->dataSource !== null) { diff --git a/src/PhpSpreadsheet/Chart/Layout.php b/src/PhpSpreadsheet/Chart/Layout.php index 98e5e6f6..9480d691 100644 --- a/src/PhpSpreadsheet/Chart/Layout.php +++ b/src/PhpSpreadsheet/Chart/Layout.php @@ -133,9 +133,9 @@ class Layout /** * Create a new Layout. * - * @param mixed $layout + * @param array $layout */ - public function __construct($layout = []) + public function __construct(array $layout = []) { if (isset($layout['layoutTarget'])) { $this->layoutTarget = $layout['layoutTarget']; diff --git a/src/PhpSpreadsheet/Chart/Legend.php b/src/PhpSpreadsheet/Chart/Legend.php index c472cd8f..2abb28e2 100644 --- a/src/PhpSpreadsheet/Chart/Legend.php +++ b/src/PhpSpreadsheet/Chart/Legend.php @@ -73,8 +73,9 @@ class Legend /** * Create a new Legend. * - * @param mixed $position - * @param mixed $overlay + * @param string $position + * @param Layout|null $layout + * @param bool $overlay */ public function __construct($position = self::POSITION_RIGHT, Layout $layout = null, $overlay = false) { @@ -96,9 +97,9 @@ class Legend /** * Get legend position using an excel string value. * - * @param string $position + * @param string $position see self::POSITION_* */ - public function setPosition($position = self::POSITION_RIGHT) + public function setPosition($position) { if (!in_array($position, self::$positionXLref)) { return false; @@ -112,7 +113,7 @@ class Legend /** * Get legend position as an Excel internal numeric value. * - * @return number + * @return int */ public function getPositionXL() { @@ -122,9 +123,9 @@ class Legend /** * Set legend position using an Excel internal numeric value. * - * @param number $positionXL + * @param int $positionXL see self::XL_LEGEND_POSITION_* */ - public function setPositionXL($positionXL = self::XL_LEGEND_POSITION_RIGHT) + public function setPositionXL($positionXL) { if (!isset(self::$positionXLref[$positionXL])) { return false; @@ -152,7 +153,7 @@ class Legend * * @return bool */ - public function setOverlay($overlay = false) + public function setOverlay($overlay) { if (!is_bool($overlay)) { return false; diff --git a/src/PhpSpreadsheet/Chart/PlotArea.php b/src/PhpSpreadsheet/Chart/PlotArea.php index afcced96..5c16db84 100644 --- a/src/PhpSpreadsheet/Chart/PlotArea.php +++ b/src/PhpSpreadsheet/Chart/PlotArea.php @@ -43,9 +43,10 @@ class PlotArea /** * Create a new PlotArea. * - * @param mixed $plotSeries + * @param Layout|null $layout + * @param array $plotSeries */ - public function __construct(Layout $layout = null, $plotSeries = []) + public function __construct(Layout $layout = null, array $plotSeries = []) { $this->layout = $layout; $this->plotSeries = $plotSeries; @@ -116,7 +117,7 @@ class PlotArea * * @return PlotArea */ - public function setPlotSeries($plotSeries = []) + public function setPlotSeries(array $plotSeries) { $this->plotSeries = $plotSeries; diff --git a/src/PhpSpreadsheet/Chart/Title.php b/src/PhpSpreadsheet/Chart/Title.php index 753e63e8..88f1e59e 100644 --- a/src/PhpSpreadsheet/Chart/Title.php +++ b/src/PhpSpreadsheet/Chart/Title.php @@ -44,6 +44,7 @@ class Title * Create a new Title. * * @param null|mixed $caption + * @param null|Layout $layout */ public function __construct($caption = null, Layout $layout = null) { @@ -68,7 +69,7 @@ class Title * * @return Title */ - public function setCaption($caption = null) + public function setCaption($caption) { $this->caption = $caption; diff --git a/src/PhpSpreadsheet/Collection/Cells.php b/src/PhpSpreadsheet/Collection/Cells.php new file mode 100644 index 00000000..137b6f90 --- /dev/null +++ b/src/PhpSpreadsheet/Collection/Cells.php @@ -0,0 +1,507 @@ +parent = $parent; + $this->cache = $cache; + $this->cachePrefix = $this->getUniqueID(); + } + + /** + * Return the parent worksheet for this cell collection. + * + * @return Worksheet + */ + public function getParent() + { + return $this->parent; + } + + /** + * Whether the collection holds a cell for the given coordinate. + * + * @param string $pCoord Coordinate of the cell to check + * + * @return bool + */ + public function has($pCoord) + { + if ($pCoord === $this->currentCoordinate) { + return true; + } + + // Check if the requested entry exists in the index + return isset($this->index[$pCoord]); + } + + /** + * Add or update a cell in the collection. + * + * @param Cell $cell Cell to update + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + * + * @return Cell + */ + public function update(Cell $cell) + { + return $this->add($cell->getCoordinate(), $cell); + } + + /** + * Delete a cell in cache identified by coordinate. + * + * @param string $pCoord Coordinate of the cell to delete + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + public function delete($pCoord) + { + if ($pCoord === $this->currentCoordinate && !is_null($this->currentCell)) { + $this->currentCell->detach(); + $this->currentCoordinate = null; + $this->currentCell = null; + $this->currentCellIsDirty = false; + } + + unset($this->index[$pCoord]); + + // Delete the entry from cache + $this->cache->delete($this->cachePrefix . $pCoord); + } + + /** + * Get a list of all cell coordinates currently held in the collection. + * + * @return string[] + */ + public function getCoordinates() + { + return array_keys($this->index); + } + + /** + * Get a sorted list of all cell coordinates currently held in the collection by row and column. + * + * @return string[] + */ + public function getSortedCoordinates() + { + $sortKeys = []; + foreach ($this->getCoordinates() as $coord) { + sscanf($coord, '%[A-Z]%d', $column, $row); + $sortKeys[sprintf('%09d%3s', $row, $column)] = $coord; + } + ksort($sortKeys); + + return array_values($sortKeys); + } + + /** + * Get highest worksheet column and highest row that have cell records. + * + * @return array Highest column name and highest row number + */ + public function getHighestRowAndColumn() + { + // Lookup highest column and highest row + $col = ['A' => '1A']; + $row = [1]; + foreach ($this->getCoordinates() as $coord) { + sscanf($coord, '%[A-Z]%d', $c, $r); + $row[$r] = $r; + $col[$c] = strlen($c) . $c; + } + if (!empty($row)) { + // Determine highest column and row + $highestRow = max($row); + $highestColumn = substr(max($col), 1); + } + + return [ + 'row' => $highestRow, + 'column' => $highestColumn, + ]; + } + + /** + * Return the cell coordinate of the currently active cell object. + * + * @return string + */ + public function getCurrentCoordinate() + { + return $this->currentCoordinate; + } + + /** + * Return the column coordinate of the currently active cell object. + * + * @return string + */ + public function getCurrentColumn() + { + sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row); + + return $column; + } + + /** + * Return the row coordinate of the currently active cell object. + * + * @return int + */ + public function getCurrentRow() + { + sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row); + + return (int) $row; + } + + /** + * Get highest worksheet column. + * + * @param string $row Return the highest column for the specified row, + * or the highest column of any row if no row number is passed + * + * @return string Highest column name + */ + public function getHighestColumn($row = null) + { + if ($row == null) { + $colRow = $this->getHighestRowAndColumn(); + + return $colRow['column']; + } + + $columnList = [1]; + foreach ($this->getCoordinates() as $coord) { + sscanf($coord, '%[A-Z]%d', $c, $r); + if ($r != $row) { + continue; + } + $columnList[] = Cell::columnIndexFromString($c); + } + + return Cell::stringFromColumnIndex(max($columnList) - 1); + } + + /** + * Get highest worksheet row. + * + * @param string $column Return the highest row for the specified column, + * or the highest row of any column if no column letter is passed + * + * @return int Highest row number + */ + public function getHighestRow($column = null) + { + if ($column == null) { + $colRow = $this->getHighestRowAndColumn(); + + return $colRow['row']; + } + + $rowList = [0]; + foreach ($this->getCoordinates() as $coord) { + sscanf($coord, '%[A-Z]%d', $c, $r); + if ($c != $column) { + continue; + } + $rowList[] = $r; + } + + return max($rowList); + } + + /** + * Generate a unique ID for cache referencing. + * + * @return string Unique Reference + */ + private function getUniqueID() + { + return uniqid('phpspreadsheet-', true) . '-'; + } + + /** + * Clone the cell collection. + * + * @param Worksheet $parent The new worksheet that we're copying to + * + * @return self + */ + public function cloneCellCollection(Worksheet $parent) + { + $this->storeCurrentCell(); + $newCollection = clone $this; + + $newCollection->parent = $parent; + if (($newCollection->currentCell !== null) && (is_object($newCollection->currentCell))) { + $newCollection->currentCell->attach($this); + } + + // Get old values + $oldKeys = $newCollection->getAllCacheKeys(); + $oldValues = $newCollection->cache->getMultiple($oldKeys); + $newValues = []; + $oldCachePrefix = $newCollection->cachePrefix; + + // Change prefix + $newCollection->cachePrefix = $newCollection->getUniqueID(); + foreach ($oldValues as $oldKey => $value) { + $newValues[str_replace($oldCachePrefix, $newCollection->cachePrefix, $oldKey)] = clone $value; + } + + // Store new values + $stored = $newCollection->cache->setMultiple($newValues); + if (!$stored) { + $newCollection->__destruct(); + throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to copy cells in cache'); + } + + return $newCollection; + } + + /** + * Remove a row, deleting all cells in that row. + * + * @param string $row Row number to remove + */ + public function removeRow($row) + { + foreach ($this->getCoordinates() as $coord) { + sscanf($coord, '%[A-Z]%d', $c, $r); + if ($r == $row) { + $this->delete($coord); + } + } + } + + /** + * Remove a column, deleting all cells in that column. + * + * @param string $column Column ID to remove + */ + public function removeColumn($column) + { + foreach ($this->getCoordinates() as $coord) { + sscanf($coord, '%[A-Z]%d', $c, $r); + if ($c == $column) { + $this->delete($coord); + } + } + } + + /** + * Store cell data in cache for the current cell object if it's "dirty", + * and the 'nullify' the current cell object. + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + private function storeCurrentCell() + { + if ($this->currentCellIsDirty && !empty($this->currentCoordinate)) { + $this->currentCell->detach(); + + $stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell); + if (!$stored) { + $this->__destruct(); + throw new \PhpOffice\PhpSpreadsheet\Exception("Failed to store cell {$this->currentCoordinate} in cache"); + } + $this->currentCellIsDirty = false; + } + + $this->currentCoordinate = null; + $this->currentCell = null; + } + + /** + * Add or update a cell identified by its coordinate into the collection. + * + * @param string $pCoord Coordinate of the cell to update + * @param Cell $cell Cell to update + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + * + * @return Cell + */ + public function add($pCoord, Cell $cell) + { + if ($pCoord !== $this->currentCoordinate) { + $this->storeCurrentCell(); + } + $this->index[$pCoord] = true; + + $this->currentCoordinate = $pCoord; + $this->currentCell = $cell; + $this->currentCellIsDirty = true; + + return $cell; + } + + /** + * Get cell at a specific coordinate. + * + * @param string $pCoord Coordinate of the cell + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + * + * @return Cell Cell that was found, or null if not found + */ + public function get($pCoord) + { + if ($pCoord === $this->currentCoordinate) { + return $this->currentCell; + } + $this->storeCurrentCell(); + + // Return null if requested entry doesn't exist in collection + if (!$this->has($pCoord)) { + return null; + } + + // Check if the entry that has been requested actually exists + $cell = $this->cache->get($this->cachePrefix . $pCoord); + if ($cell === null) { + throw new \PhpOffice\PhpSpreadsheet\Exception("Cell entry {$pCoord} no longer exists in cache. This probably means that the cache was cleared by someone else."); + } + + // Set current entry to the requested entry + $this->currentCoordinate = $pCoord; + $this->currentCell = $cell; + // Re-attach this as the cell's parent + $this->currentCell->attach($this); + + // Return requested entry + return $this->currentCell; + } + + /** + * Clear the cell collection and disconnect from our parent. + */ + public function unsetWorksheetCells() + { + if (!is_null($this->currentCell)) { + $this->currentCell->detach(); + $this->currentCell = null; + $this->currentCoordinate = null; + } + + // Flush the cache + $this->__destruct(); + + $this->index = []; + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->parent = null; + } + + /** + * Destroy this cell collection. + */ + public function __destruct() + { + $this->cache->deleteMultiple($this->getAllCacheKeys()); + } + + /** + * Returns all known cache keys. + * + * @return string + */ + private function getAllCacheKeys() + { + $keys = []; + foreach ($this->getCoordinates() as $coordinate) { + $keys[] = $this->cachePrefix . $coordinate; + } + + return $keys; + } +} diff --git a/src/PhpSpreadsheet/Collection/CellsFactory.php b/src/PhpSpreadsheet/Collection/CellsFactory.php new file mode 100644 index 00000000..64a34b35 --- /dev/null +++ b/src/PhpSpreadsheet/Collection/CellsFactory.php @@ -0,0 +1,45 @@ +cache = []; + + return true; + } + + public function delete($key) + { + unset($this->cache[$key]); + + return true; + } + + public function deleteMultiple($keys) + { + foreach ($keys as $key) { + $this->delete($key); + } + + return true; + } + + public function get($key, $default = null) + { + if ($this->has($key)) { + return $this->cache[$key]; + } + + return $default; + } + + public function getMultiple($keys, $default = null) + { + $results = []; + foreach ($keys as $key) { + $results[$key] = $this->get($key, $default); + } + + return $results; + } + + public function has($key) + { + return array_key_exists($key, $this->cache); + } + + public function set($key, $value, $ttl = null) + { + $this->cache[$key] = $value; + + return true; + } + + public function setMultiple($values, $ttl = null) + { + foreach ($values as $key => $value) { + $this->set($key, $value); + } + + return true; + } +} diff --git a/src/PhpSpreadsheet/Comment.php b/src/PhpSpreadsheet/Comment.php index f04f3ce6..28bce22c 100644 --- a/src/PhpSpreadsheet/Comment.php +++ b/src/PhpSpreadsheet/Comment.php @@ -116,13 +116,13 @@ class Comment implements IComparable /** * Set Author. * - * @param string $pValue + * @param string $author * * @return Comment */ - public function setAuthor($pValue = '') + public function setAuthor($author) { - $this->author = $pValue; + $this->author = $author; return $this; } @@ -164,13 +164,13 @@ class Comment implements IComparable /** * Set comment width (CSS style, i.e. XXpx or YYpt). * - * @param string $value + * @param string $width * * @return Comment */ - public function setWidth($value = '96pt') + public function setWidth($width) { - $this->width = $value; + $this->width = $width; return $this; } @@ -192,7 +192,7 @@ class Comment implements IComparable * * @return Comment */ - public function setHeight($value = '55.5pt') + public function setHeight($value) { $this->height = $value; @@ -216,7 +216,7 @@ class Comment implements IComparable * * @return Comment */ - public function setMarginLeft($value = '59.25pt') + public function setMarginLeft($value) { $this->marginLeft = $value; @@ -240,7 +240,7 @@ class Comment implements IComparable * * @return Comment */ - public function setMarginTop($value = '1.5pt') + public function setMarginTop($value) { $this->marginTop = $value; @@ -264,7 +264,7 @@ class Comment implements IComparable * * @return Comment */ - public function setVisible($value = false) + public function setVisible($value) { $this->visible = $value; @@ -284,13 +284,13 @@ class Comment implements IComparable /** * Set Alignment. * - * @param string $pValue + * @param string $alignment see Style\Alignment::HORIZONTAL_* * * @return Comment */ - public function setAlignment($pValue = Style\Alignment::HORIZONTAL_GENERAL) + public function setAlignment($alignment) { - $this->alignment = $pValue; + $this->alignment = $alignment; return $this; } diff --git a/src/PhpSpreadsheet/Document/Properties.php b/src/PhpSpreadsheet/Document/Properties.php index d0705dc8..da00fd2c 100644 --- a/src/PhpSpreadsheet/Document/Properties.php +++ b/src/PhpSpreadsheet/Document/Properties.php @@ -142,13 +142,13 @@ class Properties /** * Set Creator. * - * @param string $pValue + * @param string $creator * * @return Properties */ - public function setCreator($pValue = '') + public function setCreator($creator) { - $this->creator = $pValue; + $this->creator = $creator; return $this; } @@ -170,7 +170,7 @@ class Properties * * @return Properties */ - public function setLastModifiedBy($pValue = '') + public function setLastModifiedBy($pValue) { $this->lastModifiedBy = $pValue; @@ -194,7 +194,7 @@ class Properties * * @return Properties */ - public function setCreated($pValue = null) + public function setCreated($pValue) { if ($pValue === null) { $pValue = time(); @@ -228,7 +228,7 @@ class Properties * * @return Properties */ - public function setModified($pValue = null) + public function setModified($pValue) { if ($pValue === null) { $pValue = time(); @@ -258,13 +258,13 @@ class Properties /** * Set Title. * - * @param string $pValue + * @param string $title * * @return Properties */ - public function setTitle($pValue = '') + public function setTitle($title) { - $this->title = $pValue; + $this->title = $title; return $this; } @@ -282,13 +282,13 @@ class Properties /** * Set Description. * - * @param string $pValue + * @param string $description * * @return Properties */ - public function setDescription($pValue = '') + public function setDescription($description) { - $this->description = $pValue; + $this->description = $description; return $this; } @@ -306,13 +306,13 @@ class Properties /** * Set Subject. * - * @param string $pValue + * @param string $subject * * @return Properties */ - public function setSubject($pValue = '') + public function setSubject($subject) { - $this->subject = $pValue; + $this->subject = $subject; return $this; } @@ -330,13 +330,13 @@ class Properties /** * Set Keywords. * - * @param string $pValue + * @param string $keywords * * @return Properties */ - public function setKeywords($pValue = '') + public function setKeywords($keywords) { - $this->keywords = $pValue; + $this->keywords = $keywords; return $this; } @@ -354,13 +354,13 @@ class Properties /** * Set Category. * - * @param string $pValue + * @param string $category * * @return Properties */ - public function setCategory($pValue = '') + public function setCategory($category) { - $this->category = $pValue; + $this->category = $category; return $this; } @@ -378,13 +378,13 @@ class Properties /** * Set Company. * - * @param string $pValue + * @param string $company * * @return Properties */ - public function setCompany($pValue = '') + public function setCompany($company) { - $this->company = $pValue; + $this->company = $company; return $this; } @@ -402,13 +402,13 @@ class Properties /** * Set Manager. * - * @param string $pValue + * @param string $manager * * @return Properties */ - public function setManager($pValue = '') + public function setManager($manager) { - $this->manager = $pValue; + $this->manager = $manager; return $this; } diff --git a/src/PhpSpreadsheet/Document/Security.php b/src/PhpSpreadsheet/Document/Security.php index 380f7265..6bb99b97 100644 --- a/src/PhpSpreadsheet/Document/Security.php +++ b/src/PhpSpreadsheet/Document/Security.php @@ -97,7 +97,7 @@ class Security * * @return Security */ - public function setLockRevision($pValue = false) + public function setLockRevision($pValue) { $this->lockRevision = $pValue; @@ -121,7 +121,7 @@ class Security * * @return Security */ - public function setLockStructure($pValue = false) + public function setLockStructure($pValue) { $this->lockStructure = $pValue; @@ -145,7 +145,7 @@ class Security * * @return Security */ - public function setLockWindows($pValue = false) + public function setLockWindows($pValue) { $this->lockWindows = $pValue; @@ -170,7 +170,7 @@ class Security * * @return Security */ - public function setRevisionsPassword($pValue = '', $pAlreadyHashed = false) + public function setRevisionsPassword($pValue, $pAlreadyHashed = false) { if (!$pAlreadyHashed) { $pValue = \PhpOffice\PhpSpreadsheet\Shared\PasswordHasher::hashPassword($pValue); @@ -198,7 +198,7 @@ class Security * * @return Security */ - public function setWorkbookPassword($pValue = '', $pAlreadyHashed = false) + public function setWorkbookPassword($pValue, $pAlreadyHashed = false) { if (!$pAlreadyHashed) { $pValue = \PhpOffice\PhpSpreadsheet\Shared\PasswordHasher::hashPassword($pValue); diff --git a/src/PhpSpreadsheet/HashTable.php b/src/PhpSpreadsheet/HashTable.php index fededf17..e8ac403b 100644 --- a/src/PhpSpreadsheet/HashTable.php +++ b/src/PhpSpreadsheet/HashTable.php @@ -62,13 +62,11 @@ class HashTable * * @throws Exception */ - public function addFromSource($pSource = null) + public function addFromSource(array $pSource = null) { // Check if an array was passed if ($pSource == null) { return; - } elseif (!is_array($pSource)) { - throw new Exception('Invalid array parameter passed.'); } foreach ($pSource as $item) { @@ -83,7 +81,7 @@ class HashTable * * @throws Exception */ - public function add(IComparable $pSource = null) + public function add(IComparable $pSource) { $hash = $pSource->getHashCode(); if (!isset($this->items[$hash])) { @@ -99,7 +97,7 @@ class HashTable * * @throws Exception */ - public function remove(IComparable $pSource = null) + public function remove(IComparable $pSource) { $hash = $pSource->getHashCode(); if (isset($this->items[$hash])) { @@ -145,7 +143,7 @@ class HashTable * * @return int Index */ - public function getIndexForHashCode($pHashCode = '') + public function getIndexForHashCode($pHashCode) { return array_search($pHashCode, $this->keyMap); } @@ -157,7 +155,7 @@ class HashTable * * @return IComparable */ - public function getByIndex($pIndex = 0) + public function getByIndex($pIndex) { if (isset($this->keyMap[$pIndex])) { return $this->getByHashCode($this->keyMap[$pIndex]); @@ -173,7 +171,7 @@ class HashTable * * @return IComparable */ - public function getByHashCode($pHashCode = '') + public function getByHashCode($pHashCode) { if (isset($this->items[$pHashCode])) { return $this->items[$pHashCode]; diff --git a/src/PhpSpreadsheet/Helper/Migrator.php b/src/PhpSpreadsheet/Helper/Migrator.php index a00dbb52..fa6ccd82 100644 --- a/src/PhpSpreadsheet/Helper/Migrator.php +++ b/src/PhpSpreadsheet/Helper/Migrator.php @@ -40,7 +40,6 @@ class Migrator 'SingularValueDecomposition' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\JAMA\\SingularValueDecomposition', 'PHPExcel_Shared_OLE_ChainedBlockStream' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\ChainedBlockStream', 'PHPExcel_Shared_OLE_PPS' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\PPS', - 'PclZip' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\PCLZip\\PclZip', 'PHPExcel_Best_Fit' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\BestFit', 'PHPExcel_Exponential_Best_Fit' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\ExponentialBestFit', 'PHPExcel_Linear_Best_Fit' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\LinearBestFit', @@ -85,19 +84,7 @@ class Migrator 'PHPExcel_Writer_Excel2007_Workbook' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Workbook', 'PHPExcel_Writer_Excel2007_Worksheet' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Worksheet', 'PHPExcel_Writer_Excel2007_WriterPart' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\WriterPart', - 'PHPExcel_CachedObjectStorage_APC' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\APC', - 'PHPExcel_CachedObjectStorage_CacheBase' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\CacheBase', - 'PHPExcel_CachedObjectStorage_DiscISAM' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\DiscISAM', - 'PHPExcel_CachedObjectStorage_ICache' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\ICache', - 'PHPExcel_CachedObjectStorage_Igbinary' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Igbinary', - 'PHPExcel_CachedObjectStorage_Memcache' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Memcache', - 'PHPExcel_CachedObjectStorage_Memory' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Memory', - 'PHPExcel_CachedObjectStorage_MemoryGZip' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\MemoryGZip', - 'PHPExcel_CachedObjectStorage_MemorySerialized' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\MemorySerialized', - 'PHPExcel_CachedObjectStorage_PHPTemp' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\PHPTemp', - 'PHPExcel_CachedObjectStorage_SQLite' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\SQLite3', - 'PHPExcel_CachedObjectStorage_SQLite3' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\SQLite3', - 'PHPExcel_CachedObjectStorage_Wincache' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Wincache', + 'PHPExcel_CachedObjectStorage_CacheBase' => '\\PhpOffice\\PhpSpreadsheet\\Collection\\Cells', 'PHPExcel_CalcEngine_CyclicReferenceStack' => '\\PhpOffice\\PhpSpreadsheet\\CalcEngine\\CyclicReferenceStack', 'PHPExcel_CalcEngine_Logger' => '\\PhpOffice\\PhpSpreadsheet\\CalcEngine\\Logger', 'PHPExcel_Calculation_Functions' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions', @@ -163,8 +150,6 @@ class Migrator 'PHPExcel_Shared_TimeZone' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\TimeZone', 'PHPExcel_Shared_XMLWriter' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\XMLWriter', 'PHPExcel_Shared_Excel5' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\Xls', - 'PHPExcel_Shared_ZipArchive' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\ZipArchive', - 'PHPExcel_Shared_ZipStreamWrapper' => '\\PhpOffice\\PhpSpreadsheet\\Shared\\ZipStreamWrapper', 'PHPExcel_Style_Alignment' => '\\PhpOffice\\PhpSpreadsheet\\Style\\Alignment', 'PHPExcel_Style_Border' => '\\PhpOffice\\PhpSpreadsheet\\Style\\Border', 'PHPExcel_Style_Borders' => '\\PhpOffice\\PhpSpreadsheet\\Style\\Borders', @@ -204,7 +189,7 @@ class Migrator 'PHPExcel_Writer_PDF' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Pdf', 'PHPExcel_Writer_Excel5' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xls', 'PHPExcel_Writer_Excel2007' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx', - 'PHPExcel_CachedObjectStorageFactory' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorageFactory', + 'PHPExcel_CachedObjectStorageFactory' => '\\PhpOffice\\PhpSpreadsheet\\Collection\\CellsFactory', 'PHPExcel_Calculation' => '\\PhpOffice\\PhpSpreadsheet\\Calculation', 'PHPExcel_Cell' => '\\PhpOffice\\PhpSpreadsheet\\Cell', 'PHPExcel_Chart' => '\\PhpOffice\\PhpSpreadsheet\\Chart', diff --git a/src/PhpSpreadsheet/IOFactory.php b/src/PhpSpreadsheet/IOFactory.php index 949cc87d..f2d40a74 100644 --- a/src/PhpSpreadsheet/IOFactory.php +++ b/src/PhpSpreadsheet/IOFactory.php @@ -84,13 +84,9 @@ class IOFactory * * @throws Reader\Exception */ - public static function setSearchLocations($value) + public static function setSearchLocations(array $value) { - if (is_array($value)) { - self::$searchLocations = $value; - } else { - throw new Reader\Exception('Invalid parameter passed.'); - } + self::$searchLocations = $value; } /** @@ -102,7 +98,7 @@ class IOFactory * @param string $location Example: PhpSpreadsheet/Writer/{0}.php * @param string $classname Example: Writer\{0} */ - public static function addSearchLocation($type = '', $location = '', $classname = '') + public static function addSearchLocation($type, $location, $classname) { self::$searchLocations[] = ['type' => $type, 'path' => $location, 'class' => $classname]; } @@ -151,7 +147,7 @@ class IOFactory * * @return Reader\IReader */ - public static function createReader($readerType = '') + public static function createReader($readerType) { // Search type $searchType = 'IReader'; diff --git a/src/PhpSpreadsheet/NamedRange.php b/src/PhpSpreadsheet/NamedRange.php index fffe7ee9..b1783662 100644 --- a/src/PhpSpreadsheet/NamedRange.php +++ b/src/PhpSpreadsheet/NamedRange.php @@ -104,7 +104,7 @@ class NamedRange * * @return NamedRange */ - public function setName($value = null) + public function setName($value) { if ($value !== null) { // Old title @@ -171,7 +171,7 @@ class NamedRange * * @return NamedRange */ - public function setRange($value = null) + public function setRange($value) { if ($value !== null) { $this->range = $value; @@ -197,7 +197,7 @@ class NamedRange * * @return NamedRange */ - public function setLocalOnly($value = false) + public function setLocalOnly($value) { $this->localOnly = $value; $this->scope = $value ? $this->worksheet : null; @@ -238,7 +238,7 @@ class NamedRange * * @return NamedRange */ - public static function resolveRange($pNamedRange, Worksheet $pSheet = null) + public static function resolveRange($pNamedRange, Worksheet $pSheet) { return $pSheet->getParent()->getNamedRange($pNamedRange, $pSheet); } diff --git a/src/PhpSpreadsheet/Reader/BaseReader.php b/src/PhpSpreadsheet/Reader/BaseReader.php index dd8d0769..5ef189f4 100644 --- a/src/PhpSpreadsheet/Reader/BaseReader.php +++ b/src/PhpSpreadsheet/Reader/BaseReader.php @@ -92,7 +92,7 @@ abstract class BaseReader implements IReader * * @return IReader */ - public function setReadDataOnly($pValue = false) + public function setReadDataOnly($pValue) { $this->readDataOnly = (bool) $pValue; @@ -120,7 +120,7 @@ abstract class BaseReader implements IReader * * @return IReader */ - public function setReadEmptyCells($pValue = true) + public function setReadEmptyCells($pValue) { $this->readEmptyCells = (bool) $pValue; @@ -150,7 +150,7 @@ abstract class BaseReader implements IReader * * @return IReader */ - public function setIncludeCharts($pValue = false) + public function setIncludeCharts($pValue) { $this->includeCharts = (bool) $pValue; @@ -178,7 +178,7 @@ abstract class BaseReader implements IReader * * @return IReader */ - public function setLoadSheetsOnly($value = null) + public function setLoadSheetsOnly($value) { if ($value === null) { return $this->setLoadAllSheets(); diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index f36366e3..c0d064b7 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -40,7 +40,7 @@ class Csv extends BaseReader implements IReader * * @var string */ - private $delimiter = ','; + private $delimiter = null; /** * Enclosure. @@ -81,9 +81,9 @@ class Csv extends BaseReader implements IReader /** * Set input encoding. * - * @param string $pValue Input encoding + * @param string $pValue Input encoding, eg: 'UTF-8' */ - public function setInputEncoding($pValue = 'UTF-8') + public function setInputEncoding($pValue) { $this->inputEncoding = $pValue; @@ -152,6 +152,86 @@ class Csv extends BaseReader implements IReader return $this->skipBOM(); } + /** + * Infer the separator if it isn't explicitly set in the file or specified by the user. + */ + protected function inferSeparator() + { + if ($this->delimiter !== null) { + return; + } + + $potentialDelimiters = [',', ';', "\t", '|', ':', ' ']; + $counts = []; + foreach ($potentialDelimiters as $delimiter) { + $counts[$delimiter] = []; + } + + // Count how many times each of the potential delimiters appears in each line + $numberLines = 0; + while (($line = fgets($this->fileHandle)) !== false && (++$numberLines < 1000)) { + $countLine = []; + for ($i = strlen($line) - 1; $i >= 0; --$i) { + $char = $line[$i]; + if (isset($counts[$char])) { + if (!isset($countLine[$char])) { + $countLine[$char] = 0; + } + ++$countLine[$char]; + } + } + foreach ($potentialDelimiters as $delimiter) { + $counts[$delimiter][] = isset($countLine[$delimiter]) + ? $countLine[$delimiter] + : 0; + } + } + + // Calculate the mean square deviations for each delimiter (ignoring delimiters that haven't been found consistently) + $meanSquareDeviations = []; + $middleIdx = floor(($numberLines - 1) / 2); + + foreach ($potentialDelimiters as $delimiter) { + $series = $counts[$delimiter]; + sort($series); + + $median = ($numberLines % 2) + ? $series[$middleIdx] + : ($series[$middleIdx] + $series[$middleIdx + 1]) / 2; + + if ($median === 0) { + continue; + } + + $meanSquareDeviations[$delimiter] = array_reduce( + $series, + function ($sum, $value) use ($median) { + return $sum + pow($value - $median, 2); + } + ) / count($series); + } + + // ... and pick the delimiter with the smallest mean square deviation (in case of ties, the order in potentialDelimiters is respected) + $min = INF; + foreach ($potentialDelimiters as $delimiter) { + if (!isset($meanSquareDeviations[$delimiter])) { + continue; + } + + if ($meanSquareDeviations[$delimiter] < $min) { + $min = $meanSquareDeviations[$delimiter]; + $this->delimiter = $delimiter; + } + } + + // If no delimiter could be detected, fall back to the default + if ($this->delimiter === null) { + $this->delimiter = reset($potentialDelimiters); + } + + return $this->skipBOM(); + } + /** * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns). * @@ -171,8 +251,7 @@ class Csv extends BaseReader implements IReader // Skip BOM, if any $this->skipBOM(); $this->checkSeparator(); - - $escapeEnclosures = ['\\' . $this->enclosure, $this->enclosure . $this->enclosure]; + $this->inferSeparator(); $worksheetInfo = []; $worksheetInfo[0]['worksheetName'] = 'Worksheet'; @@ -239,6 +318,7 @@ class Csv extends BaseReader implements IReader // Skip BOM, if any $this->skipBOM(); $this->checkSeparator(); + $this->inferSeparator(); // Create new PhpSpreadsheet object while ($spreadsheet->getSheetCount() <= $this->sheetIndex) { @@ -246,11 +326,6 @@ class Csv extends BaseReader implements IReader } $sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex); - $escapeEnclosures = [ - '\\' . $this->enclosure, - $this->enclosure . $this->enclosure, - ]; - // Set our starting row based on whether we're in contiguous mode or not $currentRow = 1; if ($this->contiguous) { @@ -262,9 +337,6 @@ class Csv extends BaseReader implements IReader $columnLetter = 'A'; foreach ($rowData as $rowDatum) { if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) { - // Unescape enclosures - $rowDatum = str_replace($escapeEnclosures, $this->enclosure, $rowDatum); - // Convert encoding if necessary if ($this->inputEncoding !== 'UTF-8') { $rowDatum = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($rowDatum, 'UTF-8', $this->inputEncoding); @@ -304,13 +376,13 @@ class Csv extends BaseReader implements IReader /** * Set delimiter. * - * @param string $pValue Delimiter, defaults to , + * @param string $delimiter Delimiter, eg: ',' * * @return CSV */ - public function setDelimiter($pValue = ',') + public function setDelimiter($delimiter) { - $this->delimiter = $pValue; + $this->delimiter = $delimiter; return $this; } @@ -328,16 +400,16 @@ class Csv extends BaseReader implements IReader /** * Set enclosure. * - * @param string $pValue Enclosure, defaults to " + * @param string $enclosure Enclosure, defaults to " * * @return CSV */ - public function setEnclosure($pValue = '"') + public function setEnclosure($enclosure) { - if ($pValue == '') { - $pValue = '"'; + if ($enclosure == '') { + $enclosure = '"'; } - $this->enclosure = $pValue; + $this->enclosure = $enclosure; return $this; } @@ -359,7 +431,7 @@ class Csv extends BaseReader implements IReader * * @return CSV */ - public function setSheetIndex($pValue = 0) + public function setSheetIndex($pValue) { $this->sheetIndex = $pValue; @@ -371,7 +443,7 @@ class Csv extends BaseReader implements IReader * * @param bool $contiguous */ - public function setContiguous($contiguous = false) + public function setContiguous($contiguous) { $this->contiguous = (bool) $contiguous; if (!$contiguous) { diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index c31ecd15..c6418704 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -804,7 +804,7 @@ class Gnumeric extends BaseReader implements IReader return $styleArray; } - private function parseRichText($is = '') + private function parseRichText($is) { $value = new \PhpOffice\PhpSpreadsheet\RichText(); $value->createText($is); diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index 95fccc61..2f0aa473 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -208,9 +208,9 @@ class Html extends BaseReader implements IReader /** * Set input encoding. * - * @param string $pValue Input encoding + * @param string $pValue Input encoding, eg: 'ANSI' */ - public function setInputEncoding($pValue = 'ANSI') + public function setInputEncoding($pValue) { $this->inputEncoding = $pValue; @@ -263,7 +263,7 @@ class Html extends BaseReader implements IReader // Only actually write it if there's content in the string // Write to worksheet to be done here... // ... we return the cell so we can mess about with styles more easily - $sheet->setCellValue($column . $row, $cellContent, true); + $sheet->setCellValue($column . $row, $cellContent); $this->dataArray[$row][$column] = $cellContent; } } else { @@ -556,7 +556,7 @@ class Html extends BaseReader implements IReader * * @return HTML */ - public function setSheetIndex($pValue = 0) + public function setSheetIndex($pValue) { $this->sheetIndex = $pValue; diff --git a/src/PhpSpreadsheet/Reader/IReadFilter.php b/src/PhpSpreadsheet/Reader/IReadFilter.php index a4131c09..75b613f3 100644 --- a/src/PhpSpreadsheet/Reader/IReadFilter.php +++ b/src/PhpSpreadsheet/Reader/IReadFilter.php @@ -29,9 +29,9 @@ interface IReadFilter /** * Should this cell be read? * - * @param $column Column address (as a string value like "A", or "IV") - * @param $row Row number - * @param $worksheetName Optional worksheet name + * @param $column string Column address (as a string value like "A", or "IV") + * @param $row int Row number + * @param $worksheetName string Optional worksheet name * * @return bool */ diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 09d539b0..13f48ea4 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -4,7 +4,11 @@ namespace PhpOffice\PhpSpreadsheet\Reader; use DateTime; use DateTimeZone; +use PhpOffice\PhpSpreadsheet\Calculation; +use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Shared\File; +use PhpOffice\PhpSpreadsheet\Style\NumberFormat; +use ZipArchive; /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. @@ -58,11 +62,11 @@ class Ods extends BaseReader implements IReader { File::assertFile($pFilename); - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - $mimeType = 'UNKNOWN'; + // Load file - $zip = new $zipClass(); + + $zip = new ZipArchive(); if ($zip->open($pFilename) === true) { // check if it is an OOXML archive $stat = $zip->statName('mimetype'); @@ -101,32 +105,32 @@ class Ods extends BaseReader implements IReader * @param string $pFilename * * @throws Exception + * + * @return string[] */ public function listWorksheetNames($pFilename) { File::assertFile($pFilename); - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - - $zip = new $zipClass(); + $zip = new ZipArchive(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); } $worksheetNames = []; - $xml = new XMLReader(); - $res = $xml->xml( + $xml = new \XMLReader(); + $xml->xml( $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), null, \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() ); $xml->setParserProperty(2, true); - // Step into the first level of content of the XML + // Step into the first level of content of the XML $xml->read(); while ($xml->read()) { - // Quickly jump through to the office:body node + // Quickly jump through to the office:body node while ($xml->name !== 'office:body') { if ($xml->isEmptyElement) { $xml->read(); @@ -134,14 +138,14 @@ class Ods extends BaseReader implements IReader $xml->next(); } } - // Now read each node until we find our first table:table node + // Now read each node until we find our first table:table node while ($xml->read()) { - if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { - // Loop through each table:table node reading the table:name attribute for each worksheet name + if ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT) { + // Loop through each table:table node reading the table:name attribute for each worksheet name do { $worksheetNames[] = $xml->getAttribute('table:name'); $xml->next(); - } while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT); + } while ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT); } } } @@ -155,6 +159,8 @@ class Ods extends BaseReader implements IReader * @param string $pFilename * * @throws Exception + * + * @return array */ public function listWorksheetInfo($pFilename) { @@ -162,14 +168,12 @@ class Ods extends BaseReader implements IReader $worksheetInfo = []; - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - - $zip = new $zipClass(); + $zip = new ZipArchive(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); } - $xml = new XMLReader(); + $xml = new \XMLReader(); $res = $xml->xml( $this->securityScanFile('zip://' . realpath($pFilename) . '#content.xml'), null, @@ -177,10 +181,10 @@ class Ods extends BaseReader implements IReader ); $xml->setParserProperty(2, true); - // Step into the first level of content of the XML + // Step into the first level of content of the XML $xml->read(); while ($xml->read()) { - // Quickly jump through to the office:body node + // Quickly jump through to the office:body node while ($xml->name !== 'office:body') { if ($xml->isEmptyElement) { $xml->read(); @@ -188,9 +192,9 @@ class Ods extends BaseReader implements IReader $xml->next(); } } - // Now read each node until we find our first table:table node + // Now read each node until we find our first table:table node while ($xml->read()) { - if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { + if ($xml->name == 'table:table' && $xml->nodeType == \XMLReader::ELEMENT) { $worksheetNames[] = $xml->getAttribute('table:name'); $tmpInfo = [ @@ -201,27 +205,27 @@ class Ods extends BaseReader implements IReader 'totalColumns' => 0, ]; - // Loop through each child node of the table:table element reading + // Loop through each child node of the table:table element reading $currCells = 0; do { $xml->read(); - if ($xml->name == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) { + if ($xml->name == 'table:table-row' && $xml->nodeType == \XMLReader::ELEMENT) { $rowspan = $xml->getAttribute('table:number-rows-repeated'); $rowspan = empty($rowspan) ? 1 : $rowspan; $tmpInfo['totalRows'] += $rowspan; $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); $currCells = 0; - // Step into the row + // Step into the row $xml->read(); do { - if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { + if ($xml->name == 'table:table-cell' && $xml->nodeType == \XMLReader::ELEMENT) { if (!$xml->isEmptyElement) { ++$currCells; $xml->next(); } else { $xml->read(); } - } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) { + } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == \XMLReader::ELEMENT) { $mergeSize = $xml->getAttribute('table:number-columns-repeated'); $currCells += $mergeSize; $xml->read(); @@ -290,13 +294,15 @@ class Ods extends BaseReader implements IReader $timezoneObj = new DateTimeZone('Europe/London'); $GMT = new \DateTimeZone('UTC'); - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - - $zip = new $zipClass(); + $zip = new ZipArchive(); if (!$zip->open($pFilename)) { throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); } + /* + * Meta + */ + $xml = simplexml_load_string( $this->securityScan($zip->getFromName('meta.xml')), 'SimpleXMLElement', @@ -382,50 +388,94 @@ class Ods extends BaseReader implements IReader } } - $xml = simplexml_load_string( + /* + * Content + */ + + $dom = new \DOMDocument('1.01', 'UTF-8'); + $dom->loadXML( $this->securityScan($zip->getFromName('content.xml')), - 'SimpleXMLElement', \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions() ); - $namespacesContent = $xml->getNamespaces(true); - $workbook = $xml->children($namespacesContent['office']); - foreach ($workbook->body->spreadsheet as $workbookData) { - $workbookData = $workbookData->children($namespacesContent['table']); + $officeNs = $dom->lookupNamespaceUri('office'); + $tableNs = $dom->lookupNamespaceUri('table'); + $textNs = $dom->lookupNamespaceUri('text'); + $xlinkNs = $dom->lookupNamespaceUri('xlink'); + + $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body') + ->item(0) + ->getElementsByTagNameNS($officeNs, 'spreadsheet'); + + foreach ($spreadsheets as $workbookData) { + /** @var \DOMElement $workbookData */ + $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table'); + $worksheetID = 0; - foreach ($workbookData->table as $worksheetDataSet) { - $worksheetData = $worksheetDataSet->children($namespacesContent['table']); - $worksheetDataAttributes = $worksheetDataSet->attributes($namespacesContent['table']); - if ((isset($this->loadSheetsOnly)) && (isset($worksheetDataAttributes['name'])) && - (!in_array($worksheetDataAttributes['name'], $this->loadSheetsOnly))) { + foreach ($tables as $worksheetDataSet) { + /** @var \DOMElement $worksheetDataSet */ + $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name'); + + // Check loadSheetsOnly + if (isset($this->loadSheetsOnly) + && $worksheetName + && !in_array($worksheetName, $this->loadSheetsOnly)) { continue; } - // Create new Worksheet - $spreadsheet->createSheet(); + // Create sheet + if ($worksheetID > 0) { + $spreadsheet->createSheet(); // First sheet is added by default + } $spreadsheet->setActiveSheetIndex($worksheetID); - if (isset($worksheetDataAttributes['name'])) { - $worksheetName = (string) $worksheetDataAttributes['name']; - // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in - // formula cells... during the load, all formulae should be correct, and we're simply - // bringing the worksheet name in line with the formula, not the reverse + + if ($worksheetName) { + // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in + // formula cells... during the load, all formulae should be correct, and we're simply + // bringing the worksheet name in line with the formula, not the reverse $spreadsheet->getActiveSheet()->setTitle($worksheetName, false); } + // Go through every child of table element $rowID = 1; - foreach ($worksheetData as $key => $rowData) { + foreach ($worksheetDataSet->childNodes as $childNode) { + /** @var \DOMElement $childNode */ + + // Filter elements which are not under the "table" ns + if ($childNode->namespaceURI != $tableNs) { + continue; + } + + $key = $childNode->nodeName; + + // Remove ns from node name + if (strpos($key, ':') !== false) { + $keyChunks = explode(':', $key); + $key = array_pop($keyChunks); + } + switch ($key) { case 'table-header-rows': - foreach ($rowData as $keyRowData => $cellData) { - $rowData = $cellData; - break; - } + /// TODO :: Figure this out. This is only a partial implementation I guess. + // ($rowData it's not used at all and I'm not sure that PHPExcel + // has an API for this) + +// foreach ($rowData as $keyRowData => $cellData) { +// $rowData = $cellData; +// break; +// } break; case 'table-row': - $rowDataTableAttributes = $rowData->attributes($namespacesContent['table']); - $rowRepeats = (isset($rowDataTableAttributes['number-rows-repeated'])) ? $rowDataTableAttributes['number-rows-repeated'] : 1; + if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) { + $rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated'); + } else { + $rowRepeats = 1; + } + $columnID = 'A'; - foreach ($rowData as $key => $cellData) { + foreach ($childNode->childNodes as $key => $cellData) { + /* @var \DOMElement $cellData */ + if ($this->getReadFilter() !== null) { if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { ++$columnID; @@ -433,93 +483,101 @@ class Ods extends BaseReader implements IReader } } - $cellDataText = (isset($namespacesContent['text'])) ? $cellData->children($namespacesContent['text']) : ''; - $cellDataOffice = $cellData->children($namespacesContent['office']); - $cellDataOfficeAttributes = $cellData->attributes($namespacesContent['office']); - $cellDataTableAttributes = $cellData->attributes($namespacesContent['table']); - - $type = $formatting = $hyperlink = null; + // Initialize variables + $formatting = $hyperlink = null; $hasCalculatedValue = false; $cellDataFormula = ''; - if (isset($cellDataTableAttributes['formula'])) { - $cellDataFormula = $cellDataTableAttributes['formula']; + + if ($cellData->hasAttributeNS($tableNs, 'formula')) { + $cellDataFormula = $cellData->getAttributeNS($tableNs, 'formula'); $hasCalculatedValue = true; } - if (isset($cellDataOffice->annotation)) { - $annotationText = $cellDataOffice->annotation->children($namespacesContent['text']); - $textArray = []; - foreach ($annotationText as $t) { - if (isset($t->span)) { - foreach ($t->span as $text) { - $textArray[] = (string) $text; - } - } else { - $textArray[] = (string) $t; - } - } - $text = implode("\n", $textArray); - $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setText($this->parseRichText($text)); + // Annotations + $annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation'); + + if ($annotation->length > 0) { + $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p'); + + if ($textNode->length > 0) { + $text = $this->scanElementForText($textNode->item(0)); + + $spreadsheet->getActiveSheet() + ->getComment($columnID . $rowID) + ->setText($this->parseRichText($text)); // ->setAuthor( $author ) + } } - if (isset($cellDataText->p)) { + // Content + + /** @var \DOMElement[] $paragraphs */ + $paragraphs = []; + + foreach ($cellData->childNodes as $item) { + /** @var \DOMElement $item */ + + // Filter text:p elements + if ($item->nodeName == 'text:p') { + $paragraphs[] = $item; + } + } + + if (count($paragraphs) > 0) { // Consolidate if there are multiple p records (maybe with spans as well) $dataArray = []; + // Text can have multiple text:p and within those, multiple text:span. // text:p newlines, but text:span does not. // Also, here we assume there is no text data is span fields are specified, since // we have no way of knowing proper positioning anyway. - foreach ($cellDataText->p as $pData) { - if (isset($pData->span)) { - // span sections do not newline, so we just create one large string here - $spanSection = ''; - foreach ($pData->span as $spanData) { - $spanSection .= $spanData; - } - array_push($dataArray, $spanSection); - } elseif (isset($pData->a)) { - //Reading the hyperlinks in p - array_push($dataArray, $pData->a); - } else { - array_push($dataArray, $pData); - } + + foreach ($paragraphs as $pData) { + $dataArray[] = $this->scanElementForText($pData); } $allCellDataText = implode($dataArray, "\n"); - switch ($cellDataOfficeAttributes['value-type']) { + $type = $cellData->getAttributeNS($officeNs, 'value-type'); + + switch ($type) { case 'string': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING; + $type = DataType::TYPE_STRING; $dataValue = $allCellDataText; - if (isset($dataValue->a)) { - $dataValue = $dataValue->a; - $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']); - $hyperlink = $cellXLinkAttributes['href']; + + foreach ($paragraphs as $paragraph) { + $link = $paragraph->getElementsByTagNameNS($textNs, 'a'); + if ($link->length > 0) { + $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href'); + } } + break; case 'boolean': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL; + $type = DataType::TYPE_BOOL; $dataValue = ($allCellDataText == 'TRUE') ? true : false; break; case 'percentage': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = (float) $cellDataOfficeAttributes['value']; + $type = DataType::TYPE_NUMERIC; + $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); + if (floor($dataValue) == $dataValue) { $dataValue = (int) $dataValue; } $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_PERCENTAGE_00; break; case 'currency': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = (float) $cellDataOfficeAttributes['value']; + $type = DataType::TYPE_NUMERIC; + $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); + if (floor($dataValue) == $dataValue) { $dataValue = (int) $dataValue; } $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_CURRENCY_USD_SIMPLE; break; case 'float': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = (float) $cellDataOfficeAttributes['value']; + $type = DataType::TYPE_NUMERIC; + $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); + if (floor($dataValue) == $dataValue) { if ($dataValue == (int) $dataValue) { $dataValue = (int) $dataValue; @@ -529,85 +587,155 @@ class Ods extends BaseReader implements IReader } break; case 'date': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dateObj = new DateTime($cellDataOfficeAttributes['date-value'], $GMT); + $type = DataType::TYPE_NUMERIC; + $value = $cellData->getAttributeNS($officeNs, 'date-value'); + + $dateObj = new DateTime($value, $GMT); $dateObj->setTimeZone($timezoneObj); - list($year, $month, $day, $hour, $minute, $second) = explode(' ', $dateObj->format('Y m d H i s')); - $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel($year, $month, $day, $hour, $minute, $second); + list($year, $month, $day, $hour, $minute, $second) = explode( + ' ', + $dateObj->format('Y m d H i s') + ); + + $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::formattedPHPToExcel( + $year, + $month, + $day, + $hour, + $minute, + $second + ); + if ($dataValue != floor($dataValue)) { - $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15 . ' ' . \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4; + $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15 + . ' ' + . \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4; } else { $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_XLSX15; } break; case 'time': - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC; - $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime('01-01-1970 ' . implode(':', sscanf($cellDataOfficeAttributes['time-value'], 'PT%dH%dM%dS')))); + $type = DataType::TYPE_NUMERIC; + + $timeValue = $cellData->getAttributeNS($officeNs, 'time-value'); + + $dataValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( + strtotime( + '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS')) + ) + ); $formatting = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_TIME4; break; + default: + $dataValue = null; } } else { - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL; + $type = DataType::TYPE_NULL; $dataValue = null; } if ($hasCalculatedValue) { - $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA; + $type = DataType::TYPE_FORMULA; $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1); $temp = explode('"', $cellDataFormula); $tKey = false; foreach ($temp as &$value) { - // Only replace in alternate array entries (i.e. non-quoted blocks) + // Only replace in alternate array entries (i.e. non-quoted blocks) if ($tKey = !$tKey) { - $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); // Cell range reference in another sheet - $value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/Ui', '$1!$2', $value); // Cell reference in another sheet - $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value); // Cell range reference - $value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value); // Simple cell reference - $value = \PhpOffice\PhpSpreadsheet\Calculation::translateSeparator(';', ',', $value, $inBraces); + // Cell range reference in another sheet + $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/Ui', '$1!$2:$3', $value); + + // Cell reference in another sheet + $value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/Ui', '$1!$2', $value); + + // Cell range reference + $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/Ui', '$1:$2', $value); + + // Simple cell reference + $value = preg_replace('/\[\.([^\.]+)\]/Ui', '$1', $value); + + $value = Calculation::translateSeparator(';', ',', $value, $inBraces); } } unset($value); - // Then rebuild the formula string + + // Then rebuild the formula string $cellDataFormula = implode('"', $temp); } - $colRepeats = (isset($cellDataTableAttributes['number-columns-repeated'])) ? $cellDataTableAttributes['number-columns-repeated'] : 1; + if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) { + $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated'); + } else { + $colRepeats = 1; + } + if ($type !== null) { for ($i = 0; $i < $colRepeats; ++$i) { if ($i > 0) { ++$columnID; } - if ($type !== \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL) { + + if ($type !== DataType::TYPE_NULL) { for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) { $rID = $rowID + $rowAdjust; - $spreadsheet->getActiveSheet()->getCell($columnID . $rID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $dataValue), $type); + + $cell = $spreadsheet->getActiveSheet() + ->getCell($columnID . $rID); + + // Set value if ($hasCalculatedValue) { - $spreadsheet->getActiveSheet()->getCell($columnID . $rID)->setCalculatedValue($dataValue); - } - if ($formatting !== null) { - $spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode($formatting); + $cell->setValueExplicit($cellDataFormula, $type); } else { - $spreadsheet->getActiveSheet()->getStyle($columnID . $rID)->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_GENERAL); + $cell->setValueExplicit($dataValue, $type); } + + if ($hasCalculatedValue) { + $cell->setCalculatedValue($dataValue); + } + + // Set other properties + if ($formatting !== null) { + $spreadsheet->getActiveSheet() + ->getStyle($columnID . $rID) + ->getNumberFormat() + ->setFormatCode($formatting); + } else { + $spreadsheet->getActiveSheet() + ->getStyle($columnID . $rID) + ->getNumberFormat() + ->setFormatCode(NumberFormat::FORMAT_GENERAL); + } + if ($hyperlink !== null) { - $spreadsheet->getActiveSheet()->getCell($columnID . $rID)->getHyperlink()->setUrl($hyperlink); + $cell->getHyperlink() + ->setUrl($hyperlink); } } } } } - // Merged cells - if ((isset($cellDataTableAttributes['number-columns-spanned'])) || (isset($cellDataTableAttributes['number-rows-spanned']))) { - if (($type !== \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL) || (!$this->readDataOnly)) { + // Merged cells + if ($childNode->hasAttributeNS($tableNs, 'number-columns-spanned') + || $childNode->hasAttributeNS($tableNs, 'number-rows-spanned') + ) { + if (($type !== DataType::TYPE_NULL) || (!$this->readDataOnly)) { $columnTo = $columnID; - if (isset($cellDataTableAttributes['number-columns-spanned'])) { - $columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(\PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID) + $cellDataTableAttributes['number-columns-spanned'] - 2); + + if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) { + $columnIndex = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID); + $columnIndex += (int) $cellData->getAttributeNS($tableNs, 'number-columns-spanned'); + $columnIndex -= 2; + + $columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($columnIndex); } + $rowTo = $rowID; - if (isset($cellDataTableAttributes['number-rows-spanned'])) { - $rowTo = $rowTo + $cellDataTableAttributes['number-rows-spanned'] - 1; + + if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) { + $rowTo = $rowTo + (int) $cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1; } + $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo; $spreadsheet->getActiveSheet()->mergeCells($cellRange); } @@ -627,10 +755,51 @@ class Ods extends BaseReader implements IReader return $spreadsheet; } - private function parseRichText($is = '') + /** + * Recursively scan element. + * + * @param \DOMNode $element + * + * @return string + */ + protected function scanElementForText(\DOMNode $element) + { + $str = ''; + foreach ($element->childNodes as $child) { + /** @var \DOMNode $child */ + if ($child->nodeType == XML_TEXT_NODE) { + $str .= $child->nodeValue; + } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') { + // It's a space + + // Multiple spaces? + /** @var \DOMAttr $cAttr */ + $cAttr = $child->attributes->getNamedItem('c'); + if ($cAttr) { + $multiplier = (int) $cAttr->nodeValue; + } else { + $multiplier = 1; + } + + $str .= str_repeat(' ', $multiplier); + } + + if ($child->hasChildNodes()) { + $str .= $this->scanElementForText($child); + } + } + + return $str; + } + + /** + * @param string $is + * + * @return \PhpOffice\PhpSpreadsheet\RichText + */ + private function parseRichText($is) { $value = new \PhpOffice\PhpSpreadsheet\RichText(); - $value->createText($is); return $value; diff --git a/src/PhpSpreadsheet/Reader/Slk.php b/src/PhpSpreadsheet/Reader/Slk.php index 6e422fb6..dbe2cd0f 100644 --- a/src/PhpSpreadsheet/Reader/Slk.php +++ b/src/PhpSpreadsheet/Reader/Slk.php @@ -99,9 +99,9 @@ class Slk extends BaseReader implements IReader /** * Set input encoding. * - * @param string $pValue Input encoding + * @param string $pValue Input encoding, eg: 'ANSI' */ - public function setInputEncoding($pValue = 'ANSI') + public function setInputEncoding($pValue) { $this->inputEncoding = $pValue; @@ -474,7 +474,7 @@ class Slk extends BaseReader implements IReader * * @return SYLK */ - public function setSheetIndex($pValue = 0) + public function setSheetIndex($pValue) { $this->sheetIndex = $pValue; diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 4e171ac3..801b27d7 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -1629,8 +1629,20 @@ class Xls extends BaseReader implements IReader $cbRuns = self::getInt2d($recordData, 12); $text = $this->getSplicedRecordData(); + $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1; + $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte); + // get 1 byte + $is16Bit = ord($text['recordData'][0]); + // it is possible to use a compressed format, + // which omits the high bytes of all characters, if they are all zero + if (($is16Bit & 0x01) === 0) { + $textStr = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1'); + } else { + $textStr = $this->decodeCodepage($textStr); + } + $this->textObjects[$this->textObjRef] = [ - 'text' => substr($text['recordData'], $text['spliceOffsets'][0] + 1, $cchText), + 'text' => $textStr, 'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns), 'alignment' => $grbitOpts, 'rotation' => $rot, @@ -7453,7 +7465,7 @@ class Xls extends BaseReader implements IReader return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; } - private function parseRichText($is = '') + private function parseRichText($is) { $value = new \PhpOffice\PhpSpreadsheet\RichText(); $value->createText($is); diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index fa100980..a0e98187 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -14,12 +14,12 @@ use PhpOffice\PhpSpreadsheet\Shared\Drawing; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Shared\Font; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; -use PhpOffice\PhpSpreadsheet\Shared\ZipArchive; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; +use ZipArchive; /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. @@ -81,17 +81,9 @@ class Xlsx extends BaseReader implements IReader { File::assertFile($pFilename); - $zipClass = Settings::getZipClass(); - - // Check if zip class exists -// if (!class_exists($zipClass, false)) { -// throw new Exception($zipClass . " library is not enabled"); -// } - $xl = false; // Load file - /** @var \ZipArchive | ZipArchive $zip */ - $zip = new $zipClass(); + $zip = new ZipArchive(); if ($zip->open($pFilename) === true) { // check if it is an OOXML archive $rels = simplexml_load_string( @@ -133,10 +125,7 @@ class Xlsx extends BaseReader implements IReader $worksheetNames = []; - $zipClass = Settings::getZipClass(); - - /** @var \ZipArchive | ZipArchive $zip */ - $zip = new $zipClass(); + $zip = new ZipArchive(); $zip->open($pFilename); // The files we're looking at here are small enough that simpleXML is more efficient than XMLReader @@ -179,10 +168,7 @@ class Xlsx extends BaseReader implements IReader $worksheetInfo = []; - $zipClass = Settings::getZipClass(); - - /** @var \ZipArchive | ZipArchive $zip */ - $zip = new $zipClass(); + $zip = new ZipArchive(); $zip->open($pFilename); $rels = simplexml_load_string( @@ -320,12 +306,12 @@ class Xlsx extends BaseReader implements IReader } /** - * @param \ZipArchive | ZipArchive $archive + * @param ZipArchive $archive * @param string $fileName * * @return string */ - private function getFromZipArchive($archive, $fileName = '') + private function getFromZipArchive(ZipArchive $archive, $fileName = '') { // Root-relative paths if (strpos($fileName, '//') !== false) { @@ -338,11 +324,11 @@ class Xlsx extends BaseReader implements IReader // Apache POI fixes $contents = $archive->getFromIndex( - $archive->locateName($fileName, \ZipArchive::FL_NOCASE) + $archive->locateName($fileName, ZipArchive::FL_NOCASE) ); if ($contents === false) { $contents = $archive->getFromIndex( - $archive->locateName(substr($fileName, 1), \ZipArchive::FL_NOCASE) + $archive->locateName(substr($fileName, 1), ZipArchive::FL_NOCASE) ); } @@ -370,10 +356,7 @@ class Xlsx extends BaseReader implements IReader $excel->removeCellXfByIndex(0); // remove the default style } - $zipClass = Settings::getZipClass(); - - /** @var \ZipArchive | ZipArchive $zip */ - $zip = new $zipClass(); + $zip = new ZipArchive(); $zip->open($pFilename); // Read the theme first, because we need the colour scheme when reading the styles @@ -916,7 +899,6 @@ class Xlsx extends BaseReader implements IReader // Formula $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToBoolean'); if (isset($c->f['t'])) { - $att = []; $att = $c->f; $docSheet->getCell($r)->setFormulaAttributes($att); } @@ -2052,7 +2034,7 @@ class Xlsx extends BaseReader implements IReader * * @return RichText */ - private function parseRichText($is = null) + private function parseRichText($is) { $value = new RichText(); @@ -2197,7 +2179,7 @@ class Xlsx extends BaseReader implements IReader return $style; } - private static function boolean($value = null) + private static function boolean($value) { if (is_object($value)) { $value = (string) $value; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index af96c998..48564d6d 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -88,17 +88,17 @@ class Chart break; case 'catAx': if (isset($chartDetail->title)) { - $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta, 'cat'); + $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); } break; case 'dateAx': if (isset($chartDetail->title)) { - $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta, 'cat'); + $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); } break; case 'valAx': if (isset($chartDetail->title)) { - $YaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta, 'cat'); + $YaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); } break; case 'barChart': @@ -176,7 +176,7 @@ class Chart $dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string'); break; case 'title': - $title = self::chartTitle($chartDetails, $namespacesChartMeta, 'title'); + $title = self::chartTitle($chartDetails, $namespacesChartMeta); break; case 'legend': $legendPos = 'r'; @@ -206,7 +206,7 @@ class Chart return $chart; } - private static function chartTitle($titleDetails, $namespacesChartMeta, $type) + private static function chartTitle(\SimpleXMLElement $titleDetails, array $namespacesChartMeta) { $caption = []; $titleLayout = null; @@ -399,7 +399,7 @@ class Chart ]; } - private static function parseRichText($titleDetailPart = null) + private static function parseRichText(\SimpleXMLElement $titleDetailPart) { $value = new \PhpOffice\PhpSpreadsheet\RichText(); diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Theme.php b/src/PhpSpreadsheet/Reader/Xlsx/Theme.php index 5767ded8..ba014537 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Theme.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Theme.php @@ -96,7 +96,7 @@ class Theme * * @return string */ - public function getColourByIndex($index = 0) + public function getColourByIndex($index) { if (isset($this->colourMap[$index])) { return $this->colourMap[$index]; diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index df0fa48e..00e29808 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -770,7 +770,7 @@ class Xml extends BaseReader implements IReader return $string; } - protected function parseRichText($is = '') + protected function parseRichText($is) { $value = new \PhpOffice\PhpSpreadsheet\RichText(); diff --git a/src/PhpSpreadsheet/ReferenceHelper.php b/src/PhpSpreadsheet/ReferenceHelper.php index f4073a5f..30eb9436 100644 --- a/src/PhpSpreadsheet/ReferenceHelper.php +++ b/src/PhpSpreadsheet/ReferenceHelper.php @@ -384,12 +384,12 @@ class ReferenceHelper * * @throws Exception */ - public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, Worksheet $pSheet = null) + public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pSheet) { $remove = ($pNumCols < 0 || $pNumRows < 0); - $aCellCollection = $pSheet->getCellCollection(); + $allCoordinates = $pSheet->getCoordinates(); - // Get coordinates of $pBefore + // Get coordinate of $pBefore $beforeColumn = 'A'; $beforeRow = 1; list($beforeColumn, $beforeRow) = Cell::coordinateFromString($pBefore); @@ -427,39 +427,39 @@ class ReferenceHelper } } - // Loop through cells, bottom-up, and change cell coordinates + // Loop through cells, bottom-up, and change cell coordinate if ($remove) { // It's faster to reverse and pop than to use unshift, especially with large cell collections - $aCellCollection = array_reverse($aCellCollection); + $allCoordinates = array_reverse($allCoordinates); } - while ($cellID = array_pop($aCellCollection)) { - $cell = $pSheet->getCell($cellID); + while ($coordinate = array_pop($allCoordinates)) { + $cell = $pSheet->getCell($coordinate); $cellIndex = Cell::columnIndexFromString($cell->getColumn()); if ($cellIndex - 1 + $pNumCols < 0) { continue; } - // New coordinates - $newCoordinates = Cell::stringFromColumnIndex($cellIndex - 1 + $pNumCols) . ($cell->getRow() + $pNumRows); + // New coordinate + $newCoordinate = Cell::stringFromColumnIndex($cellIndex - 1 + $pNumCols) . ($cell->getRow() + $pNumRows); // Should the cell be updated? Move value and cellXf index from one cell to another. if (($cellIndex >= $beforeColumnIndex) && ($cell->getRow() >= $beforeRow)) { // Update cell styles - $pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex()); + $pSheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex()); // Insert this cell at its new location if ($cell->getDataType() == Cell\DataType::TYPE_FORMULA) { // Formula should be adjusted - $pSheet->getCell($newCoordinates) + $pSheet->getCell($newCoordinate) ->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle())); } else { // Formula should not be adjusted - $pSheet->getCell($newCoordinates)->setValue($cell->getValue()); + $pSheet->getCell($newCoordinate)->setValue($cell->getValue()); } // Clear the original cell - $pSheet->getCellCacheController()->deleteCacheData($cellID); + $pSheet->getCellCollection()->delete($coordinate); } else { /* We don't need to update styles for rows/columns before our insertion position, but we do still need to adjust any formulae in those cells */ @@ -818,8 +818,8 @@ class ReferenceHelper } foreach ($spreadsheet->getWorksheetIterator() as $sheet) { - foreach ($sheet->getCellCollection(false) as $cellID) { - $cell = $sheet->getCell($cellID); + foreach ($sheet->getCoordinates(false) as $coordinate) { + $cell = $sheet->getCell($coordinate); if (($cell !== null) && ($cell->getDataType() == Cell\DataType::TYPE_FORMULA)) { $formula = $cell->getValue(); if (strpos($formula, $oldName) !== false) { @@ -886,10 +886,10 @@ class ReferenceHelper private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) { if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) { - // Get coordinates of $pBefore + // Get coordinate of $pBefore list($beforeColumn, $beforeRow) = Cell::coordinateFromString($pBefore); - // Get coordinates of $pCellReference + // Get coordinate of $pCellReference list($newColumn, $newRow) = Cell::coordinateFromString($pCellReference); // Verify which parts should be updated diff --git a/src/PhpSpreadsheet/RichText.php b/src/PhpSpreadsheet/RichText.php index 5f4efe2a..8c9dc2f2 100644 --- a/src/PhpSpreadsheet/RichText.php +++ b/src/PhpSpreadsheet/RichText.php @@ -68,7 +68,7 @@ class RichText implements IComparable * * @return RichText */ - public function addText(RichText\ITextElement $pText = null) + public function addText(RichText\ITextElement $pText) { $this->richTextElements[] = $pText; @@ -84,7 +84,7 @@ class RichText implements IComparable * * @return RichText\TextElement */ - public function createText($pText = '') + public function createText($pText) { $objText = new RichText\TextElement($pText); $this->addText($objText); @@ -101,7 +101,7 @@ class RichText implements IComparable * * @return RichText\Run */ - public function createTextRun($pText = '') + public function createTextRun($pText) { $objText = new RichText\Run($pText); $this->addText($objText); @@ -150,19 +150,15 @@ class RichText implements IComparable /** * Set Rich Text elements. * - * @param RichText\ITextElement[] $pElements Array of elements + * @param RichText\ITextElement[] $textElements Array of elements * * @throws Exception * * @return RichText */ - public function setRichTextElements($pElements = null) + public function setRichTextElements(array $textElements) { - if (is_array($pElements)) { - $this->richTextElements = $pElements; - } else { - throw new Exception("Invalid \PhpOffice\PhpSpreadsheet\RichText\ITextElement[] array passed."); - } + $this->richTextElements = $textElements; return $this; } diff --git a/src/PhpSpreadsheet/RichText/ITextElement.php b/src/PhpSpreadsheet/RichText/ITextElement.php index ae8081da..c69916e1 100644 --- a/src/PhpSpreadsheet/RichText/ITextElement.php +++ b/src/PhpSpreadsheet/RichText/ITextElement.php @@ -34,11 +34,11 @@ interface ITextElement /** * Set text. * - * @param $pText string Text + * @param $text string Text * * @return ITextElement */ - public function setText($pText = ''); + public function setText($text); /** * Get font. diff --git a/src/PhpSpreadsheet/RichText/TextElement.php b/src/PhpSpreadsheet/RichText/TextElement.php index 4bf405e7..5bc9bfca 100644 --- a/src/PhpSpreadsheet/RichText/TextElement.php +++ b/src/PhpSpreadsheet/RichText/TextElement.php @@ -55,13 +55,13 @@ class TextElement implements ITextElement /** * Set text. * - * @param $pText string Text + * @param $text string Text * * @return ITextElement */ - public function setText($pText = '') + public function setText($text) { - $this->text = $pText; + $this->text = $text; return $this; } diff --git a/src/PhpSpreadsheet/Settings.php b/src/PhpSpreadsheet/Settings.php index 30be893f..e3bac89d 100644 --- a/src/PhpSpreadsheet/Settings.php +++ b/src/PhpSpreadsheet/Settings.php @@ -2,6 +2,9 @@ namespace PhpOffice\PhpSpreadsheet; +use PhpOffice\PhpSpreadsheet\Collection\Memory; +use Psr\SimpleCache\CacheInterface; + /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. * @@ -26,11 +29,6 @@ namespace PhpOffice\PhpSpreadsheet; */ class Settings { - /** constants */ - /** Available Zip library classes */ - const PCLZIP = \PhpOffice\PhpSpreadsheet\Shared\ZipArchive::class; - const ZIPARCHIVE = \ZipArchive::class; - /** Optional Chart Rendering libraries */ const CHART_RENDERER_JPGRAPH = 'JpGraph'; @@ -42,22 +40,12 @@ class Settings private static $chartRenderers = [ self::CHART_RENDERER_JPGRAPH, ]; - private static $pdfRenderers = [ self::PDF_RENDERER_TCPDF, self::PDF_RENDERER_DOMPDF, self::PDF_RENDERER_MPDF, ]; - /** - * Name of the class used for Zip file management - * e.g. - * ZipArchive. - * - * @var string - */ - private static $zipClass = self::ZIPARCHIVE; - /** * Name of the external Library used for rendering charts * e.g. @@ -91,70 +79,11 @@ class Settings private static $libXmlLoaderOptions = null; /** - * Set the Zip handler Class that PhpSpreadsheet should use for Zip file management (PCLZip or ZipArchive). + * The cache implementation to be used for cell collection. * - * @param string $zipClass The Zip handler class that PhpSpreadsheet should use for Zip file management - * e.g. \PhpOffice\PhpSpreadsheet\Settings::PCLZIP or \PhpOffice\PhpSpreadsheet\Settings::ZIPARCHIVE - * - * @return bool Success or failure + * @var CacheInterface */ - public static function setZipClass($zipClass) - { - if (($zipClass === self::PCLZIP) || - ($zipClass === self::ZIPARCHIVE)) { - self::$zipClass = $zipClass; - - return true; - } - - return false; - } - - /** - * Return the name of the Zip handler Class that PhpSpreadsheet is configured to use (PCLZip or ZipArchive) - * or Zip file management. - * - * @return string Name of the Zip handler Class that PhpSpreadsheet is configured to use - * for Zip file management - * e.g. \PhpOffice\PhpSpreadsheet\Settings::PCLZIP or \PhpOffice\PhpSpreadsheet\Settings::ZIPARCHIVE - */ - public static function getZipClass() - { - return self::$zipClass; - } - - /** - * Return the name of the method that is currently configured for cell cacheing. - * - * @return string Name of the cacheing method - */ - public static function getCacheStorageMethod() - { - return CachedObjectStorageFactory::getCacheStorageMethod(); - } - - /** - * Return the name of the class that is currently being used for cell cacheing. - * - * @return string Name of the class currently being used for cacheing - */ - public static function getCacheStorageClass() - { - return CachedObjectStorageFactory::getCacheStorageClass(); - } - - /** - * Set the method that should be used for cell caching. - * - * @param string $method Name of the caching method - * @param array $arguments Optional configuration arguments for the caching method - * - * @return bool Success or failure - */ - public static function setCacheStorageMethod($method = CachedObjectStorageFactory::CACHE_IN_MEMORY, $arguments = []) - { - return CachedObjectStorageFactory::initialize($method, $arguments); - } + private static $cache; /** * Set the locale code to use for formula translations and any special formatting. @@ -163,7 +92,7 @@ class Settings * * @return bool Success or failure */ - public static function setLocale($locale = 'en_us') + public static function setLocale($locale) { return Calculation::getInstance()->setLocale($locale); } @@ -279,12 +208,11 @@ class Settings * * @param int $options Default options for libxml loader */ - public static function setLibXmlLoaderOptions($options = null) + public static function setLibXmlLoaderOptions($options) { if (is_null($options) && defined('LIBXML_DTDLOAD')) { $options = LIBXML_DTDLOAD | LIBXML_DTDATTR; } - @libxml_disable_entity_loader((bool) $options); self::$libXmlLoaderOptions = $options; } @@ -301,8 +229,31 @@ class Settings } elseif (is_null(self::$libXmlLoaderOptions)) { self::$libXmlLoaderOptions = true; } - @libxml_disable_entity_loader((bool) self::$libXmlLoaderOptions); return self::$libXmlLoaderOptions; } + + /** + * Sets the implementation of cache that should be used for cell collection. + * + * @param CacheInterface $cache + */ + public static function setCache(CacheInterface $cache) + { + self::$cache = $cache; + } + + /** + * Gets the implementation of cache that should be used for cell collection. + * + * @return CacheInterface + */ + public static function getCache() + { + if (!self::$cache) { + self::$cache = new Memory(); + } + + return self::$cache; + } } diff --git a/src/PhpSpreadsheet/Shared/CodePage.php b/src/PhpSpreadsheet/Shared/CodePage.php index 4645e9fd..6e56d231 100644 --- a/src/PhpSpreadsheet/Shared/CodePage.php +++ b/src/PhpSpreadsheet/Shared/CodePage.php @@ -36,7 +36,7 @@ class CodePage * * @return string Code Page Name */ - public static function numberToName($codePage = 1252) + public static function numberToName($codePage) { switch ($codePage) { case 367: diff --git a/src/PhpSpreadsheet/Shared/Date.php b/src/PhpSpreadsheet/Shared/Date.php index d04e0bdc..a7409e38 100644 --- a/src/PhpSpreadsheet/Shared/Date.php +++ b/src/PhpSpreadsheet/Shared/Date.php @@ -176,7 +176,7 @@ class Date * * @return \DateTime PHP date/time object */ - public static function excelToDateTimeObject($excelTimestamp = 0, $timeZone = null) + public static function excelToDateTimeObject($excelTimestamp, $timeZone = null) { $timeZone = ($timeZone === null) ? self::getDefaultTimezone() : self::validateTimeZone($timeZone); if ($excelTimestamp < 1.0) { @@ -217,7 +217,7 @@ class Date * * @return int Unix timetamp for this date/time */ - public static function excelToTimestamp($excelTimestamp = 0, $timeZone = null) + public static function excelToTimestamp($excelTimestamp, $timeZone = null) { return (int) self::excelToDateTimeObject($excelTimestamp, $timeZone) ->format('U'); @@ -231,7 +231,7 @@ class Date * @return float|bool Excel date/time value * or boolean FALSE on failure */ - public static function PHPToExcel($dateValue = 0) + public static function PHPToExcel($dateValue) { if ((is_object($dateValue)) && ($dateValue instanceof \DateTimeInterface)) { return self::dateTimeToExcel($dateValue); @@ -251,7 +251,7 @@ class Date * * @return float MS Excel serialized date/time value */ - public static function dateTimeToExcel(\DateTimeInterface $dateValue = null) + public static function dateTimeToExcel(\DateTimeInterface $dateValue) { return self::formattedPHPToExcel( $dateValue->format('Y'), @@ -270,7 +270,7 @@ class Date * * @return float MS Excel serialized date/time value */ - public static function timestampToExcel($dateValue = 0) + public static function timestampToExcel($dateValue) { if (!is_numeric($dateValue)) { return false; @@ -363,7 +363,7 @@ class Date * * @return bool */ - public static function isDateTimeFormatCode($pFormatCode = '') + public static function isDateTimeFormatCode($pFormatCode) { if (strtolower($pFormatCode) === strtolower(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_GENERAL)) { // "General" contains an epoch letter 'e', so we trap for it explicitly here (case-insensitive check) @@ -437,7 +437,7 @@ class Date * * @return float|false Excel date/time serial value */ - public static function stringToExcel($dateValue = '') + public static function stringToExcel($dateValue) { if (strlen($dateValue) < 2) { return false; diff --git a/src/PhpSpreadsheet/Shared/Drawing.php b/src/PhpSpreadsheet/Shared/Drawing.php index 969d9bf8..436112be 100644 --- a/src/PhpSpreadsheet/Shared/Drawing.php +++ b/src/PhpSpreadsheet/Shared/Drawing.php @@ -33,7 +33,7 @@ class Drawing * * @return int Value in EMU */ - public static function pixelsToEMU($pValue = 0) + public static function pixelsToEMU($pValue) { return round($pValue * 9525); } @@ -45,7 +45,7 @@ class Drawing * * @return int Value in pixels */ - public static function EMUToPixels($pValue = 0) + public static function EMUToPixels($pValue) { if ($pValue != 0) { return round($pValue / 9525); @@ -118,7 +118,7 @@ class Drawing * * @return float Value in points */ - public static function pixelsToPoints($pValue = 0) + public static function pixelsToPoints($pValue) { return $pValue * 0.67777777; } @@ -130,7 +130,7 @@ class Drawing * * @return int Value in pixels */ - public static function pointsToPixels($pValue = 0) + public static function pointsToPixels($pValue) { if ($pValue != 0) { return (int) ceil($pValue * 1.333333333); @@ -146,7 +146,7 @@ class Drawing * * @return int Angle */ - public static function degreesToAngle($pValue = 0) + public static function degreesToAngle($pValue) { return (int) round($pValue * 60000); } @@ -158,7 +158,7 @@ class Drawing * * @return int Degrees */ - public static function angleToDegrees($pValue = 0) + public static function angleToDegrees($pValue) { if ($pValue != 0) { return round($pValue / 60000); diff --git a/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php index a1eac1df..c2b6787a 100644 --- a/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php +++ b/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php @@ -135,7 +135,7 @@ class SpContainer * * @param bool $value */ - public function setSpgr($value = false) + public function setSpgr($value) { $this->spgr = $value; } @@ -250,9 +250,9 @@ class SpContainer /** * Set cell coordinates of upper-left corner of shape. * - * @param string $value + * @param string $value eg: 'A1' */ - public function setStartCoordinates($value = 'A1') + public function setStartCoordinates($value) { $this->startCoordinates = $value; } @@ -272,7 +272,7 @@ class SpContainer * * @param int $startOffsetX */ - public function setStartOffsetX($startOffsetX = 0) + public function setStartOffsetX($startOffsetX) { $this->startOffsetX = $startOffsetX; } @@ -292,7 +292,7 @@ class SpContainer * * @param int $startOffsetY */ - public function setStartOffsetY($startOffsetY = 0) + public function setStartOffsetY($startOffsetY) { $this->startOffsetY = $startOffsetY; } @@ -310,9 +310,9 @@ class SpContainer /** * Set cell coordinates of bottom-right corner of shape. * - * @param string $value + * @param string $value eg: 'A1' */ - public function setEndCoordinates($value = 'A1') + public function setEndCoordinates($value) { $this->endCoordinates = $value; } @@ -332,7 +332,7 @@ class SpContainer * * @param int $endOffsetX */ - public function setEndOffsetX($endOffsetX = 0) + public function setEndOffsetX($endOffsetX) { $this->endOffsetX = $endOffsetX; } @@ -352,7 +352,7 @@ class SpContainer * * @param int $endOffsetY */ - public function setEndOffsetY($endOffsetY = 0) + public function setEndOffsetY($endOffsetY) { $this->endOffsetY = $endOffsetY; } diff --git a/src/PhpSpreadsheet/Shared/File.php b/src/PhpSpreadsheet/Shared/File.php index 8d0f1a59..9efd83ff 100644 --- a/src/PhpSpreadsheet/Shared/File.php +++ b/src/PhpSpreadsheet/Shared/File.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Shared; +use ZipArchive; + /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. * @@ -40,7 +42,7 @@ class File * * @param bool $useUploadTempDir Use File Upload Temporary directory (true or false) */ - public static function setUseUploadTempDirectory($useUploadTempDir = false) + public static function setUseUploadTempDirectory($useUploadTempDir) { self::$useUploadTempDirectory = (bool) $useUploadTempDir; } @@ -72,8 +74,7 @@ class File $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6); $archiveFile = substr($pFilename, strpos($pFilename, '#') + 1); - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - $zip = new $zipClass(); + $zip = new ZipArchive(); if ($zip->open($zipFile) === true) { $returnValue = ($zip->getFromName($archiveFile) !== false); $zip->close(); @@ -83,8 +84,8 @@ class File return false; } - // Regular file_exists - return file_exists($pFilename); + + return file_exists($pFilename); } /** diff --git a/src/PhpSpreadsheet/Shared/Font.php b/src/PhpSpreadsheet/Shared/Font.php index dbef7701..befc2751 100644 --- a/src/PhpSpreadsheet/Shared/Font.php +++ b/src/PhpSpreadsheet/Shared/Font.php @@ -182,11 +182,11 @@ class Font /** * Set autoSize method. * - * @param string $pValue + * @param string $pValue see self::AUTOSIZE_METHOD_* * * @return bool Success or failure */ - public static function setAutoSizeMethod($pValue = self::AUTOSIZE_METHOD_APPROX) + public static function setAutoSizeMethod($pValue) { if (!in_array($pValue, self::$autoSizeMethods)) { return false; @@ -217,7 +217,7 @@ class Font * * @param string $pValue */ - public static function setTrueTypeFontPath($pValue = '') + public static function setTrueTypeFontPath($pValue) { self::$trueTypeFontPath = $pValue; } @@ -330,7 +330,7 @@ class Font * * @return int Text width in pixels (no padding added) */ - public static function getTextWidthPixelsApprox($columnText, \PhpOffice\PhpSpreadsheet\Style\Font $font = null, $rotation = 0) + public static function getTextWidthPixelsApprox($columnText, \PhpOffice\PhpSpreadsheet\Style\Font $font, $rotation = 0) { $fontName = $font->getName(); $fontSize = $font->getSize(); @@ -382,7 +382,7 @@ class Font * * @return int Font size (in pixels) */ - public static function fontSizeToPixels($fontSizeInPoints = 11) + public static function fontSizeToPixels($fontSizeInPoints) { return (int) ((4 / 3) * $fontSizeInPoints); } @@ -394,7 +394,7 @@ class Font * * @return int Size (in pixels) */ - public static function inchSizeToPixels($sizeInInch = 1) + public static function inchSizeToPixels($sizeInInch) { return $sizeInInch * 96; } @@ -406,7 +406,7 @@ class Font * * @return float Size (in pixels) */ - public static function centimeterSizeToPixels($sizeInCm = 1) + public static function centimeterSizeToPixels($sizeInCm) { return $sizeInCm * 37.795275591; } diff --git a/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php b/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php index 79b96566..99d20d8f 100644 --- a/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php +++ b/src/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php @@ -48,41 +48,35 @@ class CholeskyDecomposition * @param mixed Matrix square symmetric positive definite matrix * @param null|mixed $A */ - public function __construct($A = null) + public function __construct(Matrix $A) { - if ($A instanceof Matrix) { - $this->L = $A->getArray(); - $this->m = $A->getRowDimension(); + $this->L = $A->getArray(); + $this->m = $A->getRowDimension(); - for ($i = 0; $i < $this->m; ++$i) { - for ($j = $i; $j < $this->m; ++$j) { - for ($sum = $this->L[$i][$j], $k = $i - 1; $k >= 0; --$k) { - $sum -= $this->L[$i][$k] * $this->L[$j][$k]; - } - if ($i == $j) { - if ($sum >= 0) { - $this->L[$i][$i] = sqrt($sum); - } else { - $this->isspd = false; - } - } else { - if ($this->L[$i][$i] != 0) { - $this->L[$j][$i] = $sum / $this->L[$i][$i]; - } - } + for ($i = 0; $i < $this->m; ++$i) { + for ($j = $i; $j < $this->m; ++$j) { + for ($sum = $this->L[$i][$j], $k = $i - 1; $k >= 0; --$k) { + $sum -= $this->L[$i][$k] * $this->L[$j][$k]; } - - for ($k = $i + 1; $k < $this->m; ++$k) { - $this->L[$i][$k] = 0.0; + if ($i == $j) { + if ($sum >= 0) { + $this->L[$i][$i] = sqrt($sum); + } else { + $this->isspd = false; + } + } else { + if ($this->L[$i][$i] != 0) { + $this->L[$j][$i] = $sum / $this->L[$i][$i]; + } } } - } else { - throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(JAMAError(ARGUMENT_TYPE_EXCEPTION)); + + for ($k = $i + 1; $k < $this->m; ++$k) { + $this->L[$i][$k] = 0.0; + } } } - // function __construct() - /** * Is the matrix symmetric and positive definite? * @@ -93,8 +87,6 @@ class CholeskyDecomposition return $this->isspd; } - // function isSPD() - /** * getL. * @@ -107,8 +99,6 @@ class CholeskyDecomposition return new Matrix($this->L); } - // function getL() - /** * Solve A*X = B. * @@ -116,44 +106,39 @@ class CholeskyDecomposition * * @return Matrix L * L' * X = B */ - public function solve($B = null) + public function solve(Matrix $B) { - if ($B instanceof Matrix) { - if ($B->getRowDimension() == $this->m) { - if ($this->isspd) { - $X = $B->getArrayCopy(); - $nx = $B->getColumnDimension(); + if ($B->getRowDimension() == $this->m) { + if ($this->isspd) { + $X = $B->getArrayCopy(); + $nx = $B->getColumnDimension(); - for ($k = 0; $k < $this->m; ++$k) { - for ($i = $k + 1; $i < $this->m; ++$i) { - for ($j = 0; $j < $nx; ++$j) { - $X[$i][$j] -= $X[$k][$j] * $this->L[$i][$k]; - } - } + for ($k = 0; $k < $this->m; ++$k) { + for ($i = $k + 1; $i < $this->m; ++$i) { for ($j = 0; $j < $nx; ++$j) { - $X[$k][$j] /= $this->L[$k][$k]; + $X[$i][$j] -= $X[$k][$j] * $this->L[$i][$k]; } } - - for ($k = $this->m - 1; $k >= 0; --$k) { - for ($j = 0; $j < $nx; ++$j) { - $X[$k][$j] /= $this->L[$k][$k]; - } - for ($i = 0; $i < $k; ++$i) { - for ($j = 0; $j < $nx; ++$j) { - $X[$i][$j] -= $X[$k][$j] * $this->L[$k][$i]; - } - } + for ($j = 0; $j < $nx; ++$j) { + $X[$k][$j] /= $this->L[$k][$k]; } - - return new Matrix($X, $this->m, $nx); } - throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(JAMAError(MatrixSPDException)); - } - throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(JAMAError(MATRIX_DIMENSION_EXCEPTION)); - } - throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(JAMAError(ARGUMENT_TYPE_EXCEPTION)); - } - // function solve() + for ($k = $this->m - 1; $k >= 0; --$k) { + for ($j = 0; $j < $nx; ++$j) { + $X[$k][$j] /= $this->L[$k][$k]; + } + for ($i = 0; $i < $k; ++$i) { + for ($j = 0; $j < $nx; ++$j) { + $X[$i][$j] -= $X[$k][$j] * $this->L[$k][$i]; + } + } + } + + return new Matrix($X, $this->m, $nx); + } + throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(JAMAError(MatrixSPDException)); + } + throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(JAMAError(MATRIX_DIMENSION_EXCEPTION)); + } } diff --git a/src/PhpSpreadsheet/Shared/JAMA/utils/Error.php b/src/PhpSpreadsheet/Shared/JAMA/utils/Error.php index cc6a25e7..8aa78c55 100644 --- a/src/PhpSpreadsheet/Shared/JAMA/utils/Error.php +++ b/src/PhpSpreadsheet/Shared/JAMA/utils/Error.php @@ -67,17 +67,13 @@ $error['EN'][ROW_LENGTH_EXCEPTION] = 'All rows must have the same length.'; * * @param int $errorNumber Error number */ -function JAMAError($errorNumber = null) +function JAMAError($errorNumber) { global $error; - if (isset($errorNumber)) { - if (isset($error[JAMALANG][$errorNumber])) { - return $error[JAMALANG][$errorNumber]; - } - - return $error['EN'][$errorNumber]; + if (isset($error[JAMALANG][$errorNumber])) { + return $error[JAMALANG][$errorNumber]; } - return 'Invalid argument to JAMAError()'; + return $error['EN'][$errorNumber]; } diff --git a/src/PhpSpreadsheet/Shared/OLE.php b/src/PhpSpreadsheet/Shared/OLE.php index 33153c57..a2763f72 100644 --- a/src/PhpSpreadsheet/Shared/OLE.php +++ b/src/PhpSpreadsheet/Shared/OLE.php @@ -486,7 +486,7 @@ class OLE * * @return string The string for the OLE container */ - public static function localDateToOLE($date = null) + public static function localDateToOLE($date) { if (!isset($date)) { return "\x00\x00\x00\x00\x00\x00\x00\x00"; diff --git a/src/PhpSpreadsheet/Shared/PCLZip/PclZip.php b/src/PhpSpreadsheet/Shared/PCLZip/PclZip.php deleted file mode 100644 index 3b16db2c..00000000 --- a/src/PhpSpreadsheet/Shared/PCLZip/PclZip.php +++ /dev/null @@ -1,5245 +0,0 @@ -zipname = $p_zipname; - $this->zip_fd = 0; - $this->magic_quotes_status = -1; - - // ----- Return - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // create($p_filelist, $p_add_dir="", $p_remove_dir="") - // create($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two different synopsis. The first one is historical. - // This method creates a Zip Archive. The Zip file is created in the - // filesystem. The files and directories indicated in $p_filelist - // are added in the archive. See the parameters description for the - // supported format of $p_filelist. - // When a directory is in the list, the directory and its content is added - // in the archive. - // In this synopsis, the function takes an optional variable list of - // options. See bellow the supported options. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_COMMENT : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - public function create($p_filelist) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Set default values - $v_options = []; - $v_options[PCLZIP_OPT_NO_COMPRESSION] = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove from the options list the first argument - array_shift($v_arg_list); - --$v_size; - - // ----- Look for first arg - if ((is_int($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, [ - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional', - PCLZIP_OPT_COMMENT => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional', - //, PCLZIP_OPT_CRYPT => 'optional' - ]); - if ($v_result != 1) { - return 0; - } - } else { - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - // ----- Get the first argument - $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; - } elseif ($v_size > 2) { - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid number / type of arguments'); - - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Init - $v_string_list = []; - $v_att_list = []; - $v_filedescr_list = []; - $p_result_list = []; - - // ----- Look if the $p_filelist is really an array - if (is_array($p_filelist)) { - // ----- Look if the first element is also an array - // This will mean that this is a file description entry - if (isset($p_filelist[0]) && is_array($p_filelist[0])) { - $v_att_list = $p_filelist; - } else { - // ----- The list is a list of string names - $v_string_list = $p_filelist; - } - } elseif (is_string($p_filelist)) { - // ----- Look if the $p_filelist is a string - // ----- Create a list from the string - $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); - } else { - // ----- Invalid variable type for $p_filelist - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid variable type p_filelist'); - - return 0; - } - - // ----- Reformat the string list - if (count($v_string_list) != 0) { - foreach ($v_string_list as $v_string) { - if ($v_string != '') { - $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; - } - } - } - - // ----- For each file in the list check the attributes - $v_supported_attributes = [ - PCLZIP_ATT_FILE_NAME => 'mandatory', - PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional', - PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional', - PCLZIP_ATT_FILE_MTIME => 'optional', - PCLZIP_ATT_FILE_CONTENT => 'optional', - PCLZIP_ATT_FILE_COMMENT => 'optional', - ]; - foreach ($v_att_list as $v_entry) { - $v_result = $this->privFileDescrParseAtt($v_entry, $v_filedescr_list[], $v_options, $v_supported_attributes); - if ($v_result != 1) { - return 0; - } - } - - // ----- Expand the filelist (expand directories) - $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Call the create fct - $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Return - return $p_result_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // add($p_filelist, $p_add_dir="", $p_remove_dir="") - // add($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two synopsis. The first one is historical. - // This methods add the list of files in an existing archive. - // If a file with the same name already exists, it is added at the end of the - // archive, the first one is still present. - // If the archive does not exist, it is created. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_COMMENT : - // PCLZIP_OPT_ADD_COMMENT : - // PCLZIP_OPT_PREPEND_COMMENT : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - public function add($p_filelist) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Set default values - $v_options = []; - $v_options[PCLZIP_OPT_NO_COMPRESSION] = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - --$v_size; - - // ----- Look for first arg - if ((is_int($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, [ - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional', - PCLZIP_OPT_COMMENT => 'optional', - PCLZIP_OPT_ADD_COMMENT => 'optional', - PCLZIP_OPT_PREPEND_COMMENT => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional', - //, PCLZIP_OPT_CRYPT => 'optional' - ]); - if ($v_result != 1) { - return 0; - } - } else { - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - // ----- Get the first argument - $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; - } elseif ($v_size > 2) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid number / type of arguments'); - - // ----- Return - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Init - $v_string_list = []; - $v_att_list = []; - $v_filedescr_list = []; - $p_result_list = []; - - // ----- Look if the $p_filelist is really an array - if (is_array($p_filelist)) { - // ----- Look if the first element is also an array - // This will mean that this is a file description entry - if (isset($p_filelist[0]) && is_array($p_filelist[0])) { - $v_att_list = $p_filelist; - } else { - // ----- The list is a list of string names - $v_string_list = $p_filelist; - } - } elseif (is_string($p_filelist)) { - // ----- Look if the $p_filelist is a string - // ----- Create a list from the string - $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); - } else { - // ----- Invalid variable type for $p_filelist - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '" . gettype($p_filelist) . "' for p_filelist"); - - return 0; - } - - // ----- Reformat the string list - if (count($v_string_list) != 0) { - foreach ($v_string_list as $v_string) { - $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; - } - } - - // ----- For each file in the list check the attributes - $v_supported_attributes = [ - PCLZIP_ATT_FILE_NAME => 'mandatory', - PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional', - PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional', - PCLZIP_ATT_FILE_MTIME => 'optional', - PCLZIP_ATT_FILE_CONTENT => 'optional', - PCLZIP_ATT_FILE_COMMENT => 'optional', - ]; - foreach ($v_att_list as $v_entry) { - $v_result = $this->privFileDescrParseAtt($v_entry, $v_filedescr_list[], $v_options, $v_supported_attributes); - if ($v_result != 1) { - return 0; - } - } - - // ----- Expand the filelist (expand directories) - $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Call the create fct - $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Return - return $p_result_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : listContent() - // Description : - // This public method, gives the list of the files and directories, with their - // properties. - // The properties of each entries in the list are (used also in other functions) : - // filename : Name of the file. For a create or add action it is the filename - // given by the user. For an extract function it is the filename - // of the extracted file. - // stored_filename : Name of the file / directory stored in the archive. - // size : Size of the stored file. - // compressed_size : Size of the file's data compressed in the archive - // (without the headers overhead) - // mtime : Last known modification date of the file (UNIX timestamp) - // comment : Comment associated with the file - // folder : true | false - // index : index of the file in the archive - // status : status of the action (depending of the action) : - // Values are : - // ok : OK ! - // filtered : the file / dir is not extracted (filtered by user) - // already_a_directory : the file can not be extracted because a - // directory with the same name already exists - // write_protected : the file can not be extracted because a file - // with the same name already exists and is - // write protected - // newer_exist : the file was not extracted because a newer file exists - // path_creation_fail : the file is not extracted because the folder - // does not exist and can not be created - // write_error : the file was not extracted because there was a - // error while writing the file - // read_error : the file was not extracted because there was a error - // while reading the file - // invalid_header : the file was not extracted because of an archive - // format error (bad file header) - // Note that each time a method can continue operating when there - // is an action error on a file, the error is only logged in the file status. - // Return Values : - // 0 on an unrecoverable failure, - // The list of the files in the archive. - // -------------------------------------------------------------------------------- - public function listContent() - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return 0; - } - - // ----- Call the extracting fct - $p_list = []; - if (($v_result = $this->privList($p_list)) != 1) { - unset($p_list); - - return 0; - } - - // ----- Return - return $p_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // extract($p_path="./", $p_remove_path="") - // extract([$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method extract all the files / directories from the archive to the - // folder indicated in $p_path. - // If you want to ignore the 'root' part of path of the memorized files - // you can indicate this in the optional $p_remove_path parameter. - // By default, if a newer file with the same name already exists, the - // file is not extracted. - // - // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions - // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append - // at the end of the path value of PCLZIP_OPT_PATH. - // Parameters : - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 or a negative value on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - public function extract() - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return 0; - } - - // ----- Set default values - $v_options = []; - $v_path = ''; - $v_remove_path = ''; - $v_remove_all_path = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = false; - - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Look for first arg - if ((is_int($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, [ - PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', - PCLZIP_OPT_REPLACE_NEWER => 'optional', - PCLZIP_OPT_STOP_ON_ERROR => 'optional', - PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional', - ]); - if ($v_result != 1) { - return 0; - } - - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - } else { - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - // ----- Get the first argument - $v_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } elseif ($v_size > 2) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid number / type of arguments'); - - // ----- Return - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Trace - - // ----- Call the extracting fct - $p_list = []; - $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options); - if ($v_result < 1) { - unset($p_list); - - return 0; - } - - // ----- Return - return $p_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // extractByIndex($p_index, $p_path="./", $p_remove_path="") - // extractByIndex($p_index, [$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method is doing a partial extract of the archive. - // The extracted files or folders are identified by their index in the - // archive (from 0 to n). - // Note that if the index identify a folder, only the folder entry is - // extracted, not all the files included in the archive. - // Parameters : - // $p_index : A single index (integer) or a string of indexes of files to - // extract. The form of the string is "0,4-6,8-12" with only numbers - // and '-' for range or ',' to separate ranges. No spaces or ';' - // are allowed. - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and - // not as files. - // The resulting content is in a new field 'content' in the file - // structure. - // This option must be used alone (any other options are ignored). - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - //function extractByIndex($p_index, options...) - public function extractByIndex($p_index) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return 0; - } - - // ----- Set default values - $v_options = []; - $v_path = ''; - $v_remove_path = ''; - $v_remove_all_path = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = false; - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - --$v_size; - - // ----- Look for first arg - if ((is_int($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, [ - PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_REPLACE_NEWER => 'optional', - PCLZIP_OPT_STOP_ON_ERROR => 'optional', - PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional', - ]); - if ($v_result != 1) { - return 0; - } - - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = false; - } - } else { - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - - // ----- Get the first argument - $v_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } elseif ($v_size > 2) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid number / type of arguments'); - - // ----- Return - return 0; - } - } - } - - // ----- Trace - - // ----- Trick - // Here I want to reuse extractByRule(), so I need to parse the $p_index - // with privParseOptions() - $v_arg_trick = [PCLZIP_OPT_BY_INDEX, $p_index]; - $v_options_trick = []; - $v_result = $this->privParseOptions($v_arg_trick, count($v_arg_trick), $v_options_trick, [PCLZIP_OPT_BY_INDEX => 'optional']); - if ($v_result != 1) { - return 0; - } - $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Call the extracting fct - if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { - return 0; - } - - // ----- Return - return $p_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // delete([$p_option, $p_option_value, ...]) - // Description : - // This method removes files from the archive. - // If no parameters are given, then all the archive is emptied. - // Parameters : - // None or optional arguments. - // Options : - // PCLZIP_OPT_BY_INDEX : - // PCLZIP_OPT_BY_NAME : - // PCLZIP_OPT_BY_EREG : - // PCLZIP_OPT_BY_PREG : - // Return Values : - // 0 on failure, - // The list of the files which are still present in the archive. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - public function delete() - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return 0; - } - - // ----- Set default values - $v_options = []; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, [ - PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional', - ]); - if ($v_result != 1) { - return 0; - } - } - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Call the delete fct - $v_list = []; - if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { - $this->privSwapBackMagicQuotes(); - unset($v_list); - - return 0; - } - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : deleteByIndex() - // Description : - // ***** Deprecated ***** - // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. - // -------------------------------------------------------------------------------- - public function deleteByIndex($p_index) - { - $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); - - // ----- Return - return $p_list; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : properties() - // Description : - // This method gives the properties of the archive. - // The properties are : - // nb : Number of files in the archive - // comment : Comment associated with the archive file - // status : not_exist, ok - // Parameters : - // None - // Return Values : - // 0 on failure, - // An array with the archive properties. - // -------------------------------------------------------------------------------- - public function properties() - { - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - $this->privSwapBackMagicQuotes(); - - return 0; - } - - // ----- Default properties - $v_prop = []; - $v_prop['comment'] = ''; - $v_prop['nb'] = 0; - $v_prop['status'] = 'not_exist'; - - // ----- Look if file exists - if (@is_file($this->zipname)) { - // ----- Open the zip file - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { - $this->privSwapBackMagicQuotes(); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \'' . $this->zipname . '\' in binary read mode'); - - // ----- Return - return 0; - } - - // ----- Read the central directory informations - $v_central_dir = []; - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { - $this->privSwapBackMagicQuotes(); - - return 0; - } - - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Set the user attributes - $v_prop['comment'] = $v_central_dir['comment']; - $v_prop['nb'] = $v_central_dir['entries']; - $v_prop['status'] = 'ok'; - } - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_prop; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : duplicate() - // Description : - // This method creates an archive by copying the content of an other one. If - // the archive already exist, it is replaced by the new one without any warning. - // Parameters : - // $p_archive : The filename of a valid archive, or - // a valid PclZip object. - // Return Values : - // 1 on success. - // 0 or a negative value on error (error code). - // -------------------------------------------------------------------------------- - public function duplicate($p_archive) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Look if the $p_archive is a PclZip object - if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) { - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive->zipname); - } elseif (is_string($p_archive)) { - // ----- Look if the $p_archive is a string (so a filename) - // ----- Check that $p_archive is a valid zip file - // TBC : Should also check the archive format - if (!is_file($p_archive)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '" . $p_archive . "'"); - $v_result = PCLZIP_ERR_MISSING_FILE; - } else { - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive); - } - } else { - // ----- Invalid variable - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid variable type p_archive_to_add'); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : merge() - // Description : - // This method merge the $p_archive_to_add archive at the end of the current - // one ($this). - // If the archive ($this) does not exist, the merge becomes a duplicate. - // If the $p_archive_to_add archive does not exist, the merge is a success. - // Parameters : - // $p_archive_to_add : It can be directly the filename of a valid zip archive, - // or a PclZip object archive. - // Return Values : - // 1 on success, - // 0 or negative values on error (see below). - // -------------------------------------------------------------------------------- - public function merge($p_archive_to_add) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return 0; - } - - // ----- Look if the $p_archive_to_add is a PclZip object - if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) { - // ----- Merge the archive - $v_result = $this->privMerge($p_archive_to_add); - } elseif (is_string($p_archive_to_add)) { - // ----- Look if the $p_archive_to_add is a string (so a filename) - // ----- Create a temporary archive - $v_object_archive = new self($p_archive_to_add); - - // ----- Merge the archive - $v_result = $this->privMerge($v_object_archive); - } else { - // ----- Invalid variable - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid variable type p_archive_to_add'); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorCode() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function errorCode() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return PclErrorCode(); - } - - return $this->error_code; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorName() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function errorName($p_with_code = false) - { - $v_name = [ - PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', - PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', - PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', - PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', - PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', - PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', - PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', - PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', - PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', - PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', - PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', - PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', - PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', - PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', - PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', - PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', - PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', - PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', - PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', - PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', - PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION', - ]; - - if (isset($v_name[$this->error_code])) { - $v_value = $v_name[$this->error_code]; - } else { - $v_value = 'NoName'; - } - - if ($p_with_code) { - return $v_value . ' (' . $this->error_code . ')'; - } - - return $v_value; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorInfo() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function errorInfo($p_full = false) - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return PclErrorString(); - } - if ($p_full) { - return $this->errorName(true) . ' : ' . $this->error_string; - } - - return $this->error_string . ' [code ' . $this->error_code . ']'; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** - // ***** ***** - // ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCheckFormat() - // Description : - // This method check that the archive exists and is a valid zip archive. - // Several level of check exists. (futur) - // Parameters : - // $p_level : Level of check. Default 0. - // 0 : Check the first bytes (magic codes) (default value)) - // 1 : 0 + Check the central directory (futur) - // 2 : 1 + Check each file header (futur) - // Return Values : - // true on success, - // false on error, the error code is set. - // -------------------------------------------------------------------------------- - public function privCheckFormat($p_level = 0) - { - $v_result = true; - - // ----- Reset the file system cache - clearstatcache(); - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Look if the file exits - if (!is_file($this->zipname)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '" . $this->zipname . "'"); - - return false; - } - - // ----- Check that the file is readeable - if (!is_readable($this->zipname)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '" . $this->zipname . "'"); - - return false; - } - - // ----- Check the magic code - // TBC - - // ----- Check the central header - // TBC - - // ----- Check each file header - // TBC - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privParseOptions() - // Description : - // This internal methods reads the variable list of arguments ($p_options_list, - // $p_size) and generate an array with the options and values ($v_result_list). - // $v_requested_options contains the options that can be present and those that - // must be present. - // $v_requested_options is an array, with the option value as key, and 'optional', - // or 'mandatory' as value. - // Parameters : - // See above. - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - public function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options = false) - { - $v_result = 1; - - // ----- Read the options - $i = 0; - while ($i < $p_size) { - // ----- Check if the option is supported - if (!isset($v_requested_options[$p_options_list[$i]])) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '" . $p_options_list[$i] . "' for this method"); - - // ----- Return - return self::errorCode(); - } - - // ----- Look for next option - switch ($p_options_list[$i]) { - // ----- Look for options that request a path value - case PCLZIP_OPT_PATH: - case PCLZIP_OPT_REMOVE_PATH: - case PCLZIP_OPT_ADD_PATH: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i + 1], false); - ++$i; - break; - case PCLZIP_OPT_TEMP_FILE_THRESHOLD: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - return self::errorCode(); - } - - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); - - return self::errorCode(); - } - - // ----- Check the value - $v_value = $p_options_list[$i + 1]; - if ((!is_int($v_value)) || ($v_value < 0)) { - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - return self::errorCode(); - } - - // ----- Get the value (and convert it in bytes) - $v_result_list[$p_options_list[$i]] = $v_value * 1048576; - ++$i; - break; - case PCLZIP_OPT_TEMP_FILE_ON: - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); - - return self::errorCode(); - } - - $v_result_list[$p_options_list[$i]] = true; - break; - case PCLZIP_OPT_TEMP_FILE_OFF: - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); - - return self::errorCode(); - } - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); - - return self::errorCode(); - } - $v_result_list[$p_options_list[$i]] = true; - break; - case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i + 1]) && ($p_options_list[$i + 1] != '')) { - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i + 1], false); - ++$i; - } - - break; - // ----- Look for options that request an array of string for value - case PCLZIP_OPT_BY_NAME: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i + 1])) { - $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i + 1]; - } elseif (is_array($p_options_list[$i + 1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; - } else { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - ++$i; - break; - // ----- Look for options that request an EREG or PREG expression - case PCLZIP_OPT_BY_EREG: - // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG - // to PCLZIP_OPT_BY_PREG - $p_options_list[$i] = PCLZIP_OPT_BY_PREG; - // no break; - case PCLZIP_OPT_BY_PREG: - //case PCLZIP_OPT_CRYPT : - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i + 1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; - } else { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - ++$i; - break; - // ----- Look for options that takes a string - case PCLZIP_OPT_COMMENT: - case PCLZIP_OPT_ADD_COMMENT: - case PCLZIP_OPT_PREPEND_COMMENT: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i + 1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; - } else { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - ++$i; - break; - // ----- Look for options that request an array of index - case PCLZIP_OPT_BY_INDEX: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - $v_work_list = []; - if (is_string($p_options_list[$i + 1])) { - // ----- Remove spaces - $p_options_list[$i + 1] = strtr($p_options_list[$i + 1], ' ', ''); - - // ----- Parse items - $v_work_list = explode(',', $p_options_list[$i + 1]); - } elseif (is_int($p_options_list[$i + 1])) { - $v_work_list[0] = $p_options_list[$i + 1] . '-' . $p_options_list[$i + 1]; - } elseif (is_array($p_options_list[$i + 1])) { - $v_work_list = $p_options_list[$i + 1]; - } else { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Reduce the index list - // each index item in the list must be a couple with a start and - // an end value : [0,3], [5-5], [8-10], ... - // ----- Check the format of each item - $v_sort_flag = false; - $v_sort_value = 0; - for ($j = 0; $j < count($v_work_list); ++$j) { - // ----- Explode the item - $v_item_list = explode('-', $v_work_list[$j]); - $v_size_item_list = count($v_item_list); - - // ----- TBC : Here we might check that each item is a - // real integer ... - - // ----- Look for single value - if ($v_size_item_list == 1) { - // ----- Set the option value - $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0]; - $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[0]; - } elseif ($v_size_item_list == 2) { - // ----- Set the option value - $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0]; - $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[1]; - } else { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Too many values in index range for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Look for list sort - if ($v_result_list[$p_options_list[$i]][$j]['start'] < $v_sort_value) { - $v_sort_flag = true; - - // ----- TBC : An automatic sort should be writen ... - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Invalid order of index range for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - - // ----- Return - return self::errorCode(); - } - $v_sort_value = $v_result_list[$p_options_list[$i]][$j]['start']; - } - - // ----- Sort the items - if ($v_sort_flag) { - // TBC : To Be Completed - } - // ----- Next option - ++$i; - break; - // ----- Look for options that request no value - case PCLZIP_OPT_REMOVE_ALL_PATH: - case PCLZIP_OPT_EXTRACT_AS_STRING: - case PCLZIP_OPT_NO_COMPRESSION: - case PCLZIP_OPT_EXTRACT_IN_OUTPUT: - case PCLZIP_OPT_REPLACE_NEWER: - case PCLZIP_OPT_STOP_ON_ERROR: - $v_result_list[$p_options_list[$i]] = true; - break; - // ----- Look for options that request an octal value - case PCLZIP_OPT_SET_CHMOD: - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - // ----- Get the value - $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; - ++$i; - break; - // ----- Look for options that request a call-back - case PCLZIP_CB_PRE_EXTRACT: - case PCLZIP_CB_POST_EXTRACT: - case PCLZIP_CB_PRE_ADD: - case PCLZIP_CB_POST_ADD: - /* for futur use - case PCLZIP_CB_PRE_DELETE : - case PCLZIP_CB_POST_DELETE : - case PCLZIP_CB_PRE_LIST : - case PCLZIP_CB_POST_LIST : - */ - // ----- Check the number of parameters - if (($i + 1) >= $p_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - - // ----- Get the value - $v_function_name = $p_options_list[$i + 1]; - - // ----- Check that the value is a valid existing function - if (!function_exists($v_function_name)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '" . $v_function_name . "()' is not an existing function for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return self::errorCode(); - } - - // ----- Set the attribute - $v_result_list[$p_options_list[$i]] = $v_function_name; - ++$i; - break; - default: - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown parameter '" . $p_options_list[$i] . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Next options - ++$i; - } - - // ----- Look for mandatory options - if ($v_requested_options !== false) { - for ($key = reset($v_requested_options); $key = key($v_requested_options); $key = next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - // ----- Look if present - if (!isset($v_result_list[$key])) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Missing mandatory parameter ' . PclZipUtilOptionText($key) . '(' . $key . ')'); - - // ----- Return - return self::errorCode(); - } - } - } - } - - // ----- Look for default values - if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privOptionDefaultThreshold() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privOptionDefaultThreshold(&$p_options) - { - $v_result = 1; - - if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { - return $v_result; - } - - // ----- Get 'memory_limit' configuration value - $v_memory_limit = ini_get('memory_limit'); - $v_memory_limit = trim($v_memory_limit); - $last = strtolower(substr($v_memory_limit, -1)); - - if ($last == 'g') { - //$v_memory_limit = $v_memory_limit*1024*1024*1024; - $v_memory_limit = $v_memory_limit * 1073741824; - } - if ($last == 'm') { - //$v_memory_limit = $v_memory_limit*1024*1024; - $v_memory_limit = $v_memory_limit * 1048576; - } - if ($last == 'k') { - $v_memory_limit = $v_memory_limit * 1024; - } - - $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit * PCLZIP_TEMPORARY_FILE_RATIO); - - // ----- Sanity check : No threshold if value lower than 1M - if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { - unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privFileDescrParseAtt() - // Description : - // Parameters : - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - public function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options = false) - { - $v_result = 1; - - // ----- For each file in the list check the attributes - foreach ($p_file_list as $v_key => $v_value) { - // ----- Check if the option is supported - if (!isset($v_requested_options[$v_key])) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '" . $v_key . "' for this file"); - - // ----- Return - return self::errorCode(); - } - - // ----- Look for attribute - switch ($v_key) { - case PCLZIP_ATT_FILE_NAME: - if (!is_string($v_value)) { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, 'Invalid type ' . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - - $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['filename'] == '') { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - break; - case PCLZIP_ATT_FILE_NEW_SHORT_NAME: - if (!is_string($v_value)) { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, 'Invalid type ' . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - - $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['new_short_name'] == '') { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - break; - case PCLZIP_ATT_FILE_NEW_FULL_NAME: - if (!is_string($v_value)) { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, 'Invalid type ' . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - - $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['new_full_name'] == '') { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - break; - // ----- Look for options that takes a string - case PCLZIP_ATT_FILE_COMMENT: - if (!is_string($v_value)) { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, 'Invalid type ' . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - $p_filedescr['comment'] = $v_value; - break; - case PCLZIP_ATT_FILE_MTIME: - if (!is_int($v_value)) { - self::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, 'Invalid type ' . gettype($v_value) . ". Integer expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); - - return self::errorCode(); - } - $p_filedescr['mtime'] = $v_value; - break; - case PCLZIP_ATT_FILE_CONTENT: - $p_filedescr['content'] = $v_value; - break; - default: - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown parameter '" . $v_key . "'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Look for mandatory options - if ($v_requested_options !== false) { - for ($key = reset($v_requested_options); $key = key($v_requested_options); $key = next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - // ----- Look if present - if (!isset($p_file_list[$key])) { - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Missing mandatory parameter ' . PclZipUtilOptionText($key) . '(' . $key . ')'); - - return self::errorCode(); - } - } - } - } - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privFileDescrExpand() - // Description : - // This method look for each item of the list to see if its a file, a folder - // or a string to be added as file. For any other type of files (link, other) - // just ignore the item. - // Then prepare the information that will be stored for that file. - // When its a folder, expand the folder with all the files that are in that - // folder (recursively). - // Parameters : - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - public function privFileDescrExpand(&$p_filedescr_list, &$p_options) - { - $v_result = 1; - - // ----- Create a result list - $v_result_list = []; - - // ----- Look each entry - for ($i = 0; $i < count($p_filedescr_list); ++$i) { - // ----- Get filedescr - $v_descr = $p_filedescr_list[$i]; - - // ----- Reduce the filename - $v_descr['filename'] = PclZipUtilTranslateWinPath($v_descr['filename'], false); - $v_descr['filename'] = PclZipUtilPathReduction($v_descr['filename']); - - // ----- Look for real file or folder - if (file_exists($v_descr['filename'])) { - if (@is_file($v_descr['filename'])) { - $v_descr['type'] = 'file'; - } elseif (@is_dir($v_descr['filename'])) { - $v_descr['type'] = 'folder'; - } elseif (@is_link($v_descr['filename'])) { - // skip - continue; - } else { - // skip - continue; - } - } elseif (isset($v_descr['content'])) { - // ----- Look for string added as file - $v_descr['type'] = 'virtual_file'; - } else { - // ----- Missing file - // ----- Error log - self::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '" . $v_descr['filename'] . "' does not exist"); - - // ----- Return - return self::errorCode(); - } - - // ----- Calculate the stored filename - $this->privCalculateStoredFilename($v_descr, $p_options); - - // ----- Add the descriptor in result list - $v_result_list[count($v_result_list)] = $v_descr; - - // ----- Look for folder - if ($v_descr['type'] == 'folder') { - // ----- List of items in folder - $v_dirlist_descr = []; - $v_dirlist_nb = 0; - if ($v_folder_handler = @opendir($v_descr['filename'])) { - while (($v_item_handler = @readdir($v_folder_handler)) !== false) { - // ----- Skip '.' and '..' - if (($v_item_handler == '.') || ($v_item_handler == '..')) { - continue; - } - - // ----- Compose the full filename - $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'] . '/' . $v_item_handler; - - // ----- Look for different stored filename - // Because the name of the folder was changed, the name of the - // files/sub-folders also change - if (($v_descr['stored_filename'] != $v_descr['filename']) - && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { - if ($v_descr['stored_filename'] != '') { - $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'] . '/' . $v_item_handler; - } else { - $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; - } - } - ++$v_dirlist_nb; - } - - @closedir($v_folder_handler); - } - // TBC : unable to open folder in read mode - - // ----- Expand each element of the list - if ($v_dirlist_nb != 0) { - // ----- Expand - if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { - return $v_result; - } - - // ----- Concat the resulting list - $v_result_list = array_merge($v_result_list, $v_dirlist_descr); - } - - // ----- Free local array - unset($v_dirlist_descr); - } - } - - // ----- Get the result list - $p_filedescr_list = $v_result_list; - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCreate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privCreate($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result = 1; - $v_list_detail = []; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the file in write mode - if (($v_result = $this->privOpenFd('wb')) != 1) { - // ----- Return - return $v_result; - } - - // ----- Add the list of files - $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); - - // ----- Close - $this->privCloseFd(); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAdd() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privAdd($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result = 1; - $v_list_detail = []; - - // ----- Look if the archive exists or is empty - if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) { - // ----- Do a create - $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); - - // ----- Return - return $v_result; - } - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('rb')) != 1) { - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = []; - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.tmp'; - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_zip_temp_name . '\' in binary write mode'); - - // ----- Return - return self::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Add the files - $v_header_list = []; - if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($this->zip_fd); - - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Create the Central Dir files header - for ($i = 0, $v_count = 0; $i < count($v_header_list); ++$i) { - // ----- Create the file header - if ($v_header_list[$i]['status'] == 'ok') { - if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - ++$v_count; - } - - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = $v_central_dir['comment']; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { - $v_comment = $v_comment . $p_options[PCLZIP_OPT_ADD_COMMENT]; - } - if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT] . $v_comment; - } - - // ----- Calculate the size of the central header - $v_size = @ftell($this->zip_fd) - $v_offset; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count + $v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) { - // ----- Reset the file list - unset($v_header_list); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Close - $this->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privOpenFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function privOpenFd($p_mode) - { - $v_result = 1; - - // ----- Look if already open - if ($this->zip_fd != 0) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \'' . $this->zipname . '\' already open'); - - // ----- Return - return self::errorCode(); - } - - // ----- Open the zip file - if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \'' . $this->zipname . '\' in ' . $p_mode . ' mode'); - - // ----- Return - return self::errorCode(); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCloseFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function privCloseFd() - { - $v_result = 1; - - if ($this->zip_fd != 0) { - @fclose($this->zip_fd); - } - $this->zip_fd = 0; - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddList() - // Description : - // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is - // different from the real path of the file. This is usefull if you want to have PclTar - // running in any directory, and memorize relative path from an other directory. - // Parameters : - // $p_list : An array containing the file or directory names to add in the tar - // $p_result_list : list of added files with their properties (specially the status field) - // $p_add_dir : Path to add in the filename path archived - // $p_remove_dir : Path to remove in the filename path archived - // Return Values : - // -------------------------------------------------------------------------------- - // public function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - public function privAddList($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result = 1; - - // ----- Add the files - $v_header_list = []; - if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) { - // ----- Return - return $v_result; - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($this->zip_fd); - - // ----- Create the Central Dir files header - for ($i = 0, $v_count = 0; $i < count($v_header_list); ++$i) { - // ----- Create the file header - if ($v_header_list[$i]['status'] == 'ok') { - if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) { - // ----- Return - return $v_result; - } - ++$v_count; - } - - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = ''; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - - // ----- Calculate the size of the central header - $v_size = @ftell($this->zip_fd) - $v_offset; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) { - // ----- Reset the file list - unset($v_header_list); - - // ----- Return - return $v_result; - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFileList() - // Description : - // Parameters : - // $p_filedescr_list : An array containing the file description - // or directory names to add in the zip - // $p_result_list : list of added files with their properties (specially the status field) - // Return Values : - // -------------------------------------------------------------------------------- - public function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result = 1; - $v_header = []; - - // ----- Recuperate the current number of elt in list - $v_nb = count($p_result_list); - - // ----- Loop on the files - for ($j = 0; ($j < count($p_filedescr_list)) && ($v_result == 1); ++$j) { - // ----- Format the filename - $p_filedescr_list[$j]['filename'] = PclZipUtilTranslateWinPath($p_filedescr_list[$j]['filename'], false); - - // ----- Skip empty file names - // TBC : Can this be possible ? not checked in DescrParseAtt ? - if ($p_filedescr_list[$j]['filename'] == '') { - continue; - } - - // ----- Check the filename - if (($p_filedescr_list[$j]['type'] != 'virtual_file') && (!file_exists($p_filedescr_list[$j]['filename']))) { - self::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '" . $p_filedescr_list[$j]['filename'] . "' does not exist"); - - return self::errorCode(); - } - - // ----- Look if it is a file or a dir with no all path remove option - // or a dir with all its path removed - // if ( (is_file($p_filedescr_list[$j]['filename'])) - // || ( is_dir($p_filedescr_list[$j]['filename']) - if (($p_filedescr_list[$j]['type'] == 'file') || ($p_filedescr_list[$j]['type'] == 'virtual_file') || (($p_filedescr_list[$j]['type'] == 'folder') && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]) || !$p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { - // ----- Add the file - $v_result = $this->privAddFile($p_filedescr_list[$j], $v_header, $p_options); - if ($v_result != 1) { - return $v_result; - } - - // ----- Store the file infos - $p_result_list[$v_nb++] = $v_header; - } - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privAddFile($p_filedescr, &$p_header, &$p_options) - { - $v_result = 1; - - // ----- Working variable - $p_filename = $p_filedescr['filename']; - - // TBC : Already done in the fileAtt check ... ? - if ($p_filename == '') { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, 'Invalid file list parameter (invalid or empty list)'); - - // ----- Return - return self::errorCode(); - } - - // ----- Look for a stored different filename - /* TBC : Removed - if (isset($p_filedescr['stored_filename'])) { - $v_stored_filename = $p_filedescr['stored_filename']; - } - else { - $v_stored_filename = $p_filedescr['stored_filename']; - } - */ - - // ----- Set the file properties - clearstatcache(); - $p_header['version'] = 20; - $p_header['version_extracted'] = 10; - $p_header['flag'] = 0; - $p_header['compression'] = 0; - $p_header['crc'] = 0; - $p_header['compressed_size'] = 0; - $p_header['filename_len'] = strlen($p_filename); - $p_header['extra_len'] = 0; - $p_header['disk'] = 0; - $p_header['internal'] = 0; - $p_header['offset'] = 0; - $p_header['filename'] = $p_filename; - // TBC : Removed $p_header['stored_filename'] = $v_stored_filename; - $p_header['stored_filename'] = $p_filedescr['stored_filename']; - $p_header['extra'] = ''; - $p_header['status'] = 'ok'; - $p_header['index'] = -1; - - // ----- Look for regular file - if ($p_filedescr['type'] == 'file') { - $p_header['external'] = 0x00000000; - $p_header['size'] = filesize($p_filename); - } elseif ($p_filedescr['type'] == 'folder') { - // ----- Look for regular folder - $p_header['external'] = 0x00000010; - $p_header['mtime'] = filemtime($p_filename); - $p_header['size'] = filesize($p_filename); - } elseif ($p_filedescr['type'] == 'virtual_file') { - // ----- Look for virtual file - $p_header['external'] = 0x00000000; - $p_header['size'] = strlen($p_filedescr['content']); - } - - // ----- Look for filetime - if (isset($p_filedescr['mtime'])) { - $p_header['mtime'] = $p_filedescr['mtime']; - } elseif ($p_filedescr['type'] == 'virtual_file') { - $p_header['mtime'] = time(); - } else { - $p_header['mtime'] = filemtime($p_filename); - } - - // ------ Look for file comment - if (isset($p_filedescr['comment'])) { - $p_header['comment_len'] = strlen($p_filedescr['comment']); - $p_header['comment'] = $p_filedescr['comment']; - } else { - $p_header['comment_len'] = 0; - $p_header['comment'] = ''; - } - - // ----- Look for pre-add callback - if (isset($p_options[PCLZIP_CB_PRE_ADD])) { - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_header['status'] = 'skipped'; - $v_result = 1; - } - - // ----- Update the informations - // Only some fields can be modified - if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { - $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); - } - } - - // ----- Look for empty stored filename - if ($p_header['stored_filename'] == '') { - $p_header['status'] = 'filtered'; - } - - // ----- Check the path length - if (strlen($p_header['stored_filename']) > 0xFF) { - $p_header['status'] = 'filename_too_long'; - } - - // ----- Look if no error, or file not skipped - if ($p_header['status'] == 'ok') { - // ----- Look for a file - if ($p_filedescr['type'] == 'file') { - // ----- Look for using temporary file to zip - if ((!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])))) { - $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); - if ($v_result < PCLZIP_ERR_NO_ERROR) { - return $v_result; - } - } else { - // ----- Use "in memory" zip algo - // ----- Open the source file - if (($v_file = @fopen($p_filename, 'rb')) == 0) { - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - - return self::errorCode(); - } - - // ----- Read the file content - $v_content = @fread($v_file, $p_header['size']); - - // ----- Close the file - @fclose($v_file); - - // ----- Calculate the CRC - $p_header['crc'] = @crc32($v_content); - - // ----- Look for no compression - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Set header parameters - $p_header['compressed_size'] = $p_header['size']; - $p_header['compression'] = 0; - } else { - // ----- Look for normal compression - // ----- Compress the content - $v_content = @gzdeflate($v_content); - - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content); - $p_header['compression'] = 8; - } - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - - return $v_result; - } - - // ----- Write the compressed (or not) content - @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); - } - } elseif ($p_filedescr['type'] == 'virtual_file') { - // ----- Look for a virtual file (a file from string) - $v_content = $p_filedescr['content']; - - // ----- Calculate the CRC - $p_header['crc'] = @crc32($v_content); - - // ----- Look for no compression - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Set header parameters - $p_header['compressed_size'] = $p_header['size']; - $p_header['compression'] = 0; - } else { - // ----- Look for normal compression - // ----- Compress the content - $v_content = @gzdeflate($v_content); - - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content); - $p_header['compression'] = 8; - } - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - - return $v_result; - } - - // ----- Write the compressed (or not) content - @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); - } elseif ($p_filedescr['type'] == 'folder') { - // ----- Look for a directory - // ----- Look for directory last '/' - if (@substr($p_header['stored_filename'], -1) != '/') { - $p_header['stored_filename'] .= '/'; - } - - // ----- Set the file properties - $p_header['size'] = 0; - //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked - $p_header['external'] = 0x00000010; // Value for a folder : to be checked - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - return $v_result; - } - } - } - - // ----- Look for post-add callback - if (isset($p_options[PCLZIP_CB_POST_ADD])) { - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); - if ($v_result == 0) { - // ----- Ignored - $v_result = 1; - } - - // ----- Update the informations - // Nothing can be modified - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFileUsingTempFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) - { - $v_result = PCLZIP_ERR_NO_ERROR; - - // ----- Working variable - $p_filename = $p_filedescr['filename']; - - // ----- Open the source file - if (($v_file = @fopen($p_filename, 'rb')) == 0) { - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - - return self::errorCode(); - } - - // ----- Creates a compressed temporary file - $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.gz'; - if (($v_file_compressed = @gzopen($v_gzip_temp_name, 'wb')) == 0) { - fclose($v_file); - self::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary write mode'); - - return self::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = filesize($p_filename); - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_file, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @gzputs($v_file_compressed, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close the file - @fclose($v_file); - @gzclose($v_file_compressed); - - // ----- Check the minimum file size - if (filesize($v_gzip_temp_name) < 18) { - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \'' . $v_gzip_temp_name . '\' has invalid filesize - should be minimum 18 bytes'); - - return self::errorCode(); - } - - // ----- Extract the compressed attributes - if (($v_file_compressed = @fopen($v_gzip_temp_name, 'rb')) == 0) { - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary read mode'); - - return self::errorCode(); - } - - // ----- Read the gzip file header - $v_binary_data = @fread($v_file_compressed, 10); - $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); - - // ----- Check some parameters - $v_data_header['os'] = bin2hex($v_data_header['os']); - - // ----- Read the gzip file footer - @fseek($v_file_compressed, filesize($v_gzip_temp_name) - 8); - $v_binary_data = @fread($v_file_compressed, 8); - $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); - - // ----- Set the attributes - $p_header['compression'] = ord($v_data_header['cm']); - //$p_header['mtime'] = $v_data_header['mtime']; - $p_header['crc'] = $v_data_footer['crc']; - $p_header['compressed_size'] = filesize($v_gzip_temp_name) - 18; - - // ----- Close the file - @fclose($v_file_compressed); - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - return $v_result; - } - - // ----- Add the compressed data - if (($v_file_compressed = @fopen($v_gzip_temp_name, 'rb')) == 0) { - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary read mode'); - - return self::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - fseek($v_file_compressed, 10); - $v_size = $p_header['compressed_size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_file_compressed, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close the file - @fclose($v_file_compressed); - - // ----- Unlink the temporary file - @unlink($v_gzip_temp_name); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCalculateStoredFilename() - // Description : - // Based on file descriptor properties and global options, this method - // calculate the filename that will be stored in the archive. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privCalculateStoredFilename(&$p_filedescr, &$p_options) - { - $v_result = 1; - - // ----- Working variables - $p_filename = $p_filedescr['filename']; - if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { - $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; - } else { - $p_add_dir = ''; - } - if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { - $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; - } else { - $p_remove_dir = ''; - } - if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } else { - $p_remove_all_dir = 0; - } - - // ----- Look for full name change - if (isset($p_filedescr['new_full_name'])) { - // ----- Remove drive letter if any - $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); - } else { - // ----- Look for path and/or short name change - // ----- Look for short name change - // Its when we cahnge just the filename but not the path - if (isset($p_filedescr['new_short_name'])) { - $v_path_info = pathinfo($p_filename); - $v_dir = ''; - if ($v_path_info['dirname'] != '') { - $v_dir = $v_path_info['dirname'] . '/'; - } - $v_stored_filename = $v_dir . $p_filedescr['new_short_name']; - } else { - // ----- Calculate the stored filename - $v_stored_filename = $p_filename; - } - - // ----- Look for all path to remove - if ($p_remove_all_dir) { - $v_stored_filename = basename($p_filename); - } elseif ($p_remove_dir != '') { - // ----- Look for partial path remove - if (substr($p_remove_dir, -1) != '/') { - $p_remove_dir .= '/'; - } - - if ((substr($p_filename, 0, 2) == './') || (substr($p_remove_dir, 0, 2) == './')) { - if ((substr($p_filename, 0, 2) == './') && (substr($p_remove_dir, 0, 2) != './')) { - $p_remove_dir = './' . $p_remove_dir; - } - if ((substr($p_filename, 0, 2) != './') && (substr($p_remove_dir, 0, 2) == './')) { - $p_remove_dir = substr($p_remove_dir, 2); - } - } - - $v_compare = PclZipUtilPathInclusion($p_remove_dir, $v_stored_filename); - if ($v_compare > 0) { - if ($v_compare == 2) { - $v_stored_filename = ''; - } else { - $v_stored_filename = substr($v_stored_filename, strlen($p_remove_dir)); - } - } - } - - // ----- Remove drive letter if any - $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); - - // ----- Look for path to add - if ($p_add_dir != '') { - if (substr($p_add_dir, -1) == '/') { - $v_stored_filename = $p_add_dir . $v_stored_filename; - } else { - $v_stored_filename = $p_add_dir . '/' . $v_stored_filename; - } - } - } - - // ----- Filename (reduce the path of stored name) - $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); - $p_filedescr['stored_filename'] = $v_stored_filename; - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privWriteFileHeader(&$p_header) - { - $v_result = 1; - - // ----- Store the offset position of the file - $p_header['offset'] = ftell($this->zip_fd); - - // ----- Transform UNIX mtime to DOS format mdate/mtime - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours'] << 11) + ($v_date['minutes'] << 5) + $v_date['seconds'] / 2; - $v_mdate = (($v_date['year'] - 1980) << 9) + ($v_date['mon'] << 5) + $v_date['mday']; - - // ----- Packed data - $v_binary_data = pack('VvvvvvVVVvv', 0x04034b50, $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']), $p_header['extra_len']); - - // ----- Write the first 148 bytes of the header in the archive - fwrite($this->zip_fd, $v_binary_data, 30); - - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) { - fwrite($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - } - if ($p_header['extra_len'] != 0) { - fwrite($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privWriteCentralFileHeader(&$p_header) - { - $v_result = 1; - - // TBC - //for(reset($p_header); $key = key($p_header); next($p_header)) { - //} - - // ----- Transform UNIX mtime to DOS format mdate/mtime - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours'] << 11) + ($v_date['minutes'] << 5) + $v_date['seconds'] / 2; - $v_mdate = (($v_date['year'] - 1980) << 9) + ($v_date['mon'] << 5) + $v_date['mday']; - - // ----- Packed data - $v_binary_data = pack('VvvvvvvVVVvvvvvVV', 0x02014b50, $p_header['version'], $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']), $p_header['extra_len'], $p_header['comment_len'], $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']); - - // ----- Write the 42 bytes of the header in the zip file - fwrite($this->zip_fd, $v_binary_data, 46); - - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) { - fwrite($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - } - if ($p_header['extra_len'] != 0) { - fwrite($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - } - if ($p_header['comment_len'] != 0) { - fwrite($this->zip_fd, $p_header['comment'], $p_header['comment_len']); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteCentralHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) - { - $v_result = 1; - - // ----- Packed data - $v_binary_data = pack('VvvvvVVv', 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment)); - - // ----- Write the 22 bytes of the header in the zip file - fwrite($this->zip_fd, $v_binary_data, 22); - - // ----- Write the variable fields - if (strlen($p_comment) != 0) { - fwrite($this->zip_fd, $p_comment, strlen($p_comment)); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privList() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privList(&$p_list) - { - $v_result = 1; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the zip file - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \'' . $this->zipname . '\' in binary read mode'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read the central directory informations - $v_central_dir = []; - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Go to beginning of Central Dir - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_central_dir['offset'])) { - $this->privSwapBackMagicQuotes(); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read each entry - for ($i = 0; $i < $v_central_dir['entries']; ++$i) { - // ----- Read the file header - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) { - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - $v_header['index'] = $i; - - // ----- Get the only interesting attributes - $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); - unset($v_header); - } - - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privConvertHeader2FileInfo() - // Description : - // This function takes the file informations from the central directory - // entries and extract the interesting parameters that will be given back. - // The resulting file infos are set in the array $p_info - // $p_info['filename'] : Filename with full path. Given by user (add), - // extracted in the filesystem (extract). - // $p_info['stored_filename'] : Stored filename in the archive. - // $p_info['size'] = Size of the file. - // $p_info['compressed_size'] = Compressed size of the file. - // $p_info['mtime'] = Last modification date of the file. - // $p_info['comment'] = Comment associated with the file. - // $p_info['folder'] = true/false : indicates if the entry is a folder or not. - // $p_info['status'] = status of the action on the file. - // $p_info['crc'] = CRC of the file content. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privConvertHeader2FileInfo($p_header, &$p_info) - { - $v_result = 1; - - // ----- Get the interesting attributes - $v_temp_path = PclZipUtilPathReduction($p_header['filename']); - $p_info['filename'] = $v_temp_path; - $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); - $p_info['stored_filename'] = $v_temp_path; - $p_info['size'] = $p_header['size']; - $p_info['compressed_size'] = $p_header['compressed_size']; - $p_info['mtime'] = $p_header['mtime']; - $p_info['comment'] = $p_header['comment']; - $p_info['folder'] = (($p_header['external'] & 0x00000010) == 0x00000010); - $p_info['index'] = $p_header['index']; - $p_info['status'] = $p_header['status']; - $p_info['crc'] = $p_header['crc']; - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractByRule() - // Description : - // Extract a file or directory depending of rules (by index, by name, ...) - // Parameters : - // $p_file_list : An array where will be placed the properties of each - // extracted file - // $p_path : Path to add while writing the extracted files - // $p_remove_path : Path to remove (from the file memorized path) while writing the - // extracted files. If the path does not match the file path, - // the file is extracted with its memorized path. - // $p_remove_path does not apply to 'list' mode. - // $p_path and $p_remove_path are commulative. - // Return Values : - // 1 on success,0 or less on error (see error code list) - // -------------------------------------------------------------------------------- - public function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - $v_result = 1; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Check the path - if (($p_path == '') || ((substr($p_path, 0, 1) != '/') && (substr($p_path, 0, 3) != '../') && (substr($p_path, 1, 2) != ':/'))) { - $p_path = './' . $p_path; - } - - // ----- Reduce the path last (and duplicated) '/' - if (($p_path != './') && ($p_path != '/')) { - // ----- Look for the path end '/' - while (substr($p_path, -1) == '/') { - $p_path = substr($p_path, 0, strlen($p_path) - 1); - } - } - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) { - $p_remove_path .= '/'; - } - $p_remove_path_size = strlen($p_remove_path); - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('rb')) != 1) { - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = []; - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - - // ----- Read each entry - $j_start = 0; - for ($i = 0, $v_nb_extracted = 0; $i < $v_central_dir['entries']; ++$i) { - // ----- Read next Central dir entry - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_pos_entry)) { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read the file header - $v_header = []; - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Store the index - $v_header['index'] = $i; - - // ----- Store the file position - $v_pos_entry = ftell($this->zip_fd); - - // ----- Look for the specific extract rules - $v_extract = false; - - // ----- Look for extract by name rule - if ((isset($p_options[PCLZIP_OPT_BY_NAME])) && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - // ----- Look if the filename is in the list - for ($j = 0; ($j < count($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_extract); ++$j) { - // ----- Look for a directory - if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == '/') { - // ----- Look if the directory is in the filename path - if ((strlen($v_header['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_extract = true; - } - } elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - // ----- Look for a filename - $v_extract = true; - } - } - } elseif ((isset($p_options[PCLZIP_OPT_BY_PREG])) && ($p_options[PCLZIP_OPT_BY_PREG] != '')) { - // ----- Look for extract by preg rule - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { - $v_extract = true; - } - } elseif ((isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - // ----- Look for extract by index rule - // ----- Look if the index is in the list - for ($j = $j_start; ($j < count($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_extract); ++$j) { - if (($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i <= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - $v_extract = true; - } - if ($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - $j_start = $j + 1; - } - - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start'] > $i) { - break; - } - } - } else { - // ----- Look for no rule, which means extract all the archive - $v_extract = true; - } - - // ----- Check compression method - if (($v_extract) && (($v_header['compression'] != 8) && ($v_header['compression'] != 0))) { - $v_header['status'] = 'unsupported_compression'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { - $this->privSwapBackMagicQuotes(); - - self::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, "Filename '" . $v_header['stored_filename'] . "' is compressed by an unsupported compression method (" . $v_header['compression'] . ') '); - - return self::errorCode(); - } - } - - // ----- Check encrypted files - if (($v_extract) && (($v_header['flag'] & 1) == 1)) { - $v_header['status'] = 'unsupported_encryption'; - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { - $this->privSwapBackMagicQuotes(); - - self::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, "Unsupported encryption for filename '" . $v_header['stored_filename'] . "'"); - - return self::errorCode(); - } - } - - // ----- Look for real extraction - if (($v_extract) && ($v_header['status'] != 'ok')) { - $v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++]); - if ($v_result != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - $v_extract = false; - } - - // ----- Look for real extraction - if ($v_extract) { - // ----- Go to the file position - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_header['offset'])) { - // ----- Close the zip file - $this->privCloseFd(); - - $this->privSwapBackMagicQuotes(); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return self::errorCode(); - } - - // ----- Look for extraction as string - if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { - $v_string = ''; - - // ----- Extracting the file - $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Set the file content - $p_file_list[$v_nb_extracted]['content'] = $v_string; - - // ----- Next extracted file - ++$v_nb_extracted; - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } elseif ((isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { - // ----- Look for extraction in standard output - // ----- Extracting the file in standard output - $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } else { - // ----- Look for normal extraction - // ----- Extracting the file - $v_result1 = $this->privExtractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - } - } - - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFile() - // Description : - // Parameters : - // Return Values : - // - // 1 : ... ? - // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback - // -------------------------------------------------------------------------------- - public function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - $v_result = 1; - - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) { - // ----- Return - return $v_result; - } - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for all path to remove - if ($p_remove_all_path == true) { - // ----- Look for folder entry that not need to be extracted - if (($p_entry['external'] & 0x00000010) == 0x00000010) { - $p_entry['status'] = 'filtered'; - - return $v_result; - } - - // ----- Get the basename of the path - $p_entry['filename'] = basename($p_entry['filename']); - } elseif ($p_remove_path != '') { - // ----- Look for path to remove - if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) { - // ----- Change the file status - $p_entry['status'] = 'filtered'; - - // ----- Return - return $v_result; - } - - $p_remove_path_size = strlen($p_remove_path); - if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) { - // ----- Remove the path - $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); - } - } - - // ----- Add the path - if ($p_path != '') { - $p_entry['filename'] = $p_path . '/' . $p_entry['filename']; - } - - // ----- Check a base_dir_restriction - if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { - $v_inclusion = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], $p_entry['filename']); - if ($v_inclusion == 0) { - self::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, "Filename '" . $p_entry['filename'] . "' is outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); - - return self::errorCode(); - } - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = 'skipped'; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = 'aborted'; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - // ----- Look for specific actions while the file exist - if (file_exists($p_entry['filename'])) { - // ----- Look if file is a directory - if (is_dir($p_entry['filename'])) { - // ----- Change the file status - $p_entry['status'] = 'already_a_directory'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { - self::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, "Filename '" . $p_entry['filename'] . "' is already used by an existing directory"); - - return self::errorCode(); - } - } elseif (!is_writable($p_entry['filename'])) { - // ----- Look if file is write protected - // ----- Change the file status - $p_entry['status'] = 'write_protected'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { - self::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Filename '" . $p_entry['filename'] . "' exists and is write protected"); - - return self::errorCode(); - } - } elseif (filemtime($p_entry['filename']) > $p_entry['mtime']) { - // ----- Look if the extracted file is older - // ----- Change the file status - if ((isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) && ($p_options[PCLZIP_OPT_REPLACE_NEWER] === true)) { - } else { - $p_entry['status'] = 'newer_exist'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { - self::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Newer version of '" . $p_entry['filename'] . "' exists and option PCLZIP_OPT_REPLACE_NEWER is not selected"); - - return self::errorCode(); - } - } - } - } else { - // ----- Check the directory availability and create it if necessary - if ((($p_entry['external'] & 0x00000010) == 0x00000010) || (substr($p_entry['filename'], -1) == '/')) { - $v_dir_to_check = $p_entry['filename']; - } elseif (!strstr($p_entry['filename'], '/')) { - $v_dir_to_check = ''; - } else { - $v_dir_to_check = dirname($p_entry['filename']); - } - - if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external'] & 0x00000010) == 0x00000010))) != 1) { - // ----- Change the file status - $p_entry['status'] = 'path_creation_fail'; - - // ----- Return - //return $v_result; - $v_result = 1; - } - } - } - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external'] & 0x00000010) == 0x00000010)) { - // ----- Look for not compressed file - if ($p_entry['compression'] == 0) { - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - // ----- Change the file status - $p_entry['status'] = 'write_error'; - - // ----- Return - return $v_result; - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - /* Try to speed up the code - $v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_binary_data, $v_read_size); - */ - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Closing the destination file - fclose($v_dest_file); - - // ----- Change the file mtime - touch($p_entry['filename'], $p_entry['mtime']); - } else { - // ----- TBC - // Need to be finished - if (($p_entry['flag'] & 1) == 1) { - self::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \'' . $p_entry['filename'] . '\' is encrypted. Encrypted files are not supported.'); - - return self::errorCode(); - } - - // ----- Look for using temporary file to unzip - if ((!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])))) { - $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); - if ($v_result < PCLZIP_ERR_NO_ERROR) { - return $v_result; - } - } else { - // ----- Look for extract in memory - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - - // ----- Decompress the file - $v_file_content = @gzinflate($v_buffer); - unset($v_buffer); - if ($v_file_content === false) { - // ----- Change the file status - // TBC - $p_entry['status'] = 'error'; - - return $v_result; - } - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - // ----- Change the file status - $p_entry['status'] = 'write_error'; - - return $v_result; - } - - // ----- Write the uncompressed data - @fwrite($v_dest_file, $v_file_content, $p_entry['size']); - unset($v_file_content); - - // ----- Closing the destination file - @fclose($v_dest_file); - } - - // ----- Change the file mtime - @touch($p_entry['filename'], $p_entry['mtime']); - } - - // ----- Look for chmod option - if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { - // ----- Change the mode of the file - @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); - } - } - } - - // ----- Change abort status - if ($p_entry['status'] == 'aborted') { - $p_entry['status'] = 'skipped'; - } elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - // ----- Look for post-extract callback - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileUsingTempFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privExtractFileUsingTempFile(&$p_entry, &$p_options) - { - $v_result = 1; - - // ----- Creates a temporary file - $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.gz'; - if (($v_dest_file = @fopen($v_gzip_temp_name, 'wb')) == 0) { - fclose($v_file); - self::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary write mode'); - - return self::errorCode(); - } - - // ----- Write gz file format header - $v_binary_data = pack('va1a1Va1a1', 0x8b1f, chr($p_entry['compression']), chr(0x00), time(), chr(0x00), chr(3)); - @fwrite($v_dest_file, $v_binary_data, 10); - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Write gz file format footer - $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); - @fwrite($v_dest_file, $v_binary_data, 8); - - // ----- Close the temporary file - @fclose($v_dest_file); - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - $p_entry['status'] = 'write_error'; - - return $v_result; - } - - // ----- Open the temporary gz file - if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { - @fclose($v_dest_file); - $p_entry['status'] = 'read_error'; - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary read mode'); - - return self::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($v_src_file, $v_read_size); - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - @fclose($v_dest_file); - @gzclose($v_src_file); - - // ----- Delete the temporary file - @unlink($v_gzip_temp_name); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileInOutput() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privExtractFileInOutput(&$p_entry, &$p_options) - { - $v_result = 1; - - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) { - return $v_result; - } - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = 'skipped'; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = 'aborted'; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - // ----- Trace - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external'] & 0x00000010) == 0x00000010)) { - // ----- Look for not compressed file - if ($p_entry['compressed_size'] == $p_entry['size']) { - // ----- Read the file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - - // ----- Send the file to the output - echo $v_buffer; - unset($v_buffer); - } else { - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - - // ----- Decompress the file - $v_file_content = gzinflate($v_buffer); - unset($v_buffer); - - // ----- Send the file to the output - echo $v_file_content; - unset($v_file_content); - } - } - } - - // ----- Change abort status - if ($p_entry['status'] == 'aborted') { - $p_entry['status'] = 'skipped'; - } elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - // ----- Look for post-extract callback - - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileAsString() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) - { - $v_result = 1; - - // ----- Read the file header - $v_header = []; - if (($v_result = $this->privReadFileHeader($v_header)) != 1) { - // ----- Return - return $v_result; - } - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = 'skipped'; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = 'aborted'; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external'] & 0x00000010) == 0x00000010)) { - // ----- Look for not compressed file - // if ($p_entry['compressed_size'] == $p_entry['size']) - if ($p_entry['compression'] == 0) { - // ----- Reading the file - $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); - } else { - // ----- Reading the file - $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); - - // ----- Decompress the file - if (($p_string = @gzinflate($v_data)) === false) { - // TBC - } - } - // ----- Trace - } - // TBC : error : can not extract a folder in a string - } - - // ----- Change abort status - if ($p_entry['status'] == 'aborted') { - $p_entry['status'] = 'skipped'; - } elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - // ----- Look for post-extract callback - // ----- Generate a local information - $v_local_header = []; - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Swap the content to header - $v_local_header['content'] = $p_string; - $p_string = ''; - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - // eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Swap back the content to header - $p_string = $v_local_header['content']; - unset($v_local_header['content']); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privReadFileHeader(&$p_header) - { - $v_result = 1; - - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - $v_data = unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] != 0x04034b50) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 26); - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 26) { - $p_header['filename'] = ''; - $p_header['status'] = 'invalid_header'; - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid block size : ' . strlen($v_binary_data)); - - // ----- Return - return self::errorCode(); - } - - // ----- Extract the values - $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); - - // ----- Get filename - $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); - - // ----- Get extra_fields - if ($v_data['extra_len'] != 0) { - $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); - } else { - $p_header['extra'] = ''; - } - - // ----- Extract properties - $p_header['version_extracted'] = $v_data['version']; - $p_header['compression'] = $v_data['compression']; - $p_header['size'] = $v_data['size']; - $p_header['compressed_size'] = $v_data['compressed_size']; - $p_header['crc'] = $v_data['crc']; - $p_header['flag'] = $v_data['flag']; - $p_header['filename_len'] = $v_data['filename_len']; - - // ----- Recuperate date in UNIX format - $p_header['mdate'] = $v_data['mdate']; - $p_header['mtime'] = $v_data['mtime']; - if ($p_header['mdate'] && $p_header['mtime']) { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F) * 2; - - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - - // ----- Get UNIX date format - $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - } else { - $p_header['mtime'] = time(); - } - - // TBC - //for(reset($v_data); $key = key($v_data); next($v_data)) { - //} - - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - - // ----- Set the status field - $p_header['status'] = 'ok'; - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privReadCentralFileHeader(&$p_header) - { - $v_result = 1; - - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - $v_data = unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] != 0x02014b50) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 42); - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 42) { - $p_header['filename'] = ''; - $p_header['status'] = 'invalid_header'; - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid block size : ' . strlen($v_binary_data)); - - // ----- Return - return self::errorCode(); - } - - // ----- Extract the values - $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); - - // ----- Get filename - if ($p_header['filename_len'] != 0) { - $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); - } else { - $p_header['filename'] = ''; - } - - // ----- Get extra - if ($p_header['extra_len'] != 0) { - $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); - } else { - $p_header['extra'] = ''; - } - - // ----- Get comment - if ($p_header['comment_len'] != 0) { - $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); - } else { - $p_header['comment'] = ''; - } - - // ----- Extract properties - - // ----- Recuperate date in UNIX format - //if ($p_header['mdate'] && $p_header['mtime']) - // TBC : bug : this was ignoring time with 0/0/0 - if (1) { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F) * 2; - - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - - // ----- Get UNIX date format - $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - } else { - $p_header['mtime'] = time(); - } - - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - - // ----- Set default status to ok - $p_header['status'] = 'ok'; - - // ----- Look if it is a directory - if (substr($p_header['filename'], -1) == '/') { - //$p_header['external'] = 0x41FF0010; - $p_header['external'] = 0x00000010; - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCheckFileHeaders() - // Description : - // Parameters : - // Return Values : - // 1 on success, - // 0 on error; - // -------------------------------------------------------------------------------- - public function privCheckFileHeaders(&$p_local_header, &$p_central_header) - { - $v_result = 1; - - // ----- Check the static values - // TBC - if ($p_local_header['filename'] != $p_central_header['filename']) { - } - if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { - } - if ($p_local_header['flag'] != $p_central_header['flag']) { - } - if ($p_local_header['compression'] != $p_central_header['compression']) { - } - if ($p_local_header['mtime'] != $p_central_header['mtime']) { - } - if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { - } - - // ----- Look for flag bit 3 - if (($p_local_header['flag'] & 8) == 8) { - $p_local_header['size'] = $p_central_header['size']; - $p_local_header['compressed_size'] = $p_central_header['compressed_size']; - $p_local_header['crc'] = $p_central_header['crc']; - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadEndCentralDir() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privReadEndCentralDir(&$p_central_dir) - { - $v_result = 1; - - // ----- Go to the end of the zip file - $v_size = filesize($this->zipname); - @fseek($this->zip_fd, $v_size); - if (@ftell($this->zip_fd) != $v_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \'' . $this->zipname . '\''); - - // ----- Return - return self::errorCode(); - } - - // ----- First try : look if this is an archive with no commentaries (most of the time) - // in this case the end of central dir is at 22 bytes of the file end - $v_found = 0; - if ($v_size > 26) { - @fseek($this->zip_fd, $v_size - 22); - if (($v_pos = @ftell($this->zip_fd)) != ($v_size - 22)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \'' . $this->zipname . '\''); - - // ----- Return - return self::errorCode(); - } - - // ----- Read for bytes - $v_binary_data = @fread($this->zip_fd, 4); - $v_data = @unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] == 0x06054b50) { - $v_found = 1; - } - - $v_pos = ftell($this->zip_fd); - } - - // ----- Go back to the maximum possible size of the Central Dir End Record - if (!$v_found) { - $v_maximum_size = 65557; // 0xFFFF + 22; - if ($v_maximum_size > $v_size) { - $v_maximum_size = $v_size; - } - @fseek($this->zip_fd, $v_size - $v_maximum_size); - if (@ftell($this->zip_fd) != ($v_size - $v_maximum_size)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \'' . $this->zipname . '\''); - - // ----- Return - return self::errorCode(); - } - - // ----- Read byte per byte in order to find the signature - $v_pos = ftell($this->zip_fd); - $v_bytes = 0x00000000; - while ($v_pos < $v_size) { - // ----- Read a byte - $v_byte = @fread($this->zip_fd, 1); - - // ----- Add the byte - //$v_bytes = ($v_bytes << 8) | Ord($v_byte); - // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number - // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. - $v_bytes = (($v_bytes & 0xFFFFFF) << 8) | ord($v_byte); - - // ----- Compare the bytes - if ($v_bytes == 0x504b0506) { - ++$v_pos; - break; - } - - ++$v_pos; - } - - // ----- Look if not found end of central dir - if ($v_pos == $v_size) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to find End of Central Dir Record signature'); - - // ----- Return - return self::errorCode(); - } - } - - // ----- Read the first 18 bytes of the header - $v_binary_data = fread($this->zip_fd, 18); - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 18) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid End of Central Dir Record size : ' . strlen($v_binary_data)); - - // ----- Return - return self::errorCode(); - } - - // ----- Extract the values - $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); - - // ----- Check the global size - if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { - // ----- Removed in release 2.2 see readme file - // The check of the file size is a little too strict. - // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. - // While decrypted, zip has training 0 bytes - if (0) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'The central dir is not at the end of the archive. Some trailing bytes exists after the archive.'); - - // ----- Return - return self::errorCode(); - } - } - - // ----- Get comment - if ($v_data['comment_size'] != 0) { - $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); - } else { - $p_central_dir['comment'] = ''; - } - - $p_central_dir['entries'] = $v_data['entries']; - $p_central_dir['disk_entries'] = $v_data['disk_entries']; - $p_central_dir['offset'] = $v_data['offset']; - $p_central_dir['size'] = $v_data['size']; - $p_central_dir['disk'] = $v_data['disk']; - $p_central_dir['disk_start'] = $v_data['disk_start']; - - // TBC - //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { - //} - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDeleteByRule() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privDeleteByRule(&$p_result_list, &$p_options) - { - $v_result = 1; - $v_list_detail = []; - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('rb')) != 1) { - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = []; - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { - $this->privCloseFd(); - - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - - // ----- Scan all the files - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_pos_entry)) { - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read each entry - $v_header_list = []; - $j_start = 0; - for ($i = 0, $v_nb_extracted = 0; $i < $v_central_dir['entries']; ++$i) { - // ----- Read the file header - $v_header_list[$v_nb_extracted] = []; - if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - - return $v_result; - } - - // ----- Store the index - $v_header_list[$v_nb_extracted]['index'] = $i; - - // ----- Look for the specific extract rules - $v_found = false; - - // ----- Look for extract by name rule - if ((isset($p_options[PCLZIP_OPT_BY_NAME])) && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - // ----- Look if the filename is in the list - for ($j = 0; ($j < count($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_found); ++$j) { - // ----- Look for a directory - if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == '/') { - // ----- Look if the directory is in the filename path - if ((strlen($v_header_list[$v_nb_extracted]['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_found = true; - } elseif ((($v_header_list[$v_nb_extracted]['external'] & 0x00000010) == 0x00000010) /* Indicates a folder */ && ($v_header_list[$v_nb_extracted]['stored_filename'] . '/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_found = true; - } - } elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - // ----- Look for a filename - $v_found = true; - } - } - } elseif ((isset($p_options[PCLZIP_OPT_BY_PREG])) && ($p_options[PCLZIP_OPT_BY_PREG] != '')) { - // ----- Look for extract by preg rule - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - $v_found = true; - } - } elseif ((isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - // ----- Look for extract by index rule - // ----- Look if the index is in the list - for ($j = $j_start; ($j < count($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_found); ++$j) { - if (($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i <= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - $v_found = true; - } - if ($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - $j_start = $j + 1; - } - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start'] > $i) { - break; - } - } - } else { - $v_found = true; - } - - // ----- Look for deletion - if ($v_found) { - unset($v_header_list[$v_nb_extracted]); - } else { - ++$v_nb_extracted; - } - } - - // ----- Look if something need to be deleted - if ($v_nb_extracted > 0) { - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.tmp'; - - // ----- Creates a temporary zip archive - $v_temp_zip = new self($v_zip_temp_name); - - // ----- Open the temporary zip file in write mode - if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { - $this->privCloseFd(); - - // ----- Return - return $v_result; - } - - // ----- Look which file need to be kept - for ($i = 0; $i < count($v_header_list); ++$i) { - // ----- Calculate the position of the header - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Error log - self::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return self::errorCode(); - } - - // ----- Read the file header - $v_local_header = []; - if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Check that local file header is same as central file header - if ($this->privCheckFileHeaders($v_local_header, $v_header_list[$i]) != 1) { - // TBC - } - unset($v_local_header); - - // ----- Write the file header - if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Read/write the data block - if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($v_temp_zip->zip_fd); - - // ----- Re-Create the Central Dir files header - for ($i = 0; $i < count($v_header_list); ++$i) { - // ----- Create the file header - if (($v_result = $v_temp_zip->privWriteCentralFileHeader($v_header_list[$i])) != 1) { - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Transform the header to a 'usable' info - $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = ''; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - - // ----- Calculate the size of the central header - $v_size = @ftell($v_temp_zip->zip_fd) - $v_offset; - - // ----- Create the central dir footer - if (($v_result = $v_temp_zip->privWriteCentralHeader(count($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { - // ----- Reset the file list - unset($v_header_list); - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; - } - - // ----- Close - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Destroy the temporary archive - unset($v_temp_zip); - } elseif ($v_central_dir['entries'] != 0) { - // ----- Remove every files : reset the file - $this->privCloseFd(); - - if (($v_result = $this->privOpenFd('wb')) != 1) { - return $v_result; - } - - if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { - return $v_result; - } - - $this->privCloseFd(); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDirCheck() - // Description : - // Check if a directory exists, if not it creates it and all the parents directory - // which may be useful. - // Parameters : - // $p_dir : Directory path to check. - // Return Values : - // 1 : OK - // -1 : Unable to create directory - // -------------------------------------------------------------------------------- - public function privDirCheck($p_dir, $p_is_dir = false) - { - $v_result = 1; - - // ----- Remove the final '/' - if (($p_is_dir) && (substr($p_dir, -1) == '/')) { - $p_dir = substr($p_dir, 0, strlen($p_dir) - 1); - } - - // ----- Check the directory availability - if ((is_dir($p_dir)) || ($p_dir == '')) { - return 1; - } - - // ----- Extract parent directory - $p_parent_dir = dirname($p_dir); - - // ----- Just a check - if ($p_parent_dir != $p_dir) { - // ----- Look for parent directory - if ($p_parent_dir != '') { - if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) { - return $v_result; - } - } - } - - // ----- Create the directory - if (!@mkdir($p_dir, 0777)) { - // ----- Error log - self::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); - - // ----- Return - return self::errorCode(); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privMerge() - // Description : - // If $p_archive_to_add does not exist, the function exit with a success result. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privMerge(&$p_archive_to_add) - { - $v_result = 1; - - // ----- Look if the archive_to_add exists - if (!is_file($p_archive_to_add->zipname)) { - // ----- Nothing to merge, so merge is a success - $v_result = 1; - - // ----- Return - return $v_result; - } - - // ----- Look if the archive exists - if (!is_file($this->zipname)) { - // ----- Do a duplicate - $v_result = $this->privDuplicate($p_archive_to_add->zipname); - - // ----- Return - return $v_result; - } - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('rb')) != 1) { - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = []; - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { - $this->privCloseFd(); - - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - - // ----- Open the archive_to_add file - if (($v_result = $p_archive_to_add->privOpenFd('rb')) != 1) { - $this->privCloseFd(); - - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir_to_add = []; - if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - return $v_result; - } - - // ----- Go to beginning of File - @rewind($p_archive_to_add->zip_fd); - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.tmp'; - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_zip_temp_name . '\' in binary write mode'); - - // ----- Return - return self::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Copy the files from the archive_to_add into the temporary file - $v_size = $v_central_dir_to_add['offset']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($v_zip_temp_fd); - - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Copy the block of file headers from the archive_to_add - $v_size = $v_central_dir_to_add['size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Merge the file comments - $v_comment = $v_central_dir['comment'] . ' ' . $v_central_dir_to_add['comment']; - - // ----- Calculate the size of the (new) central header - $v_size = @ftell($v_zip_temp_fd) - $v_offset; - - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive fd - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries'] + $v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - @fclose($v_zip_temp_fd); - $this->zip_fd = null; - - // ----- Reset the file list - unset($v_header_list); - - // ----- Return - return $v_result; - } - - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Close - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDuplicate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privDuplicate($p_archive_filename) - { - $v_result = 1; - - // ----- Look if the $p_archive_filename exists - if (!is_file($p_archive_filename)) { - // ----- Nothing to duplicate, so duplicate is a success. - $v_result = 1; - - // ----- Return - return $v_result; - } - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('wb')) != 1) { - // ----- Return - return $v_result; - } - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) { - $this->privCloseFd(); - - self::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \'' . $p_archive_filename . '\' in binary write mode'); - - // ----- Return - return self::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = filesize($p_archive_filename); - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close - $this->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privErrorLog() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function privErrorLog($p_error_code = 0, $p_error_string = '') - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclError($p_error_code, $p_error_string); - } else { - $this->error_code = $p_error_code; - $this->error_string = $p_error_string; - } - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privErrorReset() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - public function privErrorReset() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclErrorReset(); - } else { - $this->error_code = 0; - $this->error_string = ''; - } - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDisableMagicQuotes() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privDisableMagicQuotes() - { - $v_result = 1; - - // ----- Look if function exists - if ((!function_exists('get_magic_quotes_runtime')) || (!function_exists('set_magic_quotes_runtime'))) { - return $v_result; - } - - // ----- Look if already done - if ($this->magic_quotes_status != -1) { - return $v_result; - } - - // ----- Get and memorize the magic_quote value - $this->magic_quotes_status = @get_magic_quotes_runtime(); - - // ----- Disable magic_quotes - if ($this->magic_quotes_status == 1) { - @set_magic_quotes_runtime(0); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privSwapBackMagicQuotes() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - public function privSwapBackMagicQuotes() - { - $v_result = 1; - - // ----- Look if function exists - if ((!function_exists('get_magic_quotes_runtime')) || (!function_exists('set_magic_quotes_runtime'))) { - return $v_result; - } - - // ----- Look if something to do - if ($this->magic_quotes_status != -1) { - return $v_result; - } - - // ----- Swap back magic_quotes - if ($this->magic_quotes_status == 1) { - @set_magic_quotes_runtime($this->magic_quotes_status); - } - - // ----- Return - return $v_result; - } - - // -------------------------------------------------------------------------------- -} -// End of class -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Function : PclZipUtilPathReduction() -// Description : -// Parameters : -// Return Values : -// -------------------------------------------------------------------------------- -function PclZipUtilPathReduction($p_dir) -{ - $v_result = ''; - - // ----- Look for not empty path - if ($p_dir != '') { - // ----- Explode path by directory names - $v_list = explode('/', $p_dir); - - // ----- Study directories from last to first - $v_skip = 0; - for ($i = count($v_list) - 1; $i >= 0; --$i) { - // ----- Look for current path - if ($v_list[$i] == '.') { - // ----- Ignore this directory - // Should be the first $i=0, but no check is done - } elseif ($v_list[$i] == '..') { - ++$v_skip; - } elseif ($v_list[$i] == '') { - // ----- First '/' i.e. root slash - if ($i == 0) { - $v_result = '/' . $v_result; - if ($v_skip > 0) { - // ----- It is an invalid path, so the path is not modified - // TBC - $v_result = $p_dir; - $v_skip = 0; - } - } elseif ($i == (count($v_list) - 1)) { - // ----- Last '/' i.e. indicates a directory - $v_result = $v_list[$i]; - } - // ----- Double '/' inside the path - // ----- Ignore only the double '//' in path, - // but not the first and last '/' - } else { - // ----- Look for item to skip - if ($v_skip > 0) { - --$v_skip; - } else { - $v_result = $v_list[$i] . ($i != (count($v_list) - 1) ? '/' . $v_result : ''); - } - } - } - - // ----- Look for skip - if ($v_skip > 0) { - while ($v_skip > 0) { - $v_result = '../' . $v_result; - --$v_skip; - } - } - } - - // ----- Return - return $v_result; -} -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Function : PclZipUtilPathInclusion() -// Description : -// This function indicates if the path $p_path is under the $p_dir tree. Or, -// said in an other way, if the file or sub-dir $p_path is inside the dir -// $p_dir. -// The function indicates also if the path is exactly the same as the dir. -// This function supports path with duplicated '/' like '//', but does not -// support '.' or '..' statements. -// Parameters : -// Return Values : -// 0 if $p_path is not inside directory $p_dir -// 1 if $p_path is inside directory $p_dir -// 2 if $p_path is exactly the same as $p_dir -// -------------------------------------------------------------------------------- -function PclZipUtilPathInclusion($p_dir, $p_path) -{ - $v_result = 1; - - // ----- Look for path beginning by ./ - if (($p_dir == '.') || ((strlen($p_dir) >= 2) && (substr($p_dir, 0, 2) == './'))) { - $p_dir = PclZipUtilTranslateWinPath(getcwd(), false) . '/' . substr($p_dir, 1); - } - if (($p_path == '.') || ((strlen($p_path) >= 2) && (substr($p_path, 0, 2) == './'))) { - $p_path = PclZipUtilTranslateWinPath(getcwd(), false) . '/' . substr($p_path, 1); - } - - // ----- Explode dir and path by directory separator - $v_list_dir = explode('/', $p_dir); - $v_list_dir_size = count($v_list_dir); - $v_list_path = explode('/', $p_path); - $v_list_path_size = count($v_list_path); - - // ----- Study directories paths - $i = 0; - $j = 0; - while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { - // ----- Look for empty dir (path reduction) - if ($v_list_dir[$i] == '') { - ++$i; - continue; - } - if ($v_list_path[$j] == '') { - ++$j; - continue; - } - - // ----- Compare the items - if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ($v_list_path[$j] != '')) { - $v_result = 0; - } - - // ----- Next items - ++$i; - ++$j; - } - - // ----- Look if everything seems to be the same - if ($v_result) { - // ----- Skip all the empty items - while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) { - ++$j; - } - while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) { - ++$i; - } - - if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { - // ----- There are exactly the same - $v_result = 2; - } elseif ($i < $v_list_dir_size) { - // ----- The path is shorter than the dir - $v_result = 0; - } - } - - // ----- Return - return $v_result; -} -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Function : PclZipUtilCopyBlock() -// Description : -// Parameters : -// $p_mode : read/write compression mode -// 0 : src & dest normal -// 1 : src gzip, dest normal -// 2 : src normal, dest gzip -// 3 : src & dest gzip -// Return Values : -// -------------------------------------------------------------------------------- -function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode = 0) -{ - $v_result = 1; - - if ($p_mode == 0) { - while ($p_size != 0) { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } elseif ($p_mode == 1) { - while ($p_size != 0) { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } elseif ($p_mode == 2) { - while ($p_size != 0) { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } elseif ($p_mode == 3) { - while ($p_size != 0) { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - - // ----- Return - return $v_result; -} -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Function : PclZipUtilRename() -// Description : -// This function tries to do a simple rename() function. If it fails, it -// tries to copy the $p_src file in a new $p_dest file and then unlink the -// first one. -// Parameters : -// $p_src : Old filename -// $p_dest : New filename -// Return Values : -// 1 on success, 0 on failure. -// -------------------------------------------------------------------------------- -function PclZipUtilRename($p_src, $p_dest) -{ - $v_result = 1; - - // ----- Try to rename the files - if (!@rename($p_src, $p_dest)) { - // ----- Try to copy & unlink the src - if (!@copy($p_src, $p_dest)) { - $v_result = 0; - } elseif (!@unlink($p_src)) { - $v_result = 0; - } - } - - // ----- Return - return $v_result; -} -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Function : PclZipUtilOptionText() -// Description : -// Translate option value in text. Mainly for debug purpose. -// Parameters : -// $p_option : the option value. -// Return Values : -// The option text value. -// -------------------------------------------------------------------------------- -function PclZipUtilOptionText($p_option) -{ - $v_list = get_defined_constants(); - for (reset($v_list); $v_key = key($v_list); next($v_list)) { - $v_prefix = substr($v_key, 0, 10); - if ((($v_prefix == 'PCLZIP_OPT') || ($v_prefix == 'PCLZIP_CB_') || ($v_prefix == 'PCLZIP_ATT')) && ($v_list[$v_key] == $p_option)) { - return $v_key; - } - } - - $v_result = 'Unknown'; - - return $v_result; -} -// -------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------- -// Function : PclZipUtilTranslateWinPath() -// Description : -// Translate windows path by replacing '\' by '/' and optionally removing -// drive letter. -// Parameters : -// $p_path : path to translate. -// $p_remove_disk_letter : true | false -// Return Values : -// The path translated. -// -------------------------------------------------------------------------------- -function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter = true) -{ - if (stristr(php_uname(), 'windows')) { - // ----- Look for potential disk letter - if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position + 1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - - return $p_path; -} diff --git a/src/PhpSpreadsheet/Shared/PCLZip/gnu-lgpl.txt b/src/PhpSpreadsheet/Shared/PCLZip/gnu-lgpl.txt deleted file mode 100644 index d6213f33..00000000 --- a/src/PhpSpreadsheet/Shared/PCLZip/gnu-lgpl.txt +++ /dev/null @@ -1,503 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by John Doe. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - diff --git a/src/PhpSpreadsheet/Shared/PCLZip/readme.txt b/src/PhpSpreadsheet/Shared/PCLZip/readme.txt deleted file mode 100644 index d1b11e25..00000000 --- a/src/PhpSpreadsheet/Shared/PCLZip/readme.txt +++ /dev/null @@ -1,421 +0,0 @@ -// -------------------------------------------------------------------------------- -// PclZip 2.8.2 - readme.txt -// -------------------------------------------------------------------------------- -// License GNU/LGPL - August 2009 -// Vincent Blavet - vincent@phpconcept.net -// http://www.phpconcept.net -// -------------------------------------------------------------------------------- -// $Id: readme.txt,v 1.60 2009/09/30 20:35:21 vblavet Exp $ -// -------------------------------------------------------------------------------- - - - -0 - Sommaire -============ - 1 - Introduction - 2 - What's new - 3 - Corrected bugs - 4 - Known bugs or limitations - 5 - License - 6 - Warning - 7 - Documentation - 8 - Author - 9 - Contribute - -1 - Introduction -================ - - PclZip is a library that allow you to manage a Zip archive. - - Full documentation about PclZip can be found here : http://www.phpconcept.net/pclzip - -2 - What's new -============== - - Version 2.8.2 : - - PCLZIP_CB_PRE_EXTRACT and PCLZIP_CB_POST_EXTRACT are now supported with - extraction as a string (PCLZIP_OPT_EXTRACT_AS_STRING). The string - can also be modified in the post-extract call back. - **Bugs correction : - - PCLZIP_OPT_REMOVE_ALL_PATH was not working correctly - - Remove use of eval() and do direct call to callback functions - - Correct support of 64bits systems (Thanks to WordPress team) - - Version 2.8.1 : - - Move option PCLZIP_OPT_BY_EREG to PCLZIP_OPT_BY_PREG because ereg() is - deprecated in PHP 5.3. When using option PCLZIP_OPT_BY_EREG, PclZip will - automatically replace it by PCLZIP_OPT_BY_PREG. - - Version 2.8 : - - Improve extraction of zip archive for large files by using temporary files - This feature is working like the one defined in r2.7. - Options are renamed : PCLZIP_OPT_TEMP_FILE_ON, PCLZIP_OPT_TEMP_FILE_OFF, - PCLZIP_OPT_TEMP_FILE_THRESHOLD - - Add a ratio constant PCLZIP_TEMPORARY_FILE_RATIO to configure the auto - sense of temporary file use. - - Bug correction : Reduce filepath in returned file list to remove ennoying - './/' preambule in file path. - - Version 2.7 : - - Improve creation of zip archive for large files : - PclZip will now autosense the configured memory and use temporary files - when large file is suspected. - This feature can also ne triggered by manual options in create() and add() - methods. 'PCLZIP_OPT_ADD_TEMP_FILE_ON' force the use of temporary files, - 'PCLZIP_OPT_ADD_TEMP_FILE_OFF' disable the autosense technic, - 'PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD' allow for configuration of a size - threshold to use temporary files. - Using "temporary files" rather than "memory" might take more time, but - might give the ability to zip very large files : - Tested on my win laptop with a 88Mo file : - Zip "in-memory" : 18sec (max_execution_time=30, memory_limit=180Mo) - Zip "tmporary-files" : 23sec (max_execution_time=30, memory_limit=30Mo) - - Replace use of mktime() by time() to limit the E_STRICT error messages. - - Bug correction : When adding files with full windows path (drive letter) - PclZip is now working. Before, if the drive letter is not the default - path, PclZip was not able to add the file. - - Version 2.6 : - - Code optimisation - - New attributes PCLZIP_ATT_FILE_COMMENT gives the ability to - add a comment for a specific file. (Don't really know if this is usefull) - - New attribute PCLZIP_ATT_FILE_CONTENT gives the ability to add a string - as a file. - - New attribute PCLZIP_ATT_FILE_MTIME modify the timestamp associated with - a file. - - Correct a bug. Files archived with a timestamp with 0h0m0s were extracted - with current time - - Add CRC value in the informations returned back for each file after an - action. - - Add missing closedir() statement. - - When adding a folder, and removing the path of this folder, files were - incorrectly added with a '/' at the beginning. Which means files are - related to root in unix systems. Corrected. - - Add conditional if before constant definition. This will allow users - to redefine constants without changing the file, and then improve - upgrade of pclzip code for new versions. - - Version 2.5 : - - Introduce the ability to add file/folder with individual properties (file descriptor). - This gives for example the ability to change the filename of a zipped file. - . Able to add files individually - . Able to change full name - . Able to change short name - . Compatible with global options - - New attributes : PCLZIP_ATT_FILE_NAME, PCLZIP_ATT_FILE_NEW_SHORT_NAME, PCLZIP_ATT_FILE_NEW_FULL_NAME - - New error code : PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE - - Add a security control feature. PclZip can extract any file in any folder - of a system. People may use this to upload a zip file and try to override - a system file. The PCLZIP_OPT_EXTRACT_DIR_RESTRICTION will give the - ability to forgive any directory transversal behavior. - - New PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : check extraction path - - New error code : PCLZIP_ERR_DIRECTORY_RESTRICTION - - Modification in PclZipUtilPathInclusion() : dir and path beginning with ./ will be prepend - by current path (getcwd()) - - Version 2.4 : - - Code improvment : try to speed up the code by removing unusefull call to pack() - - Correct bug in delete() : delete() should be called with no argument. This was not - the case in 2.3. This is corrected in 2.4. - - Correct a bug in path_inclusion function. When the path has several '../../', the - result was bad. - - Add a check for magic_quotes_runtime configuration. If enabled, PclZip will - disable it while working and det it back to its original value. - This resolve a lots of bad formated archive errors. - - Bug correction : PclZip now correctly unzip file in some specific situation, - when compressed content has same size as uncompressed content. - - Bug correction : When selecting option 'PCLZIP_OPT_REMOVE_ALL_PATH', - directories are not any more created. - - Code improvment : correct unclosed opendir(), better handling of . and .. in - loops. - - - Version 2.3 : - - Correct a bug with PHP5 : affecting the value 0xFE49FFE0 to a variable does not - give the same result in PHP4 and PHP5 .... - - Version 2.2 : - - Try development of PCLZIP_OPT_CRYPT ..... - However this becomes to a stop. To crypt/decrypt I need to multiply 2 long integers, - the result (greater than a long) is not supported by PHP. Even the use of bcmath - functions does not help. I did not find yet a solution ...; - - Add missing '/' at end of directory entries - - Check is a file is encrypted or not. Returns status 'unsupported_encryption' and/or - error code PCLZIP_ERR_UNSUPPORTED_ENCRYPTION. - - Corrected : Bad "version need to extract" field in local file header - - Add private method privCheckFileHeaders() in order to check local and central - file headers. PclZip is now supporting purpose bit flag bit 3. Purpose bit flag bit 3 gives - the ability to have a local file header without size, compressed size and crc filled. - - Add a generic status 'error' for file status - - Add control of compression type. PclZip only support deflate compression method. - Before v2.2, PclZip does not check the compression method used in an archive while - extracting. With v2.2 PclZip returns a new error status for a file using an unsupported - compression method. New status is "unsupported_compression". New error code is - PCLZIP_ERR_UNSUPPORTED_COMPRESSION. - - Add optional attribute PCLZIP_OPT_STOP_ON_ERROR. This will stop the extract of files - when errors like 'a folder with same name exists' or 'a newer file exists' or - 'a write protected file' exists, rather than set a status for the concerning file - and resume the extract of the zip. - - Add optional attribute PCLZIP_OPT_REPLACE_NEWER. This will force, during an extract' the - replacement of the file, even if a newer version of the file exists. - Note that today if a file with the same name already exists but is older it will be - replaced by the extracted one. - - Improve PclZipUtilOption() - - Support of zip archive with trailing bytes. Before 2.2, PclZip checks that the central - directory structure is the last data in the archive. Crypt encryption/decryption of - zip archive put trailing 0 bytes after decryption. PclZip is now supporting this. - - Version 2.1 : - - Add the ability to abort the extraction by using a user callback function. - The user can now return the value '2' in its callback which indicates to stop the - extraction. For a pre call-back extract is stopped before the extration of the current - file. For a post call back, the extraction is stopped after. - - Add the ability to extract a file (or several files) directly in the standard output. - This is done by the new parameter PCLZIP_OPT_EXTRACT_IN_OUTPUT with method extract(). - - Add support for parameters PCLZIP_OPT_COMMENT, PCLZIP_OPT_ADD_COMMENT, - PCLZIP_OPT_PREPEND_COMMENT. This will create, replace, add, or prepend comments - in the zip archive. - - When merging two archives, the comments are not any more lost, but merged, with a - blank space separator. - - Corrected bug : Files are not deleted when all files are asked to be deleted. - - Corrected bug : Folders with name '0' made PclZip to abort the create or add feature. - - - Version 2.0 : - ***** Warning : Some new features may break the backward compatibility for your scripts. - Please carefully read the readme file. - - Add the ability to delete by Index, name and regular expression. This feature is - performed by the method delete(), which uses the optional parameters - PCLZIP_OPT_BY_INDEX, PCLZIP_OPT_BY_NAME, PCLZIP_OPT_BY_EREG or PCLZIP_OPT_BY_PREG. - - Add the ability to extract by regular expression. To extract by regexp you must use the method - extract(), with the option PCLZIP_OPT_BY_EREG or PCLZIP_OPT_BY_PREG - (depending if you want to use ereg() or preg_match() syntax) followed by the - regular expression pattern. - - Add the ability to extract by index, directly with the extract() method. This is a - code improvment of the extractByIndex() method. - - Add the ability to extract by name. To extract by name you must use the method - extract(), with the option PCLZIP_OPT_BY_NAME followed by the filename to - extract or an array of filenames to extract. To extract all a folder, use the folder - name rather than the filename with a '/' at the end. - - Add the ability to add files without compression. This is done with a new attribute - which is PCLZIP_OPT_NO_COMPRESSION. - - Add the attribute PCLZIP_OPT_EXTRACT_AS_STRING, which allow to extract a file directly - in a string without using any file (or temporary file). - - Add constant PCLZIP_SEPARATOR for static configuration of filename separators in a single string. - The default separator is now a comma (,) and not any more a blank space. - THIS BREAK THE BACKWARD COMPATIBILITY : Please check if this may have an impact with - your script. - - Improve algorythm performance by removing the use of temporary files when adding or - extracting files in an archive. - - Add (correct) detection of empty filename zipping. This can occurs when the removed - path is the same - as a zipped dir. The dir is not zipped (['status'] = filtered), only its content. - - Add better support for windows paths (thanks for help from manus@manusfreedom.com). - - Corrected bug : When the archive file already exists with size=0, the add() method - fails. Corrected in 2.0. - - Remove the use of OS_WINDOWS constant. Use php_uname() function rather. - - Control the order of index ranges in extract by index feature. - - Change the internal management of folders (better handling of internal flag). - - - Version 1.3 : - - Removing the double include check. This is now done by include_once() and require_once() - PHP directives. - - Changing the error handling mecanism : Remove the use of an external error library. - The former PclError...() functions are replaced by internal equivalent methods. - By changing the environment variable PCLZIP_ERROR_EXTERNAL you can still use the former library. - Introducing the use of constants for error codes rather than integer values. This will help - in futur improvment. - Introduction of error handling functions like errorCode(), errorName() and errorInfo(). - - Remove the deprecated use of calling function with arguments passed by reference. - - Add the calling of extract(), extractByIndex(), create() and add() functions - with variable options rather than fixed arguments. - - Add the ability to remove all the file path while extracting or adding, - without any need to specify the path to remove. - This is available for extract(), extractByIndex(), create() and add() functionS by using - the new variable options parameters : - - PCLZIP_OPT_REMOVE_ALL_PATH : by indicating this option while calling the fct. - - Ability to change the mode of a file after the extraction (chmod()). - This is available for extract() and extractByIndex() functionS by using - the new variable options parameters. - - PCLZIP_OPT_SET_CHMOD : by setting the value of this option. - - Ability to definition call-back options. These call-back will be called during the adding, - or the extracting of file (extract(), extractByIndex(), create() and add() functions) : - - PCLZIP_CB_PRE_EXTRACT : will be called before each extraction of a file. The user - can trigerred the change the filename of the extracted file. The user can triggered the - skip of the extraction. This is adding a 'skipped' status in the file list result value. - - PCLZIP_CB_POST_EXTRACT : will be called after each extraction of a file. - Nothing can be triggered from that point. - - PCLZIP_CB_PRE_ADD : will be called before each add of a file. The user - can trigerred the change the stored filename of the added file. The user can triggered the - skip of the add. This is adding a 'skipped' status in the file list result value. - - PCLZIP_CB_POST_ADD : will be called after each add of a file. - Nothing can be triggered from that point. - - Two status are added in the file list returned as function result : skipped & filename_too_long - 'skipped' is used when a call-back function ask for skipping the file. - 'filename_too_long' is used while adding a file with a too long filename to archive (the file is - not added) - - Adding the function PclZipUtilPathInclusion(), that check the inclusion of a path into - a directory. - - Add a check of the presence of the archive file before some actions (like list, ...) - - Add the initialisation of field "index" in header array. This means that by - default index will be -1 when not explicitly set by the methods. - - Version 1.2 : - - Adding a duplicate function. - - Adding a merge function. The merge function is a "quick merge" function, - it just append the content of an archive at the end of the first one. There - is no check for duplicate files or more recent files. - - Improve the search of the central directory end. - - Version 1.1.2 : - - - Changing the license of PclZip. PclZip is now released under the GNU / LGPL license - (see License section). - - Adding the optional support of a static temporary directory. You will need to configure - the constant PCLZIP_TEMPORARY_DIR if you want to use this feature. - - Improving the rename() function. In some cases rename() does not work (different - Filesystems), so it will be replaced by a copy() + unlink() functions. - - Version 1.1.1 : - - - Maintenance release, no new feature. - - Version 1.1 : - - - New method Add() : adding files in the archive - - New method ExtractByIndex() : partial extract of the archive, files are identified by - their index in the archive - - New method DeleteByIndex() : delete some files/folder entries from the archive, - files are identified by their index in the archive. - - Adding a test of the zlib extension presence. If not present abort the script. - - Version 1.0.1 : - - - No new feature - - -3 - Corrected bugs -================== - - Corrected in Version 2.0 : - - Corrected : During an extraction, if a call-back fucntion is used and try to skip - a file, all the extraction process is stopped. - - Corrected in Version 1.3 : - - Corrected : Support of static synopsis for method extract() is broken. - - Corrected : invalid size of archive content field (0xFF) should be (0xFFFF). - - Corrected : When an extract is done with a remove_path parameter, the entry for - the directory with exactly the same path is not skipped/filtered. - - Corrected : extractByIndex() and deleteByIndex() were not managing index in the - right way. For example indexes '1,3-5,11' will only extract files 1 and 11. This - is due to a sort of the index resulting table that puts 11 before 3-5 (sort on - string and not interger). The sort is temporarilly removed, this means that - you must provide a sorted list of index ranges. - - Corrected in Version 1.2 : - - - Nothing. - - Corrected in Version 1.1.2 : - - - Corrected : Winzip is unable to delete or add new files in a PclZip created archives. - - Corrected in Version 1.1.1 : - - - Corrected : When archived file is not compressed (0% compression), the - extract method fails. - - Corrected in Version 1.1 : - - - Corrected : Adding a complete tree of folder may result in a bad archive - creation. - - Corrected in Version 1.0.1 : - - - Corrected : Error while compressing files greater than PCLZIP_READ_BLOCK_SIZE (default=1024). - - -4 - Known bugs or limitations -============================= - - Please publish bugs reports in SourceForge : - http://sourceforge.net/tracker/?group_id=40254&atid=427564 - - In Version 2.x : - - PclZip does only support file uncompressed or compressed with deflate (compression method 8) - - PclZip does not support password protected zip archive - - Some concern were seen when changing mtime of a file while archiving. - Seems to be linked to Daylight Saving Time (PclTest_changing_mtime). - - In Version 1.2 : - - - merge() methods does not check for duplicate files or last date of modifications. - - In Version 1.1 : - - - Limitation : Using 'extract' fields in the file header in the zip archive is not supported. - - WinZip is unable to delete a single file in a PclZip created archive. It is also unable to - add a file in a PclZip created archive. (Corrected in v.1.2) - - In Version 1.0.1 : - - - Adding a complete tree of folder may result in a bad archive - creation. (Corrected in V.1.1). - - Path given to methods must be in the unix format (/) and not the Windows format (\). - Workaround : Use only / directory separators. - - PclZip is using temporary files that are sometime the name of the file with a .tmp or .gz - added suffix. Files with these names may already exist and may be overwritten. - Workaround : none. - - PclZip does not check if the zlib extension is present. If it is absent, the zip - file is not created and the lib abort without warning. - Workaround : enable the zlib extension on the php install - - In Version 1.0 : - - - Error while compressing files greater than PCLZIP_READ_BLOCK_SIZE (default=1024). - (Corrected in v.1.0.1) - - Limitation : Multi-disk zip archive are not supported. - - -5 - License -=========== - - Since version 1.1.2, PclZip Library is released under GNU/LGPL license. - This library is free, so you can use it at no cost. - - HOWEVER, if you release a script, an application, a library or any kind of - code using PclZip library (or a part of it), YOU MUST : - - Indicate in the documentation (or a readme file), that your work - uses PclZip Library, and make a reference to the author and the web site - http://www.phpconcept.net - - Gives the ability to the final user to update the PclZip libary. - - I will also appreciate that you send me a mail (vincent@phpconcept.net), just to - be aware that someone is using PclZip. - - For more information about GNU/LGPL license : http://www.gnu.org - -6 - Warning -================= - - This library and the associated files are non commercial, non professional work. - It should not have unexpected results. However if any damage is caused by this software - the author can not be responsible. - The use of this software is at the risk of the user. - -7 - Documentation -================= - PclZip User Manuel is available in English on PhpConcept : http://www.phpconcept.net/pclzip/man/en/index.php - A Russian translation was done by Feskov Kuzma : http://php.russofile.ru/ru/authors/unsort/zip/ - -8 - Author -========== - - This software was written by Vincent Blavet (vincent@phpconcept.net) on its leasure time. - -9 - Contribute -============== - If you want to contribute to the development of PclZip, please contact vincent@phpconcept.net. - If you can help in financing PhpConcept hosting service, please go to - http://www.phpconcept.net/soutien.php diff --git a/src/PhpSpreadsheet/Shared/PasswordHasher.php b/src/PhpSpreadsheet/Shared/PasswordHasher.php index f47a6573..2a546e0d 100644 --- a/src/PhpSpreadsheet/Shared/PasswordHasher.php +++ b/src/PhpSpreadsheet/Shared/PasswordHasher.php @@ -37,7 +37,7 @@ class PasswordHasher * * @return string Hashed password */ - public static function hashPassword($pPassword = '') + public static function hashPassword($pPassword) { $password = 0x0000; $charPos = 1; // char position diff --git a/src/PhpSpreadsheet/Shared/StringHelper.php b/src/PhpSpreadsheet/Shared/StringHelper.php index 299a9897..b1d51250 100644 --- a/src/PhpSpreadsheet/Shared/StringHelper.php +++ b/src/PhpSpreadsheet/Shared/StringHelper.php @@ -323,7 +323,7 @@ class StringHelper * * @return string */ - public static function controlCharacterOOXML2PHP($value = '') + public static function controlCharacterOOXML2PHP($value) { return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $value); } @@ -343,7 +343,7 @@ class StringHelper * * @return string */ - public static function controlCharacterPHP2OOXML($value = '') + public static function controlCharacterPHP2OOXML($value) { return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $value); } @@ -375,7 +375,7 @@ class StringHelper * * @return bool */ - public static function isUTF8($value = '') + public static function isUTF8($value) { return $value === '' || preg_match('/^./su', $value) === 1; } @@ -468,7 +468,10 @@ class StringHelper public static function convertEncoding($value, $to, $from) { if (self::getIsIconvEnabled()) { - return iconv($from, $to . '//IGNORE//TRANSLIT', $value); + $result = iconv($from, $to . '//IGNORE//TRANSLIT', $value); + if (false !== $result) { + return $result; + } } return mb_convert_encoding($value, $to, $from); @@ -496,7 +499,7 @@ class StringHelper * * @return string */ - public static function substring($pValue = '', $pStart = 0, $pLength = 0) + public static function substring($pValue, $pStart, $pLength = 0) { return mb_substr($pValue, $pStart, $pLength, 'UTF-8'); } @@ -508,7 +511,7 @@ class StringHelper * * @return string */ - public static function strToUpper($pValue = '') + public static function strToUpper($pValue) { return mb_convert_case($pValue, MB_CASE_UPPER, 'UTF-8'); } @@ -520,7 +523,7 @@ class StringHelper * * @return string */ - public static function strToLower($pValue = '') + public static function strToLower($pValue) { return mb_convert_case($pValue, MB_CASE_LOWER, 'UTF-8'); } @@ -533,7 +536,7 @@ class StringHelper * * @return string */ - public static function strToTitle($pValue = '') + public static function strToTitle($pValue) { return mb_convert_case($pValue, MB_CASE_TITLE, 'UTF-8'); } @@ -550,44 +553,6 @@ class StringHelper return preg_split('/(? $val) { - $ret[$key] = self::mbStrReplace($search, $replace, $val); - } - - return $ret; - } - - foreach ((array) $search as $key => $s) { - if ($s == '' && $s !== 0) { - continue; - } - $r = !is_array($replace) ? $replace : (isset($replace[$key]) ? $replace[$key] : ''); - $pos = mb_strpos($subject, $s, 0, 'UTF-8'); - while ($pos !== false) { - $subject = mb_substr($subject, 0, $pos, 'UTF-8') . $r . mb_substr($subject, $pos + mb_strlen($s, 'UTF-8'), null, 'UTF-8'); - $pos = mb_strpos($subject, $s, $pos + mb_strlen($r, 'UTF-8'), 'UTF-8'); - } - } - - return $subject; - } - /** * Reverse the case of a string, so that all uppercase characters become lowercase * and all lowercase characters become uppercase. @@ -596,7 +561,7 @@ class StringHelper * * @return string */ - public static function strCaseReverse($pValue = '') + public static function strCaseReverse($pValue) { $characters = self::mbStrSplit($pValue); foreach ($characters as &$character) { @@ -661,7 +626,7 @@ class StringHelper * * @param string $pValue Character for decimal separator */ - public static function setDecimalSeparator($pValue = '.') + public static function setDecimalSeparator($pValue) { self::$decimalSeparator = $pValue; } @@ -694,7 +659,7 @@ class StringHelper * * @param string $pValue Character for thousands separator */ - public static function setThousandsSeparator($pValue = ',') + public static function setThousandsSeparator($pValue) { self::$thousandsSeparator = $pValue; } @@ -732,7 +697,7 @@ class StringHelper * * @param string $pValue Character for currency code */ - public static function setCurrencyCode($pValue = '$') + public static function setCurrencyCode($pValue) { self::$currencyCode = $pValue; } @@ -744,7 +709,7 @@ class StringHelper * * @return string UTF-8 encoded string */ - public static function SYLKtoUTF8($pValue = '') + public static function SYLKtoUTF8($pValue) { // If there is no escape character in the string there is nothing to do if (strpos($pValue, '') === false) { diff --git a/src/PhpSpreadsheet/Shared/ZipArchive.php b/src/PhpSpreadsheet/Shared/ZipArchive.php deleted file mode 100644 index 3926f834..00000000 --- a/src/PhpSpreadsheet/Shared/ZipArchive.php +++ /dev/null @@ -1,165 +0,0 @@ -tempDir = File::sysGetTempDir(); - $this->zip = new PclZip($fileName); - - return true; - } - - /** - * Close this zip archive. - */ - public function close() - { - } - - /** - * Add a new file to the zip archive from a string of raw data. - * - * @param string $localname Directory/Name of the file to add to the zip archive - * @param string $contents String of data to add to the zip archive - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - */ - public function addFromString($localname, $contents) - { - $filenameParts = pathinfo($localname); - - $handle = fopen($this->tempDir . '/' . $filenameParts['basename'], 'wb'); - fwrite($handle, $contents); - fclose($handle); - - $res = $this->zip->add($this->tempDir . '/' . $filenameParts['basename'], PCLZIP_OPT_REMOVE_PATH, $this->tempDir, PCLZIP_OPT_ADD_PATH, $filenameParts['dirname']); - if ($res == 0) { - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Error zipping files : ' . $this->zip->errorInfo(true)); - } - - unlink($this->tempDir . '/' . $filenameParts['basename']); - } - - /** - * Find if given fileName exist in archive (Emulate ZipArchive locateName()). - * - * @param string $fileName Filename for the file in zip archive - * - * @return bool - */ - public function locateName($fileName) - { - $fileName = strtolower($fileName); - - $list = $this->zip->listContent(); - $listCount = count($list); - $index = -1; - for ($i = 0; $i < $listCount; ++$i) { - if (strtolower($list[$i]['filename']) == strtolower($fileName) || - strtolower($list[$i]['stored_filename']) == strtolower($fileName)) { - $index = $i; - break; - } - } - - return ($index > -1) ? $index : false; - } - - /** - * Extract file from archive by given fileName (Emulate ZipArchive getFromName()). - * - * @param string $fileName Filename for the file in zip archive - * - * @return string $contents File string contents - */ - public function getFromName($fileName) - { - $index = $this->locateName($fileName); - - if ($index !== false) { - $extracted = $this->getFromIndex($index); - } else { - $fileName = substr($fileName, 1); - $index = $this->locateName($fileName); - if ($index === false) { - return false; - } - $extracted = $this->zip->getFromIndex($index); - } - - $contents = $extracted; - if ((is_array($extracted)) && ($extracted != 0)) { - $contents = $extracted[0]['content']; - } - - return $contents; - } - - /** - * @param int $index - */ - public function getFromIndex($index) - { - $extracted = $this->zip->extractByIndex($index, PCLZIP_OPT_EXTRACT_AS_STRING); - $contents = ''; - if ((is_array($extracted)) && ($extracted != 0)) { - $contents = $extracted[0]['content']; - } - } -} diff --git a/src/PhpSpreadsheet/Shared/ZipStreamWrapper.php b/src/PhpSpreadsheet/Shared/ZipStreamWrapper.php deleted file mode 100644 index 3cedb78a..00000000 --- a/src/PhpSpreadsheet/Shared/ZipStreamWrapper.php +++ /dev/null @@ -1,206 +0,0 @@ -archive = new $zipClass(); - $this->archive->open($url['host']); - - $this->fileNameInArchive = $url['fragment']; - $this->position = 0; - $this->data = $this->archive->getFromName($this->fileNameInArchive); - - return true; - } - - /** - * Implements support for fstat(). - * - * @return string - */ - public function statName() - { - return $this->fileNameInArchive; - } - - /** - * Implements support for fstat(). - * - * @return string - */ - public function url_stat() // @codingStandardsIgnoreLine - { - return $this->statName(); - } - - /** - * Implements support for fstat(). - * - * @return bool - */ - public function stream_stat() // @codingStandardsIgnoreLine - { - return $this->archive->statName($this->fileNameInArchive); - } - - /** - * Implements support for fread(), fgets() etc. - * - * @param int $count maximum number of bytes to read - * - * @return string - */ - public function stream_read($count) // @codingStandardsIgnoreLine - { - $ret = substr($this->data, $this->position, $count); - $this->position += strlen($ret); - - return $ret; - } - - /** - * Returns the position of the file pointer, i.e. its offset into the file - * stream. Implements support for ftell(). - * - * @return int - */ - public function stream_tell() // @codingStandardsIgnoreLine - { - return $this->position; - } - - /** - * EOF stream. - * - * @return bool - */ - public function stream_eof() // @codingStandardsIgnoreLine - { - return $this->position >= strlen($this->data); - } - - /** - * Seek stream. - * - * @param int $offset byte offset - * @param int $whence SEEK_SET, SEEK_CUR or SEEK_END - * - * @return bool - */ - public function stream_seek($offset, $whence) // @codingStandardsIgnoreLine - { - switch ($whence) { - case SEEK_SET: - if ($offset < strlen($this->data) && $offset >= 0) { - $this->position = $offset; - - return true; - } - - return false; - case SEEK_CUR: - if ($offset >= 0) { - $this->position += $offset; - - return true; - } - - return false; - case SEEK_END: - if (strlen($this->data) + $offset >= 0) { - $this->position = strlen($this->data) + $offset; - - return true; - } - - return false; - default: - return false; - } - } -} diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index 91f433d5..2724286b 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -148,7 +148,7 @@ class Spreadsheet * * @param bool $hasMacros true|false */ - public function setHasMacros($hasMacros = false) + public function setHasMacros($hasMacros) { $this->hasMacros = (bool) $hasMacros; } @@ -158,7 +158,7 @@ class Spreadsheet * * @param string $macroCode string|null */ - public function setMacrosCode($macroCode = null) + public function setMacrosCode($macroCode) { $this->macrosCode = $macroCode; $this->setHasMacros(!is_null($macroCode)); @@ -179,7 +179,7 @@ class Spreadsheet * * @param string|null $certificate */ - public function setMacrosCertificate($certificate = null) + public function setMacrosCertificate($certificate) { $this->macrosCertificate = $certificate; } @@ -220,7 +220,7 @@ class Spreadsheet * @param null|mixed $target * @param null|mixed $xmlData */ - public function setRibbonXMLData($target = null, $xmlData = null) + public function setRibbonXMLData($target, $xmlData) { if (!is_null($target) && !is_null($xmlData)) { $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData]; @@ -234,7 +234,7 @@ class Spreadsheet * * return string|null|array * - * @param mixed $what + * @param string $what * * @return string */ @@ -263,7 +263,7 @@ class Spreadsheet * @param null|mixed $BinObjectsNames * @param null|mixed $BinObjectsData */ - public function setRibbonBinObjects($BinObjectsNames = null, $BinObjectsData = null) + public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData) { if (!is_null($BinObjectsNames) && !is_null($BinObjectsData)) { $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData]; @@ -285,20 +285,20 @@ class Spreadsheet /** * retrieve Binaries Ribbon Objects. * - * @param mixed $What + * @param mixed $what */ - public function getRibbonBinObjects($What = 'all') + public function getRibbonBinObjects($what = 'all') { $ReturnData = null; - $What = strtolower($What); - switch ($What) { + $what = strtolower($what); + switch ($what) { case 'all': return $this->ribbonBinObjects; break; case 'names': case 'data': - if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$What])) { - $ReturnData = $this->ribbonBinObjects[$What]; + if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) { + $ReturnData = $this->ribbonBinObjects[$what]; } break; case 'types': @@ -354,7 +354,7 @@ class Spreadsheet * * @return Worksheet */ - public function getSheetByCodeName($pName = '') + public function getSheetByCodeName($pName) { $worksheetCount = count($this->workSheetCollection); for ($i = 0; $i < $worksheetCount; ++$i) { @@ -486,16 +486,16 @@ class Spreadsheet /** * Create sheet and add it to this workbook. * - * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last) + * @param int|null $sheetIndex Index where sheet should go (0,1,..., or null for last) * * @throws Exception * * @return Worksheet */ - public function createSheet($iSheetIndex = null) + public function createSheet($sheetIndex = null) { $newSheet = new Worksheet($this); - $this->addSheet($newSheet, $iSheetIndex); + $this->addSheet($newSheet, $sheetIndex); return $newSheet; } @@ -564,7 +564,7 @@ class Spreadsheet * * @throws Exception */ - public function removeSheetByIndex($pIndex = 0) + public function removeSheetByIndex($pIndex) { $numSheets = count($this->workSheetCollection); if ($pIndex > $numSheets - 1) { @@ -590,7 +590,7 @@ class Spreadsheet * * @return Worksheet */ - public function getSheet($pIndex = 0) + public function getSheet($pIndex) { if (!isset($this->workSheetCollection[$pIndex])) { $numSheets = $this->getSheetCount(); @@ -619,7 +619,7 @@ class Spreadsheet * * @return Worksheet */ - public function getSheetByName($pName = '') + public function getSheetByName($pName) { $worksheetCount = count($this->workSheetCollection); for ($i = 0; $i < $worksheetCount; ++$i) { @@ -708,7 +708,7 @@ class Spreadsheet * * @return Worksheet */ - public function setActiveSheetIndex($pIndex = 0) + public function setActiveSheetIndex($pIndex) { $numSheets = count($this->workSheetCollection); @@ -731,7 +731,7 @@ class Spreadsheet * * @return Worksheet */ - public function setActiveSheetIndexByName($pValue = '') + public function setActiveSheetIndexByName($pValue) { if (($worksheet = $this->getSheetByName($pValue)) instanceof Worksheet) { $this->setActiveSheetIndex($this->getIndex($worksheet)); @@ -786,8 +786,8 @@ class Spreadsheet $pSheet->rebindParent($this); // update the cellXfs - foreach ($pSheet->getCellCollection(false) as $cellID) { - $cell = $pSheet->getCell($cellID); + foreach ($pSheet->getCoordinates(false) as $coordinate) { + $cell = $pSheet->getCell($coordinate); $cell->setXfIndex($cell->getXfIndex() + $countCellXfs); } @@ -931,7 +931,7 @@ class Spreadsheet * * @return Style */ - public function getCellXfByIndex($pIndex = 0) + public function getCellXfByIndex($pIndex) { return $this->cellXfCollection[$pIndex]; } @@ -943,7 +943,7 @@ class Spreadsheet * * @return Style|false */ - public function getCellXfByHashCode($pValue = '') + public function getCellXfByHashCode($pValue) { foreach ($this->cellXfCollection as $cellXf) { if ($cellXf->getHashCode() == $pValue) { @@ -961,7 +961,7 @@ class Spreadsheet * * @return bool */ - public function cellXfExists($pCellStyle = null) + public function cellXfExists($pCellStyle) { return in_array($pCellStyle, $this->cellXfCollection, true); } @@ -999,7 +999,7 @@ class Spreadsheet * * @throws Exception */ - public function removeCellXfByIndex($pIndex = 0) + public function removeCellXfByIndex($pIndex) { if ($pIndex > count($this->cellXfCollection) - 1) { throw new Exception('CellXf index is out of bounds.'); @@ -1010,8 +1010,8 @@ class Spreadsheet // then update cellXf indexes for cells foreach ($this->workSheetCollection as $worksheet) { - foreach ($worksheet->getCellCollection(false) as $cellID) { - $cell = $worksheet->getCell($cellID); + foreach ($worksheet->getCoordinates(false) as $coordinate) { + $cell = $worksheet->getCell($coordinate); $xfIndex = $cell->getXfIndex(); if ($xfIndex > $pIndex) { // decrease xf index by 1 @@ -1051,7 +1051,7 @@ class Spreadsheet * * @return Style */ - public function getCellStyleXfByIndex($pIndex = 0) + public function getCellStyleXfByIndex($pIndex) { return $this->cellStyleXfCollection[$pIndex]; } @@ -1063,7 +1063,7 @@ class Spreadsheet * * @return Style|false */ - public function getCellStyleXfByHashCode($pValue = '') + public function getCellStyleXfByHashCode($pValue) { foreach ($this->cellStyleXfCollection as $cellStyleXf) { if ($cellStyleXf->getHashCode() == $pValue) { @@ -1092,7 +1092,7 @@ class Spreadsheet * * @throws Exception */ - public function removeCellStyleXfByIndex($pIndex = 0) + public function removeCellStyleXfByIndex($pIndex) { if ($pIndex > count($this->cellStyleXfCollection) - 1) { throw new Exception('CellStyleXf index is out of bounds.'); @@ -1114,8 +1114,8 @@ class Spreadsheet foreach ($this->getWorksheetIterator() as $sheet) { // from cells - foreach ($sheet->getCellCollection(false) as $cellID) { - $cell = $sheet->getCell($cellID); + foreach ($sheet->getCoordinates(false) as $coordinate) { + $cell = $sheet->getCell($coordinate); ++$countReferencesCellXf[$cell->getXfIndex()]; } @@ -1158,8 +1158,8 @@ class Spreadsheet // update the xfIndex for all cells, row dimensions, column dimensions foreach ($this->getWorksheetIterator() as $sheet) { // for all cells - foreach ($sheet->getCellCollection(false) as $cellID) { - $cell = $sheet->getCell($cellID); + foreach ($sheet->getCoordinates(false) as $coordinate) { + $cell = $sheet->getCell($coordinate); $cell->setXfIndex($map[$cell->getXfIndex()]); } diff --git a/src/PhpSpreadsheet/Style.php b/src/PhpSpreadsheet/Style.php index b8c0675f..65902077 100644 --- a/src/PhpSpreadsheet/Style.php +++ b/src/PhpSpreadsheet/Style.php @@ -208,263 +208,259 @@ class Style extends Style\Supervisor implements IComparable * * @return Style */ - public function applyFromArray($pStyles = null, $pAdvanced = true) + public function applyFromArray(array $pStyles, $pAdvanced = true) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $pRange = $this->getSelectedCells(); + if ($this->isSupervisor) { + $pRange = $this->getSelectedCells(); - // Uppercase coordinate - $pRange = strtoupper($pRange); + // Uppercase coordinate + $pRange = strtoupper($pRange); - // Is it a cell range or a single cell? - if (strpos($pRange, ':') === false) { - $rangeA = $pRange; - $rangeB = $pRange; - } else { - list($rangeA, $rangeB) = explode(':', $pRange); - } - - // Calculate range outer borders - $rangeStart = Cell::coordinateFromString($rangeA); - $rangeEnd = Cell::coordinateFromString($rangeB); - - // Translate column into index - $rangeStart[0] = Cell::columnIndexFromString($rangeStart[0]) - 1; - $rangeEnd[0] = Cell::columnIndexFromString($rangeEnd[0]) - 1; - - // Make sure we can loop upwards on rows and columns - if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) { - $tmp = $rangeStart; - $rangeStart = $rangeEnd; - $rangeEnd = $tmp; - } - - // ADVANCED MODE: - if ($pAdvanced && isset($pStyles['borders'])) { - // 'allborders' is a shorthand property for 'outline' and 'inside' and - // it applies to components that have not been set explicitly - if (isset($pStyles['borders']['allborders'])) { - foreach (['outline', 'inside'] as $component) { - if (!isset($pStyles['borders'][$component])) { - $pStyles['borders'][$component] = $pStyles['borders']['allborders']; - } - } - unset($pStyles['borders']['allborders']); // not needed any more - } - // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left' - // it applies to components that have not been set explicitly - if (isset($pStyles['borders']['outline'])) { - foreach (['top', 'right', 'bottom', 'left'] as $component) { - if (!isset($pStyles['borders'][$component])) { - $pStyles['borders'][$component] = $pStyles['borders']['outline']; - } - } - unset($pStyles['borders']['outline']); // not needed any more - } - // 'inside' is a shorthand property for 'vertical' and 'horizontal' - // it applies to components that have not been set explicitly - if (isset($pStyles['borders']['inside'])) { - foreach (['vertical', 'horizontal'] as $component) { - if (!isset($pStyles['borders'][$component])) { - $pStyles['borders'][$component] = $pStyles['borders']['inside']; - } - } - unset($pStyles['borders']['inside']); // not needed any more - } - // width and height characteristics of selection, 1, 2, or 3 (for 3 or more) - $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3); - $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3); - - // loop through up to 3 x 3 = 9 regions - for ($x = 1; $x <= $xMax; ++$x) { - // start column index for region - $colStart = ($x == 3) ? - Cell::stringFromColumnIndex($rangeEnd[0]) - : Cell::stringFromColumnIndex($rangeStart[0] + $x - 1); - // end column index for region - $colEnd = ($x == 1) ? - Cell::stringFromColumnIndex($rangeStart[0]) - : Cell::stringFromColumnIndex($rangeEnd[0] - $xMax + $x); - - for ($y = 1; $y <= $yMax; ++$y) { - // which edges are touching the region - $edges = []; - if ($x == 1) { - // are we at left edge - $edges[] = 'left'; - } - if ($x == $xMax) { - // are we at right edge - $edges[] = 'right'; - } - if ($y == 1) { - // are we at top edge? - $edges[] = 'top'; - } - if ($y == $yMax) { - // are we at bottom edge? - $edges[] = 'bottom'; - } - - // start row index for region - $rowStart = ($y == 3) ? - $rangeEnd[1] : $rangeStart[1] + $y - 1; - - // end row index for region - $rowEnd = ($y == 1) ? - $rangeStart[1] : $rangeEnd[1] - $yMax + $y; - - // build range for region - $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd; - - // retrieve relevant style array for region - $regionStyles = $pStyles; - unset($regionStyles['borders']['inside']); - - // what are the inner edges of the region when looking at the selection - $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges); - - // inner edges that are not touching the region should take the 'inside' border properties if they have been set - foreach ($innerEdges as $innerEdge) { - switch ($innerEdge) { - case 'top': - case 'bottom': - // should pick up 'horizontal' border property if set - if (isset($pStyles['borders']['horizontal'])) { - $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal']; - } else { - unset($regionStyles['borders'][$innerEdge]); - } - break; - case 'left': - case 'right': - // should pick up 'vertical' border property if set - if (isset($pStyles['borders']['vertical'])) { - $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical']; - } else { - unset($regionStyles['borders'][$innerEdge]); - } - break; - } - } - - // apply region style to region by calling applyFromArray() in simple mode - $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false); - } - } - - return $this; - } - - // SIMPLE MODE: - // Selection type, inspect - if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) { - $selectionType = 'COLUMN'; - } elseif (preg_match('/^A[0-9]+:XFD[0-9]+$/', $pRange)) { - $selectionType = 'ROW'; - } else { - $selectionType = 'CELL'; - } - - // First loop through columns, rows, or cells to find out which styles are affected by this operation - switch ($selectionType) { - case 'COLUMN': - $oldXfIndexes = []; - for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { - $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true; - } - break; - case 'ROW': - $oldXfIndexes = []; - for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { - if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) { - $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style - } else { - $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true; - } - } - break; - case 'CELL': - $oldXfIndexes = []; - for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { - for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { - $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true; - } - } - break; - } - - // clone each of the affected styles, apply the style array, and add the new styles to the workbook - $workbook = $this->getActiveSheet()->getParent(); - foreach ($oldXfIndexes as $oldXfIndex => $dummy) { - $style = $workbook->getCellXfByIndex($oldXfIndex); - $newStyle = clone $style; - $newStyle->applyFromArray($pStyles); - - if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) { - // there is already such cell Xf in our collection - $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex(); - } else { - // we don't have such a cell Xf, need to add - $workbook->addCellXf($newStyle); - $newXfIndexes[$oldXfIndex] = $newStyle->getIndex(); - } - } - - // Loop through columns, rows, or cells again and update the XF index - switch ($selectionType) { - case 'COLUMN': - for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { - $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col); - $oldXfIndex = $columnDimension->getXfIndex(); - $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]); - } - break; - case 'ROW': - for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { - $rowDimension = $this->getActiveSheet()->getRowDimension($row); - $oldXfIndex = $rowDimension->getXfIndex() === null ? - 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style - $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]); - } - break; - case 'CELL': - for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { - for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { - $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row); - $oldXfIndex = $cell->getXfIndex(); - $cell->setXfIndex($newXfIndexes[$oldXfIndex]); - } - } - break; - } + // Is it a cell range or a single cell? + if (strpos($pRange, ':') === false) { + $rangeA = $pRange; + $rangeB = $pRange; } else { - // not a supervisor, just apply the style array directly on style object - if (isset($pStyles['fill'])) { - $this->getFill()->applyFromArray($pStyles['fill']); + list($rangeA, $rangeB) = explode(':', $pRange); + } + + // Calculate range outer borders + $rangeStart = Cell::coordinateFromString($rangeA); + $rangeEnd = Cell::coordinateFromString($rangeB); + + // Translate column into index + $rangeStart[0] = Cell::columnIndexFromString($rangeStart[0]) - 1; + $rangeEnd[0] = Cell::columnIndexFromString($rangeEnd[0]) - 1; + + // Make sure we can loop upwards on rows and columns + if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) { + $tmp = $rangeStart; + $rangeStart = $rangeEnd; + $rangeEnd = $tmp; + } + + // ADVANCED MODE: + if ($pAdvanced && isset($pStyles['borders'])) { + // 'allborders' is a shorthand property for 'outline' and 'inside' and + // it applies to components that have not been set explicitly + if (isset($pStyles['borders']['allborders'])) { + foreach (['outline', 'inside'] as $component) { + if (!isset($pStyles['borders'][$component])) { + $pStyles['borders'][$component] = $pStyles['borders']['allborders']; + } + } + unset($pStyles['borders']['allborders']); // not needed any more } - if (isset($pStyles['font'])) { - $this->getFont()->applyFromArray($pStyles['font']); + // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left' + // it applies to components that have not been set explicitly + if (isset($pStyles['borders']['outline'])) { + foreach (['top', 'right', 'bottom', 'left'] as $component) { + if (!isset($pStyles['borders'][$component])) { + $pStyles['borders'][$component] = $pStyles['borders']['outline']; + } + } + unset($pStyles['borders']['outline']); // not needed any more } - if (isset($pStyles['borders'])) { - $this->getBorders()->applyFromArray($pStyles['borders']); + // 'inside' is a shorthand property for 'vertical' and 'horizontal' + // it applies to components that have not been set explicitly + if (isset($pStyles['borders']['inside'])) { + foreach (['vertical', 'horizontal'] as $component) { + if (!isset($pStyles['borders'][$component])) { + $pStyles['borders'][$component] = $pStyles['borders']['inside']; + } + } + unset($pStyles['borders']['inside']); // not needed any more } - if (isset($pStyles['alignment'])) { - $this->getAlignment()->applyFromArray($pStyles['alignment']); + // width and height characteristics of selection, 1, 2, or 3 (for 3 or more) + $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3); + $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3); + + // loop through up to 3 x 3 = 9 regions + for ($x = 1; $x <= $xMax; ++$x) { + // start column index for region + $colStart = ($x == 3) ? + Cell::stringFromColumnIndex($rangeEnd[0]) + : Cell::stringFromColumnIndex($rangeStart[0] + $x - 1); + // end column index for region + $colEnd = ($x == 1) ? + Cell::stringFromColumnIndex($rangeStart[0]) + : Cell::stringFromColumnIndex($rangeEnd[0] - $xMax + $x); + + for ($y = 1; $y <= $yMax; ++$y) { + // which edges are touching the region + $edges = []; + if ($x == 1) { + // are we at left edge + $edges[] = 'left'; + } + if ($x == $xMax) { + // are we at right edge + $edges[] = 'right'; + } + if ($y == 1) { + // are we at top edge? + $edges[] = 'top'; + } + if ($y == $yMax) { + // are we at bottom edge? + $edges[] = 'bottom'; + } + + // start row index for region + $rowStart = ($y == 3) ? + $rangeEnd[1] : $rangeStart[1] + $y - 1; + + // end row index for region + $rowEnd = ($y == 1) ? + $rangeStart[1] : $rangeEnd[1] - $yMax + $y; + + // build range for region + $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd; + + // retrieve relevant style array for region + $regionStyles = $pStyles; + unset($regionStyles['borders']['inside']); + + // what are the inner edges of the region when looking at the selection + $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges); + + // inner edges that are not touching the region should take the 'inside' border properties if they have been set + foreach ($innerEdges as $innerEdge) { + switch ($innerEdge) { + case 'top': + case 'bottom': + // should pick up 'horizontal' border property if set + if (isset($pStyles['borders']['horizontal'])) { + $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal']; + } else { + unset($regionStyles['borders'][$innerEdge]); + } + break; + case 'left': + case 'right': + // should pick up 'vertical' border property if set + if (isset($pStyles['borders']['vertical'])) { + $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical']; + } else { + unset($regionStyles['borders'][$innerEdge]); + } + break; + } + } + + // apply region style to region by calling applyFromArray() in simple mode + $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false); + } } - if (isset($pStyles['numberformat'])) { - $this->getNumberFormat()->applyFromArray($pStyles['numberformat']); - } - if (isset($pStyles['protection'])) { - $this->getProtection()->applyFromArray($pStyles['protection']); - } - if (isset($pStyles['quotePrefix'])) { - $this->quotePrefix = $pStyles['quotePrefix']; + + return $this; + } + + // SIMPLE MODE: + // Selection type, inspect + if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) { + $selectionType = 'COLUMN'; + } elseif (preg_match('/^A[0-9]+:XFD[0-9]+$/', $pRange)) { + $selectionType = 'ROW'; + } else { + $selectionType = 'CELL'; + } + + // First loop through columns, rows, or cells to find out which styles are affected by this operation + switch ($selectionType) { + case 'COLUMN': + $oldXfIndexes = []; + for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { + $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true; + } + break; + case 'ROW': + $oldXfIndexes = []; + for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { + if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) { + $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style + } else { + $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true; + } + } + break; + case 'CELL': + $oldXfIndexes = []; + for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { + for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { + $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true; + } + } + break; + } + + // clone each of the affected styles, apply the style array, and add the new styles to the workbook + $workbook = $this->getActiveSheet()->getParent(); + foreach ($oldXfIndexes as $oldXfIndex => $dummy) { + $style = $workbook->getCellXfByIndex($oldXfIndex); + $newStyle = clone $style; + $newStyle->applyFromArray($pStyles); + + if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) { + // there is already such cell Xf in our collection + $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex(); + } else { + // we don't have such a cell Xf, need to add + $workbook->addCellXf($newStyle); + $newXfIndexes[$oldXfIndex] = $newStyle->getIndex(); } } + + // Loop through columns, rows, or cells again and update the XF index + switch ($selectionType) { + case 'COLUMN': + for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { + $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col); + $oldXfIndex = $columnDimension->getXfIndex(); + $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]); + } + break; + case 'ROW': + for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { + $rowDimension = $this->getActiveSheet()->getRowDimension($row); + $oldXfIndex = $rowDimension->getXfIndex() === null ? + 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style + $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]); + } + break; + case 'CELL': + for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { + for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { + $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row); + $oldXfIndex = $cell->getXfIndex(); + $cell->setXfIndex($newXfIndexes[$oldXfIndex]); + } + } + break; + } } else { - throw new Exception('Invalid style array passed.'); + // not a supervisor, just apply the style array directly on style object + if (isset($pStyles['fill'])) { + $this->getFill()->applyFromArray($pStyles['fill']); + } + if (isset($pStyles['font'])) { + $this->getFont()->applyFromArray($pStyles['font']); + } + if (isset($pStyles['borders'])) { + $this->getBorders()->applyFromArray($pStyles['borders']); + } + if (isset($pStyles['alignment'])) { + $this->getAlignment()->applyFromArray($pStyles['alignment']); + } + if (isset($pStyles['numberformat'])) { + $this->getNumberFormat()->applyFromArray($pStyles['numberformat']); + } + if (isset($pStyles['protection'])) { + $this->getProtection()->applyFromArray($pStyles['protection']); + } + if (isset($pStyles['quotePrefix'])) { + $this->quotePrefix = $pStyles['quotePrefix']; + } } return $this; @@ -547,15 +543,13 @@ class Style extends Style\Supervisor implements IComparable /** * Set Conditional Styles. Only used on supervisor. * - * @param Style\Conditional[] $pValue Array of condtional styles + * @param Style\Conditional[] $pValue Array of conditional styles * * @return Style */ - public function setConditionalStyles($pValue = null) + public function setConditionalStyles(array $pValue) { - if (is_array($pValue)) { - $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue); - } + $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue); return $this; } diff --git a/src/PhpSpreadsheet/Style/Alignment.php b/src/PhpSpreadsheet/Style/Alignment.php index 68ded6c4..6ee25160 100644 --- a/src/PhpSpreadsheet/Style/Alignment.php +++ b/src/PhpSpreadsheet/Style/Alignment.php @@ -162,37 +162,33 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara * * @return Alignment */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells()) - ->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['horizontal'])) { - $this->setHorizontal($pStyles['horizontal']); - } - if (isset($pStyles['vertical'])) { - $this->setVertical($pStyles['vertical']); - } - if (isset($pStyles['rotation'])) { - $this->setTextRotation($pStyles['rotation']); - } - if (isset($pStyles['wrap'])) { - $this->setWrapText($pStyles['wrap']); - } - if (isset($pStyles['shrinkToFit'])) { - $this->setShrinkToFit($pStyles['shrinkToFit']); - } - if (isset($pStyles['indent'])) { - $this->setIndent($pStyles['indent']); - } - if (isset($pStyles['readorder'])) { - $this->setReadorder($pStyles['readorder']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells()) + ->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['horizontal'])) { + $this->setHorizontal($pStyles['horizontal']); + } + if (isset($pStyles['vertical'])) { + $this->setVertical($pStyles['vertical']); + } + if (isset($pStyles['rotation'])) { + $this->setTextRotation($pStyles['rotation']); + } + if (isset($pStyles['wrap'])) { + $this->setWrapText($pStyles['wrap']); + } + if (isset($pStyles['shrinkToFit'])) { + $this->setShrinkToFit($pStyles['shrinkToFit']); + } + if (isset($pStyles['indent'])) { + $this->setIndent($pStyles['indent']); + } + if (isset($pStyles['readorder'])) { + $this->setReadorder($pStyles['readorder']); + } } return $this; @@ -215,11 +211,11 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara /** * Set Horizontal. * - * @param string $pValue + * @param string $pValue see self::HORIZONTAL_* * * @return Alignment */ - public function setHorizontal($pValue = self::HORIZONTAL_GENERAL) + public function setHorizontal($pValue) { if ($pValue == '') { $pValue = self::HORIZONTAL_GENERAL; @@ -252,11 +248,11 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara /** * Set Vertical. * - * @param string $pValue + * @param string $pValue see self::VERTICAL_* * * @return Alignment */ - public function setVertical($pValue = self::VERTICAL_BOTTOM) + public function setVertical($pValue) { if ($pValue == '') { $pValue = self::VERTICAL_BOTTOM; @@ -295,7 +291,7 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara * * @return Alignment */ - public function setTextRotation($pValue = 0) + public function setTextRotation($pValue) { // Excel2007 value 255 => PhpSpreadsheet value -165 if ($pValue == 255) { @@ -338,7 +334,7 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara * * @return Alignment */ - public function setWrapText($pValue = false) + public function setWrapText($pValue) { if ($pValue == '') { $pValue = false; @@ -374,7 +370,7 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara * * @return Alignment */ - public function setShrinkToFit($pValue = false) + public function setShrinkToFit($pValue) { if ($pValue == '') { $pValue = false; @@ -410,7 +406,7 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara * * @return Alignment */ - public function setIndent($pValue = 0) + public function setIndent($pValue) { if ($pValue > 0) { if ($this->getHorizontal() != self::HORIZONTAL_GENERAL && @@ -450,7 +446,7 @@ class Alignment extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompara * * @return Alignment */ - public function setReadorder($pValue = 0) + public function setReadorder($pValue) { if ($pValue < 0 || $pValue > 2) { $pValue = 0; diff --git a/src/PhpSpreadsheet/Style/Border.php b/src/PhpSpreadsheet/Style/Border.php index 8eed8981..9c2d8891 100644 --- a/src/PhpSpreadsheet/Style/Border.php +++ b/src/PhpSpreadsheet/Style/Border.php @@ -181,21 +181,17 @@ class Border extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Border */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['style'])) { - $this->setBorderStyle($pStyles['style']); - } - if (isset($pStyles['color'])) { - $this->getColor()->applyFromArray($pStyles['color']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['style'])) { + $this->setBorderStyle($pStyles['style']); + } + if (isset($pStyles['color'])) { + $this->getColor()->applyFromArray($pStyles['color']); + } } return $this; @@ -224,7 +220,7 @@ class Border extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Border */ - public function setBorderStyle($pValue = self::BORDER_NONE) + public function setBorderStyle($pValue) { if (empty($pValue)) { $pValue = self::BORDER_NONE; @@ -260,7 +256,7 @@ class Border extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Border */ - public function setColor(Color $pValue = null) + public function setColor(Color $pValue) { // make sure parameter is a real color and not a supervisor $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue; diff --git a/src/PhpSpreadsheet/Style/Borders.php b/src/PhpSpreadsheet/Style/Borders.php index 674da8fb..06268d8b 100644 --- a/src/PhpSpreadsheet/Style/Borders.php +++ b/src/PhpSpreadsheet/Style/Borders.php @@ -218,39 +218,35 @@ class Borders extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparabl * * @return Borders */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['left'])) { - $this->getLeft()->applyFromArray($pStyles['left']); - } - if (isset($pStyles['right'])) { - $this->getRight()->applyFromArray($pStyles['right']); - } - if (isset($pStyles['top'])) { - $this->getTop()->applyFromArray($pStyles['top']); - } - if (isset($pStyles['bottom'])) { - $this->getBottom()->applyFromArray($pStyles['bottom']); - } - if (isset($pStyles['diagonal'])) { - $this->getDiagonal()->applyFromArray($pStyles['diagonal']); - } - if (isset($pStyles['diagonaldirection'])) { - $this->setDiagonalDirection($pStyles['diagonaldirection']); - } - if (isset($pStyles['allborders'])) { - $this->getLeft()->applyFromArray($pStyles['allborders']); - $this->getRight()->applyFromArray($pStyles['allborders']); - $this->getTop()->applyFromArray($pStyles['allborders']); - $this->getBottom()->applyFromArray($pStyles['allborders']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['left'])) { + $this->getLeft()->applyFromArray($pStyles['left']); + } + if (isset($pStyles['right'])) { + $this->getRight()->applyFromArray($pStyles['right']); + } + if (isset($pStyles['top'])) { + $this->getTop()->applyFromArray($pStyles['top']); + } + if (isset($pStyles['bottom'])) { + $this->getBottom()->applyFromArray($pStyles['bottom']); + } + if (isset($pStyles['diagonal'])) { + $this->getDiagonal()->applyFromArray($pStyles['diagonal']); + } + if (isset($pStyles['diagonaldirection'])) { + $this->setDiagonalDirection($pStyles['diagonaldirection']); + } + if (isset($pStyles['allborders'])) { + $this->getLeft()->applyFromArray($pStyles['allborders']); + $this->getRight()->applyFromArray($pStyles['allborders']); + $this->getTop()->applyFromArray($pStyles['allborders']); + $this->getBottom()->applyFromArray($pStyles['allborders']); + } } return $this; @@ -403,11 +399,11 @@ class Borders extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparabl /** * Set DiagonalDirection. * - * @param int $pValue + * @param int $pValue see self::DIAGONAL_* * * @return Borders */ - public function setDiagonalDirection($pValue = self::DIAGONAL_NONE) + public function setDiagonalDirection($pValue) { if ($pValue == '') { $pValue = self::DIAGONAL_NONE; diff --git a/src/PhpSpreadsheet/Style/Color.php b/src/PhpSpreadsheet/Style/Color.php index 2079ba77..3fdd6308 100644 --- a/src/PhpSpreadsheet/Style/Color.php +++ b/src/PhpSpreadsheet/Style/Color.php @@ -152,21 +152,17 @@ class Color extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Color */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['rgb'])) { - $this->setRGB($pStyles['rgb']); - } - if (isset($pStyles['argb'])) { - $this->setARGB($pStyles['argb']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['rgb'])) { + $this->setRGB($pStyles['rgb']); + } + if (isset($pStyles['argb'])) { + $this->setARGB($pStyles['argb']); + } } return $this; @@ -189,11 +185,11 @@ class Color extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable /** * Set ARGB. * - * @param string $pValue + * @param string $pValue see self::COLOR_* * * @return Color */ - public function setARGB($pValue = self::COLOR_BLACK) + public function setARGB($pValue) { if ($pValue == '') { $pValue = self::COLOR_BLACK; @@ -229,7 +225,7 @@ class Color extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Color */ - public function setRGB($pValue = '000000') + public function setRGB($pValue) { if ($pValue == '') { $pValue = '000000'; diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index 1387d35f..84578411 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -31,6 +31,7 @@ class Conditional implements \PhpOffice\PhpSpreadsheet\IComparable const CONDITION_CELLIS = 'cellIs'; const CONDITION_CONTAINSTEXT = 'containsText'; const CONDITION_EXPRESSION = 'expression'; + const CONDITION_CONTAINSBLANKS = 'containsBlanks'; /* Operator types */ const OPERATOR_NONE = ''; @@ -107,11 +108,11 @@ class Conditional implements \PhpOffice\PhpSpreadsheet\IComparable /** * Set Condition type. * - * @param string $pValue Condition type + * @param string $pValue Condition type, see self::CONDITION_* * * @return Conditional */ - public function setConditionType($pValue = self::CONDITION_NONE) + public function setConditionType($pValue) { $this->conditionType = $pValue; @@ -131,11 +132,11 @@ class Conditional implements \PhpOffice\PhpSpreadsheet\IComparable /** * Set Operator type. * - * @param string $pValue Conditional operator type + * @param string $pValue Conditional operator type, see self::OPERATOR_* * * @return Conditional */ - public function setOperatorType($pValue = self::OPERATOR_NONE) + public function setOperatorType($pValue) { $this->operatorType = $pValue; @@ -159,7 +160,7 @@ class Conditional implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Conditional */ - public function setText($value = null) + public function setText($value) { $this->text = $value; @@ -200,7 +201,7 @@ class Conditional implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Conditional */ - public function addCondition($pValue = '') + public function addCondition($pValue) { $this->condition[] = $pValue; diff --git a/src/PhpSpreadsheet/Style/Fill.php b/src/PhpSpreadsheet/Style/Fill.php index 089fa6bb..9cbf6841 100644 --- a/src/PhpSpreadsheet/Style/Fill.php +++ b/src/PhpSpreadsheet/Style/Fill.php @@ -153,31 +153,27 @@ class Fill extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Fill */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['type'])) { - $this->setFillType($pStyles['type']); - } - if (isset($pStyles['rotation'])) { - $this->setRotation($pStyles['rotation']); - } - if (isset($pStyles['startcolor'])) { - $this->getStartColor()->applyFromArray($pStyles['startcolor']); - } - if (isset($pStyles['endcolor'])) { - $this->getEndColor()->applyFromArray($pStyles['endcolor']); - } - if (isset($pStyles['color'])) { - $this->getStartColor()->applyFromArray($pStyles['color']); - $this->getEndColor()->applyFromArray($pStyles['color']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['type'])) { + $this->setFillType($pStyles['type']); + } + if (isset($pStyles['rotation'])) { + $this->setRotation($pStyles['rotation']); + } + if (isset($pStyles['startcolor'])) { + $this->getStartColor()->applyFromArray($pStyles['startcolor']); + } + if (isset($pStyles['endcolor'])) { + $this->getEndColor()->applyFromArray($pStyles['endcolor']); + } + if (isset($pStyles['color'])) { + $this->getStartColor()->applyFromArray($pStyles['color']); + $this->getEndColor()->applyFromArray($pStyles['color']); + } } return $this; @@ -200,11 +196,11 @@ class Fill extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable /** * Set Fill Type. * - * @param string $pValue Fill type + * @param string $pValue Fill type, see self::FILL_* * * @return Fill */ - public function setFillType($pValue = self::FILL_NONE) + public function setFillType($pValue) { if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['type' => $pValue]); @@ -237,7 +233,7 @@ class Fill extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Fill */ - public function setRotation($pValue = 0) + public function setRotation($pValue) { if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['rotation' => $pValue]); @@ -268,7 +264,7 @@ class Fill extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Fill */ - public function setStartColor(Color $pValue = null) + public function setStartColor(Color $pValue) { // make sure parameter is a real color and not a supervisor $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue; @@ -302,7 +298,7 @@ class Fill extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Fill */ - public function setEndColor(Color $pValue = null) + public function setEndColor(Color $pValue) { // make sure parameter is a real color and not a supervisor $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue; diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index ba4cc7f0..3436382a 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -178,42 +178,38 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['name'])) { - $this->setName($pStyles['name']); - } - if (isset($pStyles['bold'])) { - $this->setBold($pStyles['bold']); - } - if (isset($pStyles['italic'])) { - $this->setItalic($pStyles['italic']); - } - if (isset($pStyles['superScript'])) { - $this->setSuperScript($pStyles['superScript']); - } - if (isset($pStyles['subScript'])) { - $this->setSubScript($pStyles['subScript']); - } - if (isset($pStyles['underline'])) { - $this->setUnderline($pStyles['underline']); - } - if (isset($pStyles['strike'])) { - $this->setStrikethrough($pStyles['strike']); - } - if (isset($pStyles['color'])) { - $this->getColor()->applyFromArray($pStyles['color']); - } - if (isset($pStyles['size'])) { - $this->setSize($pStyles['size']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['name'])) { + $this->setName($pStyles['name']); + } + if (isset($pStyles['bold'])) { + $this->setBold($pStyles['bold']); + } + if (isset($pStyles['italic'])) { + $this->setItalic($pStyles['italic']); + } + if (isset($pStyles['superScript'])) { + $this->setSuperScript($pStyles['superScript']); + } + if (isset($pStyles['subScript'])) { + $this->setSubScript($pStyles['subScript']); + } + if (isset($pStyles['underline'])) { + $this->setUnderline($pStyles['underline']); + } + if (isset($pStyles['strike'])) { + $this->setStrikethrough($pStyles['strike']); + } + if (isset($pStyles['color'])) { + $this->getColor()->applyFromArray($pStyles['color']); + } + if (isset($pStyles['size'])) { + $this->setSize($pStyles['size']); + } } return $this; @@ -240,7 +236,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setName($pValue = 'Calibri') + public function setName($pValue) { if ($pValue == '') { $pValue = 'Calibri'; @@ -276,7 +272,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setSize($pValue = 10) + public function setSize($pValue) { if ($pValue == '') { $pValue = 10; @@ -312,7 +308,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setBold($pValue = false) + public function setBold($pValue) { if ($pValue == '') { $pValue = false; @@ -348,7 +344,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setItalic($pValue = false) + public function setItalic($pValue) { if ($pValue == '') { $pValue = false; @@ -384,7 +380,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setSuperScript($pValue = false) + public function setSuperScript($pValue) { if ($pValue == '') { $pValue = false; @@ -421,7 +417,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setSubScript($pValue = false) + public function setSubScript($pValue) { if ($pValue == '') { $pValue = false; @@ -460,7 +456,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setUnderline($pValue = self::UNDERLINE_NONE) + public function setUnderline($pValue) { if (is_bool($pValue)) { $pValue = ($pValue) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE; @@ -498,7 +494,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setStrikethrough($pValue = false) + public function setStrikethrough($pValue) { if ($pValue == '') { $pValue = false; @@ -532,7 +528,7 @@ class Font extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Font */ - public function setColor(Color $pValue = null) + public function setColor(Color $pValue) { // make sure parameter is a real color and not a supervisor $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue; diff --git a/src/PhpSpreadsheet/Style/NumberFormat.php b/src/PhpSpreadsheet/Style/NumberFormat.php index fb729905..779fb2f9 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat.php +++ b/src/PhpSpreadsheet/Style/NumberFormat.php @@ -155,18 +155,14 @@ class NumberFormat extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComp * * @return NumberFormat */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['code'])) { - $this->setFormatCode($pStyles['code']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['code'])) { + $this->setFormatCode($pStyles['code']); + } } return $this; @@ -192,11 +188,11 @@ class NumberFormat extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComp /** * Set Format Code. * - * @param string $pValue + * @param string $pValue see self::FORMAT_* * * @return NumberFormat */ - public function setFormatCode($pValue = self::FORMAT_GENERAL) + public function setFormatCode($pValue) { if ($pValue == '') { $pValue = self::FORMAT_GENERAL; @@ -233,7 +229,7 @@ class NumberFormat extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComp * * @return NumberFormat */ - public function setBuiltInFormatCode($pValue = 0) + public function setBuiltInFormatCode($pValue) { if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['code' => self::builtInFormatCode($pValue)]); @@ -588,12 +584,12 @@ class NumberFormat extends Supervisor implements \PhpOffice\PhpSpreadsheet\IComp * Convert a value in a pre-defined format to a PHP string. * * @param mixed $value Value to format - * @param string $format Format code + * @param string $format Format code, see = self::FORMAT_* * @param array $callBack Callback function for additional formatting of string * * @return string Formatted string */ - public static function toFormattedString($value = '0', $format = self::FORMAT_GENERAL, $callBack = null) + public static function toFormattedString($value, $format, $callBack = null) { // For now we do not treat strings although section 4 of a format code affects strings if (!is_numeric($value)) { diff --git a/src/PhpSpreadsheet/Style/Protection.php b/src/PhpSpreadsheet/Style/Protection.php index 27145488..848ac1be 100644 --- a/src/PhpSpreadsheet/Style/Protection.php +++ b/src/PhpSpreadsheet/Style/Protection.php @@ -108,21 +108,17 @@ class Protection extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompar * * @return Protection */ - public function applyFromArray($pStyles = null) + public function applyFromArray(array $pStyles) { - if (is_array($pStyles)) { - if ($this->isSupervisor) { - $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); - } else { - if (isset($pStyles['locked'])) { - $this->setLocked($pStyles['locked']); - } - if (isset($pStyles['hidden'])) { - $this->setHidden($pStyles['hidden']); - } - } + if ($this->isSupervisor) { + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles)); } else { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid style array passed.'); + if (isset($pStyles['locked'])) { + $this->setLocked($pStyles['locked']); + } + if (isset($pStyles['hidden'])) { + $this->setHidden($pStyles['hidden']); + } } return $this; @@ -145,11 +141,11 @@ class Protection extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompar /** * Set locked. * - * @param string $pValue + * @param string $pValue see self::PROTECTION_* * * @return Protection */ - public function setLocked($pValue = self::PROTECTION_INHERIT) + public function setLocked($pValue) { if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['locked' => $pValue]); @@ -178,11 +174,11 @@ class Protection extends Supervisor implements \PhpOffice\PhpSpreadsheet\ICompar /** * Set hidden. * - * @param string $pValue + * @param string $pValue see self::PROTECTION_* * * @return Protection */ - public function setHidden($pValue = self::PROTECTION_INHERIT) + public function setHidden($pValue) { if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['hidden' => $pValue]); diff --git a/src/PhpSpreadsheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet.php index 65219664..4a6799d3 100644 --- a/src/PhpSpreadsheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet.php @@ -2,6 +2,10 @@ namespace PhpOffice\PhpSpreadsheet; +use ArrayObject; +use PhpOffice\PhpSpreadsheet\Collection\Cells; +use PhpOffice\PhpSpreadsheet\Collection\CellsFactory; + /** * Copyright (c) 2006 - 2016 PhpSpreadsheet. * @@ -51,9 +55,9 @@ class Worksheet implements IComparable private $parent; /** - * Cacheable collection of cells. + * Collection of cells. * - * @var CachedObjectStorage_xxx + * @var Cells */ private $cellCollection; @@ -338,7 +342,7 @@ class Worksheet implements IComparable $this->setCodeName($this->getTitle()); $this->setSheetState(self::SHEETSTATE_VISIBLE); - $this->cellCollection = CachedObjectStorageFactory::getInstance($this); + $this->cellCollection = CellsFactory::getInstance($this); // Set page setup $this->pageSetup = new Worksheet\PageSetup(); // Set page margins @@ -385,11 +389,11 @@ class Worksheet implements IComparable } /** - * Return the cache controller for the cell collection. + * Return the cell collection. * - * @return CachedObjectStorage_xxx + * @return Cells */ - public function getCellCacheController() + public function getCellCollection() { return $this->cellCollection; } @@ -459,37 +463,23 @@ class Worksheet implements IComparable } /** - * Get collection of cells. + * Get a sorted list of all cell coordinates currently held in the collection by row and column. * - * @param bool $pSorted Also sort the cell collection? + * @param bool $sorted Also sort the cell collection? * - * @return Cell[] + * @return string[] */ - public function getCellCollection($pSorted = true) + public function getCoordinates($sorted = true) { - if ($pSorted) { - // Re-order cell collection - return $this->sortCellCollection(); - } - if ($this->cellCollection !== null) { - return $this->cellCollection->getCellList(); + if ($this->cellCollection == null) { + return []; } - return []; - } - - /** - * Sort collection of cells. - * - * @return Worksheet - */ - public function sortCellCollection() - { - if ($this->cellCollection !== null) { - return $this->cellCollection->getSortedCellList(); + if ($sorted) { + return $this->cellCollection->getSortedCoordinates(); } - return []; + return $this->cellCollection->getCoordinates(); } /** @@ -560,7 +550,7 @@ class Worksheet implements IComparable * * @return Chart */ - public function addChart(Chart $pChart = null, $iChartIndex = null) + public function addChart(Chart $pChart, $iChartIndex = null) { $pChart->setWorksheet($this); if (is_null($iChartIndex)) { @@ -592,7 +582,7 @@ class Worksheet implements IComparable * * @return false|Chart */ - public function getChartByIndex($index = null) + public function getChartByIndex($index) { $chartCount = count($this->chartCollection); if ($chartCount == 0) { @@ -634,7 +624,7 @@ class Worksheet implements IComparable * * @return false|Chart */ - public function getChartByName($chartName = '') + public function getChartByName($chartName) { $chartCount = count($this->chartCollection); if ($chartCount == 0) { @@ -735,11 +725,11 @@ class Worksheet implements IComparable } // loop through all cells in the worksheet - foreach ($this->getCellCollection(false) as $cellID) { - $cell = $this->getCell($cellID, false); + foreach ($this->getCoordinates(false) as $coordinate) { + $cell = $this->getCell($coordinate, false); if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) { //Determine if cell is in merge range - $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentAddress()]); + $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentCoordinate()]); //By default merged cells should be ignored $isMergedButProceed = false; @@ -843,7 +833,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true) + public function setTitle($pValue, $updateFormulaCellReferences = true) { // Is this a 'rename' or not? if ($this->getTitle() == $pValue) { @@ -918,7 +908,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setSheetState($value = self::SHEETSTATE_VISIBLE) + public function setSheetState($value) { $this->sheetState = $value; @@ -1119,17 +1109,16 @@ class Worksheet implements IComparable /** * Set a cell value. * - * @param string $pCoordinate Coordinate of the cell + * @param string $pCoordinate Coordinate of the cell, eg: 'A1' * @param mixed $pValue Value of the cell - * @param bool $returnCell Return the worksheet (false, default) or the cell (true) * - * @return Worksheet|Cell Depending on the last parameter being specified + * @return Worksheet */ - public function setCellValue($pCoordinate = 'A1', $pValue = null, $returnCell = false) + public function setCellValue($pCoordinate, $pValue) { - $cell = $this->getCell(strtoupper($pCoordinate))->setValue($pValue); + $this->getCell($pCoordinate)->setValue($pValue); - return ($returnCell) ? $cell : $this; + return $this; } /** @@ -1138,57 +1127,55 @@ class Worksheet implements IComparable * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell * @param mixed $pValue Value of the cell - * @param bool $returnCell Return the worksheet (false, default) or the cell (true) * - * @return Worksheet|Cell Depending on the last parameter being specified + * @return Worksheet */ - public function setCellValueByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $returnCell = false) + public function setCellValueByColumnAndRow($pColumn, $pRow, $pValue) { - $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue); + $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue); - return ($returnCell) ? $cell : $this; + return $this; } /** * Set a cell value. * - * @param string $pCoordinate Coordinate of the cell + * @param string $pCoordinate Coordinate of the cell, eg: 'A1' * @param mixed $pValue Value of the cell - * @param string $pDataType Explicit data type - * @param bool $returnCell Return the worksheet (false, default) or the cell (true) + * @param string $pDataType Explicit data type, see Cell\DataType::TYPE_* * - * @return Worksheet|Cell Depending on the last parameter being specified + * @return Worksheet */ - public function setCellValueExplicit($pCoordinate = 'A1', $pValue = null, $pDataType = Cell\DataType::TYPE_STRING, $returnCell = false) + public function setCellValueExplicit($pCoordinate, $pValue, $pDataType) { // Set value - $cell = $this->getCell(strtoupper($pCoordinate))->setValueExplicit($pValue, $pDataType); + $this->getCell($pCoordinate)->setValueExplicit($pValue, $pDataType); - return ($returnCell) ? $cell : $this; + return $this; } /** * Set a cell value by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell * @param mixed $pValue Value of the cell - * @param string $pDataType Explicit data type + * @param string $pDataType Explicit data type, see Cell\DataType::TYPE_* * @param bool $returnCell Return the worksheet (false, default) or the cell (true) * - * @return Worksheet|Cell Depending on the last parameter being specified + * @return Worksheet */ - public function setCellValueExplicitByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $pDataType = Cell\DataType::TYPE_STRING, $returnCell = false) + public function setCellValueExplicitByColumnAndRow($pColumn, $pRow, $pValue, $pDataType) { - $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType); + $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType); - return ($returnCell) ? $cell : $this; + return $this; } /** * Get cell at a specific coordinate. * - * @param string $pCoordinate Coordinate of the cell + * @param string $pCoordinate Coordinate of the cell, eg: 'A1' * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't * already exist, or a null should be returned instead * @@ -1196,11 +1183,11 @@ class Worksheet implements IComparable * * @return null|Cell Cell that was found/created or null */ - public function getCell($pCoordinate = 'A1', $createIfNotExists = true) + public function getCell($pCoordinate, $createIfNotExists = true) { // Check cell collection - if ($this->cellCollection->isDataSet(strtoupper($pCoordinate))) { - return $this->cellCollection->getCacheData($pCoordinate); + if ($this->cellCollection->has(strtoupper($pCoordinate))) { + return $this->cellCollection->get($pCoordinate); } // Worksheet reference? @@ -1244,13 +1231,13 @@ class Worksheet implements IComparable * * @return null|Cell Cell that was found/created or null */ - public function getCellByColumnAndRow($pColumn = 0, $pRow = 1, $createIfNotExists = true) + public function getCellByColumnAndRow($pColumn, $pRow, $createIfNotExists = true) { $columnLetter = Cell::stringFromColumnIndex($pColumn); $coordinate = $columnLetter . $pRow; - if ($this->cellCollection->isDataSet($coordinate)) { - return $this->cellCollection->getCacheData($coordinate); + if ($this->cellCollection->has($coordinate)) { + return $this->cellCollection->get($coordinate); } // Create new cell object, if required @@ -1266,10 +1253,8 @@ class Worksheet implements IComparable */ private function createNewCell($pCoordinate) { - $cell = $this->cellCollection->addCacheData( - $pCoordinate, - new Cell(null, Cell\DataType::TYPE_NULL, $this) - ); + $cell = new Cell(null, Cell\DataType::TYPE_NULL, $this); + $this->cellCollection->add($pCoordinate, $cell); $this->cellCollectionIsSorted = false; // Coordinates @@ -1298,13 +1283,13 @@ class Worksheet implements IComparable /** * Does the cell at a specific coordinate exist? * - * @param string $pCoordinate Coordinate of the cell + * @param string $pCoordinate Coordinate of the cell eg: 'A1' * * @throws Exception * * @return bool */ - public function cellExists($pCoordinate = 'A1') + public function cellExists($pCoordinate) { // Worksheet reference? if (strpos($pCoordinate, '!') !== false) { @@ -1342,18 +1327,18 @@ class Worksheet implements IComparable $aCoordinates = Cell::coordinateFromString($pCoordinate); // Cell exists? - return $this->cellCollection->isDataSet($pCoordinate); + return $this->cellCollection->has($pCoordinate); } /** * Cell at a specific coordinate by using numeric cell coordinates exists? * - * @param string $pColumn Numeric column coordinate of the cell + * @param string $pColumn Numeric column coordinate of the cell (A = 0) * @param string $pRow Numeric row coordinate of the cell * * @return bool */ - public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1) + public function cellExistsByColumnAndRow($pColumn, $pRow) { return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow); } @@ -1366,7 +1351,7 @@ class Worksheet implements IComparable * * @return Worksheet\RowDimension */ - public function getRowDimension($pRow = 1, $create = true) + public function getRowDimension($pRow, $create = true) { // Found $found = null; @@ -1387,12 +1372,12 @@ class Worksheet implements IComparable /** * Get column dimension at a specific column. * - * @param string $pColumn String index of the column + * @param string $pColumn String index of the column eg: 'A' * @param mixed $create * * @return Worksheet\ColumnDimension */ - public function getColumnDimension($pColumn = 'A', $create = true) + public function getColumnDimension($pColumn, $create = true) { // Uppercase coordinate $pColumn = strtoupper($pColumn); @@ -1415,11 +1400,11 @@ class Worksheet implements IComparable /** * Get column dimension at a specific column by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * * @return Worksheet\ColumnDimension */ - public function getColumnDimensionByColumn($pColumn = 0) + public function getColumnDimensionByColumn($pColumn) { return $this->getColumnDimension(Cell::stringFromColumnIndex($pColumn)); } @@ -1437,13 +1422,13 @@ class Worksheet implements IComparable /** * Get style for cell. * - * @param string $pCellCoordinate Cell coordinate (or range) to get style for + * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1' * * @throws Exception * * @return Style */ - public function getStyle($pCellCoordinate = 'A1') + public function getStyle($pCellCoordinate) { // set this sheet as active $this->parent->setActiveSheetIndex($this->parent->getIndex($this)); @@ -1457,11 +1442,11 @@ class Worksheet implements IComparable /** * Get conditional styles for a cell. * - * @param string $pCoordinate + * @param string $pCoordinate eg: 'A1' * * @return Style\Conditional[] */ - public function getConditionalStyles($pCoordinate = 'A1') + public function getConditionalStyles($pCoordinate) { $pCoordinate = strtoupper($pCoordinate); if (!isset($this->conditionalStylesCollection[$pCoordinate])) { @@ -1474,11 +1459,11 @@ class Worksheet implements IComparable /** * Do conditional styles exist for this cell? * - * @param string $pCoordinate + * @param string $pCoordinate eg: 'A1' * * @return bool */ - public function conditionalStylesExists($pCoordinate = 'A1') + public function conditionalStylesExists($pCoordinate) { if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) { return true; @@ -1490,11 +1475,11 @@ class Worksheet implements IComparable /** * Removes conditional styles for a cell. * - * @param string $pCoordinate + * @param string $pCoordinate eg: 'A1' * * @return Worksheet */ - public function removeConditionalStyles($pCoordinate = 'A1') + public function removeConditionalStyles($pCoordinate) { unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]); @@ -1529,16 +1514,16 @@ class Worksheet implements IComparable /** * Get style for cell by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell - * @param int pColumn2 Numeric column coordinate of the range cell + * @param int pColumn2 Numeric column coordinate of the range cell (A = 0) * @param int pRow2 Numeric row coordinate of the range cell * @param null|mixed $pColumn2 * @param null|mixed $pRow2 * * @return Style */ - public function getStyleByColumnAndRow($pColumn = 0, $pRow = 1, $pColumn2 = null, $pRow2 = null) + public function getStyleByColumnAndRow($pColumn, $pRow, $pColumn2 = null, $pRow2 = null) { if (!is_null($pColumn2) && !is_null($pRow2)) { $cellRange = Cell::stringFromColumnIndex($pColumn) . $pRow . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2; @@ -1561,7 +1546,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function duplicateStyle(Style $pCellStyle = null, $pRange = '') + public function duplicateStyle(Style $pCellStyle, $pRange) { // make sure we have a real style and not supervisor $style = $pCellStyle->getIsSupervisor() ? $pCellStyle->getSharedComponent() : $pCellStyle; @@ -1609,7 +1594,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '') + public function duplicateConditionalStyle(array $pCellStyle, $pRange = '') { foreach ($pCellStyle as $cellStyle) { if (!($cellStyle instanceof Style\Conditional)) { @@ -1640,25 +1625,25 @@ class Worksheet implements IComparable /** * Set break on a cell. * - * @param string $pCell Cell coordinate (e.g. A1) + * @param string $pCoordinate Cell coordinate (e.g. A1) * @param int $pBreak Break type (type of Worksheet::BREAK_*) * * @throws Exception * * @return Worksheet */ - public function setBreak($pCell = 'A1', $pBreak = self::BREAK_NONE) + public function setBreak($pCoordinate, $pBreak) { // Uppercase coordinate - $pCell = strtoupper($pCell); + $pCoordinate = strtoupper($pCoordinate); - if ($pCell != '') { + if ($pCoordinate != '') { if ($pBreak == self::BREAK_NONE) { - if (isset($this->breaks[$pCell])) { - unset($this->breaks[$pCell]); + if (isset($this->breaks[$pCoordinate])) { + unset($this->breaks[$pCoordinate]); } } else { - $this->breaks[$pCell] = $pBreak; + $this->breaks[$pCoordinate] = $pBreak; } } else { throw new Exception('No cell coordinate specified.'); @@ -1670,13 +1655,13 @@ class Worksheet implements IComparable /** * Set break on a cell by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell * @param int $pBreak Break type (type of \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_*) * * @return Worksheet */ - public function setBreakByColumnAndRow($pColumn = 0, $pRow = 1, $pBreak = self::BREAK_NONE) + public function setBreakByColumnAndRow($pColumn, $pRow, $pBreak) { return $this->setBreak(Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak); } @@ -1700,7 +1685,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function mergeCells($pRange = 'A1:A1') + public function mergeCells($pRange) { // Uppercase coordinate $pRange = strtoupper($pRange); @@ -1736,16 +1721,16 @@ class Worksheet implements IComparable /** * Set merge on a cell range by using numeric cell coordinates. * - * @param int $pColumn1 Numeric column coordinate of the first cell + * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0) * @param int $pRow1 Numeric row coordinate of the first cell - * @param int $pColumn2 Numeric column coordinate of the last cell + * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0) * @param int $pRow2 Numeric row coordinate of the last cell * * @throws Exception * * @return Worksheet */ - public function mergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1) + public function mergeCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2) { $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2; @@ -1761,7 +1746,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function unmergeCells($pRange = 'A1:A1') + public function unmergeCells($pRange) { // Uppercase coordinate $pRange = strtoupper($pRange); @@ -1782,16 +1767,16 @@ class Worksheet implements IComparable /** * Remove merge on a cell range by using numeric cell coordinates. * - * @param int $pColumn1 Numeric column coordinate of the first cell + * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0) * @param int $pRow1 Numeric row coordinate of the first cell - * @param int $pColumn2 Numeric column coordinate of the last cell + * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0) * @param int $pRow2 Numeric row coordinate of the last cell * * @throws Exception * * @return Worksheet */ - public function unmergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1) + public function unmergeCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2) { $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2; @@ -1815,7 +1800,7 @@ class Worksheet implements IComparable * @param array * @param mixed $pValue */ - public function setMergeCells($pValue = []) + public function setMergeCells(array $pValue) { $this->mergeCells = $pValue; @@ -1833,7 +1818,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function protectCells($pRange = 'A1', $pPassword = '', $pAlreadyHashed = false) + public function protectCells($pRange, $pPassword, $pAlreadyHashed = false) { // Uppercase coordinate $pRange = strtoupper($pRange); @@ -1849,9 +1834,9 @@ class Worksheet implements IComparable /** * Set protection on a cell range by using numeric cell coordinates. * - * @param int $pColumn1 Numeric column coordinate of the first cell + * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0) * @param int $pRow1 Numeric row coordinate of the first cell - * @param int $pColumn2 Numeric column coordinate of the last cell + * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0) * @param int $pRow2 Numeric row coordinate of the last cell * @param string $pPassword Password to unlock the protection * @param bool $pAlreadyHashed If the password has already been hashed, set this to true @@ -1860,7 +1845,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function protectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false) + public function protectCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2, $pPassword, $pAlreadyHashed = false) { $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2; @@ -1876,7 +1861,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function unprotectCells($pRange = 'A1') + public function unprotectCells($pRange) { // Uppercase coordinate $pRange = strtoupper($pRange); @@ -1893,9 +1878,9 @@ class Worksheet implements IComparable /** * Remove protection on a cell range by using numeric cell coordinates. * - * @param int $pColumn1 Numeric column coordinate of the first cell + * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0) * @param int $pRow1 Numeric row coordinate of the first cell - * @param int $pColumn2 Numeric column coordinate of the last cell + * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0) * @param int $pRow2 Numeric row coordinate of the last cell * @param string $pPassword Password to unlock the protection * @param bool $pAlreadyHashed If the password has already been hashed, set this to true @@ -1904,7 +1889,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function unprotectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false) + public function unprotectCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2, $pPassword, $pAlreadyHashed = false) { $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2; @@ -1956,16 +1941,16 @@ class Worksheet implements IComparable /** * Set Autofilter Range by using numeric cell coordinates. * - * @param int $pColumn1 Numeric column coordinate of the first cell + * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0) * @param int $pRow1 Numeric row coordinate of the first cell - * @param int $pColumn2 Numeric column coordinate of the second cell + * @param int $pColumn2 Numeric column coordinate of the second cell (A = 0) * @param int $pRow2 Numeric row coordinate of the second cell * * @throws Exception * * @return Worksheet */ - public function setAutoFilterByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1) + public function setAutoFilterByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2) { return $this->setAutoFilter( Cell::stringFromColumnIndex($pColumn1) . $pRow1 @@ -2010,7 +1995,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function freezePane($pCell = '') + public function freezePane($pCell) { // Uppercase coordinate $pCell = strtoupper($pCell); @@ -2026,14 +2011,14 @@ class Worksheet implements IComparable /** * Freeze Pane by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell * * @throws Exception * * @return Worksheet */ - public function freezePaneByColumnAndRow($pColumn = 0, $pRow = 1) + public function freezePaneByColumnAndRow($pColumn, $pRow) { return $this->freezePane(Cell::stringFromColumnIndex($pColumn) . $pRow); } @@ -2058,7 +2043,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function insertNewRowBefore($pBefore = 1, $pNumRows = 1) + public function insertNewRowBefore($pBefore, $pNumRows = 1) { if ($pBefore >= 1) { $objReferenceHelper = ReferenceHelper::getInstance(); @@ -2073,14 +2058,14 @@ class Worksheet implements IComparable /** * Insert a new column, updating all possible related data. * - * @param int $pBefore Insert before this one + * @param int $pBefore Insert before this one, eg: 'A' * @param int $pNumCols Number of columns to insert * * @throws Exception * * @return Worksheet */ - public function insertNewColumnBefore($pBefore = 'A', $pNumCols = 1) + public function insertNewColumnBefore($pBefore, $pNumCols = 1) { if (!is_numeric($pBefore)) { $objReferenceHelper = ReferenceHelper::getInstance(); @@ -2095,14 +2080,14 @@ class Worksheet implements IComparable /** * Insert a new column, updating all possible related data. * - * @param int $pBefore Insert before this one (numeric column coordinate of the cell) + * @param int $pBefore Insert before this one (numeric column coordinate of the cell, A = 0) * @param int $pNumCols Number of columns to insert * * @throws Exception * * @return Worksheet */ - public function insertNewColumnBeforeByIndex($pBefore = 0, $pNumCols = 1) + public function insertNewColumnBeforeByIndex($pBefore, $pNumCols = 1) { if ($pBefore >= 0) { return $this->insertNewColumnBefore(Cell::stringFromColumnIndex($pBefore), $pNumCols); @@ -2120,14 +2105,14 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function removeRow($pRow = 1, $pNumRows = 1) + public function removeRow($pRow, $pNumRows = 1) { if ($pRow >= 1) { $highestRow = $this->getHighestDataRow(); $objReferenceHelper = ReferenceHelper::getInstance(); $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this); for ($r = 0; $r < $pNumRows; ++$r) { - $this->getCellCacheController()->removeRow($highestRow); + $this->getCellCollection()->removeRow($highestRow); --$highestRow; } } else { @@ -2140,14 +2125,14 @@ class Worksheet implements IComparable /** * Remove a column, updating all possible related data. * - * @param string $pColumn Remove starting with this one + * @param string $pColumn Remove starting with this one, eg: 'A' * @param int $pNumCols Number of columns to remove * * @throws Exception * * @return Worksheet */ - public function removeColumn($pColumn = 'A', $pNumCols = 1) + public function removeColumn($pColumn, $pNumCols = 1) { if (!is_numeric($pColumn)) { $highestColumn = $this->getHighestDataColumn(); @@ -2155,7 +2140,7 @@ class Worksheet implements IComparable $objReferenceHelper = ReferenceHelper::getInstance(); $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this); for ($c = 0; $c < $pNumCols; ++$c) { - $this->getCellCacheController()->removeColumn($highestColumn); + $this->getCellCollection()->removeColumn($highestColumn); $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2); } } else { @@ -2168,14 +2153,14 @@ class Worksheet implements IComparable /** * Remove a column, updating all possible related data. * - * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell) + * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell A = 0) * @param int $pNumCols Number of columns to remove * * @throws Exception * * @return Worksheet */ - public function removeColumnByIndex($pColumn = 0, $pNumCols = 1) + public function removeColumnByIndex($pColumn, $pNumCols = 1) { if ($pColumn >= 0) { return $this->removeColumn(Cell::stringFromColumnIndex($pColumn), $pNumCols); @@ -2200,7 +2185,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setShowGridlines($pValue = false) + public function setShowGridlines($pValue) { $this->showGridlines = $pValue; @@ -2224,7 +2209,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setPrintGridlines($pValue = false) + public function setPrintGridlines($pValue) { $this->printGridlines = $pValue; @@ -2248,7 +2233,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setShowRowColHeaders($pValue = false) + public function setShowRowColHeaders($pValue) { $this->showRowColHeaders = $pValue; @@ -2272,7 +2257,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setShowSummaryBelow($pValue = true) + public function setShowSummaryBelow($pValue) { $this->showSummaryBelow = $pValue; @@ -2296,7 +2281,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setShowSummaryRight($pValue = true) + public function setShowSummaryRight($pValue) { $this->showSummaryRight = $pValue; @@ -2321,7 +2306,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setComments($pValue = []) + public function setComments(array $pValue) { $this->comments = $pValue; @@ -2331,13 +2316,13 @@ class Worksheet implements IComparable /** * Get comment for cell. * - * @param string $pCellCoordinate Cell coordinate to get comment for + * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1' * * @throws Exception * * @return Comment */ - public function getComment($pCellCoordinate = 'A1') + public function getComment($pCellCoordinate) { // Uppercase coordinate $pCellCoordinate = strtoupper($pCellCoordinate); @@ -2365,12 +2350,12 @@ class Worksheet implements IComparable /** * Get comment for cell by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell * * @return Comment */ - public function getCommentByColumnAndRow($pColumn = 0, $pRow = 1) + public function getCommentByColumnAndRow($pColumn, $pRow) { return $this->getComment(Cell::stringFromColumnIndex($pColumn) . $pRow); } @@ -2402,7 +2387,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setSelectedCell($pCoordinate = 'A1') + public function setSelectedCell($pCoordinate) { return $this->setSelectedCells($pCoordinate); } @@ -2416,7 +2401,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setSelectedCells($pCoordinate = 'A1') + public function setSelectedCells($pCoordinate) { // Uppercase coordinate $pCoordinate = strtoupper($pCoordinate); @@ -2447,14 +2432,14 @@ class Worksheet implements IComparable /** * Selected cell by using numeric cell coordinates. * - * @param int $pColumn Numeric column coordinate of the cell + * @param int $pColumn Numeric column coordinate of the cell (A = 0) * @param int $pRow Numeric row coordinate of the cell * * @throws Exception * * @return Worksheet */ - public function setSelectedCellByColumnAndRow($pColumn = 0, $pRow = 1) + public function setSelectedCellByColumnAndRow($pColumn, $pRow) { return $this->setSelectedCells(Cell::stringFromColumnIndex($pColumn) . $pRow); } @@ -2476,7 +2461,7 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function setRightToLeft($value = false) + public function setRightToLeft($value) { $this->rightToLeft = $value; @@ -2495,38 +2480,34 @@ class Worksheet implements IComparable * * @return Worksheet */ - public function fromArray($source = null, $nullValue = null, $startCell = 'A1', $strictNullComparison = false) + public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false) { - if (is_array($source)) { - // Convert a 1-D array to 2-D (for ease of looping) - if (!is_array(end($source))) { - $source = [$source]; - } + // Convert a 1-D array to 2-D (for ease of looping) + if (!is_array(end($source))) { + $source = [$source]; + } - // start coordinate - list($startColumn, $startRow) = Cell::coordinateFromString($startCell); + // start coordinate + list($startColumn, $startRow) = Cell::coordinateFromString($startCell); - // Loop through $source - foreach ($source as $rowData) { - $currentColumn = $startColumn; - foreach ($rowData as $cellValue) { - if ($strictNullComparison) { - if ($cellValue !== $nullValue) { - // Set cell value - $this->getCell($currentColumn . $startRow)->setValue($cellValue); - } - } else { - if ($cellValue != $nullValue) { - // Set cell value - $this->getCell($currentColumn . $startRow)->setValue($cellValue); - } + // Loop through $source + foreach ($source as $rowData) { + $currentColumn = $startColumn; + foreach ($rowData as $cellValue) { + if ($strictNullComparison) { + if ($cellValue !== $nullValue) { + // Set cell value + $this->getCell($currentColumn . $startRow)->setValue($cellValue); + } + } else { + if ($cellValue != $nullValue) { + // Set cell value + $this->getCell($currentColumn . $startRow)->setValue($cellValue); } - ++$currentColumn; } - ++$startRow; + ++$currentColumn; } - } else { - throw new Exception('Parameter $source should be an array.'); + ++$startRow; } return $this; @@ -2544,7 +2525,7 @@ class Worksheet implements IComparable * * @return array */ - public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) + public function rangeToArray($pRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) { // Returnvalue $returnValue = []; @@ -2566,9 +2547,9 @@ class Worksheet implements IComparable $cRef = ($returnCellRef) ? $col : ++$c; // Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen // so we test and retrieve directly against cellCollection - if ($this->cellCollection->isDataSet($col . $row)) { + if ($this->cellCollection->has($col . $row)) { // Cell exists - $cell = $this->cellCollection->getCacheData($col . $row); + $cell = $this->cellCollection->get($col . $row); if ($cell->getValue() !== null) { if ($cell->getValue() instanceof RichText) { $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText(); @@ -2616,7 +2597,7 @@ class Worksheet implements IComparable * * @return array */ - public function namedRangeToArray($pNamedRange = '', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) + public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) { $namedRange = NamedRange::resolveRange($pNamedRange, $this); if ($namedRange !== null) { @@ -2686,7 +2667,7 @@ class Worksheet implements IComparable public function garbageCollect() { // Flush cache - $this->cellCollection->getCacheData('A1'); + $this->cellCollection->get('A1'); // Lookup highest column and highest row if cells are cleaned $colRow = $this->cellCollection->getHighestRowAndColumn(); @@ -2758,9 +2739,9 @@ class Worksheet implements IComparable /** * Get hyperlink. * - * @param string $pCellCoordinate Cell coordinate to get hyperlink for + * @param string $pCellCoordinate Cell coordinate to get hyperlink for, eg: 'A1' */ - public function getHyperlink($pCellCoordinate = 'A1') + public function getHyperlink($pCellCoordinate) { // return hyperlink if we already have one if (isset($this->hyperlinkCollection[$pCellCoordinate])) { @@ -2774,14 +2755,14 @@ class Worksheet implements IComparable } /** - * Set hyperlnk. + * Set hyperlink. * - * @param string $pCellCoordinate Cell coordinate to insert hyperlink - * @param Cell\Hyperlink $pHyperlink + * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1' + * @param Cell\Hyperlink|null $pHyperlink * * @return Worksheet */ - public function setHyperlink($pCellCoordinate = 'A1', Cell\Hyperlink $pHyperlink = null) + public function setHyperlink($pCellCoordinate, Cell\Hyperlink $pHyperlink = null) { if ($pHyperlink === null) { unset($this->hyperlinkCollection[$pCellCoordinate]); @@ -2795,11 +2776,11 @@ class Worksheet implements IComparable /** * Hyperlink at a specific coordinate exists? * - * @param string $pCoordinate + * @param string $pCoordinate eg: 'A1' * * @return bool */ - public function hyperlinkExists($pCoordinate = 'A1') + public function hyperlinkExists($pCoordinate) { return isset($this->hyperlinkCollection[$pCoordinate]); } @@ -2817,9 +2798,9 @@ class Worksheet implements IComparable /** * Get data validation. * - * @param string $pCellCoordinate Cell coordinate to get data validation for + * @param string $pCellCoordinate Cell coordinate to get data validation for, eg: 'A1' */ - public function getDataValidation($pCellCoordinate = 'A1') + public function getDataValidation($pCellCoordinate) { // return data validation if we already have one if (isset($this->dataValidationCollection[$pCellCoordinate])) { @@ -2835,12 +2816,12 @@ class Worksheet implements IComparable /** * Set data validation. * - * @param string $pCellCoordinate Cell coordinate to insert data validation - * @param Cell\DataValidation $pDataValidation + * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1' + * @param Cell\DataValidation|null $pDataValidation * * @return Worksheet */ - public function setDataValidation($pCellCoordinate = 'A1', Cell\DataValidation $pDataValidation = null) + public function setDataValidation($pCellCoordinate, Cell\DataValidation $pDataValidation = null) { if ($pDataValidation === null) { unset($this->dataValidationCollection[$pCellCoordinate]); @@ -2854,11 +2835,11 @@ class Worksheet implements IComparable /** * Data validation at a specific coordinate exists? * - * @param string $pCoordinate + * @param string $pCoordinate eg: 'A1' * * @return bool */ - public function dataValidationExists($pCoordinate = 'A1') + public function dataValidationExists($pCoordinate) { return isset($this->dataValidationCollection[$pCoordinate]); } @@ -2971,11 +2952,15 @@ class Worksheet implements IComparable if (is_object($val) || (is_array($val))) { if ($key == 'cellCollection') { - $newCollection = clone $this->cellCollection; - $newCollection->copyCellCollection($this); + $newCollection = $this->cellCollection->cloneCellCollection($this); $this->cellCollection = $newCollection; } elseif ($key == 'drawingCollection') { - $newCollection = clone $this->drawingCollection; + $newCollection = new ArrayObject(); + foreach ($this->drawingCollection as $id => $item) { + if (is_object($item)) { + $newCollection[$id] = clone $this->drawingCollection[$id]; + } + } $this->drawingCollection = $newCollection; } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof Worksheet\AutoFilter)) { $newAutoFilter = clone $this->autoFilter; @@ -2998,7 +2983,7 @@ class Worksheet implements IComparable * * @return objWorksheet */ - public function setCodeName($pValue = null) + public function setCodeName($pValue) { // Is this a 'rename' or not? if ($this->getCodeName() == $pValue) { diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/src/PhpSpreadsheet/Worksheet/AutoFilter.php index fd9904c8..a9298433 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -102,7 +102,7 @@ class AutoFilter * * @return AutoFilter */ - public function setRange($pRange = '') + public function setRange($pRange) { // Uppercase coordinate $cellAddress = explode('!', strtoupper($pRange)); @@ -214,7 +214,7 @@ class AutoFilter * * @return AutoFilter\Column */ - public function getColumnByOffset($pColumnOffset = 0) + public function getColumnByOffset($pColumnOffset) { list($rangeStart, $rangeEnd) = \PhpOffice\PhpSpreadsheet\Cell::rangeBoundaries($this->range); $pColumn = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($rangeStart[0] + $pColumnOffset - 1); @@ -278,7 +278,7 @@ class AutoFilter * Shift an AutoFilter Column Rule to a different column. * * Note: This method bypasses validation of the destination column to ensure it is within this AutoFilter range. - * Nor does it verify whether any column rule already exists at $toColumn, but will simply overrideany existing value. + * Nor does it verify whether any column rule already exists at $toColumn, but will simply override any existing value. * Use with caution. * * @param string $fromColumn Column name (e.g. A) @@ -286,7 +286,7 @@ class AutoFilter * * @return AutoFilter */ - public function shiftColumn($fromColumn = null, $toColumn = null) + public function shiftColumn($fromColumn, $toColumn) { $fromColumn = strtoupper($fromColumn); $toColumn = strtoupper($toColumn); diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php index b5c0fbaa..305d70bf 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php @@ -194,7 +194,7 @@ class Column * * @return Column */ - public function setFilterType($pFilterType = self::AUTOFILTER_FILTERTYPE_FILTER) + public function setFilterType($pFilterType) { if (!in_array($pFilterType, self::$filterTypes)) { throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid filter type for column AutoFilter.'); @@ -224,7 +224,7 @@ class Column * * @return Column */ - public function setJoin($pJoin = self::AUTOFILTER_COLUMN_JOIN_OR) + public function setJoin($pJoin) { // Lowercase And/Or $pJoin = strtolower($pJoin); @@ -240,15 +240,15 @@ class Column /** * Set AutoFilter Attributes. * - * @param string[] $pAttributes + * @param string[] $attributes * * @throws \PhpOffice\PhpSpreadsheet\Exception * * @return Column */ - public function setAttributes($pAttributes = []) + public function setAttributes(array $attributes) { - $this->attributes = $pAttributes; + $this->attributes = $attributes; return $this; } @@ -273,7 +273,7 @@ class Column /** * Get AutoFilter Column Attributes. * - * @return string + * @return string[] */ public function getAttributes() { @@ -340,16 +340,15 @@ class Column * Add a new AutoFilter Column Rule to the ruleset. * * @param Column\Rule $pRule - * @param bool $returnRule Flag indicating whether the rule object or the column object should be returned * - * @return Column|Column\Rule + * @return Column */ - public function addRule(Column\Rule $pRule, $returnRule = true) + public function addRule(Column\Rule $pRule) { $pRule->setParent($this); $this->ruleset[] = $pRule; - return ($returnRule) ? $pRule : $this; + return $this; } /** diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php index f9daee1e..bdf6fd52 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php @@ -277,13 +277,13 @@ class Rule /** * Set AutoFilter Rule Type. * - * @param string $pRuleType + * @param string $pRuleType see self::AUTOFILTER_RULETYPE_* * * @throws \PhpOffice\PhpSpreadsheet\Exception * * @return Rule */ - public function setRuleType($pRuleType = self::AUTOFILTER_RULETYPE_FILTER) + public function setRuleType($pRuleType) { if (!in_array($pRuleType, self::$ruleTypes)) { throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid rule type for column AutoFilter Rule.'); @@ -313,7 +313,7 @@ class Rule * * @return Rule */ - public function setValue($pValue = '') + public function setValue($pValue) { if (is_array($pValue)) { $grouping = -1; @@ -351,13 +351,13 @@ class Rule /** * Set AutoFilter Rule Operator. * - * @param string $pOperator + * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_* * * @throws \PhpOffice\PhpSpreadsheet\Exception * * @return Rule */ - public function setOperator($pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL) + public function setOperator($pOperator) { if (empty($pOperator)) { $pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL; @@ -390,7 +390,7 @@ class Rule * * @return Rule */ - public function setGrouping($pGrouping = null) + public function setGrouping($pGrouping) { if (($pGrouping !== null) && (!in_array($pGrouping, self::$dateTimeGroups)) && @@ -406,7 +406,7 @@ class Rule /** * Set AutoFilter Rule. * - * @param string $pOperator + * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_* * @param string|string[] $pValue * @param string $pGrouping * @@ -414,7 +414,7 @@ class Rule * * @return Rule */ - public function setRule($pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL, $pValue = '', $pGrouping = null) + public function setRule($pOperator, $pValue, $pGrouping = null) { $this->setOperator($pOperator); $this->setValue($pValue); diff --git a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php index dbf75f24..9f975309 100644 --- a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php +++ b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php @@ -166,7 +166,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setName($pValue = '') + public function setName($pValue) { $this->name = $pValue; @@ -186,13 +186,13 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable /** * Set Description. * - * @param string $pValue + * @param string $description * * @return BaseDrawing */ - public function setDescription($pValue = '') + public function setDescription($description) { - $this->description = $pValue; + $this->description = $description; return $this; } @@ -260,11 +260,11 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable /** * Set Coordinates. * - * @param string $pValue + * @param string $pValue eg: 'A1' * * @return BaseDrawing */ - public function setCoordinates($pValue = 'A1') + public function setCoordinates($pValue) { $this->coordinates = $pValue; @@ -288,7 +288,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setOffsetX($pValue = 0) + public function setOffsetX($pValue) { $this->offsetX = $pValue; @@ -312,7 +312,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setOffsetY($pValue = 0) + public function setOffsetY($pValue) { $this->offsetY = $pValue; @@ -336,7 +336,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setWidth($pValue = 0) + public function setWidth($pValue) { // Resize proportional? if ($this->resizeProportional && $pValue != 0) { @@ -367,7 +367,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setHeight($pValue = 0) + public function setHeight($pValue) { // Resize proportional? if ($this->resizeProportional && $pValue != 0) { @@ -396,7 +396,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setWidthAndHeight($width = 0, $height = 0) + public function setWidthAndHeight($width, $height) { $xratio = $width / ($this->width != 0 ? $this->width : 1); $yratio = $height / ($this->height != 0 ? $this->height : 1); @@ -433,7 +433,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setResizeProportional($pValue = true) + public function setResizeProportional($pValue) { $this->resizeProportional = $pValue; @@ -457,7 +457,7 @@ class BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparable * * @return BaseDrawing */ - public function setRotation($pValue = 0) + public function setRotation($pValue) { $this->rotation = $pValue; diff --git a/src/PhpSpreadsheet/Worksheet/CellIterator.php b/src/PhpSpreadsheet/Worksheet/CellIterator.php index 9dced6b3..cc3b10ea 100644 --- a/src/PhpSpreadsheet/Worksheet/CellIterator.php +++ b/src/PhpSpreadsheet/Worksheet/CellIterator.php @@ -79,7 +79,7 @@ abstract class CellIterator * * @throws \PhpOffice\PhpSpreadsheet\Exception */ - public function setIterateOnlyExistingCells($value = true) + public function setIterateOnlyExistingCells($value) { $this->onlyExistingCells = (bool) $value; diff --git a/src/PhpSpreadsheet/Worksheet/ColumnDimension.php b/src/PhpSpreadsheet/Worksheet/ColumnDimension.php index 7aa214de..b3b4b312 100644 --- a/src/PhpSpreadsheet/Worksheet/ColumnDimension.php +++ b/src/PhpSpreadsheet/Worksheet/ColumnDimension.php @@ -104,7 +104,7 @@ class ColumnDimension extends Dimension * * @return ColumnDimension */ - public function setWidth($pValue = -1) + public function setWidth($pValue) { $this->width = $pValue; @@ -128,7 +128,7 @@ class ColumnDimension extends Dimension * * @return ColumnDimension */ - public function setAutoSize($pValue = false) + public function setAutoSize($pValue) { $this->autoSize = $pValue; diff --git a/src/PhpSpreadsheet/Worksheet/Dimension.php b/src/PhpSpreadsheet/Worksheet/Dimension.php index 41493985..2ff18512 100644 --- a/src/PhpSpreadsheet/Worksheet/Dimension.php +++ b/src/PhpSpreadsheet/Worksheet/Dimension.php @@ -82,7 +82,7 @@ abstract class Dimension * * @return Dimension */ - public function setVisible($pValue = true) + public function setVisible($pValue) { $this->visible = $pValue; @@ -138,7 +138,7 @@ abstract class Dimension * * @return Dimension */ - public function setCollapsed($pValue = true) + public function setCollapsed($pValue) { $this->collapsed = $pValue; @@ -162,7 +162,7 @@ abstract class Dimension * * @return Dimension */ - public function setXfIndex($pValue = 0) + public function setXfIndex($pValue) { $this->xfIndex = $pValue; diff --git a/src/PhpSpreadsheet/Worksheet/Drawing.php b/src/PhpSpreadsheet/Worksheet/Drawing.php index 26d40f9e..8154e501 100644 --- a/src/PhpSpreadsheet/Worksheet/Drawing.php +++ b/src/PhpSpreadsheet/Worksheet/Drawing.php @@ -100,7 +100,7 @@ class Drawing extends BaseDrawing implements \PhpOffice\PhpSpreadsheet\IComparab * * @return Drawing */ - public function setPath($pValue = '', $pVerifyFile = true) + public function setPath($pValue, $pVerifyFile = true) { if ($pVerifyFile) { if (file_exists($pValue)) { diff --git a/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php b/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php index 644fe708..54f23a83 100644 --- a/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php +++ b/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php @@ -121,7 +121,7 @@ class Shadow implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Shadow */ - public function setVisible($pValue = false) + public function setVisible($pValue) { $this->visible = $pValue; @@ -145,7 +145,7 @@ class Shadow implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Shadow */ - public function setBlurRadius($pValue = 6) + public function setBlurRadius($pValue) { $this->blurRadius = $pValue; @@ -169,7 +169,7 @@ class Shadow implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Shadow */ - public function setDistance($pValue = 2) + public function setDistance($pValue) { $this->distance = $pValue; @@ -193,7 +193,7 @@ class Shadow implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Shadow */ - public function setDirection($pValue = 0) + public function setDirection($pValue) { $this->direction = $pValue; @@ -217,7 +217,7 @@ class Shadow implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Shadow */ - public function setAlignment($pValue = 0) + public function setAlignment($pValue) { $this->alignment = $pValue; @@ -267,7 +267,7 @@ class Shadow implements \PhpOffice\PhpSpreadsheet\IComparable * * @return Shadow */ - public function setAlpha($pValue = 0) + public function setAlpha($pValue) { $this->alpha = $pValue; diff --git a/src/PhpSpreadsheet/Worksheet/HeaderFooter.php b/src/PhpSpreadsheet/Worksheet/HeaderFooter.php index a8f5cf65..92760132 100644 --- a/src/PhpSpreadsheet/Worksheet/HeaderFooter.php +++ b/src/PhpSpreadsheet/Worksheet/HeaderFooter.php @@ -339,7 +339,7 @@ class HeaderFooter * * @return HeaderFooter */ - public function setDifferentOddEven($pValue = false) + public function setDifferentOddEven($pValue) { $this->differentOddEven = $pValue; @@ -363,7 +363,7 @@ class HeaderFooter * * @return HeaderFooter */ - public function setDifferentFirst($pValue = false) + public function setDifferentFirst($pValue) { $this->differentFirst = $pValue; @@ -387,7 +387,7 @@ class HeaderFooter * * @return HeaderFooter */ - public function setScaleWithDocument($pValue = true) + public function setScaleWithDocument($pValue) { $this->scaleWithDocument = $pValue; @@ -411,7 +411,7 @@ class HeaderFooter * * @return HeaderFooter */ - public function setAlignWithMargins($pValue = true) + public function setAlignWithMargins($pValue) { $this->alignWithMargins = $pValue; @@ -428,7 +428,7 @@ class HeaderFooter * * @return HeaderFooter */ - public function addImage(HeaderFooterDrawing $image = null, $location = self::IMAGE_HEADER_LEFT) + public function addImage(HeaderFooterDrawing $image, $location = self::IMAGE_HEADER_LEFT) { $this->headerFooterImages[$location] = $image; @@ -462,12 +462,8 @@ class HeaderFooter * * @return HeaderFooter */ - public function setImages($images) + public function setImages(array $images) { - if (!is_array($images)) { - throw new \PhpOffice\PhpSpreadsheet\Exception('Invalid parameter!'); - } - $this->headerFooterImages = $images; return $this; diff --git a/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php b/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php index 57a888b2..6db76ff5 100644 --- a/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php +++ b/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php @@ -107,7 +107,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setName($pValue = '') + public function setName($pValue) { $this->name = $pValue; @@ -131,7 +131,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setOffsetX($pValue = 0) + public function setOffsetX($pValue) { $this->offsetX = $pValue; @@ -155,7 +155,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setOffsetY($pValue = 0) + public function setOffsetY($pValue) { $this->offsetY = $pValue; @@ -179,7 +179,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setWidth($pValue = 0) + public function setWidth($pValue) { // Resize proportional? if ($this->resizeProportional && $pValue != 0) { @@ -210,7 +210,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setHeight($pValue = 0) + public function setHeight($pValue) { // Resize proportional? if ($this->resizeProportional && $pValue != 0) { @@ -239,7 +239,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setWidthAndHeight($width = 0, $height = 0) + public function setWidthAndHeight($width, $height) { $xratio = $width / $this->width; $yratio = $height / $this->height; @@ -273,7 +273,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setResizeProportional($pValue = true) + public function setResizeProportional($pValue) { $this->resizeProportional = $pValue; @@ -322,7 +322,7 @@ class HeaderFooterDrawing extends Drawing implements \PhpOffice\PhpSpreadsheet\I * * @return HeaderFooterDrawing */ - public function setPath($pValue = '', $pVerifyFile = true) + public function setPath($pValue, $pVerifyFile = true) { if ($pVerifyFile) { if (file_exists($pValue)) { diff --git a/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php index 130f0129..b543b8d0 100644 --- a/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php +++ b/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php @@ -94,11 +94,11 @@ class MemoryDrawing extends BaseDrawing implements \PhpOffice\PhpSpreadsheet\ICo /** * Set image resource. * - * @param $value resource + * @param resource $value * * @return MemoryDrawing */ - public function setImageResource($value = null) + public function setImageResource($value) { $this->imageResource = $value; @@ -124,11 +124,11 @@ class MemoryDrawing extends BaseDrawing implements \PhpOffice\PhpSpreadsheet\ICo /** * Set rendering function. * - * @param string $value + * @param string $value see self::RENDERING_* * * @return MemoryDrawing */ - public function setRenderingFunction($value = self::RENDERING_DEFAULT) + public function setRenderingFunction($value) { $this->renderingFunction = $value; @@ -148,11 +148,11 @@ class MemoryDrawing extends BaseDrawing implements \PhpOffice\PhpSpreadsheet\ICo /** * Set mime type. * - * @param string $value + * @param string $value see self::MIMETYPE_* * * @return MemoryDrawing */ - public function setMimeType($value = self::MIMETYPE_DEFAULT) + public function setMimeType($value) { $this->mimeType = $value; @@ -197,7 +197,7 @@ class MemoryDrawing extends BaseDrawing implements \PhpOffice\PhpSpreadsheet\ICo $vars = get_object_vars($this); foreach ($vars as $key => $value) { if (is_object($value)) { - $this->$key = clone $value; + $this->$key = unserialize(serialize($value)); } else { $this->$key = $value; } diff --git a/src/PhpSpreadsheet/Worksheet/PageSetup.php b/src/PhpSpreadsheet/Worksheet/PageSetup.php index 41a1a22e..8e40ca1d 100644 --- a/src/PhpSpreadsheet/Worksheet/PageSetup.php +++ b/src/PhpSpreadsheet/Worksheet/PageSetup.php @@ -288,11 +288,11 @@ class PageSetup /** * Set Paper Size. * - * @param int $pValue + * @param int $pValue see self::PAPERSIZE_* * * @return PageSetup */ - public function setPaperSize($pValue = self::PAPERSIZE_LETTER) + public function setPaperSize($pValue) { $this->paperSize = $pValue; @@ -312,11 +312,11 @@ class PageSetup /** * Set Orientation. * - * @param string $pValue + * @param string $pValue see self::ORIENTATION_* * * @return PageSetup */ - public function setOrientation($pValue = self::ORIENTATION_DEFAULT) + public function setOrientation($pValue) { $this->orientation = $pValue; @@ -339,14 +339,14 @@ class PageSetup * Print scaling. Valid values range from 10 to 400 * This setting is overridden when fitToWidth and/or fitToHeight are in use * - * @param int? $pValue + * @param int|null $pValue * @param bool $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth * * @throws \PhpOffice\PhpSpreadsheet\Exception * * @return PageSetup */ - public function setScale($pValue = 100, $pUpdate = true) + public function setScale($pValue, $pUpdate = true) { // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface, // but it is apparently still able to handle any scale >= 0, where 0 results in 100 @@ -379,7 +379,7 @@ class PageSetup * * @return PageSetup */ - public function setFitToPage($pValue = true) + public function setFitToPage($pValue) { $this->fitToPage = $pValue; @@ -399,12 +399,12 @@ class PageSetup /** * Set Fit To Height. * - * @param int? $pValue + * @param int|null $pValue * @param bool $pUpdate Update fitToPage so it applies rather than scaling * * @return PageSetup */ - public function setFitToHeight($pValue = 1, $pUpdate = true) + public function setFitToHeight($pValue, $pUpdate = true) { $this->fitToHeight = $pValue; if ($pUpdate) { @@ -427,12 +427,12 @@ class PageSetup /** * Set Fit To Width. * - * @param int? $pValue + * @param int|null $pValue * @param bool $pUpdate Update fitToPage so it applies rather than scaling * * @return PageSetup */ - public function setFitToWidth($pValue = 1, $pUpdate = true) + public function setFitToWidth($pValue, $pUpdate = true) { $this->fitToWidth = $pValue; if ($pUpdate) { @@ -475,11 +475,9 @@ class PageSetup * * @return PageSetup */ - public function setColumnsToRepeatAtLeft($pValue = null) + public function setColumnsToRepeatAtLeft(array $pValue) { - if (is_array($pValue)) { - $this->columnsToRepeatAtLeft = $pValue; - } + $this->columnsToRepeatAtLeft = $pValue; return $this; } @@ -487,12 +485,12 @@ class PageSetup /** * Set Columns to repeat at left by start and end. * - * @param string $pStart - * @param string $pEnd + * @param string $pStart eg: 'A' + * @param string $pEnd eg: 'B' * * @return PageSetup */ - public function setColumnsToRepeatAtLeftByStartAndEnd($pStart = 'A', $pEnd = 'A') + public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd) { $this->columnsToRepeatAtLeft = [$pStart, $pEnd]; @@ -532,11 +530,9 @@ class PageSetup * * @return PageSetup */ - public function setRowsToRepeatAtTop($pValue = null) + public function setRowsToRepeatAtTop(array $pValue) { - if (is_array($pValue)) { - $this->rowsToRepeatAtTop = $pValue; - } + $this->rowsToRepeatAtTop = $pValue; return $this; } @@ -544,12 +540,12 @@ class PageSetup /** * Set Rows to repeat at top by start and end. * - * @param int $pStart - * @param int $pEnd + * @param int $pStart eg: 1 + * @param int $pEnd eg: 1 * * @return PageSetup */ - public function setRowsToRepeatAtTopByStartAndEnd($pStart = 1, $pEnd = 1) + public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd) { $this->rowsToRepeatAtTop = [$pStart, $pEnd]; @@ -573,7 +569,7 @@ class PageSetup * * @return PageSetup */ - public function setHorizontalCentered($value = false) + public function setHorizontalCentered($value) { $this->horizontalCentered = $value; @@ -597,7 +593,7 @@ class PageSetup * * @return PageSetup */ - public function setVerticalCentered($value = false) + public function setVerticalCentered($value) { $this->verticalCentered = $value; @@ -839,7 +835,7 @@ class PageSetup * * @return PageSetup */ - public function setFirstPageNumber($value = null) + public function setFirstPageNumber($value) { $this->firstPageNumber = $value; diff --git a/src/PhpSpreadsheet/Worksheet/Protection.php b/src/PhpSpreadsheet/Worksheet/Protection.php index cda264ee..ddb2dac3 100644 --- a/src/PhpSpreadsheet/Worksheet/Protection.php +++ b/src/PhpSpreadsheet/Worksheet/Protection.php @@ -194,7 +194,7 @@ class Protection * * @return Protection */ - public function setSheet($pValue = false) + public function setSheet($pValue) { $this->sheet = $pValue; @@ -218,7 +218,7 @@ class Protection * * @return Protection */ - public function setObjects($pValue = false) + public function setObjects($pValue) { $this->objects = $pValue; @@ -242,7 +242,7 @@ class Protection * * @return Protection */ - public function setScenarios($pValue = false) + public function setScenarios($pValue) { $this->scenarios = $pValue; @@ -266,7 +266,7 @@ class Protection * * @return Protection */ - public function setFormatCells($pValue = false) + public function setFormatCells($pValue) { $this->formatCells = $pValue; @@ -290,7 +290,7 @@ class Protection * * @return Protection */ - public function setFormatColumns($pValue = false) + public function setFormatColumns($pValue) { $this->formatColumns = $pValue; @@ -314,7 +314,7 @@ class Protection * * @return Protection */ - public function setFormatRows($pValue = false) + public function setFormatRows($pValue) { $this->formatRows = $pValue; @@ -338,7 +338,7 @@ class Protection * * @return Protection */ - public function setInsertColumns($pValue = false) + public function setInsertColumns($pValue) { $this->insertColumns = $pValue; @@ -362,7 +362,7 @@ class Protection * * @return Protection */ - public function setInsertRows($pValue = false) + public function setInsertRows($pValue) { $this->insertRows = $pValue; @@ -386,7 +386,7 @@ class Protection * * @return Protection */ - public function setInsertHyperlinks($pValue = false) + public function setInsertHyperlinks($pValue) { $this->insertHyperlinks = $pValue; @@ -410,7 +410,7 @@ class Protection * * @return Protection */ - public function setDeleteColumns($pValue = false) + public function setDeleteColumns($pValue) { $this->deleteColumns = $pValue; @@ -434,7 +434,7 @@ class Protection * * @return Protection */ - public function setDeleteRows($pValue = false) + public function setDeleteRows($pValue) { $this->deleteRows = $pValue; @@ -458,7 +458,7 @@ class Protection * * @return Protection */ - public function setSelectLockedCells($pValue = false) + public function setSelectLockedCells($pValue) { $this->selectLockedCells = $pValue; @@ -482,7 +482,7 @@ class Protection * * @return Protection */ - public function setSort($pValue = false) + public function setSort($pValue) { $this->sort = $pValue; @@ -506,7 +506,7 @@ class Protection * * @return Protection */ - public function setAutoFilter($pValue = false) + public function setAutoFilter($pValue) { $this->autoFilter = $pValue; @@ -530,7 +530,7 @@ class Protection * * @return Protection */ - public function setPivotTables($pValue = false) + public function setPivotTables($pValue) { $this->pivotTables = $pValue; @@ -554,7 +554,7 @@ class Protection * * @return Protection */ - public function setSelectUnlockedCells($pValue = false) + public function setSelectUnlockedCells($pValue) { $this->selectUnlockedCells = $pValue; @@ -579,7 +579,7 @@ class Protection * * @return Protection */ - public function setPassword($pValue = '', $pAlreadyHashed = false) + public function setPassword($pValue, $pAlreadyHashed = false) { if (!$pAlreadyHashed) { $pValue = \PhpOffice\PhpSpreadsheet\Shared\PasswordHasher::hashPassword($pValue); diff --git a/src/PhpSpreadsheet/Worksheet/RowDimension.php b/src/PhpSpreadsheet/Worksheet/RowDimension.php index d48a97bd..58ba4185 100644 --- a/src/PhpSpreadsheet/Worksheet/RowDimension.php +++ b/src/PhpSpreadsheet/Worksheet/RowDimension.php @@ -104,7 +104,7 @@ class RowDimension extends Dimension * * @return RowDimension */ - public function setRowHeight($pValue = -1) + public function setRowHeight($pValue) { $this->height = $pValue; @@ -128,7 +128,7 @@ class RowDimension extends Dimension * * @return RowDimension */ - public function setZeroHeight($pValue = false) + public function setZeroHeight($pValue) { $this->zeroHeight = $pValue; diff --git a/src/PhpSpreadsheet/Worksheet/SheetView.php b/src/PhpSpreadsheet/Worksheet/SheetView.php index 64c60522..7ce233ce 100644 --- a/src/PhpSpreadsheet/Worksheet/SheetView.php +++ b/src/PhpSpreadsheet/Worksheet/SheetView.php @@ -92,7 +92,7 @@ class SheetView * * @return SheetView */ - public function setZoomScale($pValue = 100) + public function setZoomScale($pValue) { // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface, // but it is apparently still able to handle any scale >= 1 @@ -126,7 +126,7 @@ class SheetView * * @return SheetView */ - public function setZoomScaleNormal($pValue = 100) + public function setZoomScaleNormal($pValue) { if (($pValue >= 1) || is_null($pValue)) { $this->zoomScaleNormal = $pValue; @@ -161,7 +161,7 @@ class SheetView * * @return SheetView */ - public function setView($pValue = null) + public function setView($pValue) { // MS Excel 2007 allows setting the view to 'normal', 'pageLayout' or 'pageBreakPreview' via the user interface if ($pValue === null) { diff --git a/src/PhpSpreadsheet/Writer/BaseWriter.php b/src/PhpSpreadsheet/Writer/BaseWriter.php index 67d60017..eb6f2a18 100644 --- a/src/PhpSpreadsheet/Writer/BaseWriter.php +++ b/src/PhpSpreadsheet/Writer/BaseWriter.php @@ -48,14 +48,14 @@ abstract class BaseWriter implements IWriter * * @var bool */ - protected $_useDiskCaching = false; + private $useDiskCaching = false; /** * Disk caching directory. * * @var string */ - protected $_diskCachingDirectory = './'; + private $diskCachingDirectory = './'; /** * Write charts in workbook? @@ -78,7 +78,7 @@ abstract class BaseWriter implements IWriter * * @return IWriter */ - public function setIncludeCharts($pValue = false) + public function setIncludeCharts($pValue) { $this->includeCharts = (bool) $pValue; @@ -109,7 +109,7 @@ abstract class BaseWriter implements IWriter * * @return IWriter */ - public function setPreCalculateFormulas($pValue = true) + public function setPreCalculateFormulas($pValue) { $this->preCalculateFormulas = (bool) $pValue; @@ -123,7 +123,7 @@ abstract class BaseWriter implements IWriter */ public function getUseDiskCaching() { - return $this->_useDiskCaching; + return $this->useDiskCaching; } /** @@ -136,13 +136,13 @@ abstract class BaseWriter implements IWriter * * @return IWriter */ - public function setUseDiskCaching($pValue = false, $pDirectory = null) + public function setUseDiskCaching($pValue, $pDirectory = null) { - $this->_useDiskCaching = $pValue; + $this->useDiskCaching = $pValue; if ($pDirectory !== null) { if (is_dir($pDirectory)) { - $this->_diskCachingDirectory = $pDirectory; + $this->diskCachingDirectory = $pDirectory; } else { throw new Exception("Directory does not exist: $pDirectory"); } @@ -158,6 +158,6 @@ abstract class BaseWriter implements IWriter */ public function getDiskCachingDirectory() { - return $this->_diskCachingDirectory; + return $this->diskCachingDirectory; } } diff --git a/src/PhpSpreadsheet/Writer/Csv.php b/src/PhpSpreadsheet/Writer/Csv.php index 07afe03e..4ed6f89d 100644 --- a/src/PhpSpreadsheet/Writer/Csv.php +++ b/src/PhpSpreadsheet/Writer/Csv.php @@ -100,7 +100,7 @@ class Csv extends BaseWriter implements IWriter * * @throws Exception */ - public function save($pFilename = null) + public function save($pFilename) { // Fetch sheet $sheet = $this->spreadsheet->getSheet($this->sheetIndex); @@ -164,11 +164,11 @@ class Csv extends BaseWriter implements IWriter /** * Set delimiter. * - * @param string $pValue Delimiter, defaults to , + * @param string $pValue Delimiter, defaults to ',' * * @return CSV */ - public function setDelimiter($pValue = ',') + public function setDelimiter($pValue) { $this->delimiter = $pValue; @@ -192,7 +192,7 @@ class Csv extends BaseWriter implements IWriter * * @return CSV */ - public function setEnclosure($pValue = '"') + public function setEnclosure($pValue) { if ($pValue == '') { $pValue = null; @@ -219,7 +219,7 @@ class Csv extends BaseWriter implements IWriter * * @return CSV */ - public function setLineEnding($pValue = PHP_EOL) + public function setLineEnding($pValue) { $this->lineEnding = $pValue; @@ -243,7 +243,7 @@ class Csv extends BaseWriter implements IWriter * * @return CSV */ - public function setUseBOM($pValue = false) + public function setUseBOM($pValue) { $this->useBOM = $pValue; @@ -267,7 +267,7 @@ class Csv extends BaseWriter implements IWriter * * @return CSV */ - public function setIncludeSeparatorLine($pValue = false) + public function setIncludeSeparatorLine($pValue) { $this->includeSeparatorLine = $pValue; @@ -292,7 +292,7 @@ class Csv extends BaseWriter implements IWriter * * @return CSV */ - public function setExcelCompatibility($pValue = false) + public function setExcelCompatibility($pValue) { $this->excelCompatibility = $pValue; @@ -316,7 +316,7 @@ class Csv extends BaseWriter implements IWriter * * @return CSV */ - public function setSheetIndex($pValue = 0) + public function setSheetIndex($pValue) { $this->sheetIndex = $pValue; @@ -331,37 +331,33 @@ class Csv extends BaseWriter implements IWriter * * @throws Exception */ - private function writeLine($pFileHandle = null, $pValues = null) + private function writeLine($pFileHandle, array $pValues) { - if (is_array($pValues)) { - // No leading delimiter - $writeDelimiter = false; + // No leading delimiter + $writeDelimiter = false; - // Build the line - $line = ''; + // Build the line + $line = ''; - foreach ($pValues as $element) { - // Escape enclosures - $element = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $element); + foreach ($pValues as $element) { + // Escape enclosures + $element = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $element); - // Add delimiter - if ($writeDelimiter) { - $line .= $this->delimiter; - } else { - $writeDelimiter = true; - } - - // Add enclosed string - $line .= $this->enclosure . $element . $this->enclosure; + // Add delimiter + if ($writeDelimiter) { + $line .= $this->delimiter; + } else { + $writeDelimiter = true; } - // Add line ending - $line .= $this->lineEnding; - - // Write to file - fwrite($pFileHandle, $line); - } else { - throw new Exception('Invalid data row passed to CSV writer.'); + // Add enclosed string + $line .= $this->enclosure . $element . $this->enclosure; } + + // Add line ending + $line .= $this->lineEnding; + + // Write to file + fwrite($pFileHandle, $line); } } diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 779113df..f87812c7 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -147,7 +147,7 @@ class Html extends BaseWriter implements IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null) + public function save($pFilename) { // garbage collect $this->spreadsheet->garbageCollect(); @@ -296,7 +296,7 @@ class Html extends BaseWriter implements IWriter * * @return HTML */ - public function setSheetIndex($pValue = 0) + public function setSheetIndex($pValue) { $this->sheetIndex = $pValue; @@ -320,7 +320,7 @@ class Html extends BaseWriter implements IWriter * * @return HTML */ - public function setGenerateSheetNavigationBlock($pValue = true) + public function setGenerateSheetNavigationBlock($pValue) { $this->generateSheetNavigationBlock = (bool) $pValue; @@ -1172,253 +1172,250 @@ class Html extends BaseWriter implements IWriter * @param \PhpOffice\PhpSpreadsheet\Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet * @param array $pValues Array containing cells in a row * @param int $pRow Row number (0-based) - * @param mixed $cellType + * @param mixed $cellType eg: 'td' * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception * * @return string */ - private function generateRow(\PhpOffice\PhpSpreadsheet\Worksheet $pSheet, $pValues = null, $pRow = 0, $cellType = 'td') + private function generateRow(\PhpOffice\PhpSpreadsheet\Worksheet $pSheet, array $pValues, $pRow, $cellType) { - if (is_array($pValues)) { - // Construct HTML - $html = ''; + // Construct HTML + $html = ''; - // Sheet index - $sheetIndex = $pSheet->getParent()->getIndex($pSheet); + // Sheet index + $sheetIndex = $pSheet->getParent()->getIndex($pSheet); - // DomPDF and breaks - if ($this->isPdf && count($pSheet->getBreaks()) > 0) { - $breaks = $pSheet->getBreaks(); + // DomPDF and breaks + if ($this->isPdf && count($pSheet->getBreaks()) > 0) { + $breaks = $pSheet->getBreaks(); - // check if a break is needed before this row - if (isset($breaks['A' . $pRow])) { - // close table:
- $html .= $this->generateTableFooter(); + // check if a break is needed before this row + if (isset($breaks['A' . $pRow])) { + // close table: + $html .= $this->generateTableFooter(); - // insert page break - $html .= '
'; + // insert page break + $html .= '
'; - // open table again: + etc. - $html .= $this->generateTableHeader($pSheet); - } + // open table again:
+ etc. + $html .= $this->generateTableHeader($pSheet); } - - // Write row start - if (!$this->useInlineCss) { - $html .= ' ' . PHP_EOL; - } else { - $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) - ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : ''; - - $html .= ' ' . PHP_EOL; - } - - // Write cells - $colNum = 0; - foreach ($pValues as $cellAddress) { - $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : ''; - $coordinate = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colNum) . ($pRow + 1); - if (!$this->useInlineCss) { - $cssClass = ''; - $cssClass = 'column' . $colNum; - } else { - $cssClass = []; - if ($cellType == 'th') { - if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) { - $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum]; - } - } else { - if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) { - $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum]; - } - } - } - $colSpan = 1; - $rowSpan = 1; - - // initialize - $cellData = ' '; - - // \PhpOffice\PhpSpreadsheet\Cell - if ($cell instanceof \PhpOffice\PhpSpreadsheet\Cell) { - $cellData = ''; - if (is_null($cell->getParent())) { - $cell->attach($pSheet); - } - // Value - if ($cell->getValue() instanceof \PhpOffice\PhpSpreadsheet\RichText) { - // Loop through rich text elements - $elements = $cell->getValue()->getRichTextElements(); - foreach ($elements as $element) { - // Rich text start? - if ($element instanceof \PhpOffice\PhpSpreadsheet\RichText\Run) { - $cellData .= ''; - - if ($element->getFont()->getSuperScript()) { - $cellData .= ''; - } elseif ($element->getFont()->getSubScript()) { - $cellData .= ''; - } - } - - // Convert UTF8 data to PCDATA - $cellText = $element->getText(); - $cellData .= htmlspecialchars($cellText); - - if ($element instanceof \PhpOffice\PhpSpreadsheet\RichText\Run) { - if ($element->getFont()->getSuperScript()) { - $cellData .= ''; - } elseif ($element->getFont()->getSubScript()) { - $cellData .= ''; - } - - $cellData .= ''; - } - } - } else { - if ($this->preCalculateFormulas) { - $cellData = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::toFormattedString( - $cell->getCalculatedValue(), - $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(), - [$this, 'formatColor'] - ); - } else { - $cellData = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::toFormattedString( - $cell->getValue(), - $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(), - [$this, 'formatColor'] - ); - } - $cellData = htmlspecialchars($cellData); - if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperScript()) { - $cellData = '' . $cellData . ''; - } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubScript()) { - $cellData = '' . $cellData . ''; - } - } - - // Converts the cell content so that spaces occuring at beginning of each new line are replaced by   - // Example: " Hello\n to the world" is converted to "  Hello\n to the world" - $cellData = preg_replace('/(?m)(?:^|\\G) /', ' ', $cellData); - - // convert newline "\n" to '
' - $cellData = nl2br($cellData); - - // Extend CSS class? - if (!$this->useInlineCss) { - $cssClass .= ' style' . $cell->getXfIndex(); - $cssClass .= ' ' . $cell->getDataType(); - } else { - if ($cellType == 'th') { - if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) { - $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]); - } - } else { - if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) { - $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]); - } - } - - // General horizontal alignment: Actual horizontal alignment depends on dataType - $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex()); - if ($sharedStyle->getAlignment()->getHorizontal() == \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL - && isset($this->cssStyles['.' . $cell->getDataType()]['text-align']) - ) { - $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align']; - } - } - } - - // Hyperlink? - if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) { - $cellData = '' . $cellData . ''; - } - - // Should the cell be written or is it swallowed by a rowspan or colspan? - $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]) - && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]); - - // Colspan and Rowspan - $colspan = 1; - $rowspan = 1; - if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) { - $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]; - $rowSpan = $spans['rowspan']; - $colSpan = $spans['colspan']; - - // Also apply style from last cell in merge to fix borders - - // relies on !important for non-none border declarations in createCSSStyleBorder - $endCellCoord = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan); - if (!$this->useInlineCss) { - $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex(); - } - } - - // Write - if ($writeCell) { - // Column start - $html .= ' <' . $cellType; - if (!$this->useInlineCss) { - $html .= ' class="' . $cssClass . '"'; - } else { - //** Necessary redundant code for the sake of \PhpOffice\PhpSpreadsheet\Writer\Pdf ** - // We must explicitly write the width of the - $width = 0; - $i = $colNum - 1; - $e = $colNum + $colSpan - 1; - while ($i++ < $e) { - if (isset($this->columnWidths[$sheetIndex][$i])) { - $width += $this->columnWidths[$sheetIndex][$i]; - } - } - $cssClass['width'] = $width . 'pt'; - - // We must also explicitly write the height of the - if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) { - $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height']; - $cssClass['height'] = $height; - } - //** end of redundant code ** - - $html .= ' style="' . $this->assembleCSS($cssClass) . '"'; - } - if ($colSpan > 1) { - $html .= ' colspan="' . $colSpan . '"'; - } - if ($rowSpan > 1) { - $html .= ' rowspan="' . $rowSpan . '"'; - } - $html .= '>'; - - // Image? - $html .= $this->writeImageInCell($pSheet, $coordinate); - - // Chart? - if ($this->includeCharts) { - $html .= $this->writeChartInCell($pSheet, $coordinate); - } - - // Cell data - $html .= $cellData; - - // Column end - $html .= '' . PHP_EOL; - } - - // Next column - ++$colNum; - } - - // Write row end - $html .= ' ' . PHP_EOL; - - // Return - return $html; } - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Invalid parameters passed.'); + + // Write row start + if (!$this->useInlineCss) { + $html .= ' ' . PHP_EOL; + } else { + $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) + ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : ''; + + $html .= ' ' . PHP_EOL; + } + + // Write cells + $colNum = 0; + foreach ($pValues as $cellAddress) { + $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : ''; + $coordinate = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colNum) . ($pRow + 1); + if (!$this->useInlineCss) { + $cssClass = ''; + $cssClass = 'column' . $colNum; + } else { + $cssClass = []; + if ($cellType == 'th') { + if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) { + $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum]; + } + } else { + if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) { + $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum]; + } + } + } + $colSpan = 1; + $rowSpan = 1; + + // initialize + $cellData = ' '; + + // \PhpOffice\PhpSpreadsheet\Cell + if ($cell instanceof \PhpOffice\PhpSpreadsheet\Cell) { + $cellData = ''; + if (is_null($cell->getParent())) { + $cell->attach($pSheet); + } + // Value + if ($cell->getValue() instanceof \PhpOffice\PhpSpreadsheet\RichText) { + // Loop through rich text elements + $elements = $cell->getValue()->getRichTextElements(); + foreach ($elements as $element) { + // Rich text start? + if ($element instanceof \PhpOffice\PhpSpreadsheet\RichText\Run) { + $cellData .= ''; + + if ($element->getFont()->getSuperScript()) { + $cellData .= ''; + } elseif ($element->getFont()->getSubScript()) { + $cellData .= ''; + } + } + + // Convert UTF8 data to PCDATA + $cellText = $element->getText(); + $cellData .= htmlspecialchars($cellText); + + if ($element instanceof \PhpOffice\PhpSpreadsheet\RichText\Run) { + if ($element->getFont()->getSuperScript()) { + $cellData .= ''; + } elseif ($element->getFont()->getSubScript()) { + $cellData .= ''; + } + + $cellData .= ''; + } + } + } else { + if ($this->preCalculateFormulas) { + $cellData = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::toFormattedString( + $cell->getCalculatedValue(), + $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(), + [$this, 'formatColor'] + ); + } else { + $cellData = \PhpOffice\PhpSpreadsheet\Style\NumberFormat::toFormattedString( + $cell->getValue(), + $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(), + [$this, 'formatColor'] + ); + } + $cellData = htmlspecialchars($cellData); + if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperScript()) { + $cellData = '' . $cellData . ''; + } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubScript()) { + $cellData = '' . $cellData . ''; + } + } + + // Converts the cell content so that spaces occuring at beginning of each new line are replaced by   + // Example: " Hello\n to the world" is converted to "  Hello\n to the world" + $cellData = preg_replace('/(?m)(?:^|\\G) /', ' ', $cellData); + + // convert newline "\n" to '
' + $cellData = nl2br($cellData); + + // Extend CSS class? + if (!$this->useInlineCss) { + $cssClass .= ' style' . $cell->getXfIndex(); + $cssClass .= ' ' . $cell->getDataType(); + } else { + if ($cellType == 'th') { + if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) { + $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]); + } + } else { + if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) { + $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]); + } + } + + // General horizontal alignment: Actual horizontal alignment depends on dataType + $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex()); + if ($sharedStyle->getAlignment()->getHorizontal() == \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL + && isset($this->cssStyles['.' . $cell->getDataType()]['text-align']) + ) { + $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align']; + } + } + } + + // Hyperlink? + if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) { + $cellData = '' . $cellData . ''; + } + + // Should the cell be written or is it swallowed by a rowspan or colspan? + $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]) + && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]); + + // Colspan and Rowspan + $colspan = 1; + $rowspan = 1; + if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) { + $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]; + $rowSpan = $spans['rowspan']; + $colSpan = $spans['colspan']; + + // Also apply style from last cell in merge to fix borders - + // relies on !important for non-none border declarations in createCSSStyleBorder + $endCellCoord = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan); + if (!$this->useInlineCss) { + $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex(); + } + } + + // Write + if ($writeCell) { + // Column start + $html .= ' <' . $cellType; + if (!$this->useInlineCss) { + $html .= ' class="' . $cssClass . '"'; + } else { + //** Necessary redundant code for the sake of \PhpOffice\PhpSpreadsheet\Writer\Pdf ** + // We must explicitly write the width of the + $width = 0; + $i = $colNum - 1; + $e = $colNum + $colSpan - 1; + while ($i++ < $e) { + if (isset($this->columnWidths[$sheetIndex][$i])) { + $width += $this->columnWidths[$sheetIndex][$i]; + } + } + $cssClass['width'] = $width . 'pt'; + + // We must also explicitly write the height of the + if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) { + $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height']; + $cssClass['height'] = $height; + } + //** end of redundant code ** + + $html .= ' style="' . $this->assembleCSS($cssClass) . '"'; + } + if ($colSpan > 1) { + $html .= ' colspan="' . $colSpan . '"'; + } + if ($rowSpan > 1) { + $html .= ' rowspan="' . $rowSpan . '"'; + } + $html .= '>'; + + // Image? + $html .= $this->writeImageInCell($pSheet, $coordinate); + + // Chart? + if ($this->includeCharts) { + $html .= $this->writeChartInCell($pSheet, $coordinate); + } + + // Cell data + $html .= $cellData; + + // Column end + $html .= '' . PHP_EOL; + } + + // Next column + ++$colNum; + } + + // Write row end + $html .= ' ' . PHP_EOL; + + // Return + return $html; } /** @@ -1457,7 +1454,7 @@ class Html extends BaseWriter implements IWriter * * @return HTML */ - public function setImagesRoot($pValue = '.') + public function setImagesRoot($pValue) { $this->imagesRoot = $pValue; @@ -1481,7 +1478,7 @@ class Html extends BaseWriter implements IWriter * * @return HTML */ - public function setEmbedImages($pValue = true) + public function setEmbedImages($pValue) { $this->embedImages = $pValue; @@ -1505,7 +1502,7 @@ class Html extends BaseWriter implements IWriter * * @return HTML */ - public function setUseInlineCss($pValue = false) + public function setUseInlineCss($pValue) { $this->useInlineCss = $pValue; diff --git a/src/PhpSpreadsheet/Writer/IWriter.php b/src/PhpSpreadsheet/Writer/IWriter.php index 4093a34b..c1443481 100644 --- a/src/PhpSpreadsheet/Writer/IWriter.php +++ b/src/PhpSpreadsheet/Writer/IWriter.php @@ -33,5 +33,5 @@ interface IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null); + public function save($pFilename); } diff --git a/src/PhpSpreadsheet/Writer/Ods.php b/src/PhpSpreadsheet/Writer/Ods.php index ecddf9bd..f82d4a73 100644 --- a/src/PhpSpreadsheet/Writer/Ods.php +++ b/src/PhpSpreadsheet/Writer/Ods.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use ZipArchive; /** * Copyright (c) 2006 - 2015 PhpSpreadsheet. @@ -47,7 +48,7 @@ class Ods extends BaseWriter implements IWriter * * @param \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet */ - public function __construct(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function __construct(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { $this->setSpreadsheet($spreadsheet); @@ -73,7 +74,7 @@ class Ods extends BaseWriter implements IWriter * * @return Ods\WriterPart|null */ - public function getWriterPart($pPartName = '') + public function getWriterPart($pPartName) { if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) { return $this->writerParts[strtolower($pPartName)]; @@ -89,7 +90,7 @@ class Ods extends BaseWriter implements IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null) + public function save($pFilename) { if (!$this->spreadSheet) { throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('PhpSpreadsheet object unassigned.'); @@ -107,18 +108,18 @@ class Ods extends BaseWriter implements IWriter } } - $objZip = $this->createZip($pFilename); + $zip = $this->createZip($pFilename); - $objZip->addFromString('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest()); - $objZip->addFromString('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail()); - $objZip->addFromString('content.xml', $this->getWriterPart('content')->write()); - $objZip->addFromString('meta.xml', $this->getWriterPart('meta')->write()); - $objZip->addFromString('mimetype', $this->getWriterPart('mimetype')->write()); - $objZip->addFromString('settings.xml', $this->getWriterPart('settings')->write()); - $objZip->addFromString('styles.xml', $this->getWriterPart('styles')->write()); + $zip->addFromString('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest()); + $zip->addFromString('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail()); + $zip->addFromString('content.xml', $this->getWriterPart('content')->write()); + $zip->addFromString('meta.xml', $this->getWriterPart('meta')->write()); + $zip->addFromString('mimetype', $this->getWriterPart('mimetype')->write()); + $zip->addFromString('settings.xml', $this->getWriterPart('settings')->write()); + $zip->addFromString('styles.xml', $this->getWriterPart('styles')->write()); // Close file - if ($objZip->close() === false) { + if ($zip->close() === false) { throw new \PhpOffice\PhpSpreadsheet\Writer\Exception("Could not close zip file $pFilename."); } @@ -143,26 +144,19 @@ class Ods extends BaseWriter implements IWriter private function createZip($pFilename) { // Create new ZIP file and open it for writing - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - $objZip = new $zipClass(); - - // Retrieve OVERWRITE and CREATE constants from the instantiated zip class - // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP - $ro = new \ReflectionObject($objZip); - $zipOverWrite = $ro->getConstant('OVERWRITE'); - $zipCreate = $ro->getConstant('CREATE'); + $zip = new ZipArchive(); if (file_exists($pFilename)) { unlink($pFilename); } // Try opening the ZIP file - if ($objZip->open($pFilename, $zipOverWrite) !== true) { - if ($objZip->open($pFilename, $zipCreate) !== true) { + if ($zip->open($pFilename, ZipArchive::OVERWRITE) !== true) { + if ($zip->open($pFilename, ZipArchive::CREATE) !== true) { throw new \PhpOffice\PhpSpreadsheet\Writer\Exception("Could not open $pFilename for writing."); } } - return $objZip; + return $zip; } /** @@ -189,7 +183,7 @@ class Ods extends BaseWriter implements IWriter * * @return self */ - public function setSpreadsheet(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function setSpreadsheet(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { $this->spreadSheet = $spreadsheet; diff --git a/src/PhpSpreadsheet/Writer/Ods/Content.php b/src/PhpSpreadsheet/Writer/Ods/Content.php index 0d960d6d..b8597028 100644 --- a/src/PhpSpreadsheet/Writer/Ods/Content.php +++ b/src/PhpSpreadsheet/Writer/Ods/Content.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Ods; -/** +/* * PhpSpreadsheet. * * Copyright (c) 2006 - 2015 PhpSpreadsheet @@ -27,9 +27,22 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Ods; * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL */ +use PhpOffice\PhpSpreadsheet\Cell; +use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Style\Fill; +use PhpOffice\PhpSpreadsheet\Style\Font; +use PhpOffice\PhpSpreadsheet\Worksheet; +use PhpOffice\PhpSpreadsheet\Writer\Exception; +use PhpOffice\PhpSpreadsheet\Writer\Ods; +use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment; + /** * @category PhpSpreadsheet * + * @method Ods getParentWriter + * * @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) * @author Alexander Pervakov */ @@ -37,27 +50,22 @@ class Content extends WriterPart { const NUMBER_COLS_REPEATED_MAX = 1024; const NUMBER_ROWS_REPEATED_MAX = 1048576; + const CELL_STYLE_PREFIX = 'ce'; /** * Write content.xml to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet - * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception * * @return string XML Output */ - public function write(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function write() { - if (!$spreadsheet) { - $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /* @var $spreadsheet PhpSpreadsheet */ - } - $objWriter = null; if ($this->getParentWriter()->getUseDiskCaching()) { - $objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); } else { - $objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_MEMORY); + $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); } // XML header @@ -101,12 +109,18 @@ class Content extends WriterPart $objWriter->writeElement('office:scripts'); $objWriter->writeElement('office:font-face-decls'); - $objWriter->writeElement('office:automatic-styles'); + + // Styles XF + $objWriter->startElement('office:automatic-styles'); + $this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet()); + $objWriter->endElement(); $objWriter->startElement('office:body'); $objWriter->startElement('office:spreadsheet'); $objWriter->writeElement('table:calculation-settings'); + $this->writeSheets($objWriter); + $objWriter->writeElement('table:named-expressions'); $objWriter->endElement(); $objWriter->endElement(); @@ -118,14 +132,14 @@ class Content extends WriterPart /** * Write sheets. * - * @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter + * @param XMLWriter $objWriter */ - private function writeSheets(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter) + private function writeSheets(XMLWriter $objWriter) { - $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /* @var $spreadsheet PhpSpreadsheet */ + $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /* @var $spreadsheet Spreadsheet */ - $sheet_count = $spreadsheet->getSheetCount(); - for ($i = 0; $i < $sheet_count; ++$i) { + $sheetCount = $spreadsheet->getSheetCount(); + for ($i = 0; $i < $sheetCount; ++$i) { $objWriter->startElement('table:table'); $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($i)->getTitle()); $objWriter->writeElement('office:forms'); @@ -140,16 +154,16 @@ class Content extends WriterPart /** * Write rows of the specified sheet. * - * @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter - * @param \PhpOffice\PhpSpreadsheet\Worksheet $sheet + * @param XMLWriter $objWriter + * @param Worksheet $sheet */ - private function writeRows(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $sheet) + private function writeRows(XMLWriter $objWriter, Worksheet $sheet) { - $number_rows_repeated = self::NUMBER_ROWS_REPEATED_MAX; + $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX; $span_row = 0; $rows = $sheet->getRowIterator(); while ($rows->valid()) { - --$number_rows_repeated; + --$numberRowsRepeated; $row = $rows->current(); if ($row->getCellIterator()->valid()) { if ($span_row) { @@ -176,70 +190,77 @@ class Content extends WriterPart /** * Write cells of the specified row. * - * @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Row $row + * @param XMLWriter $objWriter + * @param Worksheet\Row $row * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws Exception */ - private function writeCells(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet\Row $row) + private function writeCells(XMLWriter $objWriter, Worksheet\Row $row) { - $number_cols_repeated = self::NUMBER_COLS_REPEATED_MAX; - $prev_column = -1; + $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX; + $prevColumn = -1; $cells = $row->getCellIterator(); while ($cells->valid()) { + /** @var Cell $cell */ $cell = $cells->current(); - $column = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($cell->getColumn()) - 1; + $column = Cell::columnIndexFromString($cell->getColumn()) - 1; - $this->writeCellSpan($objWriter, $column, $prev_column); + $this->writeCellSpan($objWriter, $column, $prevColumn); $objWriter->startElement('table:table-cell'); + // Style XF + $style = $cell->getXfIndex(); + if ($style !== null) { + $objWriter->writeAttribute('table:style-name', self::CELL_STYLE_PREFIX . $style); + } + switch ($cell->getDataType()) { - case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL: + case DataType::TYPE_BOOL: $objWriter->writeAttribute('office:value-type', 'boolean'); $objWriter->writeAttribute('office:value', $cell->getValue()); $objWriter->writeElement('text:p', $cell->getValue()); break; - case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR: - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Writing of error not implemented yet.'); + case DataType::TYPE_ERROR: + throw new Exception('Writing of error not implemented yet.'); break; - case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA: + case DataType::TYPE_FORMULA: try { - $formula_value = $cell->getCalculatedValue(); - } catch (Exception $e) { - $formula_value = $cell->getValue(); + $formulaValue = $cell->getCalculatedValue(); + } catch (\Exception $e) { + $formulaValue = $cell->getValue(); } $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue()); - if (is_numeric($formula_value)) { + if (is_numeric($formulaValue)) { $objWriter->writeAttribute('office:value-type', 'float'); } else { $objWriter->writeAttribute('office:value-type', 'string'); } - $objWriter->writeAttribute('office:value', $formula_value); - $objWriter->writeElement('text:p', $formula_value); + $objWriter->writeAttribute('office:value', $formulaValue); + $objWriter->writeElement('text:p', $formulaValue); break; - case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_INLINE: - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Writing of inline not implemented yet.'); + case DataType::TYPE_INLINE: + throw new Exception('Writing of inline not implemented yet.'); break; - case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC: + case DataType::TYPE_NUMERIC: $objWriter->writeAttribute('office:value-type', 'float'); $objWriter->writeAttribute('office:value', $cell->getValue()); $objWriter->writeElement('text:p', $cell->getValue()); break; - case \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING: + case DataType::TYPE_STRING: $objWriter->writeAttribute('office:value-type', 'string'); $objWriter->writeElement('text:p', $cell->getValue()); break; } - Cell\Comment::write($objWriter, $cell); + Comment::write($objWriter, $cell); $objWriter->endElement(); - $prev_column = $column; + $prevColumn = $column; $cells->next(); } - $number_cols_repeated = $number_cols_repeated - $prev_column - 1; - if ($number_cols_repeated > 0) { - if ($number_cols_repeated > 1) { + $numberColsRepeated = $numberColsRepeated - $prevColumn - 1; + if ($numberColsRepeated > 0) { + if ($numberColsRepeated > 1) { $objWriter->startElement('table:table-cell'); - $objWriter->writeAttribute('table:number-columns-repeated', $number_cols_repeated); + $objWriter->writeAttribute('table:number-columns-repeated', $numberColsRepeated); $objWriter->endElement(); } else { $objWriter->writeElement('table:table-cell'); @@ -250,11 +271,11 @@ class Content extends WriterPart /** * Write span. * - * @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter + * @param XMLWriter $objWriter * @param int $curColumn * @param int $prevColumn */ - private function writeCellSpan(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $curColumn, $prevColumn) + private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn) { $diff = $curColumn - $prevColumn - 1; if (1 === $diff) { @@ -265,4 +286,101 @@ class Content extends WriterPart $objWriter->endElement(); } } + + /** + * Write XF cell styles. + * + * @param XMLWriter $writer + * @param Spreadsheet $spreadsheet + */ + private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet) + { + foreach ($spreadsheet->getCellXfCollection() as $style) { + $writer->startElement('style:style'); + $writer->writeAttribute('style:name', self::CELL_STYLE_PREFIX . $style->getIndex()); + $writer->writeAttribute('style:family', 'table-cell'); + $writer->writeAttribute('style:parent-style-name', 'Default'); + + /* + * style:text-properties + */ + + // Font + $writer->startElement('style:text-properties'); + + $font = $style->getFont(); + + if ($font->getBold()) { + $writer->writeAttribute('fo:font-weight', 'bold'); + $writer->writeAttribute('style:font-weight-complex', 'bold'); + $writer->writeAttribute('style:font-weight-asian', 'bold'); + } + + if ($font->getItalic()) { + $writer->writeAttribute('fo:font-style', 'italic'); + } + + if ($color = $font->getColor()) { + $writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB())); + } + + if ($family = $font->getName()) { + $writer->writeAttribute('fo:font-family', $family); + } + + if ($size = $font->getSize()) { + $writer->writeAttribute('fo:font-size', sprintf('%.1fpt', $size)); + } + + if ($font->getUnderline() && $font->getUnderline() != Font::UNDERLINE_NONE) { + $writer->writeAttribute('style:text-underline-style', 'solid'); + $writer->writeAttribute('style:text-underline-width', 'auto'); + $writer->writeAttribute('style:text-underline-color', 'font-color'); + + switch ($font->getUnderline()) { + case Font::UNDERLINE_DOUBLE: + $writer->writeAttribute('style:text-underline-type', 'double'); + break; + case Font::UNDERLINE_SINGLE: + $writer->writeAttribute('style:text-underline-type', 'single'); + break; + } + } + + $writer->endElement(); // Close style:text-properties + + /* + * style:table-cell-properties + */ + + $writer->startElement('style:table-cell-properties'); + $writer->writeAttribute('style:rotation-align', 'none'); + + // Fill + if ($fill = $style->getFill()) { + switch ($fill->getFillType()) { + case Fill::FILL_SOLID: + $writer->writeAttribute('fo:background-color', sprintf( + '#%s', + strtolower($fill->getStartColor()->getRGB()) + )); + break; + case Fill::FILL_GRADIENT_LINEAR: + case Fill::FILL_GRADIENT_PATH: + /// TODO :: To be implemented + break; + case Fill::FILL_NONE: + default: + } + } + + $writer->endElement(); // Close style:table-cell-properties + + /* + * End + */ + + $writer->endElement(); // Close style:style + } + } } diff --git a/src/PhpSpreadsheet/Writer/Ods/WriterPart.php b/src/PhpSpreadsheet/Writer/Ods/WriterPart.php index dee7532c..b07b38cb 100644 --- a/src/PhpSpreadsheet/Writer/Ods/WriterPart.php +++ b/src/PhpSpreadsheet/Writer/Ods/WriterPart.php @@ -24,6 +24,34 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Ods; * @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL */ -abstract class WriterPart extends \PhpOffice\PhpSpreadsheet\Writer\Xlsx\WriterPart +abstract class WriterPart { + /** + * Parent Ods object. + * + * @var \PhpOffice\PhpSpreadsheet\Writer\Ods + */ + private $parentWriter; + + /** + * Get Ods writer. + * + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * + * @return \PhpOffice\PhpSpreadsheet\Writer\Ods + */ + public function getParentWriter() + { + return $this->parentWriter; + } + + /** + * Set parent Ods writer. + * + * @param \PhpOffice\PhpSpreadsheet\Writer\Ods $writer + */ + public function __construct(\PhpOffice\PhpSpreadsheet\Writer\Ods $writer) + { + $this->parentWriter = $writer; + } } diff --git a/src/PhpSpreadsheet/Writer/Pdf.php b/src/PhpSpreadsheet/Writer/Pdf.php index 2bb20fc7..eeec2334 100644 --- a/src/PhpSpreadsheet/Writer/Pdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf.php @@ -73,7 +73,7 @@ class Pdf implements IWriter /** * {@inheritdoc} */ - public function save($pFilename = null) + public function save($pFilename) { $this->renderer->save($pFilename); } diff --git a/src/PhpSpreadsheet/Writer/Pdf/Core.php b/src/PhpSpreadsheet/Writer/Pdf/Core.php index 6e429817..084b514c 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Core.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Core.php @@ -186,11 +186,11 @@ abstract class Core extends \PhpOffice\PhpSpreadsheet\Writer\Html /** * Set Paper Size. * - * @param string $pValue Paper size + * @param string $pValue Paper size see \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::PAPERSIZE_* * * @return self */ - public function setPaperSize($pValue = \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::PAPERSIZE_LETTER) + public function setPaperSize($pValue) { $this->paperSize = $pValue; @@ -210,11 +210,11 @@ abstract class Core extends \PhpOffice\PhpSpreadsheet\Writer\Html /** * Set Orientation. * - * @param string $pValue Page orientation + * @param string $pValue Page orientation see \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_* * * @return self */ - public function setOrientation($pValue = \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_DEFAULT) + public function setOrientation($pValue) { $this->orientation = $pValue; @@ -240,7 +240,7 @@ abstract class Core extends \PhpOffice\PhpSpreadsheet\Writer\Html * * @return self */ - public function setTempDir($pValue = '') + public function setTempDir($pValue) { if (is_dir($pValue)) { $this->tempDir = $pValue; @@ -258,7 +258,7 @@ abstract class Core extends \PhpOffice\PhpSpreadsheet\Writer\Html * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - protected function prepareForSave($pFilename = null) + protected function prepareForSave($pFilename) { // garbage collect $this->spreadsheet->garbageCollect(); diff --git a/src/PhpSpreadsheet/Writer/Pdf/DomPDF.php b/src/PhpSpreadsheet/Writer/Pdf/DomPDF.php index 2fa219c8..2ddba79e 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/DomPDF.php +++ b/src/PhpSpreadsheet/Writer/Pdf/DomPDF.php @@ -33,7 +33,7 @@ class DomPDF extends Core implements \PhpOffice\PhpSpreadsheet\Writer\IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null) + public function save($pFilename) { $fileHandle = parent::prepareForSave($pFilename); diff --git a/src/PhpSpreadsheet/Writer/Pdf/MPDF.php b/src/PhpSpreadsheet/Writer/Pdf/MPDF.php index 14feb1ff..06cb83e1 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/MPDF.php +++ b/src/PhpSpreadsheet/Writer/Pdf/MPDF.php @@ -34,7 +34,7 @@ class MPDF extends Core implements \PhpOffice\PhpSpreadsheet\Writer\IWriter * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception * @throws \PhpOffice\PhpSpreadsheet\Exception */ - public function save($pFilename = null) + public function save($pFilename) { $fileHandle = parent::prepareForSave($pFilename); diff --git a/src/PhpSpreadsheet/Writer/Pdf/TcPDF.php b/src/PhpSpreadsheet/Writer/Pdf/TcPDF.php index 678a5591..858ff27e 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/TcPDF.php +++ b/src/PhpSpreadsheet/Writer/Pdf/TcPDF.php @@ -33,7 +33,7 @@ class TcPDF extends Core implements \PhpOffice\PhpSpreadsheet\Writer\IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null) + public function save($pFilename) { $fileHandle = parent::prepareForSave($pFilename); diff --git a/src/PhpSpreadsheet/Writer/Xls.php b/src/PhpSpreadsheet/Writer/Xls.php index b7845ae3..30698647 100644 --- a/src/PhpSpreadsheet/Writer/Xls.php +++ b/src/PhpSpreadsheet/Writer/Xls.php @@ -108,7 +108,7 @@ class Xls extends BaseWriter implements IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null) + public function save($pFilename) { // garbage collect $this->spreadsheet->garbageCollect(); @@ -148,8 +148,8 @@ class Xls extends BaseWriter implements IWriter // add fonts from rich text eleemnts for ($i = 0; $i < $countSheets; ++$i) { - foreach ($this->writerWorksheets[$i]->phpSheet->getCellCollection() as $cellID) { - $cell = $this->writerWorksheets[$i]->phpSheet->getCell($cellID); + foreach ($this->writerWorksheets[$i]->phpSheet->getCoordinates() as $coordinate) { + $cell = $this->writerWorksheets[$i]->phpSheet->getCell($coordinate); $cVal = $cell->getValue(); if ($cVal instanceof \PhpOffice\PhpSpreadsheet\RichText) { $elements = $cVal->getRichTextElements(); diff --git a/src/PhpSpreadsheet/Writer/Xls/Font.php b/src/PhpSpreadsheet/Writer/Xls/Font.php index 5b4ad203..ba5c51bd 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Font.php +++ b/src/PhpSpreadsheet/Writer/Xls/Font.php @@ -45,7 +45,7 @@ class Font * * @param \PhpOffice\PhpSpreadsheet\Style\Font $font */ - public function __construct(\PhpOffice\PhpSpreadsheet\Style\Font $font = null) + public function __construct(\PhpOffice\PhpSpreadsheet\Style\Font $font) { $this->colorIndex = 0x7FFF; $this->font = $font; diff --git a/src/PhpSpreadsheet/Writer/Xls/Workbook.php b/src/PhpSpreadsheet/Writer/Xls/Workbook.php index 38ac7b30..2f2056e1 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xls/Workbook.php @@ -422,7 +422,7 @@ class Workbook extends BIFFwriter * * @return string Binary data for workbook stream */ - public function writeWorkbook($pWorksheetSizes = null) + public function writeWorkbook(array $pWorksheetSizes) { $this->worksheetSizes = $pWorksheetSizes; diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 7b050290..464df8c3 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -287,8 +287,6 @@ class Worksheet extends BIFFwriter { $phpSheet = $this->phpSheet; - $num_sheets = $phpSheet->getParent()->getSheetCount(); - // Write BOF record $this->storeBof(0x0010); @@ -400,15 +398,14 @@ class Worksheet extends BIFFwriter } // Write Cells - foreach ($phpSheet->getCellCollection() as $cellID) { - $cell = $phpSheet->getCell($cellID); + foreach ($phpSheet->getCoordinates() as $coordinate) { + $cell = $phpSheet->getCell($coordinate); $row = $cell->getRow() - 1; $column = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($cell->getColumn()) - 1; - // Don't break Excel! -// if ($row + 1 > 65536 or $column + 1 > 256) { + // Don't break Excel break the code! if ($row > 65535 || $column > 255) { - break; + throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Rows or columns overflow! Excel5 has limit to 65535 rows and 255 columns. Use XLSX instead.'); } // Write cell value @@ -542,7 +539,7 @@ class Worksheet extends BIFFwriter * * @return string Binary data */ - private function writeBIFF8CellRangeAddressFixed($range = 'A1') + private function writeBIFF8CellRangeAddressFixed($range) { $explodes = explode(':', $range); @@ -2837,10 +2834,11 @@ class Worksheet extends BIFFwriter $type = 0x07; break; } + $options |= $type << 0; // error style - $errorStyle = $dataValidation->getType(); + $errorStyle = $dataValidation->getErrorStyle(); switch ($errorStyle) { case \PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_STOP: $errorStyle = 0x00; @@ -2852,6 +2850,7 @@ class Worksheet extends BIFFwriter $errorStyle = 0x02; break; } + $options |= $errorStyle << 4; // explicit formula? @@ -2899,6 +2898,7 @@ class Worksheet extends BIFFwriter $operator = 0x07; break; } + $options |= $operator << 20; $data = pack('V', $options); diff --git a/src/PhpSpreadsheet/Writer/Xls/Xf.php b/src/PhpSpreadsheet/Writer/Xls/Xf.php index 3b5af382..23f76f6a 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Xf.php +++ b/src/PhpSpreadsheet/Writer/Xls/Xf.php @@ -136,7 +136,7 @@ class Xf * * @param \PhpOffice\PhpSpreadsheet\Style The XF format */ - public function __construct(\PhpOffice\PhpSpreadsheet\Style $style = null) + public function __construct(\PhpOffice\PhpSpreadsheet\Style $style) { $this->isStyleXf = false; $this->fontIndex = 0; diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index ed383ce2..56f1c508 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use ZipArchive; /** * Copyright (c) 2006 - 2015 PhpSpreadsheet. @@ -110,7 +111,7 @@ class Xlsx extends BaseWriter implements IWriter * * @param \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet */ - public function __construct(\PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet = null) + public function __construct(\PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet) { // Assign PhpSpreadsheet $this->setSpreadsheet($spreadsheet); @@ -155,7 +156,7 @@ class Xlsx extends BaseWriter implements IWriter * * @return \PhpOffice\PhpSpreadsheet\Writer\Xlsx\WriterPart */ - public function getWriterPart($pPartName = '') + public function getWriterPart($pPartName) { if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) { return $this->writerParts[strtolower($pPartName)]; @@ -171,7 +172,7 @@ class Xlsx extends BaseWriter implements IWriter * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename = null) + public function save($pFilename) { if ($this->spreadSheet !== null) { // garbage collect @@ -208,92 +209,83 @@ class Xlsx extends BaseWriter implements IWriter // Create drawing dictionary $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet)); - // Create new ZIP file and open it for writing - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - /** @var \ZipArchive $objZip */ - $objZip = new $zipClass(); - - // Retrieve OVERWRITE and CREATE constants from the instantiated zip class - // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP - $ro = new \ReflectionObject($objZip); - $zipOverWrite = $ro->getConstant('OVERWRITE'); - $zipCreate = $ro->getConstant('CREATE'); + $zip = new ZipArchive(); if (file_exists($pFilename)) { unlink($pFilename); } // Try opening the ZIP file - if ($objZip->open($pFilename, $zipOverWrite) !== true) { - if ($objZip->open($pFilename, $zipCreate) !== true) { + if ($zip->open($pFilename, ZipArchive::OVERWRITE) !== true) { + if ($zip->open($pFilename, ZipArchive::CREATE) !== true) { throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Could not open ' . $pFilename . ' for writing.'); } } // Add [Content_Types].xml to ZIP file - $objZip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts)); + $zip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts)); //if hasMacros, add the vbaProject.bin file, Certificate file(if exists) if ($this->spreadSheet->hasMacros()) { $macrosCode = $this->spreadSheet->getMacrosCode(); if (!is_null($macrosCode)) { // we have the code ? - $objZip->addFromString('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin + $zip->addFromString('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin if ($this->spreadSheet->hasMacrosCertificate()) { //signed macros ? // Yes : add the certificate file and the related rels file - $objZip->addFromString('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate()); - $objZip->addFromString('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet)); + $zip->addFromString('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate()); + $zip->addFromString('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet)); } } } //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels) if ($this->spreadSheet->hasRibbon()) { $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target'); - $objZip->addFromString($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data')); + $zip->addFromString($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data')); if ($this->spreadSheet->hasRibbonBinObjects()) { $tmpRootPath = dirname($tmpRibbonTarget) . '/'; $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write foreach ($ribbonBinObjects as $aPath => $aContent) { - $objZip->addFromString($tmpRootPath . $aPath, $aContent); + $zip->addFromString($tmpRootPath . $aPath, $aContent); } //the rels for files - $objZip->addFromString($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet)); + $zip->addFromString($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet)); } } // Add relationships to ZIP file - $objZip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet)); - $objZip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet)); + $zip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet)); + $zip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet)); // Add document properties to ZIP file - $objZip->addFromString('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet)); - $objZip->addFromString('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet)); + $zip->addFromString('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet)); + $zip->addFromString('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet)); $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet); if ($customPropertiesPart !== null) { - $objZip->addFromString('docProps/custom.xml', $customPropertiesPart); + $zip->addFromString('docProps/custom.xml', $customPropertiesPart); } // Add theme to ZIP file - $objZip->addFromString('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet)); + $zip->addFromString('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet)); // Add string table to ZIP file - $objZip->addFromString('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable)); + $zip->addFromString('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable)); // Add styles to ZIP file - $objZip->addFromString('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet)); + $zip->addFromString('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet)); // Add workbook to ZIP file - $objZip->addFromString('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas)); + $zip->addFromString('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas)); $chartCount = 0; // Add worksheets for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { - $objZip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts)); + $zip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts)); if ($this->includeCharts) { $charts = $this->spreadSheet->getSheet($i)->getChartCollection(); if (count($charts) > 0) { foreach ($charts as $chart) { - $objZip->addFromString('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas)); + $zip->addFromString('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas)); ++$chartCount; } } @@ -304,7 +296,7 @@ class Xlsx extends BaseWriter implements IWriter // Add worksheet relationships (drawings, ...) for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { // Add relationships - $objZip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts)); + $zip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts)); $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection(); $drawingCount = count($drawings); @@ -315,32 +307,32 @@ class Xlsx extends BaseWriter implements IWriter // Add drawing and image relationship parts if (($drawingCount > 0) || ($chartCount > 0)) { // Drawing relationships - $objZip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts)); + $zip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts)); // Drawings - $objZip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $chartRef2, $this->includeCharts)); + $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $chartRef2, $this->includeCharts)); } // Add comment relationship parts if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) { // VML Comments - $objZip->addFromString('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i))); + $zip->addFromString('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i))); // Comments - $objZip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i))); + $zip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i))); } // Add header/footer relationship parts if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) { // VML Drawings - $objZip->addFromString('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i))); + $zip->addFromString('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i))); // VML Drawing relationships - $objZip->addFromString('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i))); + $zip->addFromString('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i))); // Media foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) { - $objZip->addFromString('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath())); + $zip->addFromString('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath())); } } } @@ -354,8 +346,7 @@ class Xlsx extends BaseWriter implements IWriter $imagePath = substr($imagePath, 6); $imagePathSplitted = explode('#', $imagePath); - $zipClass = \PhpOffice\PhpSpreadsheet\Settings::getZipClass(); - $imageZip = new $zipClass(); + $imageZip = new ZipArchive(); $imageZip->open($imagePathSplitted[0]); $imageContents = $imageZip->getFromName($imagePathSplitted[1]); $imageZip->close(); @@ -364,7 +355,7 @@ class Xlsx extends BaseWriter implements IWriter $imageContents = file_get_contents($imagePath); } - $objZip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); + $zip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing) { ob_start(); call_user_func( @@ -374,7 +365,7 @@ class Xlsx extends BaseWriter implements IWriter $imageContents = ob_get_contents(); ob_end_clean(); - $objZip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); + $zip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); } } @@ -382,7 +373,7 @@ class Xlsx extends BaseWriter implements IWriter \PhpOffice\PhpSpreadsheet\Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); // Close file - if ($objZip->close() === false) { + if ($zip->close() === false) { throw new \PhpOffice\PhpSpreadsheet\Writer\Exception("Could not close zip file $pFilename."); } @@ -420,7 +411,7 @@ class Xlsx extends BaseWriter implements IWriter * * @return Xlsx */ - public function setSpreadsheet(Spreadsheet $spreadsheet = null) + public function setSpreadsheet(Spreadsheet $spreadsheet) { $this->spreadSheet = $spreadsheet; @@ -524,7 +515,7 @@ class Xlsx extends BaseWriter implements IWriter * * @return Xlsx */ - public function setOffice2003Compatibility($pValue = false) + public function setOffice2003Compatibility($pValue) { $this->office2003compatibility = $pValue; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index b6396f3f..418688f7 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -47,7 +47,7 @@ class Chart extends WriterPart * * @return string XML Output */ - public function writeChart(\PhpOffice\PhpSpreadsheet\Chart $pChart = null, $calculateCellValues = true) + public function writeChart(\PhpOffice\PhpSpreadsheet\Chart $pChart, $calculateCellValues = true) { $this->calculateCellValues = $calculateCellValues; @@ -358,7 +358,7 @@ class Chart extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDataLabels($objWriter, Layout $chartLayout = null) + private function writeDataLabels(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, Layout $chartLayout = null) { $objWriter->startElement('c:dLbls'); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Comments.php b/src/PhpSpreadsheet/Writer/Xlsx/Comments.php index 72dba7ea..e630b797 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Comments.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Comments.php @@ -35,7 +35,7 @@ class Comments extends WriterPart * * @return string XML Output */ - public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet = null) + public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet) { // Create XML writer $objWriter = null; @@ -94,7 +94,7 @@ class Comments extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeComment(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pCellReference = 'A1', \PhpOffice\PhpSpreadsheet\Comment $pComment = null, $pAuthors = null) + private function writeComment(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pCellReference, \PhpOffice\PhpSpreadsheet\Comment $pComment, array $pAuthors) { // comment $objWriter->startElement('comment'); @@ -118,7 +118,7 @@ class Comments extends WriterPart * * @return string XML Output */ - public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet = null) + public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet) { // Create XML writer $objWriter = null; @@ -187,12 +187,12 @@ class Comments extends WriterPart * Write VML comment to XML format. * * @param \PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter XML Writer - * @param string $pCellReference Cell reference + * @param string $pCellReference Cell reference, eg: 'A1' * @param \PhpOffice\PhpSpreadsheet\Comment $pComment Comment * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeVMLComment(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pCellReference = 'A1', \PhpOffice\PhpSpreadsheet\Comment $pComment = null) + private function writeVMLComment(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pCellReference, \PhpOffice\PhpSpreadsheet\Comment $pComment) { // Metadata list($column, $row) = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($pCellReference); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php index ca6211f8..bf01e978 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php @@ -36,7 +36,7 @@ class ContentTypes extends WriterPart * * @return string XML Output */ - public function writeContentTypes(\PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet = null, $includeCharts = false) + public function writeContentTypes(\PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet, $includeCharts = false) { // Create XML writer $objWriter = null; @@ -165,8 +165,8 @@ class ContentTypes extends WriterPart } $sheetCount = $spreadsheet->getSheetCount(); for ($i = 0; $i < $sheetCount; ++$i) { - if (count($spreadsheet->getSheet()->getHeaderFooter()->getImages()) > 0) { - foreach ($spreadsheet->getSheet()->getHeaderFooter()->getImages() as $image) { + if (count($spreadsheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) { + foreach ($spreadsheet->getSheet($i)->getHeaderFooter()->getImages() as $image) { if (!isset($aMediaContentTypes[strtolower($image->getExtension())])) { $aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath()); @@ -191,7 +191,7 @@ class ContentTypes extends WriterPart * * @return string Mime Type */ - private function getImageMimeType($pFile = '') + private function getImageMimeType($pFile) { if (\PhpOffice\PhpSpreadsheet\Shared\File::fileExists($pFile)) { $image = getimagesize($pFile); @@ -210,7 +210,7 @@ class ContentTypes extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDefaultContentType(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pPartname = '', $pContentType = '') + private function writeDefaultContentType(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pPartname, $pContentType) { if ($pPartname != '' && $pContentType != '') { // Write content type @@ -232,7 +232,7 @@ class ContentTypes extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeOverrideContentType(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pPartname = '', $pContentType = '') + private function writeOverrideContentType(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pPartname, $pContentType) { if ($pPartname != '' && $pContentType != '') { // Write content type diff --git a/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php b/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php index 3cc78cbe..7eb9a06e 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php @@ -35,7 +35,7 @@ class DocProps extends WriterPart * * @return string XML Output */ - public function writeDocPropsApp(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeDocPropsApp(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; @@ -134,7 +134,7 @@ class DocProps extends WriterPart * * @return string XML Output */ - public function writeDocPropsCore(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeDocPropsCore(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; @@ -203,7 +203,7 @@ class DocProps extends WriterPart * * @return string XML Output */ - public function writeDocPropsCustom(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeDocPropsCustom(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { $customPropertyList = $spreadsheet->getProperties()->getCustomProperties(); if (empty($customPropertyList)) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php b/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php index e4221ec4..afd27f30 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php @@ -90,7 +90,7 @@ class Drawing extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function writeChart(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Chart $pChart = null, $pRelationId = -1) + public function writeChart(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart $pChart, $pRelationId = -1) { $tl = $pChart->getTopLeftPosition(); $tl['colRow'] = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($tl['cell']); @@ -163,7 +163,7 @@ class Drawing extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function writeDrawing(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing $pDrawing = null, $pRelationId = -1) + public function writeDrawing(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing $pDrawing, $pRelationId = -1) { if ($pRelationId >= 0) { // xdr:oneCellAnchor @@ -271,54 +271,6 @@ class Drawing extends WriterPart $objWriter->endElement(); } -/* - - // a:scene3d - $objWriter->startElement('a:scene3d'); - - // a:camera - $objWriter->startElement('a:camera'); - $objWriter->writeAttribute('prst', 'orthographicFront'); - $objWriter->endElement(); - - // a:lightRig - $objWriter->startElement('a:lightRig'); - $objWriter->writeAttribute('rig', 'twoPt'); - $objWriter->writeAttribute('dir', 't'); - - // a:rot - $objWriter->startElement('a:rot'); - $objWriter->writeAttribute('lat', '0'); - $objWriter->writeAttribute('lon', '0'); - $objWriter->writeAttribute('rev', '0'); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); -*/ -/* - // a:sp3d - $objWriter->startElement('a:sp3d'); - - // a:bevelT - $objWriter->startElement('a:bevelT'); - $objWriter->writeAttribute('w', '25400'); - $objWriter->writeAttribute('h', '19050'); - $objWriter->endElement(); - - // a:contourClr - $objWriter->startElement('a:contourClr'); - - // a:srgbClr - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', 'FFFFFF'); - $objWriter->endElement(); - - $objWriter->endElement(); - - $objWriter->endElement(); -*/ $objWriter->endElement(); $objWriter->endElement(); @@ -341,7 +293,7 @@ class Drawing extends WriterPart * * @return string XML Output */ - public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet = null) + public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet) { // Create XML writer $objWriter = null; @@ -490,7 +442,7 @@ class Drawing extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeVMLHeaderFooterImage(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pReference = '', \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing $pImage = null) + private function writeVMLHeaderFooterImage(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pReference, \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing $pImage) { // Calculate object id preg_match('{(\d+)}', md5($pReference), $m); @@ -533,7 +485,7 @@ class Drawing extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Worksheet\Drawing[] All drawings in PhpSpreadsheet */ - public function allDrawings(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allDrawings(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Get an array of all drawings $aDrawings = []; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php index 842eec07..2d4ceef5 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php @@ -35,7 +35,7 @@ class Rels extends WriterPart * * @return string XML Output */ - public function writeRelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeRelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; @@ -110,7 +110,7 @@ class Rels extends WriterPart * * @return string XML Output */ - public function writeWorkbookRelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeWorkbookRelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; @@ -193,7 +193,7 @@ class Rels extends WriterPart * * @return string XML Output */ - public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet = null, $pWorksheetId = 1, $includeCharts = false) + public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet, $pWorksheetId = 1, $includeCharts = false) { // Create XML writer $objWriter = null; @@ -353,7 +353,7 @@ class Rels extends WriterPart * * @return string XML Output */ - public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet = null) + public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet $pWorksheet) { // Create XML writer $objWriter = null; @@ -397,7 +397,7 @@ class Rels extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeRelationship(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '') + private function writeRelationship(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pId, $pType, $pTarget, $pTargetMode = '') { if ($pType != '' && $pTarget != '') { // Write relationship diff --git a/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php b/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php index d89a3dcd..da414a2f 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php @@ -35,7 +35,7 @@ class RelsRibbon extends WriterPart * * @return string XML Output */ - public function writeRibbonRelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeRibbonRelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php b/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php index b25e6705..dbf4dfed 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php @@ -35,7 +35,7 @@ class RelsVBA extends WriterPart * * @return string XML Output */ - public function writeVBARelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeVBARelationships(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php index fc780307..4232445a 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -53,8 +53,8 @@ class StringTable extends WriterPart $aFlippedStringTable = $this->flipStringTable($aStringTable); // Loop through cells - foreach ($pSheet->getCellCollection() as $cellID) { - $cell = $pSheet->getCell($cellID); + foreach ($pSheet->getCoordinates() as $coordinate) { + $cell = $pSheet->getCell($coordinate); $cellValue = $cell->getValue(); if (!is_object($cellValue) && ($cellValue !== null) && @@ -85,49 +85,46 @@ class StringTable extends WriterPart * * @return string XML Output */ - public function writeStringTable($pStringTable = null) + public function writeStringTable(array $pStringTable) { - if ($pStringTable !== null) { - // Create XML writer - $objWriter = null; - if ($this->getParentWriter()->getUseDiskCaching()) { - $objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); - } else { - $objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_MEMORY); - } + // Create XML writer + $objWriter = null; + if ($this->getParentWriter()->getUseDiskCaching()) { + $objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter::STORAGE_MEMORY); + } - // XML header - $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); - // String table - $objWriter->startElement('sst'); - $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); - $objWriter->writeAttribute('uniqueCount', count($pStringTable)); + // String table + $objWriter->startElement('sst'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + $objWriter->writeAttribute('uniqueCount', count($pStringTable)); - // Loop through string table - foreach ($pStringTable as $textElement) { - $objWriter->startElement('si'); + // Loop through string table + foreach ($pStringTable as $textElement) { + $objWriter->startElement('si'); - if (!$textElement instanceof \PhpOffice\PhpSpreadsheet\RichText) { - $textToWrite = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::controlCharacterPHP2OOXML($textElement); - $objWriter->startElement('t'); - if ($textToWrite !== trim($textToWrite)) { - $objWriter->writeAttribute('xml:space', 'preserve'); - } - $objWriter->writeRawData($textToWrite); - $objWriter->endElement(); - } elseif ($textElement instanceof \PhpOffice\PhpSpreadsheet\RichText) { - $this->writeRichText($objWriter, $textElement); + if (!$textElement instanceof \PhpOffice\PhpSpreadsheet\RichText) { + $textToWrite = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::controlCharacterPHP2OOXML($textElement); + $objWriter->startElement('t'); + if ($textToWrite !== trim($textToWrite)) { + $objWriter->writeAttribute('xml:space', 'preserve'); } - + $objWriter->writeRawData($textToWrite); $objWriter->endElement(); + } elseif ($textElement instanceof \PhpOffice\PhpSpreadsheet\RichText) { + $this->writeRichText($objWriter, $textElement); } $objWriter->endElement(); - - return $objWriter->getData(); } - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Invalid string table array passed.'); + + $objWriter->endElement(); + + return $objWriter->getData(); } /** @@ -139,7 +136,7 @@ class StringTable extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function writeRichText(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\RichText $pRichText = null, $prefix = null) + public function writeRichText(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\RichText $pRichText, $prefix = null) { if ($prefix !== null) { $prefix .= ':'; @@ -224,7 +221,7 @@ class StringTable extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function writeRichTextForCharts(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pRichText = null, $prefix = null) + public function writeRichTextForCharts(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pRichText = null, $prefix = null) { if (!$pRichText instanceof \PhpOffice\PhpSpreadsheet\RichText) { $textRun = $pRichText; @@ -286,7 +283,7 @@ class StringTable extends WriterPart * * @return array */ - public function flipStringTable($stringTable = []) + public function flipStringTable(array $stringTable) { // Return value $returnValue = []; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Style.php b/src/PhpSpreadsheet/Writer/Xlsx/Style.php index 3be4b76c..96457aa1 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Style.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Style.php @@ -35,7 +35,7 @@ class Style extends WriterPart * * @return string XML Output */ - public function writeStyles(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeStyles(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; @@ -166,7 +166,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeFill(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style\Fill $pFill = null) + private function writeFill(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Fill $pFill) { // Check if this is a pattern type or gradient type if ($pFill->getFillType() === \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_GRADIENT_LINEAR || @@ -187,7 +187,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeGradientFill(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style\Fill $pFill = null) + private function writeGradientFill(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Fill $pFill) { // fill $objWriter->startElement('fill'); @@ -232,7 +232,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writePatternFill(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style\Fill $pFill = null) + private function writePatternFill(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Fill $pFill) { // fill $objWriter->startElement('fill'); @@ -271,7 +271,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeFont(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style\Font $pFont = null) + private function writeFont(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Font $pFont) { // font $objWriter->startElement('font'); @@ -352,7 +352,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeBorder(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style\Borders $pBorders = null) + private function writeBorder(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Borders $pBorders) { // Write border $objWriter->startElement('border'); @@ -390,7 +390,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeCellStyleXf(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style $pStyle = null, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + private function writeCellStyleXf(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style $pStyle, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // xf $objWriter->startElement('xf'); @@ -466,7 +466,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeCellStyleDxf(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style $pStyle = null) + private function writeCellStyleDxf(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style $pStyle) { // dxf $objWriter->startElement('dxf'); @@ -532,7 +532,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeBorderPr(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, $pName = 'left', \PhpOffice\PhpSpreadsheet\Style\Border $pBorder = null) + private function writeBorderPr(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pName, \PhpOffice\PhpSpreadsheet\Style\Border $pBorder) { // Write BorderPr if ($pBorder->getBorderStyle() != \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_NONE) { @@ -557,7 +557,7 @@ class Style extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeNumFmt(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Style\NumberFormat $pNumberFormat = null, $pId = 0) + private function writeNumFmt(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\NumberFormat $pNumberFormat, $pId = 0) { // Translate formatcode $formatCode = $pNumberFormat->getFormatCode(); @@ -580,7 +580,7 @@ class Style extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Style[] All styles in PhpSpreadsheet */ - public function allStyles(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allStyles(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { return $spreadsheet->getCellXfCollection(); } @@ -594,7 +594,7 @@ class Style extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Style\Conditional[] All conditional styles in PhpSpreadsheet */ - public function allConditionalStyles(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allConditionalStyles(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Get an array of all styles $aStyles = []; @@ -620,7 +620,7 @@ class Style extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Style\Fill[] All fills in PhpSpreadsheet */ - public function allFills(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allFills(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Get an array of unique fills $aFills = []; @@ -654,7 +654,7 @@ class Style extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Style\Font[] All fonts in PhpSpreadsheet */ - public function allFonts(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allFonts(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Get an array of unique fonts $aFonts = []; @@ -679,7 +679,7 @@ class Style extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Style\Borders[] All borders in PhpSpreadsheet */ - public function allBorders(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allBorders(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Get an array of unique borders $aBorders = []; @@ -704,7 +704,7 @@ class Style extends WriterPart * * @return \PhpOffice\PhpSpreadsheet\Style\NumberFormat[] All number formats in PhpSpreadsheet */ - public function allNumberFormats(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function allNumberFormats(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Get an array of unique number formats $aNumFmts = []; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Theme.php b/src/PhpSpreadsheet/Writer/Xlsx/Theme.php index 89ec67e6..4dde8153 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Theme.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Theme.php @@ -137,7 +137,7 @@ class Theme extends WriterPart * * @return string XML Output */ - public function writeTheme(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + public function writeTheme(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Create XML writer $objWriter = null; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php index 133d8585..16f9f73b 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php @@ -36,7 +36,7 @@ class Workbook extends WriterPart * * @return string XML Output */ - public function writeWorkbook(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null, $recalcRequired = false) + public function writeWorkbook(\PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet, $recalcRequired = false) { // Create XML writer if ($this->getParentWriter()->getUseDiskCaching()) { @@ -128,7 +128,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeBookViews(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + private function writeBookViews(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // bookViews $objWriter->startElement('bookViews'); @@ -159,7 +159,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeWorkbookProtection(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + private function writeWorkbookProtection(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { if ($spreadsheet->getSecurity()->isSecurityEnabled()) { $objWriter->startElement('workbookProtection'); @@ -211,7 +211,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheets(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + private function writeSheets(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Write sheets $objWriter->startElement('sheets'); @@ -241,7 +241,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheet(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pSheetname = '', $pSheetId = 1, $pRelId = 1, $sheetState = 'visible') + private function writeSheet(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible') { if ($pSheetname != '') { // Write sheet @@ -266,7 +266,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDefinedNames(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet = null) + private function writeDefinedNames(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\SpreadSheet $spreadsheet) { // Write defined names $objWriter->startElement('definedNames'); @@ -351,7 +351,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDefinedNameForAutofilter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null, $pSheetId = 0) + private function writeDefinedNameForAutofilter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet, $pSheetId = 0) { // definedName for autoFilter $autoFilterRange = $pSheet->getAutoFilter()->getRange(); @@ -388,7 +388,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDefinedNameForPrintTitles(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null, $pSheetId = 0) + private function writeDefinedNameForPrintTitles(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet, $pSheetId = 0) { // definedName for PrintTitles if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { @@ -432,7 +432,7 @@ class Workbook extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDefinedNameForPrintArea(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null, $pSheetId = 0) + private function writeDefinedNameForPrintArea(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet, $pSheetId = 0) { // definedName for PrintArea if ($pSheet->getPageSetup()->isPrintAreaSet()) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 4bea3a71..e8d3b715 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -144,7 +144,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheetPr(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeSheetPr(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // sheetPr $objWriter->startElement('sheetPr'); @@ -192,7 +192,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDimension(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeDimension(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // dimension $objWriter->startElement('dimension'); @@ -208,7 +208,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheetViews(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeSheetViews(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // sheetViews $objWriter->startElement('sheetViews'); @@ -321,7 +321,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheetFormatPr(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeSheetFormatPr(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // sheetFormatPr $objWriter->startElement('sheetFormatPr'); @@ -374,7 +374,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeCols(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeCols(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // cols if (count($pSheet->getColumnDimensions()) > 0) { @@ -440,7 +440,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheetProtection(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeSheetProtection(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // sheetProtection $objWriter->startElement('sheetProtection'); @@ -476,7 +476,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeConditionalFormatting(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeConditionalFormatting(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // Conditional id $id = 1; @@ -550,7 +550,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeDataValidations(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeDataValidations(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // Datavalidation collection $dataValidationCollection = $pSheet->getDataValidationCollection(); @@ -617,7 +617,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeHyperlinks(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeHyperlinks(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // Hyperlink collection $hyperlinkCollection = $pSheet->getHyperlinkCollection(); @@ -659,7 +659,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeProtectedRanges(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeProtectedRanges(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { if (count($pSheet->getProtectedCells()) > 0) { // protectedRanges @@ -689,7 +689,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeMergeCells(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeMergeCells(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { if (count($pSheet->getMergeCells()) > 0) { // mergeCells @@ -715,7 +715,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writePrintOptions(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writePrintOptions(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // printOptions $objWriter->startElement('printOptions'); @@ -742,7 +742,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writePageMargins(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writePageMargins(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // pageMargins $objWriter->startElement('pageMargins'); @@ -763,7 +763,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeAutoFilter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeAutoFilter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { $autoFilterRange = $pSheet->getAutoFilter()->getRange(); if (!empty($autoFilterRange)) { @@ -857,7 +857,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writePageSetup(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writePageSetup(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // pageSetup $objWriter->startElement('pageSetup'); @@ -893,7 +893,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeHeaderFooter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeHeaderFooter(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // headerFooter $objWriter->startElement('headerFooter'); @@ -919,7 +919,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeBreaks(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeBreaks(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // Get row and column breaks $aRowBreaks = []; @@ -978,86 +978,82 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeSheetData(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null, $pStringTable = null) + private function writeSheetData(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet, array $pStringTable) { - if (is_array($pStringTable)) { - // Flipped stringtable, for faster index searching - $aFlippedStringTable = $this->getParentWriter()->getWriterPart('stringtable')->flipStringTable($pStringTable); + // Flipped stringtable, for faster index searching + $aFlippedStringTable = $this->getParentWriter()->getWriterPart('stringtable')->flipStringTable($pStringTable); - // sheetData - $objWriter->startElement('sheetData'); + // sheetData + $objWriter->startElement('sheetData'); - // Get column count - $colCount = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($pSheet->getHighestColumn()); + // Get column count + $colCount = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($pSheet->getHighestColumn()); - // Highest row number - $highestRow = $pSheet->getHighestRow(); + // Highest row number + $highestRow = $pSheet->getHighestRow(); - // Loop through cells - $cellsByRow = []; - foreach ($pSheet->getCellCollection() as $cellID) { - $cellAddress = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($cellID); - $cellsByRow[$cellAddress[1]][] = $cellID; - } - - $currentRow = 0; - while ($currentRow++ < $highestRow) { - // Get row dimension - $rowDimension = $pSheet->getRowDimension($currentRow); - - // Write current row? - $writeCurrentRow = isset($cellsByRow[$currentRow]) || $rowDimension->getRowHeight() >= 0 || $rowDimension->getVisible() == false || $rowDimension->getCollapsed() == true || $rowDimension->getOutlineLevel() > 0 || $rowDimension->getXfIndex() !== null; - - if ($writeCurrentRow) { - // Start a new row - $objWriter->startElement('row'); - $objWriter->writeAttribute('r', $currentRow); - $objWriter->writeAttribute('spans', '1:' . $colCount); - - // Row dimensions - if ($rowDimension->getRowHeight() >= 0) { - $objWriter->writeAttribute('customHeight', '1'); - $objWriter->writeAttribute('ht', \PhpOffice\PhpSpreadsheet\Shared\StringHelper::formatNumber($rowDimension->getRowHeight())); - } - - // Row visibility - if ($rowDimension->getVisible() == false) { - $objWriter->writeAttribute('hidden', 'true'); - } - - // Collapsed - if ($rowDimension->getCollapsed() == true) { - $objWriter->writeAttribute('collapsed', 'true'); - } - - // Outline level - if ($rowDimension->getOutlineLevel() > 0) { - $objWriter->writeAttribute('outlineLevel', $rowDimension->getOutlineLevel()); - } - - // Style - if ($rowDimension->getXfIndex() !== null) { - $objWriter->writeAttribute('s', $rowDimension->getXfIndex()); - $objWriter->writeAttribute('customFormat', '1'); - } - - // Write cells - if (isset($cellsByRow[$currentRow])) { - foreach ($cellsByRow[$currentRow] as $cellAddress) { - // Write cell - $this->writeCell($objWriter, $pSheet, $cellAddress, $pStringTable, $aFlippedStringTable); - } - } - - // End row - $objWriter->endElement(); - } - } - - $objWriter->endElement(); - } else { - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Invalid parameters passed.'); + // Loop through cells + $cellsByRow = []; + foreach ($pSheet->getCoordinates() as $coordinate) { + $cellAddress = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($coordinate); + $cellsByRow[$cellAddress[1]][] = $coordinate; } + + $currentRow = 0; + while ($currentRow++ < $highestRow) { + // Get row dimension + $rowDimension = $pSheet->getRowDimension($currentRow); + + // Write current row? + $writeCurrentRow = isset($cellsByRow[$currentRow]) || $rowDimension->getRowHeight() >= 0 || $rowDimension->getVisible() == false || $rowDimension->getCollapsed() == true || $rowDimension->getOutlineLevel() > 0 || $rowDimension->getXfIndex() !== null; + + if ($writeCurrentRow) { + // Start a new row + $objWriter->startElement('row'); + $objWriter->writeAttribute('r', $currentRow); + $objWriter->writeAttribute('spans', '1:' . $colCount); + + // Row dimensions + if ($rowDimension->getRowHeight() >= 0) { + $objWriter->writeAttribute('customHeight', '1'); + $objWriter->writeAttribute('ht', \PhpOffice\PhpSpreadsheet\Shared\StringHelper::formatNumber($rowDimension->getRowHeight())); + } + + // Row visibility + if ($rowDimension->getVisible() == false) { + $objWriter->writeAttribute('hidden', 'true'); + } + + // Collapsed + if ($rowDimension->getCollapsed() == true) { + $objWriter->writeAttribute('collapsed', 'true'); + } + + // Outline level + if ($rowDimension->getOutlineLevel() > 0) { + $objWriter->writeAttribute('outlineLevel', $rowDimension->getOutlineLevel()); + } + + // Style + if ($rowDimension->getXfIndex() !== null) { + $objWriter->writeAttribute('s', $rowDimension->getXfIndex()); + $objWriter->writeAttribute('customFormat', '1'); + } + + // Write cells + if (isset($cellsByRow[$currentRow])) { + foreach ($cellsByRow[$currentRow] as $cellAddress) { + // Write cell + $this->writeCell($objWriter, $pSheet, $cellAddress, $aFlippedStringTable); + } + } + + // End row + $objWriter->endElement(); + } + } + + $objWriter->endElement(); } /** @@ -1071,113 +1067,109 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeCell(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null, $pCellAddress = null, $pStringTable = null, $pFlippedStringTable = null) + private function writeCell(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet, $pCellAddress, array $pFlippedStringTable) { - if (is_array($pStringTable) && is_array($pFlippedStringTable)) { - // Cell - $pCell = $pSheet->getCell($pCellAddress); - $objWriter->startElement('c'); - $objWriter->writeAttribute('r', $pCellAddress); + // Cell + $pCell = $pSheet->getCell($pCellAddress); + $objWriter->startElement('c'); + $objWriter->writeAttribute('r', $pCellAddress); - // Sheet styles - if ($pCell->getXfIndex() != '') { - $objWriter->writeAttribute('s', $pCell->getXfIndex()); + // Sheet styles + if ($pCell->getXfIndex() != '') { + $objWriter->writeAttribute('s', $pCell->getXfIndex()); + } + + // If cell value is supplied, write cell value + $cellValue = $pCell->getValue(); + if (is_object($cellValue) || $cellValue !== '') { + // Map type + $mappedType = $pCell->getDataType(); + + // Write data type depending on its type + switch (strtolower($mappedType)) { + case 'inlinestr': // Inline string + case 's': // String + case 'b': // Boolean + $objWriter->writeAttribute('t', $mappedType); + break; + case 'f': // Formula + $calculatedValue = ($this->getParentWriter()->getPreCalculateFormulas()) ? + $pCell->getCalculatedValue() : $cellValue; + if (is_string($calculatedValue)) { + $objWriter->writeAttribute('t', 'str'); + } + break; + case 'e': // Error + $objWriter->writeAttribute('t', $mappedType); } - // If cell value is supplied, write cell value - $cellValue = $pCell->getValue(); - if (is_object($cellValue) || $cellValue !== '') { - // Map type - $mappedType = $pCell->getDataType(); + // Write data depending on its type + switch (strtolower($mappedType)) { + case 'inlinestr': // Inline string + if (!$cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { + $objWriter->writeElement('t', \PhpOffice\PhpSpreadsheet\Shared\StringHelper::controlCharacterPHP2OOXML(htmlspecialchars($cellValue))); + } elseif ($cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { + $objWriter->startElement('is'); + $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $cellValue); + $objWriter->endElement(); + } - // Write data type depending on its type - switch (strtolower($mappedType)) { - case 'inlinestr': // Inline string - case 's': // String - case 'b': // Boolean - $objWriter->writeAttribute('t', $mappedType); - break; - case 'f': // Formula - $calculatedValue = ($this->getParentWriter()->getPreCalculateFormulas()) ? - $pCell->getCalculatedValue() : $cellValue; - if (is_string($calculatedValue)) { - $objWriter->writeAttribute('t', 'str'); + break; + case 's': // String + if (!$cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { + if (isset($pFlippedStringTable[$cellValue])) { + $objWriter->writeElement('v', $pFlippedStringTable[$cellValue]); } - break; - case 'e': // Error - $objWriter->writeAttribute('t', $mappedType); - } + } elseif ($cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { + $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]); + } - // Write data depending on its type - switch (strtolower($mappedType)) { - case 'inlinestr': // Inline string - if (!$cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { - $objWriter->writeElement('t', \PhpOffice\PhpSpreadsheet\Shared\StringHelper::controlCharacterPHP2OOXML(htmlspecialchars($cellValue))); - } elseif ($cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { - $objWriter->startElement('is'); - $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $cellValue); - $objWriter->endElement(); - } - - break; - case 's': // String - if (!$cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { - if (isset($pFlippedStringTable[$cellValue])) { - $objWriter->writeElement('v', $pFlippedStringTable[$cellValue]); - } - } elseif ($cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText) { - $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]); - } - - break; - case 'f': // Formula - $attributes = $pCell->getFormulaAttributes(); - if ($attributes['t'] == 'array') { - $objWriter->startElement('f'); - $objWriter->writeAttribute('t', 'array'); - $objWriter->writeAttribute('ref', $pCellAddress); - $objWriter->writeAttribute('aca', '1'); - $objWriter->writeAttribute('ca', '1'); - $objWriter->text(substr($cellValue, 1)); - $objWriter->endElement(); - } else { - $objWriter->writeElement('f', substr($cellValue, 1)); - } - if ($this->getParentWriter()->getOffice2003Compatibility() === false) { - if ($this->getParentWriter()->getPreCalculateFormulas()) { - if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') { - $objWriter->writeElement('v', \PhpOffice\PhpSpreadsheet\Shared\StringHelper::formatNumber($calculatedValue)); - } else { - $objWriter->writeElement('v', '0'); - } + break; + case 'f': // Formula + $attributes = $pCell->getFormulaAttributes(); + if ($attributes['t'] == 'array') { + $objWriter->startElement('f'); + $objWriter->writeAttribute('t', 'array'); + $objWriter->writeAttribute('ref', $pCellAddress); + $objWriter->writeAttribute('aca', '1'); + $objWriter->writeAttribute('ca', '1'); + $objWriter->text(substr($cellValue, 1)); + $objWriter->endElement(); + } else { + $objWriter->writeElement('f', substr($cellValue, 1)); + } + if ($this->getParentWriter()->getOffice2003Compatibility() === false) { + if ($this->getParentWriter()->getPreCalculateFormulas()) { + if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') { + $objWriter->writeElement('v', \PhpOffice\PhpSpreadsheet\Shared\StringHelper::formatNumber($calculatedValue)); } else { $objWriter->writeElement('v', '0'); } - } - break; - case 'n': // Numeric - // force point as decimal separator in case current locale uses comma - $objWriter->writeElement('v', str_replace(',', '.', $cellValue)); - break; - case 'b': // Boolean - $objWriter->writeElement('v', ($cellValue ? '1' : '0')); - break; - case 'e': // Error - if (substr($cellValue, 0, 1) == '=') { - $objWriter->writeElement('f', substr($cellValue, 1)); - $objWriter->writeElement('v', substr($cellValue, 1)); } else { - $objWriter->writeElement('v', $cellValue); + $objWriter->writeElement('v', '0'); } + } + break; + case 'n': // Numeric + // force point as decimal separator in case current locale uses comma + $objWriter->writeElement('v', str_replace(',', '.', $cellValue)); + break; + case 'b': // Boolean + $objWriter->writeElement('v', ($cellValue ? '1' : '0')); + break; + case 'e': // Error + if (substr($cellValue, 0, 1) == '=') { + $objWriter->writeElement('f', substr($cellValue, 1)); + $objWriter->writeElement('v', substr($cellValue, 1)); + } else { + $objWriter->writeElement('v', $cellValue); + } - break; - } + break; } - - $objWriter->endElement(); - } else { - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('Invalid parameters passed.'); } + + $objWriter->endElement(); } /** @@ -1209,7 +1201,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeLegacyDrawing(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeLegacyDrawing(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // If sheet contains comments, add the relationships if (count($pSheet->getComments()) > 0) { @@ -1227,7 +1219,7 @@ class Worksheet extends WriterPart * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeLegacyDrawingHF(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter = null, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet = null) + private function writeLegacyDrawingHF(\PhpOffice\PhpSpreadsheet\Shared\XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet $pSheet) { // If sheet contains images, add the relationships if (count($pSheet->getHeaderFooter()->getImages()) > 0) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php b/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php index 48744bb9..a743ab12 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php @@ -27,50 +27,31 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx; abstract class WriterPart { /** - * Parent IWriter object. + * Parent Xlsx object. * - * @var \PhpOffice\PhpSpreadsheet\Writer\IWriter + * @var \PhpOffice\PhpSpreadsheet\Writer\Xlsx */ private $parentWriter; /** - * Set parent IWriter object. - * - * @param \PhpOffice\PhpSpreadsheet\Writer\IWriter $pWriter - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - */ - public function setParentWriter(\PhpOffice\PhpSpreadsheet\Writer\IWriter $pWriter = null) - { - $this->parentWriter = $pWriter; - } - - /** - * Get parent IWriter object. + * Get parent Xlsx object. * * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception * - * @return \PhpOffice\PhpSpreadsheet\Writer\IWriter + * @return \PhpOffice\PhpSpreadsheet\Writer\Xlsx */ public function getParentWriter() { - if (!is_null($this->parentWriter)) { - return $this->parentWriter; - } - throw new \PhpOffice\PhpSpreadsheet\Writer\Exception('No parent \\PhpOffice\\PhpSpreadsheet\\Writer\\IWriter assigned.'); + return $this->parentWriter; } /** - * Set parent IWriter object. + * Set parent Xlsx object. * - * @param \PhpOffice\PhpSpreadsheet\Writer\IWriter $pWriter - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @param \PhpOffice\PhpSpreadsheet\Writer\Xlsx $pWriter */ - public function __construct(\PhpOffice\PhpSpreadsheet\Writer\IWriter $pWriter = null) + public function __construct(\PhpOffice\PhpSpreadsheet\Writer\Xlsx $pWriter) { - if (!is_null($pWriter)) { - $this->parentWriter = $pWriter; - } + $this->parentWriter = $pWriter; } } diff --git a/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php b/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php index c7e82139..39dfa17d 100644 --- a/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php @@ -48,4 +48,21 @@ class LookupRefTest extends \PHPUnit_Framework_TestCase { return require 'data/Calculation/LookupRef/VLOOKUP.php'; } + + /** + * @dataProvider providerMATCH + * @group fail19 + * + * @param mixed $expectedResult + */ + public function testMATCH($expectedResult, ...$args) + { + $result = LookupRef::MATCH(...$args); + $this->assertEquals($expectedResult, $result); + } + + public function providerMATCH() + { + return require 'data/Calculation/LookupRef/MATCH.php'; + } } diff --git a/tests/PhpSpreadsheetTests/Cell/AdvancedValueBinderTest.php b/tests/PhpSpreadsheetTests/Cell/AdvancedValueBinderTest.php index 4ac37aba..2f8b29a3 100644 --- a/tests/PhpSpreadsheetTests/Cell/AdvancedValueBinderTest.php +++ b/tests/PhpSpreadsheetTests/Cell/AdvancedValueBinderTest.php @@ -2,10 +2,10 @@ namespace PhpOffice\PhpSpreadsheetTests\Cell; -use PhpOffice\PhpSpreadsheet\CachedObjectStorage\Memory; use PhpOffice\PhpSpreadsheet\Cell; use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\Collection\Cells; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Worksheet; @@ -42,12 +42,12 @@ class AdvancedValueBinderTest extends \PHPUnit_Framework_TestCase public function testCurrency($value, $valueBinded, $format, $thousandsSeparator, $decimalSeparator, $currencyCode) { $sheet = $this->getMockBuilder(Worksheet::class) - ->setMethods(['getStyle', 'getNumberFormat', 'setFormatCode', 'getCellCacheController']) + ->setMethods(['getStyle', 'getNumberFormat', 'setFormatCode', 'getCellCollection']) ->getMock(); - $cache = $this->getMockBuilder(Memory::class) + $cellCollection = $this->getMockBuilder(Cells::class) ->disableOriginalConstructor() ->getMock(); - $cache->expects($this->any()) + $cellCollection->expects($this->any()) ->method('getParent') ->will($this->returnValue($sheet)); @@ -62,8 +62,8 @@ class AdvancedValueBinderTest extends \PHPUnit_Framework_TestCase ->with($format) ->will($this->returnSelf()); $sheet->expects($this->any()) - ->method('getCellCacheController') - ->will($this->returnValue($cache)); + ->method('getCellCollection') + ->will($this->returnValue($cellCollection)); StringHelper::setCurrencyCode($currencyCode); StringHelper::setDecimalSeparator($decimalSeparator); diff --git a/tests/PhpSpreadsheetTests/CellTest.php b/tests/PhpSpreadsheetTests/CellTest.php index d6961621..6e60efe5 100644 --- a/tests/PhpSpreadsheetTests/CellTest.php +++ b/tests/PhpSpreadsheetTests/CellTest.php @@ -223,18 +223,17 @@ class CellTest extends \PHPUnit_Framework_TestCase return require 'data/CellBuildRange.php'; } + /** + * @expectedException \TypeError + */ public function testBuildRangeInvalid() { - $cellRange = ''; - try { - Cell::buildRange($cellRange); - } catch (\Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals($e->getMessage(), 'Range does not contain any information'); - - return; + if (PHP_MAJOR_VERSION < 7) { + $this->markTestSkipped('Cannot catch type hinting error with PHP 5.6'); } - $this->fail('An expected exception has not been raised.'); + + $cellRange = ''; + Cell::buildRange($cellRange); } /** diff --git a/tests/PhpSpreadsheetTests/Collection/CellsTest.php b/tests/PhpSpreadsheetTests/Collection/CellsTest.php new file mode 100644 index 00000000..6b91ec6f --- /dev/null +++ b/tests/PhpSpreadsheetTests/Collection/CellsTest.php @@ -0,0 +1,118 @@ +getActiveSheet(); + $collection = $sheet->getCellCollection(); + + // Assert empty state + $this->assertEquals([], $collection->getCoordinates(), 'cell list should be empty'); + $this->assertEquals([], $collection->getSortedCoordinates(), 'sorted cell list should be empty'); + $this->assertNull($collection->get('B2'), 'getting non-existing cell must return null'); + $this->assertFalse($collection->has('B2'), 'non-existing cell should be non-existent'); + + // Add one cell + $cell1 = $sheet->getCell('B2'); + $this->assertSame($cell1, $collection->add('B2', $cell1), 'adding a cell should return the cell'); + + // Assert cell presence + $this->assertEquals(['B2'], $collection->getCoordinates(), 'cell list should contains the cell'); + $this->assertEquals(['B2'], $collection->getSortedCoordinates(), 'sorted cell list contains the cell'); + $this->assertSame($cell1, $collection->get('B2'), 'should get exact same object'); + $this->assertTrue($collection->has('B2'), 'cell should exists'); + + // Add a second cell + $cell2 = $sheet->getCell('A1'); + $this->assertSame($cell2, $collection->add('A1', $cell2), 'adding a second cell should return the cell'); + $this->assertEquals(['B2', 'A1'], $collection->getCoordinates(), 'cell list should contains the cell'); + $this->assertEquals(['A1', 'B2'], $collection->getSortedCoordinates(), 'sorted cell list contains the cell'); + + // Assert collection copy + $sheet2 = $spreadsheet->createSheet(); + $collection2 = $collection->cloneCellCollection($sheet2); + $this->assertTrue($collection2->has('A1')); + $copiedCell2 = $collection2->get('A1'); + $this->assertNotSame($cell2, $copiedCell2, 'copied cell should not be the same object any more'); + $this->assertSame($collection2, $copiedCell2->getParent(), 'copied cell should be owned by the copied collection'); + $this->assertSame('A1', $copiedCell2->getCoordinate(), 'copied cell should keep attributes'); + + // Assert deletion + $collection->delete('B2'); + $this->assertFalse($collection->has('B2'), 'cell should have been deleted'); + $this->assertEquals(['A1'], $collection->getCoordinates(), 'cell list should contains the cell'); + + // Assert update + $cell2 = $sheet->getCell('A1'); + $this->assertSame($sheet->getCellCollection(), $collection); + $this->assertSame($cell2, $collection->update($cell2), 'should update existing cell'); + + $cell3 = $sheet->getCell('C3'); + $this->assertSame($cell3, $collection->update($cell3), 'should silently add non-existing cell'); + $this->assertEquals(['A1', 'C3'], $collection->getCoordinates(), 'cell list should contains the cell'); + } + + public function testCacheLastCell() + { + $workbook = new Spreadsheet(); + $cells = ['A1', 'A2']; + $sheet = $workbook->getActiveSheet(); + $sheet->setCellValue('A1', 1); + $sheet->setCellValue('A2', 2); + $this->assertEquals($cells, $sheet->getCoordinates(), 'list should include last added cell'); + } + + public function testCanGetCellAfterAnotherIsDeleted() + { + $workbook = new Spreadsheet(); + $sheet = $workbook->getActiveSheet(); + $collection = $sheet->getCellCollection(); + $sheet->setCellValue('A1', 1); + $sheet->setCellValue('A2', 1); + $collection->delete('A1'); + $sheet->setCellValue('A3', 1); + $this->assertNotNull($collection->get('A2'), 'should be able to get back the cell even when another cell was deleted while this one was the current one'); + } + + /** + * @expectedException \PhpOffice\PhpSpreadsheet\Exception + */ + public function testThrowsWhenCellCannotBeRetrievedFromCache() + { + $collection = $this->getMockBuilder(Cells::class) + ->setConstructorArgs([new Worksheet(), new Memory()]) + ->setMethods(['has']) + ->getMock(); + + $collection->method('has') + ->willReturn(true); + + $collection->get('A2'); + } + + /** + * @expectedException \PhpOffice\PhpSpreadsheet\Exception + */ + public function testThrowsWhenCellCannotBeStoredInCache() + { + $cache = $this->createMock(Memory::class); + $cell = $this->createMock(Cell::class); + $cache->method('set') + ->willReturn(false); + + $collection = new Cells(new Worksheet(), $cache); + + $collection->add('A1', $cell); + $collection->add('A2', $cell); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/CsvTest.php b/tests/PhpSpreadsheetTests/Reader/CsvTest.php new file mode 100644 index 00000000..6727b10b --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/CsvTest.php @@ -0,0 +1,41 @@ +'; + $filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet'); + + // Write temp file with value + $spreadsheet = new Spreadsheet(); + $spreadsheet->getActiveSheet()->getCell('A1')->setValue($value); + $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet); + $writer->save($filename); + + // Read written file + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); + $reloadedSpreadsheet = $reader->load($filename); + $actual = $reloadedSpreadsheet->getActiveSheet()->getCell('A1')->getCalculatedValue(); + $this->assertSame($value, $actual, 'should be able to write and read strings with multiples quotes'); + } + + public function testDelimiterDetection() + { + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); + $this->assertNull($reader->getDelimiter()); + + $filename = __DIR__ . '/../../data/Reader/CSV/semicolon_separated.csv'; + $spreadsheet = $reader->load($filename); + + $this->assertSame(';', $reader->getDelimiter(), 'should be able to infer the delimiter'); + + $actual = $spreadsheet->getActiveSheet()->getCell('C2')->getValue(); + $this->assertSame('25,5', $actual, 'should be able to retrieve values with commas'); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/OdsTest.php b/tests/PhpSpreadsheetTests/Reader/OdsTest.php new file mode 100644 index 00000000..ccda9958 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/OdsTest.php @@ -0,0 +1,227 @@ +spreadsheetOOCalcTest) { + $filename = __DIR__ . '/../../../samples/templates/OOCalcTest.ods'; + + // Load into this instance + $reader = new Ods(); + $this->spreadsheetOOCalcTest = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); + } + + return $this->spreadsheetOOCalcTest; + } + + /** + * @return \PhpOffice\PhpSpreadsheet\Spreadsheet + */ + protected function loadDataFile() + { + if (!$this->spreadsheetData) { + $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; + + // Load into this instance + $reader = new Ods(); + $this->spreadsheetData = $reader->loadIntoExisting($filename, new \PhpOffice\PhpSpreadsheet\Spreadsheet()); + } + + return $this->spreadsheetData; + } + + public function testReadFileProperties() + { + $filename = __DIR__ . '/../../data/Reader/Ods/data.ods'; + + // Load into this instance + $reader = new Ods(); + + // Test "listWorksheetNames" method + + $this->assertEquals([ + 'Sheet1', + 'Second Sheet', + ], $reader->listWorksheetNames($filename)); + } + + public function testLoadWorksheets() + { + $spreadsheet = $this->loadDataFile(); + + $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Spreadsheet', $spreadsheet); + + $this->assertEquals(2, $spreadsheet->getSheetCount()); + + $firstSheet = $spreadsheet->getSheet(0); + $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Worksheet', $firstSheet); + + $secondSheet = $spreadsheet->getSheet(1); + $this->assertInstanceOf('PhpOffice\PhpSpreadsheet\Worksheet', $secondSheet); + } + + public function testReadValueAndComments() + { + $spreadsheet = $this->loadOOCalcTestFile(); + + $firstSheet = $spreadsheet->getSheet(0); + + $this->assertEquals(29, $firstSheet->getHighestRow()); + $this->assertEquals('N', $firstSheet->getHighestColumn()); + + // Simple cell value + $this->assertEquals('Test String 1', $firstSheet->getCell('A1')->getValue()); + + // Merged cell + $this->assertEquals('BOX', $firstSheet->getCell('B18')->getValue()); + + // Comments/Annotations + $this->assertEquals( + 'Test for a simple colour-formatted string', + $firstSheet->getComment('A1')->getText()->getPlainText() + ); + + // Data types + $this->assertEquals(DataType::TYPE_STRING, $firstSheet->getCell('A1')->getDataType()); + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('B1')->getDataType()); // Int + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('B6')->getDataType()); // Float + $this->assertEquals(1.23, $firstSheet->getCell('B6')->getValue()); + $this->assertEquals(0, $firstSheet->getCell('G10')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A10')->getDataType()); // Date + $this->assertEquals(22269.0, $firstSheet->getCell('A10')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A13')->getDataType()); // Time + $this->assertEquals(25569.0625, $firstSheet->getCell('A13')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A15')->getDataType()); // Date + Time + $this->assertEquals(22269.0625, $firstSheet->getCell('A15')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A11')->getDataType()); // Fraction + + $this->assertEquals(DataType::TYPE_BOOL, $firstSheet->getCell('D6')->getDataType()); + $this->assertTrue($firstSheet->getCell('D6')->getValue()); + + $this->assertEquals(DataType::TYPE_FORMULA, $firstSheet->getCell('C6')->getDataType()); // Formula + $this->assertEquals('=TRUE()', $firstSheet->getCell('C6')->getValue()); // Formula + + /* + * Percentage, Currency + */ + + $spreadsheet = $this->loadDataFile(); + + $firstSheet = $spreadsheet->getSheet(0); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A1')->getDataType()); // Percentage (10%) + $this->assertEquals(0.1, $firstSheet->getCell('A1')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A2')->getDataType()); // Percentage (10.00%) + $this->assertEquals(0.1, $firstSheet->getCell('A2')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A4')->getDataType()); // Currency (€10.00) + $this->assertEquals(10, $firstSheet->getCell('A4')->getValue()); + + $this->assertEquals(DataType::TYPE_NUMERIC, $firstSheet->getCell('A5')->getDataType()); // Currency ($20) + $this->assertEquals(20, $firstSheet->getCell('A5')->getValue()); + } + + public function testReadColors() + { + $spreadsheet = $this->loadOOCalcTestFile(); + $firstSheet = $spreadsheet->getSheet(0); + + // Background color + + $style = $firstSheet->getCell('K3')->getStyle(); + + $this->assertEquals('none', $style->getFill()->getFillType()); + $this->assertEquals('FFFFFFFF', $style->getFill()->getStartColor()->getARGB()); + $this->assertEquals('FF000000', $style->getFill()->getEndColor()->getARGB()); + } + + public function testReadRichText() + { + $spreadsheet = $this->loadOOCalcTestFile(); + $firstSheet = $spreadsheet->getSheet(0); + + $this->assertEquals( + "I don't know if OOCalc supports Rich Text in the same way as Excel, " . + 'And this row should be autofit height with text wrap', + $firstSheet->getCell('A28')->getValue() + ); + } + + public function testReadCellsWithRepeatedSpaces() + { + $spreadsheet = $this->loadDataFile(); + $firstSheet = $spreadsheet->getSheet(0); + + $this->assertEquals('This has 4 spaces before and 2 after ', $firstSheet->getCell('A8')->getValue()); + $this->assertEquals('This only one after ', $firstSheet->getCell('A9')->getValue()); + $this->assertEquals('Test with DIFFERENT styles and multiple spaces: ', $firstSheet->getCell('A10')->getValue()); + $this->assertEquals("test with new \nLines", $firstSheet->getCell('A11')->getValue()); + } + + public function testReadHyperlinks() + { + $spreadsheet = $this->loadOOCalcTestFile(); + $firstSheet = $spreadsheet->getSheet(0); + + $hyperlink = $firstSheet->getCell('A29'); + + $this->assertEquals(DataType::TYPE_STRING, $hyperlink->getDataType()); + $this->assertEquals('PHPExcel', $hyperlink->getValue()); + $this->assertEquals('http://www.phpexcel.net/', $hyperlink->getHyperlink()->getUrl()); + } + + /* + * Below some test for features not implemented yet + */ + + public function testReadBoldItalicUnderline() + { + $this->markTestIncomplete('Features not implemented yet'); + + $spreadsheet = $this->loadOOCalcTestFile(); + $firstSheet = $spreadsheet->getSheet(0); + + // Font styles + + $style = $firstSheet->getCell('A1')->getStyle(); + $this->assertEquals('FF000000', $style->getFont()->getColor()->getARGB()); + $this->assertEquals(11, $style->getFont()->getSize()); + $this->assertEquals(Font::UNDERLINE_NONE, $style->getFont()->getUnderline()); + + $style = $firstSheet->getCell('E3')->getStyle(); + $this->assertEquals(Font::UNDERLINE_SINGLE, $style->getFont()->getUnderline()); + + $style = $firstSheet->getCell('E1')->getStyle(); + $this->assertTrue($style->getFont()->getBold()); + $this->assertTrue($style->getFont()->getItalic()); + } +} diff --git a/tests/PhpSpreadsheetTests/SampleTest.php b/tests/PhpSpreadsheetTests/SampleTest.php index 0a4a4337..a7e1e075 100644 --- a/tests/PhpSpreadsheetTests/SampleTest.php +++ b/tests/PhpSpreadsheetTests/SampleTest.php @@ -22,12 +22,7 @@ class SampleTest extends \PHPUnit_Framework_TestCase public function providerSample() { - $skipped = [ - '07 Reader PCLZip', // Xlsx cannot load file, leading to OpenOffice trying to and crashing. This is a bug that should be fixed - '20 Read Ods with PCLZip', // Crash: Call to undefined method \PhpOffice\PhpSpreadsheet\Shared\ZipArchive::statName() - '21 Pdf', // for now we don't have 3rdparty libs to tests PDF, but it should be added - '06 Largescale with cellcaching sqlite3', // Travis started crashing after they upgraded from PHP 7.0.13 to 7.0.14, so we disable it for now - ]; + $skipped = []; // Unfortunately some tests are too long be ran with code-coverage // analysis on Travis, so we need to exclude them @@ -35,8 +30,6 @@ class SampleTest extends \PHPUnit_Framework_TestCase if (in_array('--coverage-clover', $argv)) { $tooLongToBeCovered = [ '06 Largescale', - '06 Largescale with cellcaching', - '06 Largescale with cellcaching sqlite3', '13 CalculationCyclicFormulae', ]; $skipped = array_merge($skipped, $tooLongToBeCovered); diff --git a/tests/PhpSpreadsheetTests/SettingsTest.php b/tests/PhpSpreadsheetTests/SettingsTest.php index 51ff8f1b..02a8c36b 100644 --- a/tests/PhpSpreadsheetTests/SettingsTest.php +++ b/tests/PhpSpreadsheetTests/SettingsTest.php @@ -4,14 +4,27 @@ namespace PhpOffice\PhpSpreadsheetTests; class SettingsTest extends \PHPUnit_Framework_TestCase { + /** + * @var string + */ + protected $prevValue; + public function setUp() { + $this->prevValue = libxml_disable_entity_loader(); + libxml_disable_entity_loader(false); // Enable entity loader + } + + protected function tearDown() + { + libxml_disable_entity_loader($this->prevValue); } public function testGetXMLSettings() { $result = \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions(); $this->assertTrue((bool) ((LIBXML_DTDLOAD | LIBXML_DTDATTR) & $result)); + $this->assertFalse(libxml_disable_entity_loader()); } public function testSetXMLSettings() @@ -19,5 +32,6 @@ class SettingsTest extends \PHPUnit_Framework_TestCase \PhpOffice\PhpSpreadsheet\Settings::setLibXmlLoaderOptions(LIBXML_DTDLOAD | LIBXML_DTDATTR | LIBXML_DTDVALID); $result = \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions(); $this->assertTrue((bool) ((LIBXML_DTDLOAD | LIBXML_DTDATTR | LIBXML_DTDVALID) & $result)); + $this->assertFalse(libxml_disable_entity_loader()); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilterTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilterTest.php index 6850b2fe..7a943642 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilterTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilterTest.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Worksheet; -use PhpOffice\PhpSpreadsheet\CachedObjectStorage\Memory; +use PhpOffice\PhpSpreadsheet\Collection\Cells; use PhpOffice\PhpSpreadsheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; @@ -12,19 +12,19 @@ class AutoFilterTest extends \PHPUnit_Framework_TestCase private $testInitialRange = 'H2:O256'; private $testAutoFilterObject; private $mockWorksheetObject; - private $mockCacheController; + private $cellCollection; public function setUp() { $this->mockWorksheetObject = $this->getMockBuilder(Worksheet::class) ->disableOriginalConstructor() ->getMock(); - $this->mockCacheController = $this->getMockBuilder(Memory::class) + $this->cellCollection = $this->getMockBuilder(Cells::class) ->disableOriginalConstructor() ->getMock(); $this->mockWorksheetObject->expects($this->any()) - ->method('getCellCacheController') - ->will($this->returnValue($this->mockCacheController)); + ->method('getCellCollection') + ->will($this->returnValue($this->cellCollection)); $this->testAutoFilterObject = new AutoFilter($this->testInitialRange, $this->mockWorksheetObject); } @@ -83,7 +83,7 @@ class AutoFilterTest extends \PHPUnit_Framework_TestCase $expectedResult = ''; // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setRange(); + $result = $this->testAutoFilterObject->setRange(''); $this->assertInstanceOf(AutoFilter::class, $result); // Result should be a clear range @@ -268,7 +268,7 @@ class AutoFilterTest extends \PHPUnit_Framework_TestCase public function testGetColumnWithoutRangeSet() { // Clear the range - $result = $this->testAutoFilterObject->setRange(); + $result = $this->testAutoFilterObject->setRange(''); $result = $this->testAutoFilterObject->getColumn('A'); } @@ -282,7 +282,7 @@ class AutoFilterTest extends \PHPUnit_Framework_TestCase } // Setters return the instance to implement the fluent interface - $result = $this->testAutoFilterObject->setRange(); + $result = $this->testAutoFilterObject->setRange(''); $this->assertInstanceOf(AutoFilter::class, $result); // Range should be cleared diff --git a/tests/PhpSpreadsheetTests/Worksheet/CellCollectionTest.php b/tests/PhpSpreadsheetTests/Worksheet/CellCollectionTest.php deleted file mode 100644 index 164c0ea8..00000000 --- a/tests/PhpSpreadsheetTests/Worksheet/CellCollectionTest.php +++ /dev/null @@ -1,24 +0,0 @@ -getActiveSheet(); - $worksheet->setCellValue('A1', 1); - $worksheet->setCellValue('A2', 2); - $this->assertEquals($cells, $worksheet->getCellCollection(), "Cache method \"$method\"."); - CachedObjectStorageFactory::finalize(); - } - } -} diff --git a/tests/PhpSpreadsheetTests/Writer/Ods/ContentTest.php b/tests/PhpSpreadsheetTests/Writer/Ods/ContentTest.php new file mode 100644 index 00000000..8a4aae64 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Ods/ContentTest.php @@ -0,0 +1,97 @@ +compatibilityMode = Functions::getCompatibilityMode(); + Functions::setCompatibilityMode(Functions::COMPATIBILITY_OPENOFFICE); + } + + protected function tearDown() + { + parent::tearDown(); + Functions::setCompatibilityMode($this->compatibilityMode); + } + + public function testWriteEmptySpreadsheet() + { + $content = new Content(new Ods(new Spreadsheet())); + $xml = $content->write(); + + $this->assertXmlStringEqualsXmlFile($this->samplesPath . '/content-empty.xml', $xml); + } + + public function testWriteSpreadsheet() + { + $workbook = new Spreadsheet(); + + // Worksheet 1 + $worksheet1 = $workbook->getActiveSheet(); + $worksheet1->setCellValue('A1', 1); // Number + $worksheet1->setCellValue('B1', 12345.6789); // Number + $worksheet1->setCellValue('C1', '1'); // Number without cast + $worksheet1->setCellValueExplicit('D1', '01234', DataType::TYPE_STRING); // Number casted to string + $worksheet1->setCellValue('E1', 'Lorem ipsum'); // String + + $worksheet1->setCellValue('A2', true); // Boolean + $worksheet1->setCellValue('B2', false); // Boolean + $worksheet1->setCellValueExplicit( + 'C2', + '=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))', + DataType::TYPE_FORMULA + ); // Formula + + $worksheet1->setCellValue('D2', Date::PHPToExcel(1488635026)); // Date + $worksheet1->getStyle('D2') + ->getNumberFormat() + ->setFormatCode(NumberFormat::FORMAT_DATE_DATETIME); + + // Styles + $worksheet1->getStyle('A1')->getFont()->setBold(true); + $worksheet1->getStyle('B1')->getFont()->setItalic(true); + $worksheet1->getStyle('C1')->getFont()->setName('Courier'); + $worksheet1->getStyle('C1')->getFont()->setSize(14); + $worksheet1->getStyle('C1')->getFont()->setColor(new Color(Color::COLOR_BLUE)); + + $worksheet1->getStyle('C1')->getFill()->setFillType(Fill::FILL_SOLID); + $worksheet1->getStyle('C1')->getFill()->setStartColor(new Color(Color::COLOR_RED)); + + $worksheet1->getStyle('C1')->getFont()->setUnderline(Font::UNDERLINE_SINGLE); + $worksheet1->getStyle('C2')->getFont()->setUnderline(Font::UNDERLINE_DOUBLE); + $worksheet1->getStyle('D2')->getFont()->setUnderline(Font::UNDERLINE_NONE); + + // Worksheet 2 + $worksheet2 = $workbook->createSheet(); + $worksheet2->setTitle('New Worksheet'); + $worksheet2->setCellValue('A1', 2); + + // Write + $content = new Content(new Ods($workbook)); + $xml = $content->write(); + + $this->assertXmlStringEqualsXmlFile($this->samplesPath . '/content-with-data.xml', $xml); + } +} diff --git a/tests/data/Calculation/LookupRef/MATCH.php b/tests/data/Calculation/LookupRef/MATCH.php new file mode 100644 index 00000000..a82acdde --- /dev/null +++ b/tests/data/Calculation/LookupRef/MATCH.php @@ -0,0 +1,99 @@ + [ + "\u{0061}\u{030A}", + "\u{0061}\u{030A}", + "\u{00E5}", + 'x', + ], + 'Multibytes are supported' => [ + 'x', + "\u{00E5}", + "\u{00E5}", + 'x', + ], ]; diff --git a/tests/data/Reader/CSV/semicolon_separated.csv b/tests/data/Reader/CSV/semicolon_separated.csv new file mode 100644 index 00000000..811d8153 --- /dev/null +++ b/tests/data/Reader/CSV/semicolon_separated.csv @@ -0,0 +1,3 @@ +This;Are;Headers +Cell A2;Number with comma;25,5 +Two colons and a comma;B|3;:,: diff --git a/tests/data/Reader/Ods/data.ods b/tests/data/Reader/Ods/data.ods new file mode 100644 index 00000000..3171eb6b Binary files /dev/null and b/tests/data/Reader/Ods/data.ods differ diff --git a/tests/data/Writer/Ods/content-empty.xml b/tests/data/Writer/Ods/content-empty.xml new file mode 100644 index 00000000..87f756db --- /dev/null +++ b/tests/data/Writer/Ods/content-empty.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/data/Writer/Ods/content-with-data.xml b/tests/data/Writer/Ods/content-with-data.xml new file mode 100644 index 00000000..48184386 --- /dev/null +++ b/tests/data/Writer/Ods/content-with-data.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + 12345.6789 + + + 1 + + + 01234 + + + Lorem ipsum + + + + + + 1 + + + + + + 1 1 + + + 42798.572060185 + + + + + + + + + + + 2 + + + + + + + + \ No newline at end of file
element because TCPDF - // does not recognize e.g.
element because TCPDF - // does not recognize e.g.
element because TCPDF + // does not recognize e.g.
element because TCPDF + // does not recognize e.g.