From 509f27e5c601c002090af18a5dd545b06b7183ca Mon Sep 17 00:00:00 2001
From: Maarten Balliauw
Date: Thu, 26 Aug 2010 19:14:53 +0000
Subject: [PATCH] Migration from off-site Subversion repository (part 1).
git-svn-id: https://phpexcel.svn.codeplex.com/svn/trunk@59884 2327b42d-5241-43d6-9e2a-de5ac946f064
---
Build/build-release-documentation.bat | 2 +
Build/build-release-pear.bat | 2 +
Build/build-release-standard.bat | 2 +
Build/build.xml | 216 +
Build/old/build.bat | 4 +
Build/old/build.php | 336 +
Build/old/package.xml | 54 +
Classes/PHPExcel.php | 802 +
Classes/PHPExcel/Autoloader.php | 51 +
Classes/PHPExcel/CachedObjectStorage/APC.php | 218 +
.../CachedObjectStorage/CacheBase.php | 174 +
.../PHPExcel/CachedObjectStorage/DiscISAM.php | 157 +
.../PHPExcel/CachedObjectStorage/ICache.php | 104 +
.../PHPExcel/CachedObjectStorage/Memcache.php | 239 +
.../PHPExcel/CachedObjectStorage/Memory.php | 85 +
.../CachedObjectStorage/MemoryGZip.php | 107 +
.../CachedObjectStorage/MemorySerialized.php | 107 +
.../PHPExcel/CachedObjectStorage/PHPTemp.php | 157 +
.../PHPExcel/CachedObjectStorage/Wincache.php | 233 +
.../PHPExcel/CachedObjectStorageFactory.php | 131 +
Classes/PHPExcel/Calculation.php | 3702 ++
Classes/PHPExcel/Calculation/Exception.php | 52 +
.../PHPExcel/Calculation/ExceptionHandler.php | 49 +
.../PHPExcel/Calculation/FormulaParser.php | 614 +
Classes/PHPExcel/Calculation/FormulaToken.php | 176 +
Classes/PHPExcel/Calculation/Function.php | 149 +
Classes/PHPExcel/Calculation/Functions.php | 12038 ++++++
Classes/PHPExcel/Calculation/functionlist.txt | 351 +
Classes/PHPExcel/Cell.php | 834 +
Classes/PHPExcel/Cell/AdvancedValueBinder.php | 127 +
Classes/PHPExcel/Cell/DataType.php | 113 +
Classes/PHPExcel/Cell/DataValidation.php | 474 +
Classes/PHPExcel/Cell/DefaultValueBinder.php | 96 +
Classes/PHPExcel/Cell/Hyperlink.php | 127 +
Classes/PHPExcel/Cell/IValueBinder.php | 46 +
Classes/PHPExcel/Comment.php | 288 +
Classes/PHPExcel/DocumentProperties.php | 366 +
Classes/PHPExcel/DocumentSecurity.php | 218 +
Classes/PHPExcel/HashTable.php | 200 +
Classes/PHPExcel/IComparable.php | 43 +
Classes/PHPExcel/IOFactory.php | 277 +
Classes/PHPExcel/NamedRange.php | 245 +
Classes/PHPExcel/Reader/CSV.php | 351 +
Classes/PHPExcel/Reader/DefaultReadFilter.php | 64 +
Classes/PHPExcel/Reader/Excel2003XML.php | 693 +
Classes/PHPExcel/Reader/Excel2007.php | 1656 +
Classes/PHPExcel/Reader/Excel5.php | 6184 ++++
Classes/PHPExcel/Reader/Excel5/Escher.php | 677 +
Classes/PHPExcel/Reader/IReadFilter.php | 47 +
Classes/PHPExcel/Reader/IReader.php | 53 +
Classes/PHPExcel/Reader/OOCalc.php | 507 +
Classes/PHPExcel/Reader/SYLK.php | 509 +
Classes/PHPExcel/Reader/Serialized.php | 130 +
Classes/PHPExcel/ReferenceHelper.php | 614 +
Classes/PHPExcel/RichText.php | 196 +
Classes/PHPExcel/RichText/ITextElement.php | 64 +
Classes/PHPExcel/RichText/Run.php | 102 +
Classes/PHPExcel/RichText/TextElement.php | 108 +
Classes/PHPExcel/Settings.php | 65 +
Classes/PHPExcel/Shared/CodePage.php | 94 +
Classes/PHPExcel/Shared/Date.php | 303 +
Classes/PHPExcel/Shared/Drawing.php | 272 +
Classes/PHPExcel/Shared/Escher.php | 91 +
.../PHPExcel/Shared/Escher/DgContainer.php | 83 +
.../Escher/DgContainer/SpgrContainer.php | 109 +
.../DgContainer/SpgrContainer/SpContainer.php | 368 +
.../PHPExcel/Shared/Escher/DggContainer.php | 203 +
.../Escher/DggContainer/BstoreContainer.php | 65 +
.../DggContainer/BstoreContainer/BSE.php | 120 +
.../DggContainer/BstoreContainer/BSE/Blip.php | 91 +
Classes/PHPExcel/Shared/Excel5.php | 317 +
Classes/PHPExcel/Shared/File.php | 139 +
Classes/PHPExcel/Shared/Font.php | 763 +
Classes/PHPExcel/Shared/JAMA/CHANGELOG.TXT | 16 +
.../Shared/JAMA/CholeskyDecomposition.php | 149 +
.../Shared/JAMA/EigenvalueDecomposition.php | 862 +
.../PHPExcel/Shared/JAMA/LUDecomposition.php | 255 +
Classes/PHPExcel/Shared/JAMA/Matrix.php | 1445 +
.../PHPExcel/Shared/JAMA/QRDecomposition.php | 232 +
.../JAMA/SingularValueDecomposition.php | 526 +
Classes/PHPExcel/Shared/JAMA/docs/docs.php | 6 +
.../PHPExcel/Shared/JAMA/docs/download.php | 65 +
Classes/PHPExcel/Shared/JAMA/docs/example.php | 166 +
.../Shared/JAMA/docs/includes/credits.php | 14 +
.../Shared/JAMA/docs/includes/footer.php | 2 +
.../Shared/JAMA/docs/includes/header.php | 11 +
.../Shared/JAMA/docs/includes/navbar.php | 5 +
Classes/PHPExcel/Shared/JAMA/docs/index.php | 30 +
Classes/PHPExcel/Shared/JAMA/docs/package.php | 37 +
Classes/PHPExcel/Shared/JAMA/docs/test.php | 28 +
.../Shared/JAMA/examples/LMQuadTest.php | 116 +
.../JAMA/examples/LagrangeInterpolation.php | 59 +
.../JAMA/examples/LagrangeInterpolation2.php | 59 +
.../JAMA/examples/LevenbergMarquardt.php | 185 +
.../JAMA/examples/MagicSquareExample.php | 182 +
.../PHPExcel/Shared/JAMA/examples/Stats.php | 1605 +
.../Shared/JAMA/examples/benchmark.php | 263 +
.../PHPExcel/Shared/JAMA/examples/polyfit.php | 73 +
.../PHPExcel/Shared/JAMA/examples/tile.php | 78 +
.../PHPExcel/Shared/JAMA/tests/TestMatrix.php | 415 +
Classes/PHPExcel/Shared/JAMA/utils/Error.php | 82 +
Classes/PHPExcel/Shared/JAMA/utils/Maths.php | 43 +
Classes/PHPExcel/Shared/OLE.php | 531 +
.../Shared/OLE/ChainedBlockStream.php | 224 +
Classes/PHPExcel/Shared/OLE/PPS.php | 218 +
Classes/PHPExcel/Shared/OLE/PPS/File.php | 84 +
Classes/PHPExcel/Shared/OLE/PPS/Root.php | 453 +
Classes/PHPExcel/Shared/OLERead.php | 361 +
Classes/PHPExcel/Shared/PDF/2dbarcodes.php | 126 +
Classes/PHPExcel/Shared/PDF/CHANGELOG.TXT | 1141 +
Classes/PHPExcel/Shared/PDF/LICENSE.TXT | 504 +
Classes/PHPExcel/Shared/PDF/README.TXT | 87 +
Classes/PHPExcel/Shared/PDF/barcodes.php | 1978 +
.../Shared/PDF/cache/chapter_demo_1.txt | 10 +
.../Shared/PDF/cache/chapter_demo_2.txt | 23 +
.../Shared/PDF/cache/table_data_demo.txt | 15 +
.../PHPExcel/Shared/PDF/cache/utf8test.txt | 135 +
.../PHPExcel/Shared/PDF/config/lang/eng.php | 50 +
.../PHPExcel/Shared/PDF/config/lang/ita.php | 50 +
.../Shared/PDF/config/tcpdf_config.php | 232 +
.../Shared/PDF/config/tcpdf_config_alt.php | 227 +
Classes/PHPExcel/Shared/PDF/fonts/.noencode | 0
Classes/PHPExcel/Shared/PDF/fonts/README.TXT | 3 +
.../fonts/arialunicid0-chinese-simplified.php | 1768 +
.../arialunicid0-chinese-traditional.php | 1768 +
.../PDF/fonts/arialunicid0-japanese.php | 1768 +
.../Shared/PDF/fonts/arialunicid0-korean.php | 1768 +
.../Shared/PDF/fonts/arialunicid0.php | 1768 +
Classes/PHPExcel/Shared/PDF/fonts/courier.php | 33 +
.../PDF/fonts/freefont-20090104/AUTHORS | 208 +
.../PDF/fonts/freefont-20090104/COPYING | 674 +
.../PDF/fonts/freefont-20090104/CREDITS | 528 +
.../PDF/fonts/freefont-20090104/ChangeLog | 4525 +++
.../PDF/fonts/freefont-20090104/INSTALL | 86 +
.../Shared/PDF/fonts/freefont-20090104/README | 108 +
.../PHPExcel/Shared/PDF/fonts/freesans.ctg.z | Bin 0 -> 5822 bytes
.../PHPExcel/Shared/PDF/fonts/freesans.php | 311 +
Classes/PHPExcel/Shared/PDF/fonts/freesans.z | Bin 0 -> 308238 bytes
.../PHPExcel/Shared/PDF/fonts/freesansb.ctg.z | Bin 0 -> 4308 bytes
.../PHPExcel/Shared/PDF/fonts/freesansb.php | 236 +
Classes/PHPExcel/Shared/PDF/fonts/freesansb.z | Bin 0 -> 149951 bytes
.../Shared/PDF/fonts/freesansbi.ctg.z | Bin 0 -> 4065 bytes
.../PHPExcel/Shared/PDF/fonts/freesansbi.php | 225 +
.../PHPExcel/Shared/PDF/fonts/freesansbi.z | Bin 0 -> 144094 bytes
.../PHPExcel/Shared/PDF/fonts/freesansi.ctg.z | Bin 0 -> 4349 bytes
.../PHPExcel/Shared/PDF/fonts/freesansi.php | 239 +
Classes/PHPExcel/Shared/PDF/fonts/freesansi.z | Bin 0 -> 224360 bytes
.../PHPExcel/Shared/PDF/fonts/helvetica.php | 33 +
.../PHPExcel/Shared/PDF/fonts/helveticab.php | 33 +
.../PHPExcel/Shared/PDF/fonts/helveticabi.php | 33 +
.../PHPExcel/Shared/PDF/fonts/helveticai.php | 33 +
.../Shared/PDF/fonts/uni2cid_ac15.php | 23613 ++++++++++++
.../Shared/PDF/fonts/uni2cid_ag15.php | 30222 ++++++++++++++++
.../Shared/PDF/fonts/uni2cid_aj16.php | 15705 ++++++++
.../Shared/PDF/fonts/uni2cid_ak12.php | 17530 +++++++++
Classes/PHPExcel/Shared/PDF/htmlcolors.php | 210 +
Classes/PHPExcel/Shared/PDF/images/_blank.png | Bin 0 -> 137 bytes
Classes/PHPExcel/Shared/PDF/tcpdf.crt | 40 +
Classes/PHPExcel/Shared/PDF/tcpdf.fdf | Bin 0 -> 1286 bytes
Classes/PHPExcel/Shared/PDF/tcpdf.php | 16057 ++++++++
Classes/PHPExcel/Shared/PDF/unicode_data.php | 18360 ++++++++++
Classes/PHPExcel/Shared/PasswordHasher.php | 67 +
Classes/PHPExcel/Shared/String.php | 677 +
Classes/PHPExcel/Shared/XMLWriter.php | 148 +
Classes/PHPExcel/Shared/ZipStreamWrapper.php | 163 +
.../PHPExcel/Shared/trend/bestFitClass.php | 331 +
.../Shared/trend/exponentialBestFitClass.php | 98 +
.../Shared/trend/linearBestFitClass.php | 73 +
.../Shared/trend/logarithmicBestFitClass.php | 82 +
.../Shared/trend/polynomialBestFitClass.php | 167 +
.../Shared/trend/powerBestFitClass.php | 98 +
Classes/PHPExcel/Shared/trend/trendClass.php | 108 +
Classes/PHPExcel/Style.php | 685 +
Classes/PHPExcel/Style/Alignment.php | 489 +
Classes/PHPExcel/Style/Border.php | 382 +
Classes/PHPExcel/Style/Borders.php | 499 +
Classes/PHPExcel/Style/Color.php | 408 +
Classes/PHPExcel/Style/Conditional.php | 277 +
Classes/PHPExcel/Style/Fill.php | 401 +
Classes/PHPExcel/Style/Font.php | 619 +
Classes/PHPExcel/Style/NumberFormat.php | 708 +
Classes/PHPExcel/Style/Protection.php | 281 +
Classes/PHPExcel/Worksheet.php | 2480 ++
Classes/PHPExcel/Worksheet/BaseDrawing.php | 485 +
Classes/PHPExcel/Worksheet/CellIterator.php | 161 +
.../PHPExcel/Worksheet/ColumnDimension.php | 271 +
Classes/PHPExcel/Worksheet/Drawing.php | 148 +
Classes/PHPExcel/Worksheet/Drawing/Shadow.php | 288 +
Classes/PHPExcel/Worksheet/HeaderFooter.php | 477 +
.../Worksheet/HeaderFooterDrawing.php | 350 +
Classes/PHPExcel/Worksheet/MemoryDrawing.php | 200 +
Classes/PHPExcel/Worksheet/PageMargins.php | 227 +
Classes/PHPExcel/Worksheet/PageSetup.php | 811 +
Classes/PHPExcel/Worksheet/Protection.php | 563 +
Classes/PHPExcel/Worksheet/Row.php | 90 +
Classes/PHPExcel/Worksheet/RowDimension.php | 242 +
Classes/PHPExcel/Worksheet/RowIterator.php | 111 +
Classes/PHPExcel/Worksheet/SheetView.php | 135 +
Classes/PHPExcel/WorksheetIterator.php | 111 +
Classes/PHPExcel/Writer/CSV.php | 302 +
Classes/PHPExcel/Writer/Excel2007.php | 520 +
.../PHPExcel/Writer/Excel2007/Comments.php | 268 +
.../Writer/Excel2007/ContentTypes.php | 238 +
.../PHPExcel/Writer/Excel2007/DocProps.php | 199 +
Classes/PHPExcel/Writer/Excel2007/Drawing.php | 513 +
Classes/PHPExcel/Writer/Excel2007/Rels.php | 365 +
.../PHPExcel/Writer/Excel2007/StringTable.php | 245 +
Classes/PHPExcel/Writer/Excel2007/Style.php | 665 +
Classes/PHPExcel/Writer/Excel2007/Theme.php | 1202 +
.../PHPExcel/Writer/Excel2007/Workbook.php | 443 +
.../PHPExcel/Writer/Excel2007/Worksheet.php | 1107 +
.../PHPExcel/Writer/Excel2007/WriterPart.php | 68 +
Classes/PHPExcel/Writer/Excel5.php | 476 +
Classes/PHPExcel/Writer/Excel5/BIFFwriter.php | 270 +
Classes/PHPExcel/Writer/Excel5/Escher.php | 512 +
Classes/PHPExcel/Writer/Excel5/Font.php | 193 +
Classes/PHPExcel/Writer/Excel5/Parser.php | 1581 +
Classes/PHPExcel/Writer/Excel5/Workbook.php | 1446 +
Classes/PHPExcel/Writer/Excel5/Worksheet.php | 2956 ++
Classes/PHPExcel/Writer/Excel5/Xf.php | 573 +
Classes/PHPExcel/Writer/HTML.php | 1328 +
Classes/PHPExcel/Writer/IWriter.php | 45 +
Classes/PHPExcel/Writer/PDF.php | 318 +
Classes/PHPExcel/Writer/Serialized.php | 181 +
Classes/PHPExcel/locale/cs/config | 47 +
Classes/PHPExcel/locale/cs/functions | 438 +
Classes/PHPExcel/locale/da/config | 48 +
Classes/PHPExcel/locale/da/functions | 438 +
Classes/PHPExcel/locale/de/config | 47 +
Classes/PHPExcel/locale/de/functions | 438 +
Classes/PHPExcel/locale/en/uk/config | 32 +
Classes/PHPExcel/locale/es/config | 47 +
Classes/PHPExcel/locale/es/functions | 438 +
Classes/PHPExcel/locale/fi/config | 47 +
Classes/PHPExcel/locale/fi/functions | 438 +
Classes/PHPExcel/locale/fr/config | 47 +
Classes/PHPExcel/locale/fr/functions | 438 +
Classes/PHPExcel/locale/hu/config | 47 +
Classes/PHPExcel/locale/hu/functions | 438 +
Classes/PHPExcel/locale/it/config | 47 +
Classes/PHPExcel/locale/it/functions | 438 +
Classes/PHPExcel/locale/nl/config | 47 +
Classes/PHPExcel/locale/nl/functions | 438 +
Classes/PHPExcel/locale/no/config | 47 +
Classes/PHPExcel/locale/no/functions | 438 +
Classes/PHPExcel/locale/pl/config | 47 +
Classes/PHPExcel/locale/pl/functions | 438 +
Classes/PHPExcel/locale/pt/br/config | 47 +
Classes/PHPExcel/locale/pt/br/functions | 408 +
Classes/PHPExcel/locale/pt/config | 47 +
Classes/PHPExcel/locale/pt/functions | 408 +
Classes/PHPExcel/locale/ru/config | 47 +
Classes/PHPExcel/locale/ru/functions | 438 +
Classes/PHPExcel/locale/sv/config | 47 +
Classes/PHPExcel/locale/sv/functions | 408 +
Documentation/FunctionListByCategory.txt | 377 +
Documentation/FunctionListByName.txt | 381 +
...tion Reference developer documentation.doc | Bin 0 -> 588800 bytes
.../PHPExcel developer documentation.doc | Bin 0 -> 756736 bytes
.../assets/ClassDiagrams/Architecture.cd | 51 +
.../assets/ClassDiagrams/Architecture.png | Bin 0 -> 16945 bytes
.../assets/ClassDiagrams/ClassDiagrams.csproj | 64 +
.../ClassDiagrams/ClassDiagrams.csproj.user | 5 +
.../assets/ClassDiagrams/ClassDiagrams.sln | 20 +
.../assets/ClassDiagrams/Classes/IReader.cs | 15 +
.../assets/ClassDiagrams/Classes/IWriter.cs | 15 +
.../assets/ClassDiagrams/Classes/PHPExcel.cs | 40 +
.../Classes/PHPExcel_IOFactory.cs | 41 +
.../Classes/PHPExcel_Reader_Excel2007.cs | 25 +
.../Classes/PHPExcel_Reader_Excel5.cs | 63 +
.../Classes/PHPExcel_Reader_Serialized.cs | 44 +
.../Classes/PHPExcel_Writer_Excel2007.cs | 25 +
.../Classes/PHPExcel_Writer_Serialized.cs | 82 +
.../assets/ClassDiagrams/Classes/Worksheet.cs | 14 +
.../ClassDiagrams/Exports/Architecture.png | Bin 0 -> 15122 bytes
.../ClassDiagrams/Exports/ReaderWriter.png | Bin 0 -> 46094 bytes
.../assets/ClassDiagrams/ReaderWriter.cd | 135 +
.../assets/ClassDiagrams/ReaderWriter.png | Bin 0 -> 57944 bytes
Tests/01simple-download-pdf.php | 77 +
Tests/01simple-download-xls.php | 77 +
Tests/01simple-download-xlsx.php | 77 +
Tests/01simple.php | 84 +
Tests/02types.php | 109 +
Tests/03formulas.php | 93 +
Tests/04printing.php | 96 +
Tests/05featuredemo.inc.php | 393 +
Tests/05featuredemo.php | 48 +
Tests/06largescale.php | 122 +
Tests/07reader.php | 52 +
Tests/08conditionalformatting.php | 158 +
Tests/09pagebreaks.php | 92 +
Tests/10autofilter.php | 112 +
Tests/11documentsecurity.php | 95 +
Tests/12serializedfileformat.php | 60 +
Tests/13calculation.php | 246 +
Tests/14excel5.php | 48 +
Tests/15datavalidation.php | 106 +
Tests/16csv.php | 64 +
Tests/17html.php | 50 +
Tests/18extendedcalculation.php | 231 +
Tests/19namedrange.php | 114 +
Tests/20readexcel5.php | 53 +
Tests/21pdf.php | 55 +
Tests/22heavilyformatted.php | 90 +
Tests/23sharedstyles.php | 94 +
Tests/24readfilter.php | 71 +
Tests/25inmemoryimage.php | 77 +
Tests/26utf8.php | 88 +
Tests/27imagesexcel5.php | 61 +
Tests/28iterator.php | 67 +
Tests/29advancedvaluebinder.php | 105 +
Tests/OOCalcReader.php | 49 +
Tests/OOCalcTest.ods | Bin 0 -> 26671 bytes
Tests/SylkReader.php | 49 +
Tests/SylkTest.slk | 152 +
Tests/XMLReader.php | 49 +
Tests/XMLTest.xml | 441 +
Tests/images/officelogo.jpg | Bin 0 -> 5597 bytes
Tests/images/paid.png | Bin 0 -> 1605 bytes
Tests/images/phpexcel_logo.gif | Bin 0 -> 6104 bytes
Tests/images/termsconditions.jpg | Bin 0 -> 528 bytes
Tests/runall.php | 85 +
Tests/templates/26template.xlsx | Bin 0 -> 9165 bytes
Tests/templates/27template.xls | Bin 0 -> 376832 bytes
changelog.txt | 1110 +
install.txt | 75 +
license.txt | 344 +
phpdoc-home.ini | 21 +
phpdoc.ini | 21 +
setpath.bat | 5 +
start_shell.bat | 1 +
331 files changed, 234454 insertions(+)
create mode 100644 Build/build-release-documentation.bat
create mode 100644 Build/build-release-pear.bat
create mode 100644 Build/build-release-standard.bat
create mode 100644 Build/build.xml
create mode 100644 Build/old/build.bat
create mode 100644 Build/old/build.php
create mode 100644 Build/old/package.xml
create mode 100644 Classes/PHPExcel.php
create mode 100644 Classes/PHPExcel/Autoloader.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/APC.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/CacheBase.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/DiscISAM.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/ICache.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/Memcache.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/Memory.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/PHPTemp.php
create mode 100644 Classes/PHPExcel/CachedObjectStorage/Wincache.php
create mode 100644 Classes/PHPExcel/CachedObjectStorageFactory.php
create mode 100644 Classes/PHPExcel/Calculation.php
create mode 100644 Classes/PHPExcel/Calculation/Exception.php
create mode 100644 Classes/PHPExcel/Calculation/ExceptionHandler.php
create mode 100644 Classes/PHPExcel/Calculation/FormulaParser.php
create mode 100644 Classes/PHPExcel/Calculation/FormulaToken.php
create mode 100644 Classes/PHPExcel/Calculation/Function.php
create mode 100644 Classes/PHPExcel/Calculation/Functions.php
create mode 100644 Classes/PHPExcel/Calculation/functionlist.txt
create mode 100644 Classes/PHPExcel/Cell.php
create mode 100644 Classes/PHPExcel/Cell/AdvancedValueBinder.php
create mode 100644 Classes/PHPExcel/Cell/DataType.php
create mode 100644 Classes/PHPExcel/Cell/DataValidation.php
create mode 100644 Classes/PHPExcel/Cell/DefaultValueBinder.php
create mode 100644 Classes/PHPExcel/Cell/Hyperlink.php
create mode 100644 Classes/PHPExcel/Cell/IValueBinder.php
create mode 100644 Classes/PHPExcel/Comment.php
create mode 100644 Classes/PHPExcel/DocumentProperties.php
create mode 100644 Classes/PHPExcel/DocumentSecurity.php
create mode 100644 Classes/PHPExcel/HashTable.php
create mode 100644 Classes/PHPExcel/IComparable.php
create mode 100644 Classes/PHPExcel/IOFactory.php
create mode 100644 Classes/PHPExcel/NamedRange.php
create mode 100644 Classes/PHPExcel/Reader/CSV.php
create mode 100644 Classes/PHPExcel/Reader/DefaultReadFilter.php
create mode 100644 Classes/PHPExcel/Reader/Excel2003XML.php
create mode 100644 Classes/PHPExcel/Reader/Excel2007.php
create mode 100644 Classes/PHPExcel/Reader/Excel5.php
create mode 100644 Classes/PHPExcel/Reader/Excel5/Escher.php
create mode 100644 Classes/PHPExcel/Reader/IReadFilter.php
create mode 100644 Classes/PHPExcel/Reader/IReader.php
create mode 100644 Classes/PHPExcel/Reader/OOCalc.php
create mode 100644 Classes/PHPExcel/Reader/SYLK.php
create mode 100644 Classes/PHPExcel/Reader/Serialized.php
create mode 100644 Classes/PHPExcel/ReferenceHelper.php
create mode 100644 Classes/PHPExcel/RichText.php
create mode 100644 Classes/PHPExcel/RichText/ITextElement.php
create mode 100644 Classes/PHPExcel/RichText/Run.php
create mode 100644 Classes/PHPExcel/RichText/TextElement.php
create mode 100644 Classes/PHPExcel/Settings.php
create mode 100644 Classes/PHPExcel/Shared/CodePage.php
create mode 100644 Classes/PHPExcel/Shared/Date.php
create mode 100644 Classes/PHPExcel/Shared/Drawing.php
create mode 100644 Classes/PHPExcel/Shared/Escher.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DgContainer.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DggContainer.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE.php
create mode 100644 Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
create mode 100644 Classes/PHPExcel/Shared/Excel5.php
create mode 100644 Classes/PHPExcel/Shared/File.php
create mode 100644 Classes/PHPExcel/Shared/Font.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/CHANGELOG.TXT
create mode 100644 Classes/PHPExcel/Shared/JAMA/CholeskyDecomposition.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/EigenvalueDecomposition.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/LUDecomposition.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/Matrix.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/QRDecomposition.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/SingularValueDecomposition.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/docs.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/download.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/example.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/includes/credits.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/includes/footer.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/includes/header.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/includes/navbar.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/index.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/package.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/docs/test.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/LMQuadTest.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation2.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/LevenbergMarquardt.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/MagicSquareExample.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/Stats.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/benchmark.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/polyfit.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/examples/tile.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/tests/TestMatrix.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/utils/Error.php
create mode 100644 Classes/PHPExcel/Shared/JAMA/utils/Maths.php
create mode 100644 Classes/PHPExcel/Shared/OLE.php
create mode 100644 Classes/PHPExcel/Shared/OLE/ChainedBlockStream.php
create mode 100644 Classes/PHPExcel/Shared/OLE/PPS.php
create mode 100644 Classes/PHPExcel/Shared/OLE/PPS/File.php
create mode 100644 Classes/PHPExcel/Shared/OLE/PPS/Root.php
create mode 100644 Classes/PHPExcel/Shared/OLERead.php
create mode 100644 Classes/PHPExcel/Shared/PDF/2dbarcodes.php
create mode 100644 Classes/PHPExcel/Shared/PDF/CHANGELOG.TXT
create mode 100644 Classes/PHPExcel/Shared/PDF/LICENSE.TXT
create mode 100644 Classes/PHPExcel/Shared/PDF/README.TXT
create mode 100644 Classes/PHPExcel/Shared/PDF/barcodes.php
create mode 100644 Classes/PHPExcel/Shared/PDF/cache/chapter_demo_1.txt
create mode 100644 Classes/PHPExcel/Shared/PDF/cache/chapter_demo_2.txt
create mode 100644 Classes/PHPExcel/Shared/PDF/cache/table_data_demo.txt
create mode 100644 Classes/PHPExcel/Shared/PDF/cache/utf8test.txt
create mode 100644 Classes/PHPExcel/Shared/PDF/config/lang/eng.php
create mode 100644 Classes/PHPExcel/Shared/PDF/config/lang/ita.php
create mode 100644 Classes/PHPExcel/Shared/PDF/config/tcpdf_config.php
create mode 100644 Classes/PHPExcel/Shared/PDF/config/tcpdf_config_alt.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/.noencode
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/README.TXT
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/arialunicid0-chinese-simplified.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/arialunicid0-chinese-traditional.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/arialunicid0-japanese.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/arialunicid0-korean.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/arialunicid0.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/courier.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freefont-20090104/AUTHORS
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freefont-20090104/COPYING
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freefont-20090104/CREDITS
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freefont-20090104/ChangeLog
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freefont-20090104/INSTALL
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freefont-20090104/README
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesans.ctg.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesans.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesans.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansb.ctg.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansb.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansb.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansbi.ctg.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansbi.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansbi.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansi.ctg.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansi.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/freesansi.z
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/helvetica.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/helveticab.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/helveticabi.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/helveticai.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/uni2cid_ac15.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/uni2cid_ag15.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/uni2cid_aj16.php
create mode 100644 Classes/PHPExcel/Shared/PDF/fonts/uni2cid_ak12.php
create mode 100644 Classes/PHPExcel/Shared/PDF/htmlcolors.php
create mode 100644 Classes/PHPExcel/Shared/PDF/images/_blank.png
create mode 100644 Classes/PHPExcel/Shared/PDF/tcpdf.crt
create mode 100644 Classes/PHPExcel/Shared/PDF/tcpdf.fdf
create mode 100644 Classes/PHPExcel/Shared/PDF/tcpdf.php
create mode 100644 Classes/PHPExcel/Shared/PDF/unicode_data.php
create mode 100644 Classes/PHPExcel/Shared/PasswordHasher.php
create mode 100644 Classes/PHPExcel/Shared/String.php
create mode 100644 Classes/PHPExcel/Shared/XMLWriter.php
create mode 100644 Classes/PHPExcel/Shared/ZipStreamWrapper.php
create mode 100644 Classes/PHPExcel/Shared/trend/bestFitClass.php
create mode 100644 Classes/PHPExcel/Shared/trend/exponentialBestFitClass.php
create mode 100644 Classes/PHPExcel/Shared/trend/linearBestFitClass.php
create mode 100644 Classes/PHPExcel/Shared/trend/logarithmicBestFitClass.php
create mode 100644 Classes/PHPExcel/Shared/trend/polynomialBestFitClass.php
create mode 100644 Classes/PHPExcel/Shared/trend/powerBestFitClass.php
create mode 100644 Classes/PHPExcel/Shared/trend/trendClass.php
create mode 100644 Classes/PHPExcel/Style.php
create mode 100644 Classes/PHPExcel/Style/Alignment.php
create mode 100644 Classes/PHPExcel/Style/Border.php
create mode 100644 Classes/PHPExcel/Style/Borders.php
create mode 100644 Classes/PHPExcel/Style/Color.php
create mode 100644 Classes/PHPExcel/Style/Conditional.php
create mode 100644 Classes/PHPExcel/Style/Fill.php
create mode 100644 Classes/PHPExcel/Style/Font.php
create mode 100644 Classes/PHPExcel/Style/NumberFormat.php
create mode 100644 Classes/PHPExcel/Style/Protection.php
create mode 100644 Classes/PHPExcel/Worksheet.php
create mode 100644 Classes/PHPExcel/Worksheet/BaseDrawing.php
create mode 100644 Classes/PHPExcel/Worksheet/CellIterator.php
create mode 100644 Classes/PHPExcel/Worksheet/ColumnDimension.php
create mode 100644 Classes/PHPExcel/Worksheet/Drawing.php
create mode 100644 Classes/PHPExcel/Worksheet/Drawing/Shadow.php
create mode 100644 Classes/PHPExcel/Worksheet/HeaderFooter.php
create mode 100644 Classes/PHPExcel/Worksheet/HeaderFooterDrawing.php
create mode 100644 Classes/PHPExcel/Worksheet/MemoryDrawing.php
create mode 100644 Classes/PHPExcel/Worksheet/PageMargins.php
create mode 100644 Classes/PHPExcel/Worksheet/PageSetup.php
create mode 100644 Classes/PHPExcel/Worksheet/Protection.php
create mode 100644 Classes/PHPExcel/Worksheet/Row.php
create mode 100644 Classes/PHPExcel/Worksheet/RowDimension.php
create mode 100644 Classes/PHPExcel/Worksheet/RowIterator.php
create mode 100644 Classes/PHPExcel/Worksheet/SheetView.php
create mode 100644 Classes/PHPExcel/WorksheetIterator.php
create mode 100644 Classes/PHPExcel/Writer/CSV.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Comments.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/ContentTypes.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/DocProps.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Drawing.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Rels.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/StringTable.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Style.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Theme.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Workbook.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/Worksheet.php
create mode 100644 Classes/PHPExcel/Writer/Excel2007/WriterPart.php
create mode 100644 Classes/PHPExcel/Writer/Excel5.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/BIFFwriter.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/Escher.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/Font.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/Parser.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/Workbook.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/Worksheet.php
create mode 100644 Classes/PHPExcel/Writer/Excel5/Xf.php
create mode 100644 Classes/PHPExcel/Writer/HTML.php
create mode 100644 Classes/PHPExcel/Writer/IWriter.php
create mode 100644 Classes/PHPExcel/Writer/PDF.php
create mode 100644 Classes/PHPExcel/Writer/Serialized.php
create mode 100644 Classes/PHPExcel/locale/cs/config
create mode 100644 Classes/PHPExcel/locale/cs/functions
create mode 100644 Classes/PHPExcel/locale/da/config
create mode 100644 Classes/PHPExcel/locale/da/functions
create mode 100644 Classes/PHPExcel/locale/de/config
create mode 100644 Classes/PHPExcel/locale/de/functions
create mode 100644 Classes/PHPExcel/locale/en/uk/config
create mode 100644 Classes/PHPExcel/locale/es/config
create mode 100644 Classes/PHPExcel/locale/es/functions
create mode 100644 Classes/PHPExcel/locale/fi/config
create mode 100644 Classes/PHPExcel/locale/fi/functions
create mode 100644 Classes/PHPExcel/locale/fr/config
create mode 100644 Classes/PHPExcel/locale/fr/functions
create mode 100644 Classes/PHPExcel/locale/hu/config
create mode 100644 Classes/PHPExcel/locale/hu/functions
create mode 100644 Classes/PHPExcel/locale/it/config
create mode 100644 Classes/PHPExcel/locale/it/functions
create mode 100644 Classes/PHPExcel/locale/nl/config
create mode 100644 Classes/PHPExcel/locale/nl/functions
create mode 100644 Classes/PHPExcel/locale/no/config
create mode 100644 Classes/PHPExcel/locale/no/functions
create mode 100644 Classes/PHPExcel/locale/pl/config
create mode 100644 Classes/PHPExcel/locale/pl/functions
create mode 100644 Classes/PHPExcel/locale/pt/br/config
create mode 100644 Classes/PHPExcel/locale/pt/br/functions
create mode 100644 Classes/PHPExcel/locale/pt/config
create mode 100644 Classes/PHPExcel/locale/pt/functions
create mode 100644 Classes/PHPExcel/locale/ru/config
create mode 100644 Classes/PHPExcel/locale/ru/functions
create mode 100644 Classes/PHPExcel/locale/sv/config
create mode 100644 Classes/PHPExcel/locale/sv/functions
create mode 100644 Documentation/FunctionListByCategory.txt
create mode 100644 Documentation/FunctionListByName.txt
create mode 100644 Documentation/PHPExcel Function Reference developer documentation.doc
create mode 100644 Documentation/PHPExcel developer documentation.doc
create mode 100644 Documentation/assets/ClassDiagrams/Architecture.cd
create mode 100644 Documentation/assets/ClassDiagrams/Architecture.png
create mode 100644 Documentation/assets/ClassDiagrams/ClassDiagrams.csproj
create mode 100644 Documentation/assets/ClassDiagrams/ClassDiagrams.csproj.user
create mode 100644 Documentation/assets/ClassDiagrams/ClassDiagrams.sln
create mode 100644 Documentation/assets/ClassDiagrams/Classes/IReader.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/IWriter.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel_IOFactory.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel_Reader_Excel2007.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel_Reader_Excel5.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel_Reader_Serialized.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel_Writer_Excel2007.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/PHPExcel_Writer_Serialized.cs
create mode 100644 Documentation/assets/ClassDiagrams/Classes/Worksheet.cs
create mode 100644 Documentation/assets/ClassDiagrams/Exports/Architecture.png
create mode 100644 Documentation/assets/ClassDiagrams/Exports/ReaderWriter.png
create mode 100644 Documentation/assets/ClassDiagrams/ReaderWriter.cd
create mode 100644 Documentation/assets/ClassDiagrams/ReaderWriter.png
create mode 100644 Tests/01simple-download-pdf.php
create mode 100644 Tests/01simple-download-xls.php
create mode 100644 Tests/01simple-download-xlsx.php
create mode 100644 Tests/01simple.php
create mode 100644 Tests/02types.php
create mode 100644 Tests/03formulas.php
create mode 100644 Tests/04printing.php
create mode 100644 Tests/05featuredemo.inc.php
create mode 100644 Tests/05featuredemo.php
create mode 100644 Tests/06largescale.php
create mode 100644 Tests/07reader.php
create mode 100644 Tests/08conditionalformatting.php
create mode 100644 Tests/09pagebreaks.php
create mode 100644 Tests/10autofilter.php
create mode 100644 Tests/11documentsecurity.php
create mode 100644 Tests/12serializedfileformat.php
create mode 100644 Tests/13calculation.php
create mode 100644 Tests/14excel5.php
create mode 100644 Tests/15datavalidation.php
create mode 100644 Tests/16csv.php
create mode 100644 Tests/17html.php
create mode 100644 Tests/18extendedcalculation.php
create mode 100644 Tests/19namedrange.php
create mode 100644 Tests/20readexcel5.php
create mode 100644 Tests/21pdf.php
create mode 100644 Tests/22heavilyformatted.php
create mode 100644 Tests/23sharedstyles.php
create mode 100644 Tests/24readfilter.php
create mode 100644 Tests/25inmemoryimage.php
create mode 100644 Tests/26utf8.php
create mode 100644 Tests/27imagesexcel5.php
create mode 100644 Tests/28iterator.php
create mode 100644 Tests/29advancedvaluebinder.php
create mode 100644 Tests/OOCalcReader.php
create mode 100644 Tests/OOCalcTest.ods
create mode 100644 Tests/SylkReader.php
create mode 100644 Tests/SylkTest.slk
create mode 100644 Tests/XMLReader.php
create mode 100644 Tests/XMLTest.xml
create mode 100644 Tests/images/officelogo.jpg
create mode 100644 Tests/images/paid.png
create mode 100644 Tests/images/phpexcel_logo.gif
create mode 100644 Tests/images/termsconditions.jpg
create mode 100644 Tests/runall.php
create mode 100644 Tests/templates/26template.xlsx
create mode 100644 Tests/templates/27template.xls
create mode 100644 changelog.txt
create mode 100644 install.txt
create mode 100644 license.txt
create mode 100644 phpdoc-home.ini
create mode 100644 phpdoc.ini
create mode 100644 setpath.bat
create mode 100644 start_shell.bat
diff --git a/Build/build-release-documentation.bat b/Build/build-release-documentation.bat
new file mode 100644
index 0000000..3a942e6
--- /dev/null
+++ b/Build/build-release-documentation.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+phing -f build.xml release-documentation
\ No newline at end of file
diff --git a/Build/build-release-pear.bat b/Build/build-release-pear.bat
new file mode 100644
index 0000000..1e056ea
--- /dev/null
+++ b/Build/build-release-pear.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+phing -f build.xml release-pear
\ No newline at end of file
diff --git a/Build/build-release-standard.bat b/Build/build-release-standard.bat
new file mode 100644
index 0000000..3cce027
--- /dev/null
+++ b/Build/build-release-standard.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+phing -f build.xml release-standard
\ No newline at end of file
diff --git a/Build/build.xml b/Build/build.xml
new file mode 100644
index 0000000..5a2fdda
--- /dev/null
+++ b/Build/build.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+ destinationFile = $f;
+ }
+
+ function createFileSet() {
+ $num = array_push($this->filesets, new FileSet());
+ return $this->filesets[$num-1];
+ }
+
+ function main() {
+ if ($this->destinationFile === null || empty($this->filesets)) {
+ throw new BuildException("You must specify a file or fileset(s) for the task.");
+ }
+
+ // compile a list of all files to add to the file, both file attrib and fileset elements
+ // can be used.
+ $files = array();
+ if (!empty($this->filesets)) {
+ $filenames = array();
+ foreach($this->filesets as $fs) {
+ try {
+ $ds = $fs->getDirectoryScanner($this->project);
+ $filenames = $ds->getIncludedFiles(); // get included filenames
+ $dir = $fs->getDir($this->project);
+ foreach ($filenames as $fname) {
+ $files[] = new PhingFile($dir, $fname);
+ }
+ } catch (BuildException $be) {
+ $this->log($be->getMessage(), Project::MSG_WARN);
+ }
+ }
+ }
+
+ $objZip = new ZipArchive();
+ if ($objZip->open($this->destinationFile, ZIPARCHIVE::OVERWRITE) !== true) {
+ throw new Exeption("Could not open " . $strResultingFile . " for writing!");
+ }
+
+ $this->log("Creating ZIP archive of " . count($files) . " files...");
+
+ foreach($files as $file) {
+ $this->log("Processing file " . $this->_cleanFileName($file) . " ...");
+ $contents = file_get_contents($file);
+ $objZip->addFromString( $this->_cleanFileName($file), $contents );
+ }
+
+ $objZip->close();
+
+ $this->log("Created ZIP archive " . $this->destinationFile . '.');
+ }
+
+ /**
+ * Cleanup a filename
+ *
+ * @param string $strFile Filename
+ * @return string Filename
+ */
+ protected function _cleanFileName($strFile) {
+ $strFile = str_replace('../', '', $strFile);
+ $strFile = str_replace('.\\build\\', '', $strFile);
+ $strFile = str_replace('WINDOWS', '', $strFile);
+
+ while (preg_match('/\/\//i', $strFile)) {
+ $strFile = str_replace('//', '/', $strFile);
+ }
+
+ return $strFile;
+ }
+ }
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PHPExcel
+ PHP Excel classes
+ pear.pearplex.net
+ Project providing a set of classes for the PHP programming language, which allow you to write to Excel 2007 files and read from Excel 2007 files.
+ This package ONLY contains the class files, not the documentation and example code. Please refer to http://www.codeplex.com/PHPExcel for those files.
+
+ LGPL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Build/old/build.bat b/Build/old/build.bat
new file mode 100644
index 0000000..10721cb
--- /dev/null
+++ b/Build/old/build.bat
@@ -0,0 +1,4 @@
+@set PHPINSTALLDIR=C:\php\5.2.9\;C:\php5;C:\LAMP\php;C:\LAMP\php5;D:\LAMP\php5
+@set PATH=%PHPINSTALLDIR%;%PATH%;
+cls
+php build.php
\ No newline at end of file
diff --git a/Build/old/build.php b/Build/old/build.php
new file mode 100644
index 0000000..b4d9f20
--- /dev/null
+++ b/Build/old/build.php
@@ -0,0 +1,336 @@
+open($strResultingFile, ZIPARCHIVE::OVERWRITE) !== true) {
+ throw new Exeption("Could not open " . $strResultingFile . " for writing!");
+}
+
+// Add files to include
+foreach ($aFilesToInclude as $strFile) {
+ echo date('H:i:s') . " Adding file $strFile\n";
+ addFileToZIP($strFile, $objZip, $sVersion, $sDate);
+}
+
+// Add paths to include
+foreach ($aPathsToInclude as $strPath) {
+ addPathToZIP($strPath, $objZip, $sVersion, $sDate);
+}
+
+// Set archive comment...
+echo date('H:i:s') . " Set archive comment...\n";
+$objZip->setArchiveComment('PHPExcel - http://www.codeplex.com/PHPExcel');
+
+// Close file
+echo date('H:i:s') . " Saving ZIP archive...\n";
+$objZip->close();
+
+// Copy classes directory
+echo date('H:i:s') . " Copying class directory...\n";
+mkdir('./tmp');
+dircopy($sClassPath, './tmp');
+
+// Create PEAR package.xml
+echo date('H:i:s') . " Creating PEAR package.xml...\n";
+$packageFile = file_get_contents('package.xml');
+$packageFile = replaceMetaData($packageFile, $sVersion, $sDate);
+
+$packageFile = str_replace('##PEAR_DIR##', addPathToPEAR('./tmp', '', $sVersion, $sDate), $packageFile);
+$fh = fopen('./tmp/package.xml', 'w');
+fwrite($fh, $packageFile);
+fclose($fh);
+
+// Create PEAR package
+echo date('H:i:s') . " Creating PEAR package...\n";
+echo shell_exec("$sPEARPath package ./tmp/package.xml");
+
+// Wait a minute (TortoiseSVN on USB stick is slow!)
+echo date('H:i:s') . " Waiting...\n";
+sleep(120);
+
+// Clean temporary files
+echo date('H:i:s') . " Cleaning temporary files...\n";
+unlink('./tmp/package.xml');
+rm('./tmp');
+
+// Finished build
+echo date('H:i:s') . " Finished build!\n";
+fclose($stdin);
+
+/**
+ * Add a specific path's files and folders to a ZIP object
+ *
+ * @param string $strPath Path to add
+ * @param ZipArchive $objZip ZipArchive object
+ * @param string $strVersion Version string
+ * @param string $strDate Date string
+ */
+function addPathToZIP($strPath, $objZip, $strVersion, $strDate) {
+ global $aIgnorePatterns;
+
+ echo date('H:i:s') . " Adding path $strPath...\n";
+
+ $currentDir = opendir($strPath);
+ while ($strFile = readdir($currentDir)) {
+ if ($strFile != '.' && $strFile != '..') {
+ if (is_file($strPath . '/' . $strFile)) {
+ addFileToZIP($strPath . '/' . $strFile, $objZip, $strVersion, $strDate);
+ } else if (is_dir($strPath . '/' . $strFile)) {
+ if (!shouldIgnore($strFile)) {
+ addPathToZIP( ($strPath . '/' . $strFile), $objZip, $strVersion, $strDate );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Add a specific file to ZIP
+ *
+ * @param string $strFile File to add
+ * @param ZipArchive $objZip ZipArchive object
+ * @param string $strVersion Version string
+ * @param string $strDate Date string
+ */
+function addFileToZIP($strFile, $objZip, $strVersion, $strDate) {
+ if (!shouldIgnore($strFile)) {
+ $fileContents = file_get_contents($strFile);
+ $fileContents = replaceMetaData($fileContents, $strVersion, $strDate);
+
+ //$objZip->addFile($strFile, cleanFileName($strFile));
+ $objZip->addFromString( cleanFileName($strFile), $fileContents );
+ }
+}
+
+/**
+ * Cleanup a filename
+ *
+ * @param string $strFile Filename
+ * @return string Filename
+ */
+function cleanFileName($strFile) {
+ $strFile = str_replace('../', '', $strFile);
+ $strFile = str_replace('WINDOWS', '', $strFile);
+
+ while (preg_match('/\/\//i', $strFile)) {
+ $strFile = str_replace('//', '/', $strFile);
+ }
+
+ return $strFile;
+}
+
+/**
+ * Replace metadata in string
+ *
+ * @param string $strString String contents
+ * @param string $strVersion Version string
+ * @param string $strDate Date string
+ * @return string String contents
+ */
+function replaceMetaData($strString, $strVersion, $strDate) {
+ $strString = str_replace('##VERSION##', $strVersion, $strString);
+ $strString = str_replace('##DATE##', $strDate, $strString);
+ return $strString;
+}
+
+/**
+ * Add a specific path's files and folders to a PEAR dir list
+ *
+ * @param string $strPath Path to add
+ * @param string $strPEAR String containing PEAR dir definitions
+ * @param string $strVersion Version string
+ * @param string $strDate Date string
+ * @return string String containing PEAR dir definitions
+ */
+function addPathToPEAR($strPath, $strPEAR, $strVersion, $strDate) {
+ global $aIgnorePatterns;
+
+ $currentDir = opendir($strPath);
+ while ($strFile = readdir($currentDir)) {
+ if ($strFile != '.' && $strFile != '..') {
+ if (is_file($strPath . '/' . $strFile) && !preg_match('/package.xml/i', $strFile)) {
+ $strPEAR .= addFileToPEAR($strPath . '/' . $strFile, '', $strVersion, $strDate);
+ } else if (is_dir($strPath . '/' . $strFile)) {
+ if (!shouldIgnore($strFile)) {
+ $strPEAR .= '';
+ $strPEAR .= addPathToPEAR( ($strPath . '/' . $strFile), '', $strVersion, $strDate );
+ $strPEAR .= '';
+ }
+ }
+ }
+ }
+
+ return $strPEAR;
+}
+
+/**
+ * Add a specific file to a PEAR dir list
+ *
+ * @param string $strFile File to add
+ * @param string $strPEAR String containing PEAR dir definitions
+ * @param string $strVersion Version string
+ * @param string $strDate Date string
+ * @return string String containing PEAR dir definitions
+ */
+function addFileToPEAR($strFile, $strPEAR, $strVersion, $strDate) {
+ if (!shouldIgnore($strFile)) {
+ $fileContents = file_get_contents($strFile);
+ $fileContents = replaceMetaData($fileContents, $strVersion, $strDate);
+ $fh = fopen($strFile, 'w');
+ fwrite($fh, $fileContents);
+ fclose($fh);
+
+ $strPEAR .= '';
+
+ return $strPEAR;
+ } else {
+ return '';
+ }
+}
+
+/**
+ * Copy a complete directory
+ *
+ * @param string $srcdir Source directory
+ * @param string $dstdir Destination directory
+ * @return int Number of copied files
+ */
+function dircopy($srcdir, $dstdir, $verbose = false) {
+ $num = 0;
+ if(!is_dir($dstdir) && !shouldIgnore($dstdir)) mkdir($dstdir);
+ if($curdir = opendir($srcdir)) {
+ while($file = readdir($curdir)) {
+ if($file != '.' && $file != '..') {
+ $srcfile = $srcdir . '\\' . $file;
+ $dstfile = $dstdir . '\\' . $file;
+ if(is_file($srcfile) && !shouldIgnore($srcfile)) {
+ if(is_file($dstfile)) $ow = filemtime($srcfile) - filemtime($dstfile); else $ow = 1;
+ if($ow > 0) {
+ if($verbose) echo "Copying '$srcfile' to '$dstfile'...";
+ if(copy($srcfile, $dstfile)) {
+ touch($dstfile, filemtime($srcfile)); $num++;
+ if($verbose) echo "OK\n";
+ }
+ else echo "Error: File '$srcfile' could not be copied!\n";
+ }
+ }
+ else if(is_dir($srcfile) && !shouldIgnore($srcfile)) {
+ $num += dircopy($srcfile, $dstfile, $verbose);
+ }
+ }
+ }
+ closedir($curdir);
+ }
+ return $num;
+}
+
+/**
+ * rm() -- Very Vigorously erase files and directories. Also hidden files !!!!
+ *
+ * @param $dir string
+ * be carefull to:
+ * if($obj=='.' || $obj=='..') continue;
+ * if not it will erase all the server...it happened to me ;)
+ * the function is permission dependent.
+ */
+function rm($dir) {
+ if(!$dh = @opendir($dir)) return;
+ while (($obj = readdir($dh))) {
+ if($obj=='.' || $obj=='..') continue;
+ @chmod($dir.'/'.$obj, 0777);
+ if (!@unlink($dir.'/'.$obj)) rm($dir.'/'.$obj);
+ }
+ @rmdir($dir);
+ @shell_exec('rmdir /S /Q "' . $dir . '"');
+}
+
+/**
+ * Should a file/folder be ignored?
+ *
+ * @param string $pName
+ * @return boolean
+ */
+function shouldIgnore($pName = '') {
+ global $aIgnorePatterns;
+
+ $ignore = false;
+ foreach ($aIgnorePatterns as $ignorePattern) {
+ if (preg_match($ignorePattern, $pName)) {
+ $ignore = true;
+ }
+ }
+ return $ignore;
+}
\ No newline at end of file
diff --git a/Build/old/package.xml b/Build/old/package.xml
new file mode 100644
index 0000000..8e1101e
--- /dev/null
+++ b/Build/old/package.xml
@@ -0,0 +1,54 @@
+
+
+ PHPExcel
+ http://www.codeplex.com/PHPExcel/PHPExcel-##VERSION##
+ PHP Excel classes
+
+ Project providing a set of classes for the PHP programming language, which allow you to write to Excel 2007 files and read from Excel 2007 files.
+
+
+ Maarten Balliauw
+ maartenba
+ maarten@phpexcel.net
+ yes
+
+ ##DATE##
+
+ ##VERSION##
+ ##VERSION##
+
+
+ stable
+ stable
+
+ LGPL
+ This package ONLY contains the class files, not the documentation and example code. Please refer to http://www.codeplex.com/PHPExcel for those files.
+
+
+##PEAR_DIR##
+
+
+
+
+
+ 5.0
+
+
+ 1.4.0
+
+
+ zip
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Classes/PHPExcel.php b/Classes/PHPExcel.php
new file mode 100644
index 0000000..fe81333
--- /dev/null
+++ b/Classes/PHPExcel.php
@@ -0,0 +1,802 @@
+_workSheetCollection = array();
+ $this->_workSheetCollection[] = new PHPExcel_Worksheet($this);
+ $this->_activeSheetIndex = 0;
+
+ // Create document properties
+ $this->_properties = new PHPExcel_DocumentProperties();
+
+ // Create document security
+ $this->_security = new PHPExcel_DocumentSecurity();
+
+ // Set named ranges
+ $this->_namedRanges = array();
+
+ // Create the cellXf supervisor
+ $this->_cellXfSupervisor = new PHPExcel_Style(true);
+ $this->_cellXfSupervisor->bindParent($this);
+
+ // Create the default style
+ $this->addCellXf(new PHPExcel_Style);
+ $this->addCellStyleXf(new PHPExcel_Style);
+ }
+
+
+ public function disconnectWorksheets() {
+ foreach($this->_workSheetCollection as $k => &$worksheet) {
+ $worksheet->disconnectCells();
+ $this->_workSheetCollection[$k] = null;
+ }
+ unset($worksheet);
+ $this->_workSheetCollection = array();
+ }
+
+ /**
+ * Get properties
+ *
+ * @return PHPExcel_DocumentProperties
+ */
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+
+ /**
+ * Set properties
+ *
+ * @param PHPExcel_DocumentProperties $pValue
+ */
+ public function setProperties(PHPExcel_DocumentProperties $pValue)
+ {
+ $this->_properties = $pValue;
+ }
+
+ /**
+ * Get security
+ *
+ * @return PHPExcel_DocumentSecurity
+ */
+ public function getSecurity()
+ {
+ return $this->_security;
+ }
+
+ /**
+ * Set security
+ *
+ * @param PHPExcel_DocumentSecurity $pValue
+ */
+ public function setSecurity(PHPExcel_DocumentSecurity $pValue)
+ {
+ $this->_security = $pValue;
+ }
+
+ /**
+ * Get active sheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getActiveSheet()
+ {
+ return $this->_workSheetCollection[$this->_activeSheetIndex];
+ }
+
+ /**
+ * Create sheet and add it to this workbook
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function createSheet($iSheetIndex = null)
+ {
+ $newSheet = new PHPExcel_Worksheet($this);
+ $this->addSheet($newSheet, $iSheetIndex);
+ return $newSheet;
+ }
+
+ /**
+ * Add sheet
+ *
+ * @param PHPExcel_Worksheet $pSheet
+ * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function addSheet(PHPExcel_Worksheet $pSheet = null, $iSheetIndex = null)
+ {
+ if(is_null($iSheetIndex))
+ {
+ $this->_workSheetCollection[] = $pSheet;
+ }
+ else
+ {
+ // Insert the sheet at the requested index
+ array_splice(
+ $this->_workSheetCollection,
+ $iSheetIndex,
+ 0,
+ array($pSheet)
+ );
+
+ // Adjust active sheet index if necessary
+ if ($this->_activeSheetIndex >= $iSheetIndex) {
+ ++$this->_activeSheetIndex;
+ }
+
+ }
+ return $pSheet;
+ }
+
+ /**
+ * Remove sheet by index
+ *
+ * @param int $pIndex Active sheet index
+ * @throws Exception
+ */
+ public function removeSheetByIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_workSheetCollection) - 1) {
+ throw new Exception("Sheet index is out of bounds.");
+ } else {
+ array_splice($this->_workSheetCollection, $pIndex, 1);
+ }
+ }
+
+ /**
+ * Get sheet by index
+ *
+ * @param int $pIndex Sheet index
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function getSheet($pIndex = 0)
+ {
+ if ($pIndex > count($this->_workSheetCollection) - 1) {
+ throw new Exception("Sheet index is out of bounds.");
+ } else {
+ return $this->_workSheetCollection[$pIndex];
+ }
+ }
+
+ /**
+ * Get all sheets
+ *
+ * @return PHPExcel_Worksheet[]
+ */
+ public function getAllSheets()
+ {
+ return $this->_workSheetCollection;
+ }
+
+ /**
+ * Get sheet by name
+ *
+ * @param string $pName Sheet name
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function getSheetByName($pName = '')
+ {
+ $worksheetCount = count($this->_workSheetCollection);
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ if ($this->_workSheetCollection[$i]->getTitle() == $pName) {
+ return $this->_workSheetCollection[$i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get index for sheet
+ *
+ * @param PHPExcel_Worksheet $pSheet
+ * @return Sheet index
+ * @throws Exception
+ */
+ public function getIndex(PHPExcel_Worksheet $pSheet)
+ {
+ foreach ($this->_workSheetCollection as $key => $value) {
+ if ($value->getHashCode() == $pSheet->getHashCode()) {
+ return $key;
+ }
+ }
+ }
+
+ /**
+ * Set index for sheet by sheet name.
+ *
+ * @param string $sheetName Sheet name to modify index for
+ * @param int $newIndex New index for the sheet
+ * @return New sheet index
+ * @throws Exception
+ */
+ public function setIndexByName($sheetName, $newIndex)
+ {
+ $oldIndex = $this->getIndex($this->getSheetByName($sheetName));
+ $pSheet = array_splice(
+ $this->_workSheetCollection,
+ $oldIndex,
+ 1
+ );
+ array_splice(
+ $this->_workSheetCollection,
+ $newIndex,
+ 0,
+ $pSheet
+ );
+ return $newIndex;
+ }
+
+ /**
+ * Get sheet count
+ *
+ * @return int
+ */
+ public function getSheetCount()
+ {
+ return count($this->_workSheetCollection);
+ }
+
+ /**
+ * Get active sheet index
+ *
+ * @return int Active sheet index
+ */
+ public function getActiveSheetIndex()
+ {
+ return $this->_activeSheetIndex;
+ }
+
+ /**
+ * Set active sheet index
+ *
+ * @param int $pIndex Active sheet index
+ * @throws Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setActiveSheetIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_workSheetCollection) - 1) {
+ throw new Exception("Active sheet index is out of bounds.");
+ } else {
+ $this->_activeSheetIndex = $pIndex;
+ }
+ return $this->getActiveSheet();
+ }
+
+ /**
+ * Set active sheet index by name
+ *
+ * @param string $pValue Sheet title
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function setActiveSheetIndexByName($pValue = '')
+ {
+ if (($worksheet = $this->getSheetByName($pValue)) instanceof PHPExcel_Worksheet) {
+ $this->setActiveSheetIndex($worksheet->getParent()->getIndex($worksheet));
+ return $worksheet;
+ }
+
+ throw new Exception('Workbook does not contain sheet:' . $pValue);
+ }
+
+ /**
+ * Get sheet names
+ *
+ * @return string[]
+ */
+ public function getSheetNames()
+ {
+ $returnValue = array();
+ $worksheetCount = $this->getSheetCount();
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ array_push($returnValue, $this->getSheet($i)->getTitle());
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Add external sheet
+ *
+ * @param PHPExcel_Worksheet $pSheet External sheet to add
+ * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
+ * @throws Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function addExternalSheet(PHPExcel_Worksheet $pSheet, $iSheetIndex = null) {
+ if (!is_null($this->getSheetByName($pSheet->getTitle()))) {
+ throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first.");
+ }
+
+ // count how many cellXfs there are in this workbook currently, we will need this below
+ $countCellXfs = count($this->_cellXfCollection);
+
+ // copy all the shared cellXfs from the external workbook and append them to the current
+ foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
+ $this->addCellXf(clone $cellXf);
+ }
+
+ // move sheet to this workbook
+ $pSheet->rebindParent($this);
+
+ // update the cellXfs
+ foreach ($pSheet->getCellCollection(false) as $cellID) {
+ $cell = $pSheet->getCell($cellID);
+ $cell->setXfIndex( $cell->getXfIndex() + $countCellXfs );
+ }
+
+ return $this->addSheet($pSheet, $iSheetIndex);
+ }
+
+ /**
+ * Get named ranges
+ *
+ * @return PHPExcel_NamedRange[]
+ */
+ public function getNamedRanges() {
+ return $this->_namedRanges;
+ }
+
+ /**
+ * Add named range
+ *
+ * @param PHPExcel_NamedRange $namedRange
+ * @return PHPExcel
+ */
+ public function addNamedRange(PHPExcel_NamedRange $namedRange) {
+ if ($namedRange->getScope() == null) {
+ // global scope
+ $this->_namedRanges[$namedRange->getName()] = $namedRange;
+ } else {
+ // local scope
+ $this->_namedRanges[$namedRange->getScope()->getTitle().'!'.$namedRange->getName()] = $namedRange;
+ }
+ return true;
+ }
+
+ /**
+ * Get named range
+ *
+ * @param string $namedRange
+ * @param PHPExcel_Worksheet|null $pSheet Scope. Use null for global scope
+ * @return PHPExcel_NamedRange|null
+ */
+ public function getNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) {
+ $returnValue = null;
+
+ if ($namedRange != '' && !is_null($namedRange)) {
+ // first look for global defined name
+ if (isset($this->_namedRanges[$namedRange])) {
+ $returnValue = $this->_namedRanges[$namedRange];
+ }
+
+ // then look for local defined name (has priority over global defined name if both names exist)
+ if (!is_null($pSheet) && isset($this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
+ $returnValue = $this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange];
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Remove named range
+ *
+ * @param string $namedRange
+ * @param PHPExcel_Worksheet|null $pSheet. Scope. Use null for global scope.
+ * @return PHPExcel
+ */
+ public function removeNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) {
+ if (is_null($pSheet)) {
+ if (isset($this->_namedRanges[$namedRange])) {
+ unset($this->_namedRanges[$namedRange]);
+ }
+ } else {
+ if (isset($this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
+ unset($this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange]);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get worksheet iterator
+ *
+ * @return PHPExcel_WorksheetIterator
+ */
+ public function getWorksheetIterator() {
+ return new PHPExcel_WorksheetIterator($this);
+ }
+
+ /**
+ * Copy workbook (!= clone!)
+ *
+ * @return PHPExcel
+ */
+ public function copy() {
+ $copied = clone $this;
+
+ $worksheetCount = count($this->_workSheetCollection);
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ $this->_workSheetCollection[$i] = $this->_workSheetCollection[$i]->copy();
+ $this->_workSheetCollection[$i]->rebindParent($this);
+ }
+
+ return $copied;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ foreach($this as $key => $val) {
+ if (is_object($val) || (is_array($val))) {
+ $this->{$key} = unserialize(serialize($val));
+ }
+ }
+ }
+
+ /**
+ * Get the workbook collection of cellXfs
+ *
+ * @return PHPExcel_Style[]
+ */
+ public function getCellXfCollection()
+ {
+ return $this->_cellXfCollection;
+ }
+
+ /**
+ * Get cellXf by index
+ *
+ * @param int $index
+ * @return PHPExcel_Style
+ */
+ public function getCellXfByIndex($pIndex = 0)
+ {
+ return $this->_cellXfCollection[$pIndex];
+ }
+
+ /**
+ * Get cellXf by hash code
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style|false
+ */
+ public function getCellXfByHashCode($pValue = '')
+ {
+ foreach ($this->_cellXfCollection as $cellXf) {
+ if ($cellXf->getHashCode() == $pValue) {
+ return $cellXf;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get default style
+ *
+ * @return PHPExcel_Style
+ * @throws Exception
+ */
+ public function getDefaultStyle()
+ {
+ if (isset($this->_cellXfCollection[0])) {
+ return $this->_cellXfCollection[0];
+ }
+ throw new Exception('No default style found for this workbook');
+ }
+
+ /**
+ * Add a cellXf to the workbook
+ *
+ * @param PHPExcel_Style
+ */
+ public function addCellXf(PHPExcel_Style $style)
+ {
+ $this->_cellXfCollection[] = $style;
+ $style->setIndex(count($this->_cellXfCollection) - 1);
+ }
+
+ /**
+ * Remove cellXf by index. It is ensured that all cells get their xf index updated.
+ *
+ * @param int $pIndex Index to cellXf
+ * @throws Exception
+ */
+ public function removeCellXfByIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_cellXfCollection) - 1) {
+ throw new Exception("CellXf index is out of bounds.");
+ } else {
+ // first remove the cellXf
+ array_splice($this->_cellXfCollection, $pIndex, 1);
+
+ // then update cellXf indexes for cells
+ foreach ($this->_workSheetCollection as $worksheet) {
+ foreach ($worksheet->getCellCollection(false) as $cellID) {
+ $cell = $sheet->getCell($cellID);
+ $xfIndex = $cell->getXfIndex();
+ if ($xfIndex > $pIndex ) {
+ // decrease xf index by 1
+ $cell->setXfIndex($xfIndex - 1);
+ } else if ($xfIndex == $pIndex) {
+ // set to default xf index 0
+ $cell->setXfIndex(0);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the cellXf supervisor
+ *
+ * @return PHPExcel_Style
+ */
+ public function getCellXfSupervisor()
+ {
+ return $this->_cellXfSupervisor;
+ }
+
+ /**
+ * Get the workbook collection of cellStyleXfs
+ *
+ * @return PHPExcel_Style[]
+ */
+ public function getCellStyleXfCollection()
+ {
+ return $this->_cellStyleXfCollection;
+ }
+
+ /**
+ * Get cellStyleXf by index
+ *
+ * @param int $pIndex
+ * @return PHPExcel_Style
+ */
+ public function getCellStyleXfByIndex($pIndex = 0)
+ {
+ return $this->_cellStyleXfCollection[$pIndex];
+ }
+
+ /**
+ * Get cellStyleXf by hash code
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style|false
+ */
+ public function getCellStyleXfByHashCode($pValue = '')
+ {
+ foreach ($this->_cellXfStyleCollection as $cellStyleXf) {
+ if ($cellStyleXf->getHashCode() == $pValue) {
+ return $cellStyleXf;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a cellStyleXf to the workbook
+ *
+ * @param PHPExcel_Style $pStyle
+ */
+ public function addCellStyleXf(PHPExcel_Style $pStyle)
+ {
+ $this->_cellStyleXfCollection[] = $pStyle;
+ $pStyle->setIndex(count($this->_cellStyleXfCollection) - 1);
+ }
+
+ /**
+ * Remove cellStyleXf by index
+ *
+ * @param int $pIndex
+ * @throws Exception
+ */
+ public function removeCellStyleXfByIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_cellStyleXfCollection) - 1) {
+ throw new Exception("CellStyleXf index is out of bounds.");
+ } else {
+ array_splice($this->_cellStyleXfCollection, $pIndex, 1);
+ }
+ }
+
+ /**
+ * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
+ * and columns in the workbook
+ */
+ public function garbageCollect()
+ {
+ // how many references are there to each cellXf ?
+ $countReferencesCellXf = array();
+ foreach ($this->_cellXfCollection as $index => $cellXf) {
+ $countReferencesCellXf[$index] = 0;
+ }
+
+ foreach ($this->getWorksheetIterator() as $sheet) {
+
+ // from cells
+ foreach ($sheet->getCellCollection(false) as $cellID) {
+ $cell = $sheet->getCell($cellID);
+ ++$countReferencesCellXf[$cell->getXfIndex()];
+ }
+
+ // from row dimensions
+ foreach ($sheet->getRowDimensions() as $rowDimension) {
+ if ($rowDimension->getXfIndex() !== null) {
+ ++$countReferencesCellXf[$rowDimension->getXfIndex()];
+ }
+ }
+
+ // from column dimensions
+ foreach ($sheet->getColumnDimensions() as $columnDimension) {
+ ++$countReferencesCellXf[$columnDimension->getXfIndex()];
+ }
+ }
+
+ // remove cellXfs without references and create mapping so we can update xfIndex
+ // for all cells and columns
+ $countNeededCellXfs = 0;
+ foreach ($this->_cellXfCollection as $index => $cellXf) {
+ if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
+ ++$countNeededCellXfs;
+ } else {
+ unset($this->_cellXfCollection[$index]);
+ }
+ $map[$index] = $countNeededCellXfs - 1;
+ }
+ $this->_cellXfCollection = array_values($this->_cellXfCollection);
+
+ // update the index for all cellXfs
+ foreach ($this->_cellXfCollection as $i => $cellXf) {
+ $cellXf->setIndex($i);
+ }
+
+ // make sure there is always at least one cellXf (there should be)
+ if (count($this->_cellXfCollection) == 0) {
+ $this->_cellXfCollection[] = new PHPExcel_Style();
+ }
+
+ // 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);
+ $cell->setXfIndex( $map[$cell->getXfIndex()] );
+ }
+
+ // for all row dimensions
+ foreach ($sheet->getRowDimensions() as $rowDimension) {
+ if ($rowDimension->getXfIndex() !== null) {
+ $rowDimension->setXfIndex( $map[$rowDimension->getXfIndex()] );
+ }
+ }
+
+ // for all column dimensions
+ foreach ($sheet->getColumnDimensions() as $columnDimension) {
+ $columnDimension->setXfIndex( $map[$columnDimension->getXfIndex()] );
+ }
+ }
+
+ // also do garbage collection for all the sheets
+ foreach ($this->getWorksheetIterator() as $sheet) {
+ $sheet->garbageCollect();
+ }
+ }
+
+}
diff --git a/Classes/PHPExcel/Autoloader.php b/Classes/PHPExcel/Autoloader.php
new file mode 100644
index 0000000..9135bc9
--- /dev/null
+++ b/Classes/PHPExcel/Autoloader.php
@@ -0,0 +1,51 @@
+_currentObject->detach();
+
+ if (!apc_store($this->_cachePrefix.$this->_currentObjectID.'.cache',serialize($this->_currentObject),$this->_cacheTime)) {
+ $this->__destruct();
+ throw new Exception('Failed to store cell '.$cellID.' in APC');
+ }
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+ $this->_cellCache[$pCoord] = true;
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell?
+ *
+ * @param string $pCoord Coordinate address of the cell to check
+ * @return void
+ * @return boolean
+ */
+ 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 Exception('Cell entry '.$cellID.' no longer exists in APC');
+ }
+ return true;
+ }
+ return false;
+ } // function isDataSet()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 Exception('Cell entry '.$cellID.' no longer exists in APC');
+ }
+ } 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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ /**
+ * Delete a cell in cache identified by coordinate address
+ *
+ * @param string $pCoord Coordinate address of the cell to delete
+ * @throws 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);
+ } // function deleteCacheData()
+
+
+ /**
+ * Clone the cell collection
+ *
+ * @return void
+ */
+ public function copyCellCollection(PHPExcel_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 Exception('Cell entry '.$cellID.' no longer exists in APC');
+ }
+ if (!apc_store($newCachePrefix.$cellID.'.cache',$obj,$this->_cacheTime)) {
+ $this->__destruct();
+ throw new Exception('Failed to store cell '.$cellID.' in APC');
+ }
+ }
+ }
+ $this->_cachePrefix = $newCachePrefix;
+ } // function copyCellCollection()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+
+ // Flush the APC cache
+ $this->__destruct();
+
+ $this->_cellCache = array();
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->_parent = null;
+ } // function unsetWorksheetCells()
+
+
+ public function __construct(PHPExcel_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);
+ }
+ } // function __construct()
+
+
+ public function __destruct() {
+ $cacheList = $this->getCellList();
+ foreach($cacheList as $cellID) {
+ apc_delete($this->_cachePrefix.$cellID.'.cache');
+ }
+ } // function __destruct()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/CacheBase.php b/Classes/PHPExcel/CachedObjectStorage/CacheBase.php
new file mode 100644
index 0000000..7372db2
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/CacheBase.php
@@ -0,0 +1,174 @@
+_parent = $parent;
+ } // function __construct()
+
+
+ /**
+ * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell?
+ *
+ * @param string $pCoord Coordinate address of the cell to check
+ * @return void
+ * @return boolean
+ */
+ public function isDataSet($pCoord) {
+ if ($pCoord === $this->_currentObjectID) {
+ return true;
+ }
+ // Check if the requested entry exists in the cache
+ return isset($this->_cellCache[$pCoord]);
+ } // function isDataSet()
+
+
+ /**
+ * Add or Update a cell in cache
+ *
+ * @param PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function updateCacheData(PHPExcel_Cell $cell) {
+ $pCoord = $cell->getCoordinate();
+
+ return $this->addCacheData($pCoord,$cell);
+ } // function updateCacheData()
+
+
+ /**
+ * Delete a cell in cache identified by coordinate address
+ *
+ * @param string $pCoord Coordinate address of the cell to delete
+ * @throws Exception
+ */
+ public function deleteCacheData($pCoord) {
+ if ($pCoord === $this->_currentObjectID) {
+ $this->_currentObject->detach();
+ $this->_currentObjectID = $this->_currentObject = null;
+ }
+
+ if (is_object($this->_cellCache[$pCoord])) {
+ $this->_cellCache[$pCoord]->detach();
+ unset($this->_cellCache[$pCoord]);
+ }
+ } // function deleteCacheData()
+
+
+ /**
+ * Get a list of all cell addresses currently held in cache
+ *
+ * @return array of string
+ */
+ public function getCellList() {
+ return array_keys($this->_cellCache);
+ } // function getCellList()
+
+
+ /**
+ * Sort the list of all cell addresses currently held in cache by row and column
+ *
+ * @return void
+ */
+ public function getSortedCellList() {
+ $sortKeys = array();
+ foreach ($this->_cellCache as $coord => $value) {
+ list($colNum,$rowNum) = sscanf($coord,'%[A-Z]%d');
+ $sortKeys[sprintf('%09d%3s',$rowNum,$colNum)] = $coord;
+ }
+ ksort($sortKeys);
+
+ return array_values($sortKeys);
+ } // function sortCellList()
+
+
+ protected function _getUniqueID() {
+ if (function_exists('posix_getpid')) {
+ $baseUnique = posix_getpid();
+ } else {
+ $baseUnique = mt_rand();
+ }
+ return uniqid($baseUnique,true);
+ }
+
+ /**
+ * Clone the cell collection
+ *
+ * @return void
+ */
+ public function copyCellCollection(PHPExcel_Worksheet $parent) {
+ $this->_parent = $parent;
+ if ((!is_null($this->_currentObject)) && (is_object($this->_currentObject))) {
+ $this->_currentObject->attach($parent);
+ }
+ } // function copyCellCollection()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php b/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php
new file mode 100644
index 0000000..59690bb
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/DiscISAM.php
@@ -0,0 +1,157 @@
+_currentObject->detach();
+
+ fseek($this->_fileHandle,0,SEEK_END);
+ $offset = ftell($this->_fileHandle);
+ fwrite($this->_fileHandle, serialize($this->_currentObject));
+ $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset,
+ 'sz' => ftell($this->_fileHandle) - $offset
+ );
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ /**
+ * Clone the cell collection
+ *
+ * @return void
+ */
+ public function copyCellCollection(PHPExcel_Worksheet $parent) {
+ parent::copyCellCollection($parent);
+ // Get a new id for the new file name
+ $baseUnique = $this->_getUniqueID();
+ $newFileName = sys_get_temp_dir().'/PHPExcel.'.$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+');
+ } // function copyCellCollection()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+ $this->_cellCache = array();
+
+ // 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();
+ } // function unsetWorksheetCells()
+
+
+ public function __construct(PHPExcel_Worksheet $parent) {
+ parent::__construct($parent);
+ if (is_null($this->_fileHandle)) {
+ $baseUnique = $this->_getUniqueID();
+ $this->_fileName = sys_get_temp_dir().'/PHPExcel.'.$baseUnique.'.cache';
+ $this->_fileHandle = fopen($this->_fileName,'a+');
+ }
+ } // function __construct()
+
+
+ public function __destruct() {
+ if (!is_null($this->_fileHandle)) {
+ fclose($this->_fileHandle);
+ unlink($this->_fileName);
+ }
+ $this->_fileHandle = null;
+ } // function __destruct()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/ICache.php b/Classes/PHPExcel/CachedObjectStorage/ICache.php
new file mode 100644
index 0000000..3856135
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/ICache.php
@@ -0,0 +1,104 @@
+_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 Exception('Failed to store cell '.$cellID.' in MemCache');
+ }
+ }
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+ $this->_cellCache[$pCoord] = true;
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell?
+ *
+ * @param string $pCoord Coordinate address of the cell to check
+ * @return void
+ * @return boolean
+ */
+ 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 Exception('Cell entry '.$cellID.' no longer exists in MemCache');
+ }
+ return true;
+ }
+ return false;
+ } // function isDataSet()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 Exception('Cell entry '.$cellID.' 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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ /**
+ * Delete a cell in cache identified by coordinate address
+ *
+ * @param string $pCoord Coordinate address of the cell to delete
+ * @throws 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);
+ } // function deleteCacheData()
+
+
+ /**
+ * Clone the cell collection
+ *
+ * @return void
+ */
+ public function copyCellCollection(PHPExcel_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 Exception('Cell entry '.$cellID.' no longer exists in MemCache');
+ }
+ if (!$this->_memcache->add($newCachePrefix.$cellID.'.cache',$obj,NULL,$this->_cacheTime)) {
+ $this->__destruct();
+ throw new Exception('Failed to store cell '.$cellID.' in MemCache');
+ }
+ }
+ }
+ $this->_cachePrefix = $newCachePrefix;
+ } // function copyCellCollection()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+
+ // Flush the Memcache cache
+ $this->__destruct();
+
+ $this->_cellCache = array();
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->_parent = null;
+ } // function unsetWorksheetCells()
+
+
+ public function __construct(PHPExcel_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, array($this, 'failureCallback'))) {
+ throw new Exception('Could not connect to MemCache server at '.$memcacheServer.':'.$memcachePort);
+ }
+ $this->_cacheTime = $cacheTime;
+
+ parent::__construct($parent);
+ }
+ } // function __construct()
+
+
+ public function failureCallback($host, $port) {
+ throw new Exception('memcache '.$host.':'.$port.' failed');
+ }
+
+
+ public function __destruct() {
+ $cacheList = $this->getCellList();
+ foreach($cacheList as $cellID) {
+ $this->_memcache->delete($this->_cachePrefix.$cellID.'.cache');
+ }
+ } // function __destruct()
+
+}
+
+
+?>
diff --git a/Classes/PHPExcel/CachedObjectStorage/Memory.php b/Classes/PHPExcel/CachedObjectStorage/Memory.php
new file mode 100644
index 0000000..8595070
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/Memory.php
@@ -0,0 +1,85 @@
+_cellCache[$pCoord] = $cell;
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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])) {
+ // Return null if requested entry doesn't exist in cache
+ return null;
+ }
+
+ // Return requested entry
+ return $this->_cellCache[$pCoord];
+ } // function getCacheData()
+
+
+ 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 = array();
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->_parent = null;
+ } // function unsetWorksheetCells()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php b/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php
new file mode 100644
index 0000000..45b6a15
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/MemoryGZip.php
@@ -0,0 +1,107 @@
+_currentObject->detach();
+
+ $this->_cellCache[$this->_currentObjectID] = gzdeflate(serialize($this->_currentObject));
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+ $this->_cellCache = array();
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->_parent = null;
+ } // function unsetWorksheetCells()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php b/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php
new file mode 100644
index 0000000..c96f34f
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/MemorySerialized.php
@@ -0,0 +1,107 @@
+_currentObject->detach();
+
+ $this->_cellCache[$this->_currentObjectID] = serialize($this->_currentObject);
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+ $this->_cellCache = array();
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->_parent = null;
+ } // function unsetWorksheetCells()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php b/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php
new file mode 100644
index 0000000..10bd5a8
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/PHPTemp.php
@@ -0,0 +1,157 @@
+_currentObject->detach();
+
+ fseek($this->_fileHandle,0,SEEK_END);
+ $offset = ftell($this->_fileHandle);
+ fwrite($this->_fileHandle, serialize($this->_currentObject));
+ $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset,
+ 'sz' => ftell($this->_fileHandle) - $offset
+ );
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ /**
+ * Clone the cell collection
+ *
+ * @return void
+ */
+ public function copyCellCollection(PHPExcel_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;
+ } // function copyCellCollection()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+ $this->_cellCache = array();
+
+ // 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();
+ } // function unsetWorksheetCells()
+
+
+ public function __construct(PHPExcel_Worksheet $parent, $memoryCacheSize = '1MB') {
+ $this->_memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : '1MB';
+
+ parent::__construct($parent);
+ if (is_null($this->_fileHandle)) {
+ $this->_fileHandle = fopen('php://temp/maxmemory:'.$this->_memoryCacheSize,'a+');
+ }
+ } // function __construct()
+
+
+ public function __destruct() {
+ if (!is_null($this->_fileHandle)) {
+ fclose($this->_fileHandle);
+ }
+ $this->_fileHandle = null;
+ } // function __destruct()
+
+}
diff --git a/Classes/PHPExcel/CachedObjectStorage/Wincache.php b/Classes/PHPExcel/CachedObjectStorage/Wincache.php
new file mode 100644
index 0000000..2f10aea
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorage/Wincache.php
@@ -0,0 +1,233 @@
+_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 Exception('Failed to store cell '.$cellID.' in WinCache');
+ }
+ } else {
+ if (!wincache_ucache_add($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime)) {
+ $this->__destruct();
+ throw new Exception('Failed to store cell '.$cellID.' in WinCache');
+ }
+ }
+
+ $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 PHPExcel_Cell $cell Cell to update
+ * @return void
+ * @throws Exception
+ */
+ public function addCacheData($pCoord, PHPExcel_Cell $cell) {
+ if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
+ $this->_storeData();
+ }
+ $this->_cellCache[$pCoord] = true;
+
+ $this->_currentObjectID = $pCoord;
+ $this->_currentObject = $cell;
+
+ return $cell;
+ } // function addCacheData()
+
+
+ /**
+ * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell?
+ *
+ * @param string $pCoord Coordinate address of the cell to check
+ * @return void
+ * @return boolean
+ */
+ 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 Exception('Cell entry '.$cellID.' no longer exists in WinCache');
+ }
+ return true;
+ }
+ return false;
+ } // function isDataSet()
+
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoord Coordinate of the cell
+ * @throws Exception
+ * @return PHPExcel_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 Exception('Cell entry '.$cellID.' 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 the parent worksheet
+ $this->_currentObject->attach($this->_parent);
+
+ // Return requested entry
+ return $this->_currentObject;
+ } // function getCacheData()
+
+
+ /**
+ * Delete a cell in cache identified by coordinate address
+ *
+ * @param string $pCoord Coordinate address of the cell to delete
+ * @throws 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);
+ } // function deleteCacheData()
+
+
+ /**
+ * Clone the cell collection
+ *
+ * @return void
+ */
+ public function copyCellCollection(PHPExcel_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 Exception('Cell entry '.$cellID.' no longer exists in Wincache');
+ }
+ if (!wincache_ucache_add($newCachePrefix.$cellID.'.cache', $obj, $this->_cacheTime)) {
+ $this->__destruct();
+ throw new Exception('Failed to store cell '.$cellID.' in Wincache');
+ }
+ }
+ }
+ $this->_cachePrefix = $newCachePrefix;
+ } // function copyCellCollection()
+
+
+ public function unsetWorksheetCells() {
+ if(!is_null($this->_currentObject)) {
+ $this->_currentObject->detach();
+ $this->_currentObject = $this->_currentObjectID = null;
+ }
+
+ // Flush the WinCache cache
+ $this->__destruct();
+
+ $this->_cellCache = array();
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->_parent = null;
+ } // function unsetWorksheetCells()
+
+
+ public function __construct(PHPExcel_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);
+ }
+ } // function __construct()
+
+
+ public function __destruct() {
+ $cacheList = $this->getCellList();
+ foreach($cacheList as $cellID) {
+ wincache_ucache_delete($this->_cachePrefix.$cellID.'.cache');
+ }
+ } // function __destruct()
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/Classes/PHPExcel/CachedObjectStorageFactory.php b/Classes/PHPExcel/CachedObjectStorageFactory.php
new file mode 100644
index 0000000..d4c9479
--- /dev/null
+++ b/Classes/PHPExcel/CachedObjectStorageFactory.php
@@ -0,0 +1,131 @@
+ array(
+ ),
+ self::cache_in_memory_gzip => array(
+ ),
+ self::cache_in_memory_serialized => array(
+ ),
+ self::cache_to_phpTemp => array( 'memoryCacheSize' => '1MB'
+ ),
+ self::cache_to_discISAM => array(
+ ),
+ self::cache_to_apc => array( 'cacheTime' => 600
+ ),
+ self::cache_to_memcache => array( 'memcacheServer' => 'localhost',
+ 'memcachePort' => 11211,
+ 'cacheTime' => 600
+ ),
+ self::cache_to_wincache => array( 'cacheTime' => 600
+ )
+ );
+
+
+ private static $_storageMethodParameters = array();
+
+
+ public static function getCacheStorageMethod() {
+ if (!is_null(self::$_cacheStorageMethod)) {
+ return self::$_cacheStorageMethod;
+ }
+ return null;
+ } // function getCacheStorageMethod()
+
+
+ public static function getCacheStorageClass() {
+ if (!is_null(self::$_cacheStorageClass)) {
+ return self::$_cacheStorageClass;
+ }
+ return null;
+ } // function getCacheStorageClass()
+
+
+ public static function getCacheStorageMethods() {
+ return self::$_storageMethods;
+ } // function getCacheStorageMethods()
+
+
+ public static function initialize($method = self::cache_in_memory, $arguments = array()) {
+ if (!in_array($method,self::$_storageMethods)) {
+ return false;
+ }
+
+ switch($method) {
+ case self::cache_to_apc :
+ if (!function_exists('apc_store')) {
+ return false;
+ }
+ if (apc_sma_info() === false) {
+ return false;
+ }
+ break;
+ case self::cache_to_memcache :
+ if (!function_exists('memcache_add')) {
+ return false;
+ }
+ break;
+ case self::cache_to_wincache :
+ if (!function_exists('wincache_ucache_add')) {
+ return false;
+ }
+ break;
+ }
+
+ self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method];
+ foreach($arguments as $k => $v) {
+ if (isset(self::$_storageMethodParameters[$method][$k])) {
+ self::$_storageMethodParameters[$method][$k] = $v;
+ }
+ }
+
+ if (is_null(self::$_cacheStorageMethod)) {
+ self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method;
+ self::$_cacheStorageMethod = $method;
+ }
+ return true;
+ } // function initialize()
+
+
+ public static function getInstance(PHPExcel_Worksheet $parent) {
+ if (is_null(self::$_cacheStorageMethod)) {
+ self::initialize();
+ }
+
+ $instance = new self::$_cacheStorageClass($parent,self::$_storageMethodParameters[self::$_cacheStorageMethod]);
+ if (!is_null($instance)) {
+ return $instance;
+ }
+
+ return false;
+ } // function getInstance()
+
+}
\ No newline at end of file
diff --git a/Classes/PHPExcel/Calculation.php b/Classes/PHPExcel/Calculation.php
new file mode 100644
index 0000000..d7ef873
--- /dev/null
+++ b/Classes/PHPExcel/Calculation.php
@@ -0,0 +1,3702 @@
+', '<', '=', '>=', '<=', '<>', '|', ':');
+
+
+ /**
+ * List of binary operators (those that expect two operands)
+ *
+ * @access private
+ * @var array
+ */
+ private static $_binaryOperators = array('+', '-', '*', '/', '^', '&', '>', '<', '=', '>=', '<=', '<>', '|', ':');
+
+ /**
+ * Flag to determine how formula errors should be handled
+ * If true, then a user error will be triggered
+ * If false, then an exception will be thrown
+ *
+ * @access public
+ * @var boolean
+ *
+ */
+ public $suppressFormulaErrors = false;
+
+ /**
+ * Error message for any error that was raised/thrown by the calculation engine
+ *
+ * @access public
+ * @var string
+ *
+ */
+ public $formulaError = null;
+
+ /**
+ * Flag to determine whether a debug log should be generated by the calculation engine
+ * If true, then a debug log will be generated
+ * If false, then a debug log will not be generated
+ *
+ * @access public
+ * @var boolean
+ *
+ */
+ public $writeDebugLog = false;
+
+ /**
+ * An array of the nested cell references accessed by the calculation engine, used for the debug log
+ *
+ * @access private
+ * @var array of string
+ *
+ */
+ private $debugLogStack = array();
+
+ /**
+ * The debug log generated by the calculation engine
+ *
+ * @access public
+ * @var array of string
+ *
+ */
+ public $debugLog = array();
+ private $_cyclicFormulaCount = 0;
+ private $_cyclicFormulaCell = '';
+ public $cyclicFormulaCount = 0;
+
+
+ private static $_localeLanguage = 'en_us'; // US English (default locale)
+ private static $_validLocaleLanguages = array( 'en' // English (default language)
+ );
+ private static $_localeArgumentSeparator = ',';
+ private static $_localeFunctions = array();
+ private static $_localeBoolean = array( 'TRUE' => 'TRUE',
+ 'FALSE' => 'FALSE',
+ 'NULL' => 'NULL'
+ );
+
+
+ // Constant conversion from text name/value to actual (datatyped) value
+ private static $_ExcelConstants = array('TRUE' => True,
+ 'FALSE' => False,
+ 'NULL' => Null
+ );
+
+ // PHPExcel functions
+ private static $_PHPExcelFunctions = array( // PHPExcel functions
+ 'ABS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'abs',
+ 'argumentCount' => '1'
+ ),
+ 'ACCRINT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ACCRINT',
+ 'argumentCount' => '4-7'
+ ),
+ 'ACCRINTM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ACCRINTM',
+ 'argumentCount' => '3-5'
+ ),
+ 'ACOS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'acos',
+ 'argumentCount' => '1'
+ ),
+ 'ACOSH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'acosh',
+ 'argumentCount' => '1'
+ ),
+ 'ADDRESS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CELL_ADDRESS',
+ 'argumentCount' => '2-5'
+ ),
+ 'AMORDEGRC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AMORDEGRC',
+ 'argumentCount' => '6,7'
+ ),
+ 'AMORLINC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AMORLINC',
+ 'argumentCount' => '6,7'
+ ),
+ 'AND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_AND',
+ 'argumentCount' => '1+'
+ ),
+ 'AREAS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'ASC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'ASIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'asin',
+ 'argumentCount' => '1'
+ ),
+ 'ASINH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'asinh',
+ 'argumentCount' => '1'
+ ),
+ 'ATAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'atan',
+ 'argumentCount' => '1'
+ ),
+ 'ATAN2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::REVERSE_ATAN2',
+ 'argumentCount' => '2'
+ ),
+ 'ATANH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'atanh',
+ 'argumentCount' => '1'
+ ),
+ 'AVEDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AVEDEV',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AVERAGE',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGEA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AVERAGEA',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGEIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2,3'
+ ),
+ 'AVERAGEIFS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3+'
+ ),
+ 'BAHTTEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'BESSELI' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELI',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELJ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELJ',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELK',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELY',
+ 'argumentCount' => '2'
+ ),
+ 'BETADIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BETADIST',
+ 'argumentCount' => '3-5'
+ ),
+ 'BETAINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BETAINV',
+ 'argumentCount' => '3-5'
+ ),
+ 'BIN2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'BIN2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'BIN2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'BINOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINOMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'CEILING' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CEILING',
+ 'argumentCount' => '2'
+ ),
+ 'CELL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1,2'
+ ),
+ 'CHAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CHARACTER',
+ 'argumentCount' => '1'
+ ),
+ 'CHIDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CHIDIST',
+ 'argumentCount' => '2'
+ ),
+ 'CHIINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CHIINV',
+ 'argumentCount' => '2'
+ ),
+ 'CHITEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'CHOOSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CHOOSE',
+ 'argumentCount' => '2+'
+ ),
+ 'CLEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRIMNONPRINTABLE',
+ 'argumentCount' => '1'
+ ),
+ 'CODE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ASCIICODE',
+ 'argumentCount' => '1'
+ ),
+ 'COLUMN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COLUMN',
+ 'argumentCount' => '-1',
+ 'passByReference' => array(true)
+ ),
+ 'COLUMNS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COLUMNS',
+ 'argumentCount' => '1'
+ ),
+ 'COMBIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COMBIN',
+ 'argumentCount' => '2'
+ ),
+ 'COMPLEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COMPLEX',
+ 'argumentCount' => '2,3'
+ ),
+ 'CONCATENATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CONCATENATE',
+ 'argumentCount' => '1+'
+ ),
+ 'CONFIDENCE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CONFIDENCE',
+ 'argumentCount' => '3'
+ ),
+ 'CONVERT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CONVERTUOM',
+ 'argumentCount' => '3'
+ ),
+ 'CORREL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CORREL',
+ 'argumentCount' => '2'
+ ),
+ 'COS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'cos',
+ 'argumentCount' => '1'
+ ),
+ 'COSH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'cosh',
+ 'argumentCount' => '1'
+ ),
+ 'COUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUNT',
+ 'argumentCount' => '1+'
+ ),
+ 'COUNTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUNTA',
+ 'argumentCount' => '1+'
+ ),
+ 'COUNTBLANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUNTBLANK',
+ 'argumentCount' => '1'
+ ),
+ 'COUNTIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUNTIF',
+ 'argumentCount' => '2'
+ ),
+ 'COUNTIFS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'COUPDAYBS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPDAYBS',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPDAYS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPDAYS',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPDAYSNC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPDAYSNC',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPNCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPNCD',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPNUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPNUM',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPPCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPPCD',
+ 'argumentCount' => '3,4'
+ ),
+ 'COVAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COVAR',
+ 'argumentCount' => '2'
+ ),
+ 'CRITBINOM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CRITBINOM',
+ 'argumentCount' => '3'
+ ),
+ 'CUBEKPIMEMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBEMEMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBEMEMBERPROPERTY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBERANKEDMEMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBESET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBESETCOUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUMIPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CUMIPMT',
+ 'argumentCount' => '6'
+ ),
+ 'CUMPRINC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CUMPRINC',
+ 'argumentCount' => '6'
+ ),
+ 'DATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATE',
+ 'argumentCount' => '3'
+ ),
+ 'DATEDIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATEDIF',
+ 'argumentCount' => '2,3'
+ ),
+ 'DATEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATEVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'DAVERAGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DAYOFMONTH',
+ 'argumentCount' => '1'
+ ),
+ 'DAYS360' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DAYS360',
+ 'argumentCount' => '2,3'
+ ),
+ 'DB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DB',
+ 'argumentCount' => '4,5'
+ ),
+ 'DCOUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DCOUNTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DDB',
+ 'argumentCount' => '4,5'
+ ),
+ 'DEC2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DECTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEC2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DECTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEC2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DECTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEGREES' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'rad2deg',
+ 'argumentCount' => '1'
+ ),
+ 'DELTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DELTA',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEVSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DEVSQ',
+ 'argumentCount' => '1+'
+ ),
+ 'DGET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'DMAX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DMIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DOLLAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DOLLAR',
+ 'argumentCount' => '1,2'
+ ),
+ 'DOLLARDE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DOLLARDE',
+ 'argumentCount' => '2'
+ ),
+ 'DOLLARFR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DOLLARFR',
+ 'argumentCount' => '2'
+ ),
+ 'DPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DSTDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DSTDEVP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DURATION' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '5,6'
+ ),
+ 'DVAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'DVARP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'EDATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EDATE',
+ 'argumentCount' => '2'
+ ),
+ 'EFFECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EFFECT',
+ 'argumentCount' => '2'
+ ),
+ 'EOMONTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EOMONTH',
+ 'argumentCount' => '2'
+ ),
+ 'ERF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ERF',
+ 'argumentCount' => '1,2'
+ ),
+ 'ERFC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ERFC',
+ 'argumentCount' => '1'
+ ),
+ 'ERROR.TYPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ERROR_TYPE',
+ 'argumentCount' => '1'
+ ),
+ 'EVEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EVEN',
+ 'argumentCount' => '1'
+ ),
+ 'EXACT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'EXP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'exp',
+ 'argumentCount' => '1'
+ ),
+ 'EXPONDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EXPONDIST',
+ 'argumentCount' => '3'
+ ),
+ 'FACT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FACT',
+ 'argumentCount' => '1'
+ ),
+ 'FACTDOUBLE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FACTDOUBLE',
+ 'argumentCount' => '1'
+ ),
+ 'FALSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_FALSE',
+ 'argumentCount' => '0'
+ ),
+ 'FDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'FIND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SEARCHSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'FINDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SEARCHSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'FINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'FISHER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FISHER',
+ 'argumentCount' => '1'
+ ),
+ 'FISHERINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FISHERINV',
+ 'argumentCount' => '1'
+ ),
+ 'FIXED' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FIXEDFORMAT',
+ 'argumentCount' => '1-3'
+ ),
+ 'FLOOR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FLOOR',
+ 'argumentCount' => '2'
+ ),
+ 'FORECAST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FORECAST',
+ 'argumentCount' => '3'
+ ),
+ 'FREQUENCY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'FTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'FV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FV',
+ 'argumentCount' => '3-5'
+ ),
+ 'FVSCHEDULE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FVSCHEDULE',
+ 'argumentCount' => '2'
+ ),
+ 'GAMMADIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GAMMADIST',
+ 'argumentCount' => '4'
+ ),
+ 'GAMMAINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GAMMAINV',
+ 'argumentCount' => '3'
+ ),
+ 'GAMMALN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GAMMALN',
+ 'argumentCount' => '1'
+ ),
+ 'GCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GCD',
+ 'argumentCount' => '1+'
+ ),
+ 'GEOMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GEOMEAN',
+ 'argumentCount' => '1+'
+ ),
+ 'GESTEP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GESTEP',
+ 'argumentCount' => '1,2'
+ ),
+ 'GETPIVOTDATA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'GROWTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GROWTH',
+ 'argumentCount' => '1-4'
+ ),
+ 'HARMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HARMEAN',
+ 'argumentCount' => '1+'
+ ),
+ 'HEX2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HEXTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'HEX2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HEXTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'HEX2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HEXTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'HLOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'HOUR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HOUROFDAY',
+ 'argumentCount' => '1'
+ ),
+ 'HYPERLINK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HYPERLINK',
+ 'argumentCount' => '1,2',
+ 'passCellReference'=> true
+ ),
+ 'HYPGEOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HYPGEOMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'IF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STATEMENT_IF',
+ 'argumentCount' => '1-3'
+ ),
+ 'IFERROR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STATEMENT_IFERROR',
+ 'argumentCount' => '2'
+ ),
+ 'IMABS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMABS',
+ 'argumentCount' => '1'
+ ),
+ 'IMAGINARY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMAGINARY',
+ 'argumentCount' => '1'
+ ),
+ 'IMARGUMENT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMARGUMENT',
+ 'argumentCount' => '1'
+ ),
+ 'IMCONJUGATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMCONJUGATE',
+ 'argumentCount' => '1'
+ ),
+ 'IMCOS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMCOS',
+ 'argumentCount' => '1'
+ ),
+ 'IMDIV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMDIV',
+ 'argumentCount' => '2'
+ ),
+ 'IMEXP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMEXP',
+ 'argumentCount' => '1'
+ ),
+ 'IMLN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMLN',
+ 'argumentCount' => '1'
+ ),
+ 'IMLOG10' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMLOG10',
+ 'argumentCount' => '1'
+ ),
+ 'IMLOG2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMLOG2',
+ 'argumentCount' => '1'
+ ),
+ 'IMPOWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMPOWER',
+ 'argumentCount' => '2'
+ ),
+ 'IMPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMPRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'IMREAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMREAL',
+ 'argumentCount' => '1'
+ ),
+ 'IMSIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSIN',
+ 'argumentCount' => '1'
+ ),
+ 'IMSQRT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSQRT',
+ 'argumentCount' => '1'
+ ),
+ 'IMSUB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSUB',
+ 'argumentCount' => '2'
+ ),
+ 'IMSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSUM',
+ 'argumentCount' => '1+'
+ ),
+ 'INDEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INDEX',
+ 'argumentCount' => '1-4'
+ ),
+ 'INDIRECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INDIRECT',
+ 'argumentCount' => '1,2',
+ 'passCellReference'=> true
+ ),
+ 'INFO' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'INT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INTVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'INTERCEPT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INTERCEPT',
+ 'argumentCount' => '2'
+ ),
+ 'INTRATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INTRATE',
+ 'argumentCount' => '4,5'
+ ),
+ 'IPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IPMT',
+ 'argumentCount' => '4-6'
+ ),
+ 'IRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IRR',
+ 'argumentCount' => '1,2'
+ ),
+ 'ISBLANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_BLANK',
+ 'argumentCount' => '1'
+ ),
+ 'ISERR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERR',
+ 'argumentCount' => '1'
+ ),
+ 'ISERROR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERROR',
+ 'argumentCount' => '1'
+ ),
+ 'ISEVEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_EVEN',
+ 'argumentCount' => '1'
+ ),
+ 'ISLOGICAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_LOGICAL',
+ 'argumentCount' => '1'
+ ),
+ 'ISNA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NA',
+ 'argumentCount' => '1'
+ ),
+ 'ISNONTEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NONTEXT',
+ 'argumentCount' => '1'
+ ),
+ 'ISNUMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NUMBER',
+ 'argumentCount' => '1'
+ ),
+ 'ISODD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ODD',
+ 'argumentCount' => '1'
+ ),
+ 'ISPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ISPMT',
+ 'argumentCount' => '4'
+ ),
+ 'ISREF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'ISTEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_TEXT',
+ 'argumentCount' => '1'
+ ),
+ 'JIS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'KURT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::KURT',
+ 'argumentCount' => '1+'
+ ),
+ 'LARGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LARGE',
+ 'argumentCount' => '2'
+ ),
+ 'LCM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LCM',
+ 'argumentCount' => '1+'
+ ),
+ 'LEFT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LEFT',
+ 'argumentCount' => '1,2'
+ ),
+ 'LEFTB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LEFT',
+ 'argumentCount' => '1,2'
+ ),
+ 'LEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STRINGLENGTH',
+ 'argumentCount' => '1'
+ ),
+ 'LENB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STRINGLENGTH',
+ 'argumentCount' => '1'
+ ),
+ 'LINEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LINEST',
+ 'argumentCount' => '1-4'
+ ),
+ 'LN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'log',
+ 'argumentCount' => '1'
+ ),
+ 'LOG' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOG_BASE',
+ 'argumentCount' => '1,2'
+ ),
+ 'LOG10' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'log10',
+ 'argumentCount' => '1'
+ ),
+ 'LOGEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGEST',
+ 'argumentCount' => '1-4'
+ ),
+ 'LOGINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGINV',
+ 'argumentCount' => '3'
+ ),
+ 'LOGNORMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGNORMDIST',
+ 'argumentCount' => '3'
+ ),
+ 'LOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOOKUP',
+ 'argumentCount' => '2,3'
+ ),
+ 'LOWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOWERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'MATCH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MATCH',
+ 'argumentCount' => '2,3'
+ ),
+ 'MAX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MAX',
+ 'argumentCount' => '1+'
+ ),
+ 'MAXA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MAXA',
+ 'argumentCount' => '1+'
+ ),
+ 'MAXIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'MDETERM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MDETERM',
+ 'argumentCount' => '1'
+ ),
+ 'MDURATION' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '5,6'
+ ),
+ 'MEDIAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MEDIAN',
+ 'argumentCount' => '1+'
+ ),
+ 'MEDIANIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'MID' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MID',
+ 'argumentCount' => '3'
+ ),
+ 'MIDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MID',
+ 'argumentCount' => '3'
+ ),
+ 'MIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MIN',
+ 'argumentCount' => '1+'
+ ),
+ 'MINA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MINA',
+ 'argumentCount' => '1+'
+ ),
+ 'MINIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'MINUTE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MINUTEOFHOUR',
+ 'argumentCount' => '1'
+ ),
+ 'MINVERSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MINVERSE',
+ 'argumentCount' => '1'
+ ),
+ 'MIRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MIRR',
+ 'argumentCount' => '3'
+ ),
+ 'MMULT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MMULT',
+ 'argumentCount' => '2'
+ ),
+ 'MOD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MOD',
+ 'argumentCount' => '2'
+ ),
+ 'MODE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MODE',
+ 'argumentCount' => '1+'
+ ),
+ 'MONTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MONTHOFYEAR',
+ 'argumentCount' => '1'
+ ),
+ 'MROUND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MROUND',
+ 'argumentCount' => '2'
+ ),
+ 'MULTINOMIAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MULTINOMIAL',
+ 'argumentCount' => '1+'
+ ),
+ 'N' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::N',
+ 'argumentCount' => '1'
+ ),
+ 'NA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NA',
+ 'argumentCount' => '0'
+ ),
+ 'NEGBINOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NEGBINOMDIST',
+ 'argumentCount' => '3'
+ ),
+ 'NETWORKDAYS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NETWORKDAYS',
+ 'argumentCount' => '2+'
+ ),
+ 'NOMINAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NOMINAL',
+ 'argumentCount' => '2'
+ ),
+ 'NORMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'NORMINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMINV',
+ 'argumentCount' => '3'
+ ),
+ 'NORMSDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMSDIST',
+ 'argumentCount' => '1'
+ ),
+ 'NORMSINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMSINV',
+ 'argumentCount' => '1'
+ ),
+ 'NOT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_NOT',
+ 'argumentCount' => '1'
+ ),
+ 'NOW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATETIMENOW',
+ 'argumentCount' => '0'
+ ),
+ 'NPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NPER',
+ 'argumentCount' => '3-5'
+ ),
+ 'NPV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NPV',
+ 'argumentCount' => '2+'
+ ),
+ 'OCT2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OCTTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'OCT2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OCTTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'OCT2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OCTTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'ODD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ODD',
+ 'argumentCount' => '1'
+ ),
+ 'ODDFPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '8,9'
+ ),
+ 'ODDFYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '8,9'
+ ),
+ 'ODDLPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '7,8'
+ ),
+ 'ODDLYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '7,8'
+ ),
+ 'OFFSET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OFFSET',
+ 'argumentCount' => '3,5',
+ 'passCellReference'=> true,
+ 'passByReference' => array(true)
+ ),
+ 'OR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_OR',
+ 'argumentCount' => '1+'
+ ),
+ 'PEARSON' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CORREL',
+ 'argumentCount' => '2'
+ ),
+ 'PERCENTILE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PERCENTILE',
+ 'argumentCount' => '2'
+ ),
+ 'PERCENTRANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PERCENTRANK',
+ 'argumentCount' => '2,3'
+ ),
+ 'PERMUT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PERMUT',
+ 'argumentCount' => '2'
+ ),
+ 'PHONETIC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'PI' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'pi',
+ 'argumentCount' => '0'
+ ),
+ 'PMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PMT',
+ 'argumentCount' => '3-5'
+ ),
+ 'POISSON' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::POISSON',
+ 'argumentCount' => '3'
+ ),
+ 'POWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::POWER',
+ 'argumentCount' => '2'
+ ),
+ 'PPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PPMT',
+ 'argumentCount' => '4-6'
+ ),
+ 'PRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PRICE',
+ 'argumentCount' => '6,7'
+ ),
+ 'PRICEDISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PRICEDISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'PRICEMAT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PRICEMAT',
+ 'argumentCount' => '5,6'
+ ),
+ 'PROB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'PRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'PROPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PROPERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'PV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PV',
+ 'argumentCount' => '3-5'
+ ),
+ 'QUARTILE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::QUARTILE',
+ 'argumentCount' => '2'
+ ),
+ 'QUOTIENT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::QUOTIENT',
+ 'argumentCount' => '2'
+ ),
+ 'RADIANS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'deg2rad',
+ 'argumentCount' => '1'
+ ),
+ 'RAND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RAND',
+ 'argumentCount' => '0'
+ ),
+ 'RANDBETWEEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RAND',
+ 'argumentCount' => '2'
+ ),
+ 'RANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RANK',
+ 'argumentCount' => '2,3'
+ ),
+ 'RATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RATE',
+ 'argumentCount' => '3-6'
+ ),
+ 'RECEIVED' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RECEIVED',
+ 'argumentCount' => '4-5'
+ ),
+ 'REPLACE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::REPLACE',
+ 'argumentCount' => '4'
+ ),
+ 'REPLACEB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::REPLACE',
+ 'argumentCount' => '4'
+ ),
+ 'REPT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'str_repeat',
+ 'argumentCount' => '2'
+ ),
+ 'RIGHT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RIGHT',
+ 'argumentCount' => '1,2'
+ ),
+ 'RIGHTB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RIGHT',
+ 'argumentCount' => '1,2'
+ ),
+ 'ROMAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROMAN',
+ 'argumentCount' => '1,2'
+ ),
+ 'ROUND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'round',
+ 'argumentCount' => '2'
+ ),
+ 'ROUNDDOWN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROUNDDOWN',
+ 'argumentCount' => '2'
+ ),
+ 'ROUNDUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROUNDUP',
+ 'argumentCount' => '2'
+ ),
+ 'ROW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROW',
+ 'argumentCount' => '-1',
+ 'passByReference' => array(true)
+ ),
+ 'ROWS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROWS',
+ 'argumentCount' => '1'
+ ),
+ 'RSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RSQ',
+ 'argumentCount' => '2'
+ ),
+ 'RTD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1+'
+ ),
+ 'SEARCH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SEARCHINSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'SEARCHB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SEARCHINSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'SECOND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SECONDOFMINUTE',
+ 'argumentCount' => '1'
+ ),
+ 'SERIESSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SERIESSUM',
+ 'argumentCount' => '4'
+ ),
+ 'SIGN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SIGN',
+ 'argumentCount' => '1'
+ ),
+ 'SIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'sin',
+ 'argumentCount' => '1'
+ ),
+ 'SINH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'sinh',
+ 'argumentCount' => '1'
+ ),
+ 'SKEW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SKEW',
+ 'argumentCount' => '1+'
+ ),
+ 'SLN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SLN',
+ 'argumentCount' => '3'
+ ),
+ 'SLOPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SLOPE',
+ 'argumentCount' => '2'
+ ),
+ 'SMALL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SMALL',
+ 'argumentCount' => '2'
+ ),
+ 'SQRT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'sqrt',
+ 'argumentCount' => '1'
+ ),
+ 'SQRTPI' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SQRTPI',
+ 'argumentCount' => '1'
+ ),
+ 'STANDARDIZE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STANDARDIZE',
+ 'argumentCount' => '3'
+ ),
+ 'STDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEV',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEVA',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEVP',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVPA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEVPA',
+ 'argumentCount' => '1+'
+ ),
+ 'STEYX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STEYX',
+ 'argumentCount' => '2'
+ ),
+ 'SUBSTITUTE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUBSTITUTE',
+ 'argumentCount' => '3,4'
+ ),
+ 'SUBTOTAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUBTOTAL',
+ 'argumentCount' => '2+'
+ ),
+ 'SUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUM',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMIF',
+ 'argumentCount' => '2,3'
+ ),
+ 'SUMIFS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'SUMPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMPRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMSQ',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMX2MY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMX2MY2',
+ 'argumentCount' => '2'
+ ),
+ 'SUMX2PY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMX2PY2',
+ 'argumentCount' => '2'
+ ),
+ 'SUMXMY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMXMY2',
+ 'argumentCount' => '2'
+ ),
+ 'SYD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SYD',
+ 'argumentCount' => '4'
+ ),
+ 'T' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RETURNSTRING',
+ 'argumentCount' => '1'
+ ),
+ 'TAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'tan',
+ 'argumentCount' => '1'
+ ),
+ 'TANH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'tanh',
+ 'argumentCount' => '1'
+ ),
+ 'TBILLEQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TBILLEQ',
+ 'argumentCount' => '3'
+ ),
+ 'TBILLPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TBILLPRICE',
+ 'argumentCount' => '3'
+ ),
+ 'TBILLYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TBILLYIELD',
+ 'argumentCount' => '3'
+ ),
+ 'TDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TDIST',
+ 'argumentCount' => '3'
+ ),
+ 'TEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TEXTFORMAT',
+ 'argumentCount' => '2'
+ ),
+ 'TIME' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TIME',
+ 'argumentCount' => '3'
+ ),
+ 'TIMEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TIMEVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'TINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TINV',
+ 'argumentCount' => '2'
+ ),
+ 'TODAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATENOW',
+ 'argumentCount' => '0'
+ ),
+ 'TRANSPOSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRANSPOSE',
+ 'argumentCount' => '1'
+ ),
+ 'TREND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TREND',
+ 'argumentCount' => '1-4'
+ ),
+ 'TRIM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRIMSPACES',
+ 'argumentCount' => '1'
+ ),
+ 'TRIMMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRIMMEAN',
+ 'argumentCount' => '2'
+ ),
+ 'TRUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_TRUE',
+ 'argumentCount' => '0'
+ ),
+ 'TRUNC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRUNC',
+ 'argumentCount' => '1,2'
+ ),
+ 'TTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '4'
+ ),
+ 'TYPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TYPE',
+ 'argumentCount' => '1'
+ ),
+ 'UPPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::UPPERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'USDOLLAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'VALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'VAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARFunc',
+ 'argumentCount' => '1+'
+ ),
+ 'VARA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARA',
+ 'argumentCount' => '1+'
+ ),
+ 'VARP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARP',
+ 'argumentCount' => '1+'
+ ),
+ 'VARPA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARPA',
+ 'argumentCount' => '1+'
+ ),
+ 'VDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '5-7'
+ ),
+ 'VERSION' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VERSION',
+ 'argumentCount' => '0'
+ ),
+ 'VLOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VLOOKUP',
+ 'argumentCount' => '3,4'
+ ),
+ 'WEEKDAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DAYOFWEEK',
+ 'argumentCount' => '1,2'
+ ),
+ 'WEEKNUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::WEEKOFYEAR',
+ 'argumentCount' => '1,2'
+ ),
+ 'WEIBULL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::WEIBULL',
+ 'argumentCount' => '4'
+ ),
+ 'WORKDAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::WORKDAY',
+ 'argumentCount' => '2+'
+ ),
+ 'XIRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::XIRR',
+ 'argumentCount' => '2,3'
+ ),
+ 'XNPV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::XNPV',
+ 'argumentCount' => '3'
+ ),
+ 'YEAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::YEAR',
+ 'argumentCount' => '1'
+ ),
+ 'YEARFRAC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::YEARFRAC',
+ 'argumentCount' => '2,3'
+ ),
+ 'YIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '6,7'
+ ),
+ 'YIELDDISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::YIELDDISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'YIELDMAT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::YIELDMAT',
+ 'argumentCount' => '5,6'
+ ),
+ 'ZTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ZTEST',
+ 'argumentCount' => '2-3'
+ )
+ );
+
+
+ // Internal functions used for special control purposes
+ private static $_controlFunctions = array(
+ 'MKMATRIX' => array('argumentCount' => '*',
+ 'functionCall' => 'self::_mkMatrix'
+ )
+ );
+
+
+
+
+ function __construct() {
+ $localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
+ foreach (glob($localeFileDirectory.'/*',GLOB_ONLYDIR) as $filename) {
+ $filename = substr($filename,strlen($localeFileDirectory)+1);
+ if ($filename != 'en') {
+ self::$_validLocaleLanguages[] = $filename;
+ }
+ }
+ } // function __construct()
+
+
+ /**
+ * Get an instance of this class
+ *
+ * @access public
+ * @return PHPExcel_Calculation
+ */
+ public static function getInstance() {
+ if (!isset(self::$_instance) || is_null(self::$_instance)) {
+ self::$_instance = new PHPExcel_Calculation();
+ }
+
+ return self::$_instance;
+ } // function getInstance()
+
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ *
+ * @access public
+ * @throws Exception
+ */
+ public final function __clone() {
+ throw new Exception ('Cloning a Singleton is not allowed!');
+ } // function __clone()
+
+
+ /**
+ * Set the Array Return Type (Array or Value of first element in the array)
+ *
+ * @access public
+ * @param string $returnType Array return type
+ * @return boolean Success or failure
+ */
+ public static function setArrayReturnType($returnType) {
+ if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
+ ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
+ ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
+ self::$returnArrayAsType = $returnType;
+ return True;
+ }
+ return False;
+ } // function setExcelCalendar()
+
+
+ /**
+ * Return the Array Return Type (Array or Value of first element in the array)
+ *
+ * @access public
+ * @return string $returnType Array return type
+ */
+ public static function getArrayReturnType() {
+ return self::$returnArrayAsType;
+ } // function getExcelCalendar()
+
+
+ /**
+ * Is calculation caching enabled?
+ *
+ * @access public
+ * @return boolean
+ */
+ public function getCalculationCacheEnabled() {
+ return self::$_calculationCacheEnabled;
+ } // function getCalculationCacheEnabled()
+
+
+ /**
+ * Enable/disable calculation cache
+ *
+ * @access public
+ * @param boolean $pValue
+ */
+ public function setCalculationCacheEnabled($pValue = true) {
+ self::$_calculationCacheEnabled = $pValue;
+ $this->clearCalculationCache();
+ } // function setCalculationCacheEnabled()
+
+
+ /**
+ * Enable calculation cache
+ */
+ public function enableCalculationCache() {
+ $this->setCalculationCacheEnabled(true);
+ } // function enableCalculationCache()
+
+
+ /**
+ * Disable calculation cache
+ */
+ public function disableCalculationCache() {
+ $this->setCalculationCacheEnabled(false);
+ } // function disableCalculationCache()
+
+
+ /**
+ * Clear calculation cache
+ */
+ public function clearCalculationCache() {
+ self::$_calculationCache = array();
+ } // function clearCalculationCache()
+
+
+ /**
+ * Get calculation cache expiration time
+ *
+ * @return float
+ */
+ public function getCalculationCacheExpirationTime() {
+ return self::$_calculationCacheExpirationTime;
+ } // getCalculationCacheExpirationTime()
+
+
+ /**
+ * Set calculation cache expiration time
+ *
+ * @param float $pValue
+ */
+ public function setCalculationCacheExpirationTime($pValue = 2.5) {
+ self::$_calculationCacheExpirationTime = $pValue;
+ } // function setCalculationCacheExpirationTime()
+
+
+
+
+ /**
+ * Get the currently defined locale code
+ *
+ * @return string
+ */
+ public function getLocale() {
+ return self::$_localeLanguage;
+ } // function getLocale()
+
+
+ /**
+ * Set the locale code
+ *
+ * @return boolean
+ */
+ public function setLocale($locale='en_us') {
+ // Identify our locale and language
+ $language = $locale = strtolower($locale);
+ if (strpos($locale,'_') !== false) {
+ list($language) = explode('_',$locale);
+ }
+
+ // Test whether we have any language data for this language (any locale)
+ if (in_array($language,self::$_validLocaleLanguages)) {
+ // initialise language/locale settings
+ self::$_localeFunctions = array();
+ self::$_localeArgumentSeparator = ',';
+ self::$_localeBoolean = array('TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL');
+ // Default is English, if user isn't requesting english, then read the necessary data from the locale files
+ if ($locale != 'en_us') {
+ // Search for a file with a list of function names for locale
+ $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel/locale/'.str_replace('_','/',$locale).'/functions';
+ if (!file_exists($functionNamesFile)) {
+ // If there isn't a locale specific function file, look for a language specific function file
+ $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel/locale/'.$language.'/functions';
+ if (!file_exists($functionNamesFile)) {
+ return false;
+ }
+ }
+ // Retrieve the list of locale or language specific function names
+ $localeFunctions = file($functionNamesFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ foreach ($localeFunctions as $localeFunction) {
+ list($localeFunction) = explode('##',$localeFunction); // Strip out comments
+ if (strpos($localeFunction,'=') !== false) {
+ list($fName,$lfName) = explode('=',$localeFunction);
+ $fName = trim($fName);
+ $lfName = trim($lfName);
+ if ((isset(self::$_PHPExcelFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
+ self::$_localeFunctions[$fName] = $lfName;
+ }
+ }
+ }
+ // Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
+ if (isset(self::$_localeFunctions['TRUE'])) { self::$_localeBoolean['TRUE'] = self::$_localeFunctions['TRUE']; }
+ if (isset(self::$_localeFunctions['FALSE'])) { self::$_localeBoolean['FALSE'] = self::$_localeFunctions['FALSE']; }
+
+ $configFile = PHPEXCEL_ROOT . 'PHPExcel/locale/'.str_replace('_','/',$locale).'/config';
+ if (!file_exists($configFile)) {
+ $configFile = PHPEXCEL_ROOT . 'PHPExcel/locale/'.$language.'/config';
+ }
+ if (file_exists($configFile)) {
+ $localeSettings = file($configFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ foreach ($localeSettings as $localeSetting) {
+ list($localeSetting) = explode('##',$localeSetting); // Strip out comments
+ if (strpos($localeSetting,'=') !== false) {
+ list($settingName,$settingValue) = explode('=',$localeSetting);
+ $settingName = strtoupper(trim($settingName));
+ switch ($settingName) {
+ case 'ARGUMENTSEPARATOR' :
+ self::$_localeArgumentSeparator = trim($settingValue);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
+ self::$functionReplaceFromLocale = self::$functionReplaceToLocale = NULL;
+ self::$_localeLanguage = $locale;
+ return true;
+ }
+ return false;
+ } // function setLocale()
+
+
+
+ public static function _translateSeparator($fromSeparator,$toSeparator,$formula,&$inBraces) {
+ $strlen = mb_strlen($formula);
+ for ($i = 0; $i < $strlen; ++$i) {
+ $chr = mb_substr($formula,$i,1);
+ switch ($chr) {
+ case '{' : $inBraces = True;
+ break;
+ case '}' : $inBraces = False;
+ break;
+ case $fromSeparator :
+ if (!$inBraces) {
+ $formula = mb_substr($formula,0,$i).$toSeparator.mb_substr($formula,$i+1);
+ }
+ }
+ }
+ return $formula;
+ }
+
+ private static function _translateFormula($from,$to,$formula,$fromSeparator,$toSeparator) {
+ $inBraces = False;
+ // Convert any Excel function names to the required language
+ if (self::$_localeLanguage !== 'en_us') {
+ // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+ if (strpos($formula,'"') !== false) {
+ // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+ // the formula
+ $temp = explode('"',$formula);
+ foreach($temp as $i => &$value) {
+ // Only count/replace in alternate array entries
+ if (($i % 2) == 0) {
+ $value = preg_replace($from,$to,$value);
+ $value = self::_translateSeparator($fromSeparator,$toSeparator,$value,$inBraces);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $formula = implode('"',$temp);
+ } else {
+ // If there's no quoted strings, then we do a simple count/replace
+ $formula = preg_replace($from,$to,$formula);
+ $formula = self::_translateSeparator($fromSeparator,$toSeparator,$formula);
+ }
+ }
+
+ return $formula;
+ }
+
+ private static $functionReplaceFromExcel = NULL;
+ private static $functionReplaceToLocale = NULL;
+
+ public function _translateFormulaToLocale($formula) {
+ if (is_null(self::$functionReplaceFromExcel)) {
+ self::$functionReplaceFromExcel = array();
+ foreach(array_keys(self::$_localeFunctions) as $excelFunctionName) {
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui';
+ }
+ foreach(array_keys(self::$_localeBoolean) as $excelBoolean) {
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
+ }
+
+ }
+
+ if (is_null(self::$functionReplaceToLocale)) {
+ self::$functionReplaceToLocale = array();
+ foreach(array_values(self::$_localeFunctions) as $localeFunctionName) {
+ self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2';
+ }
+ foreach(array_values(self::$_localeBoolean) as $localeBoolean) {
+ self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2';
+ }
+ }
+
+ return self::_translateFormula(self::$functionReplaceFromExcel,self::$functionReplaceToLocale,$formula,',',self::$_localeArgumentSeparator);
+ } // function _translateFormulaToLocale()
+
+
+ private static $functionReplaceFromLocale = NULL;
+ private static $functionReplaceToExcel = NULL;
+
+ public function _translateFormulaToEnglish($formula) {
+ if (is_null(self::$functionReplaceFromLocale)) {
+ self::$functionReplaceFromLocale = array();
+ foreach(array_values(self::$_localeFunctions) as $localeFunctionName) {
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui';
+ }
+ foreach(array_values(self::$_localeBoolean) as $excelBoolean) {
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
+ }
+ }
+
+ if (is_null(self::$functionReplaceToExcel)) {
+ self::$functionReplaceToExcel = array();
+ foreach(array_keys(self::$_localeFunctions) as $excelFunctionName) {
+ self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2';
+ }
+ foreach(array_keys(self::$_localeBoolean) as $excelBoolean) {
+ self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2';
+ }
+ }
+
+ return self::_translateFormula(self::$functionReplaceFromLocale,self::$functionReplaceToExcel,$formula,self::$_localeArgumentSeparator,',');
+ } // function _translateFormulaToEnglish()
+
+
+ public static function _localeFunc($function) {
+ if (self::$_localeLanguage !== 'en_us') {
+ $functionName = trim($function,'(');
+ if (isset(self::$_localeFunctions[$functionName])) {
+ $brace = ($functionName != $function);
+ $function = self::$_localeFunctions[$functionName];
+ if ($brace) { $function .= '('; }
+ }
+ }
+ return $function;
+ }
+
+
+
+
+ /**
+ * Wrap string values in quotes
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ public static function _wrapResult($value) {
+ if (is_string($value)) {
+ // Error values cannot be "wrapped"
+ if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) {
+ // Return Excel errors "as is"
+ return $value;
+ }
+ // Return strings wrapped in quotes
+ return '"'.$value.'"';
+ // Convert numeric errors to NaN error
+ } else if((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return $value;
+ } // function _wrapResult()
+
+
+ /**
+ * Remove quotes used as a wrapper to identify string values
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ public static function _unwrapResult($value) {
+ if (is_string($value)) {
+ if ((strlen($value) > 0) && ($value{0} == '"') && (substr($value,-1) == '"')) {
+ return substr($value,1,-1);
+ }
+ // Convert numeric errors to NaN error
+ } else if((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return $value;
+ } // function _unwrapResult()
+
+
+
+
+ /**
+ * Calculate cell value (using formula from a cell ID)
+ * Retained for backward compatibility
+ *
+ * @access public
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @return mixed
+ * @throws Exception
+ */
+ public function calculate(PHPExcel_Cell $pCell = null) {
+ try {
+ return $this->calculateCellValue($pCell);
+ } catch (Exception $e) {
+ throw(new Exception($e->getMessage()));
+ }
+ } // function calculate()
+
+
+ /**
+ * Calculate the value of a cell formula
+ *
+ * @access public
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @param Boolean $resetLog Flag indicating whether the debug log should be reset or not
+ * @return mixed
+ * @throws Exception
+ */
+ public function calculateCellValue(PHPExcel_Cell $pCell = null, $resetLog = true) {
+ if ($resetLog) {
+ // Initialise the logging settings if requested
+ $this->formulaError = null;
+ $this->debugLog = $this->debugLogStack = array();
+ $this->_cyclicFormulaCount = 1;
+
+ $returnArrayAsType = self::$returnArrayAsType;
+ self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
+ }
+
+ // Read the formula from the cell
+ if (is_null($pCell)) {
+ return null;
+ }
+
+ if ($resetLog) {
+ self::$returnArrayAsType = $returnArrayAsType;
+ }
+ // Execute the calculation for the cell formula
+ try {
+ $result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
+ } catch (Exception $e) {
+ throw(new Exception($e->getMessage()));
+ }
+
+ if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+ $testResult = PHPExcel_Calculation_Functions::flattenArray($result);
+ if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ // If there's only a single cell in the array, then we allow it
+ if (count($testResult) != 1) {
+ // If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
+ $r = array_keys($result);
+ $r = array_shift($r);
+ if (!is_numeric($r)) { return PHPExcel_Calculation_Functions::VALUE(); }
+ if (is_array($result[$r])) {
+ $c = array_keys($result[$r]);
+ $c = array_shift($c);
+ if (!is_numeric($c)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ }
+ $result = array_shift($testResult);
+ }
+
+ if (is_null($result)) {
+ return 0;
+ } elseif((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return $result;
+ } // function calculateCellValue(
+
+
+ /**
+ * Validate and parse a formula string
+ *
+ * @param string $formula Formula to parse
+ * @return array
+ * @throws Exception
+ */
+ public function parseFormula($formula) {
+ // Basic validation that this is indeed a formula
+ // We return an empty array if not
+ $formula = trim($formula);
+ if ((strlen($formula) == 0) || ($formula{0} != '=')) return array();
+ $formula = trim(substr($formula,1));
+ $formulaLength = strlen($formula);
+ if ($formulaLength < 1) return array();
+
+ // Parse the formula and return the token stack
+ return $this->_parseFormula($formula);
+ } // function parseFormula()
+
+
+ /**
+ * Calculate the value of a formula
+ *
+ * @param string $formula Formula to parse
+ * @return mixed
+ * @throws Exception
+ */
+ public function calculateFormula($formula, $cellID=null, PHPExcel_Cell $pCell = null) {
+ // Initialise the logging settings
+ $this->formulaError = null;
+ $this->debugLog = $this->debugLogStack = array();
+
+ // Disable calculation cacheing because it only applies to cell calculations, not straight formulae
+ // But don't actually flush any cache
+ $resetCache = $this->getCalculationCacheEnabled();
+ self::$_calculationCacheEnabled = false;
+ // Execute the calculation
+ try {
+ $result = self::_unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
+ } catch (Exception $e) {
+ throw(new Exception($e->getMessage()));
+ }
+
+ // Reset calculation cacheing to its previous state
+ self::$_calculationCacheEnabled = $resetCache;
+
+ return $result;
+ } // function calculateFormula()
+
+
+ /**
+ * Parse a cell formula and calculate its value
+ *
+ * @param string $formula The formula to parse and calculate
+ * @param string $cellID The ID (e.g. A3) of the cell that we are calculating
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @return mixed
+ * @throws Exception
+ */
+ public function _calculateFormulaValue($formula, $cellID=null, PHPExcel_Cell $pCell = null) {
+// echo ''.$cellID.'
';
+ $cellValue = '';
+
+ // Basic validation that this is indeed a formula
+ // We simply return the "cell value" (formula) if not
+ $formula = trim($formula);
+ if ($formula{0} != '=') return self::_wrapResult($formula);
+ $formula = trim(substr($formula,1));
+ $formulaLength = strlen($formula);
+ if ($formulaLength < 1) return self::_wrapResult($formula);
+
+ $wsTitle = 'Wrk';
+ if (!is_null($pCell)) {
+ $pCellParent = $pCell->getParent();
+ if (!is_null($pCellParent)) {
+ $wsTitle = $pCellParent->getTitle();
+ }
+ }
+ // Is calculation cacheing enabled?
+ if (!is_null($cellID)) {
+ if (self::$_calculationCacheEnabled) {
+ // Is the value present in calculation cache?
+// echo 'Testing cache value
';
+ if (isset(self::$_calculationCache[$wsTitle][$cellID])) {
+// echo 'Value is in cache
';
+ $this->_writeDebug('Testing cache value for cell '.$cellID);
+ // Is cache still valid?
+ if ((time() + microtime(true)) - self::$_calculationCache[$wsTitle][$cellID]['time'] < self::$_calculationCacheExpirationTime) {
+// echo 'Cache time is still valid
';
+ $this->_writeDebug('Retrieving value for '.$cellID.' from cache');
+ // Return the cached result
+ $returnValue = self::$_calculationCache[$wsTitle][$cellID]['data'];
+// echo 'Retrieving data value of '.$returnValue.' for '.$cellID.' from cache
';
+ if (is_array($returnValue)) {
+ $returnValue = PHPExcel_Calculation_Functions::flattenArray($returnValue);
+ return array_shift($returnValue);
+ }
+ return $returnValue;
+ } else {
+// echo 'Cache has expired
';
+ $this->_writeDebug('Cache value for '.$cellID.' has expired');
+ // Clear the cache if it's no longer valid
+ unset(self::$_calculationCache[$wsTitle][$cellID]);
+ }
+ }
+ }
+ }
+
+ if ((in_array($wsTitle.'!'.$cellID,$this->debugLogStack)) && ($wsTitle != 'Wrk')) {
+ if ($this->cyclicFormulaCount <= 0) {
+ return $this->_raiseFormulaError('Cyclic Reference in Formula');
+ } elseif (($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) &&
+ ($this->_cyclicFormulaCell == $wsTitle.'!'.$cellID)) {
+ return $cellValue;
+ } elseif ($this->_cyclicFormulaCell == $wsTitle.'!'.$cellID) {
+ $this->_cyclicFormulaCount++;
+ if ($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) {
+ return $cellValue;
+ }
+ } elseif ($this->_cyclicFormulaCell == '') {
+ $this->_cyclicFormulaCell = $wsTitle.'!'.$cellID;
+ if ($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) {
+ return $cellValue;
+ }
+ }
+ }
+ $this->debugLogStack[] = $wsTitle.'!'.$cellID;
+ // Parse the formula onto the token stack and calculate the value
+ $cellValue = $this->_processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
+ array_pop($this->debugLogStack);
+
+ // Save to calculation cache
+ if (!is_null($cellID)) {
+ if (self::$_calculationCacheEnabled) {
+ self::$_calculationCache[$wsTitle][$cellID]['time'] = (time() + microtime(true));
+ self::$_calculationCache[$wsTitle][$cellID]['data'] = $cellValue;
+ }
+ }
+
+ // Return the calculated value
+ return $cellValue;
+ } // function _calculateFormulaValue()
+
+
+ /**
+ * Ensure that paired matrix operands are both matrices and of the same size
+ *
+ * @param mixed &$operand1 First matrix operand
+ * @param mixed &$operand2 Second matrix operand
+ * @param integer $resize Flag indicating whether the matrices should be resized to match
+ * and (if so), whether the smaller dimension should grow or the
+ * larger should shrink.
+ * 0 = no resize
+ * 1 = shrink to fit
+ * 2 = extend to fit
+ */
+ private static function _checkMatrixOperands(&$operand1,&$operand2,$resize = 1) {
+ // Examine each of the two operands, and turn them into an array if they aren't one already
+ // Note that this function should only be called if one or both of the operand is already an array
+ if (!is_array($operand1)) {
+ list($matrixRows,$matrixColumns) = self::_getMatrixDimensions($operand2);
+ $operand1 = array_fill(0,$matrixRows,array_fill(0,$matrixColumns,$operand1));
+ $resize = 0;
+ } elseif (!is_array($operand2)) {
+ list($matrixRows,$matrixColumns) = self::_getMatrixDimensions($operand1);
+ $operand2 = array_fill(0,$matrixRows,array_fill(0,$matrixColumns,$operand2));
+ $resize = 0;
+ }
+
+ list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($operand1);
+ list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($operand2);
+ if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
+ $resize = 1;
+ }
+
+ if ($resize == 2) {
+ // Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
+ self::_resizeMatricesExtend($operand1,$operand2);
+ } elseif ($resize == 1) {
+ // Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
+ self::_resizeMatricesShrink($operand1,$operand2);
+ }
+ } // function _checkMatrixOperands()
+
+
+ /**
+ * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
+ *
+ * @param mixed &$matrix matrix operand
+ * @return array An array comprising the number of rows, and number of columns
+ */
+ public static function _getMatrixDimensions(&$matrix) {
+ $matrixRows = count($matrix);
+ $matrixColumns = 0;
+ foreach($matrix as $rowKey => $rowValue) {
+ $colCount = count($rowValue);
+ if ($colCount > $matrixColumns) {
+ $matrixColumns = $colCount;
+ }
+ if (!is_array($rowValue)) {
+ $matrix[$rowKey] = array($rowValue);
+ } else {
+ $matrix[$rowKey] = array_values($rowValue);
+ }
+ }
+ $matrix = array_values($matrix);
+ return array($matrixRows,$matrixColumns);
+ } // function _getMatrixDimensions()
+
+
+ /**
+ * Ensure that paired matrix operands are both matrices of the same size
+ *
+ * @param mixed &$matrix1 First matrix operand
+ * @param mixed &$matrix2 Second matrix operand
+ */
+ private static function _resizeMatricesShrink(&$matrix1,&$matrix2) {
+ list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($matrix1);
+ list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($matrix2);
+
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ unset($matrix1[$i][$j]);
+ }
+ }
+ }
+ if ($matrix2Rows < $matrix1Rows) {
+ for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
+ unset($matrix1[$i]);
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ unset($matrix2[$i][$j]);
+ }
+ }
+ }
+ if ($matrix1Rows < $matrix2Rows) {
+ for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
+ unset($matrix2[$i]);
+ }
+ }
+ }
+ } // function _resizeMatricesShrink()
+
+
+ /**
+ * Ensure that paired matrix operands are both matrices of the same size
+ *
+ * @param mixed &$matrix1 First matrix operand
+ * @param mixed &$matrix2 Second matrix operand
+ */
+ private static function _resizeMatricesExtend(&$matrix1,&$matrix2) {
+ list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($matrix1);
+ list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($matrix2);
+
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ $x = $matrix2[$i][$matrix2Columns-1];
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ $matrix2[$i][$j] = $x;
+ }
+ }
+ }
+ if ($matrix2Rows < $matrix1Rows) {
+ $x = $matrix2[$matrix2Rows-1];
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ $matrix2[$i] = $x;
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ $x = $matrix1[$i][$matrix1Columns-1];
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ $matrix1[$i][$j] = $x;
+ }
+ }
+ }
+ if ($matrix1Rows < $matrix2Rows) {
+ $x = $matrix1[$matrix1Rows-1];
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ $matrix1[$i] = $x;
+ }
+ }
+ }
+ } // function _resizeMatricesExtend()
+
+
+ /**
+ * Format details of an operand for display in the log (based on operand type)
+ *
+ * @param mixed $value First matrix operand
+ * @return mixed
+ */
+ private static function _showValue($value) {
+ $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
+ if (count($testArray) == 1) {
+ $value = array_pop($testArray);
+ }
+
+ if (is_array($value)) {
+ $returnMatrix = array();
+ $pad = $rpad = ', ';
+ foreach($value as $row) {
+ if (is_array($row)) {
+ $returnMatrix[] = implode($pad,$row);
+ $rpad = '; ';
+ } else {
+ $returnMatrix[] = $row;
+ }
+ }
+ return '{ '.implode($rpad,$returnMatrix).' }';
+ } elseif(is_bool($value)) {
+ return ($value) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+ }
+
+ return $value;
+ } // function _showValue()
+
+
+ /**
+ * Format type and details of an operand for display in the log (based on operand type)
+ *
+ * @param mixed $value First matrix operand
+ * @return mixed
+ */
+ private static function _showTypeDetails($value) {
+ $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
+ if (count($testArray) == 1) {
+ $value = array_pop($testArray);
+ }
+
+ if (is_null($value)) {
+ return 'a null value';
+ } elseif (is_float($value)) {
+ $typeString = 'a floating point number';
+ } elseif(is_int($value)) {
+ $typeString = 'an integer number';
+ } elseif(is_bool($value)) {
+ $typeString = 'a boolean';
+ } elseif(is_array($value)) {
+ $typeString = 'a matrix';
+ } else {
+ if ($value == '') {
+ return 'an empty string';
+ } elseif ($value{0} == '#') {
+ return 'a '.$value.' error';
+ } else {
+ $typeString = 'a string';
+ }
+ }
+ return $typeString.' with a value of '.self::_showValue($value);
+ } // function _showTypeDetails()
+
+
+ private static function _convertMatrixReferences($formula) {
+ static $matrixReplaceFrom = array('{',';','}');
+ static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(','),MKMATRIX(','))');
+
+ // Convert any Excel matrix references to the MKMATRIX() function
+ if (strpos($formula,'{') !== false) {
+ // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+ if (strpos($formula,'"') !== false) {
+ // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+ // the formula
+ $temp = explode('"',$formula);
+ // Open and Closed counts used for trapping mismatched braces in the formula
+ $openCount = $closeCount = 0;
+ foreach($temp as $i => &$value) {
+ // Only count/replace in alternate array entries
+ if (($i % 2) == 0) {
+ $openCount += substr_count($value,'{');
+ $closeCount += substr_count($value,'}');
+ $value = str_replace($matrixReplaceFrom,$matrixReplaceTo,$value);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $formula = implode('"',$temp);
+ } else {
+ // If there's no quoted strings, then we do a simple count/replace
+ $openCount = substr_count($formula,'{');
+ $closeCount = substr_count($formula,'}');
+ $formula = str_replace($matrixReplaceFrom,$matrixReplaceTo,$formula);
+ }
+ // Trap for mismatched braces and trigger an appropriate error
+ if ($openCount < $closeCount) {
+ if ($openCount > 0) {
+ return $this->_raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected '}' encountered");
+ }
+ } elseif ($openCount > $closeCount) {
+ if ($closeCount > 0) {
+ return $this->_raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected '{' encountered");
+ }
+ }
+ }
+
+ return $formula;
+ } // function _convertMatrixReferences()
+
+
+ private static function _mkMatrix() {
+ return func_get_args();
+ } // function _mkMatrix()
+
+
+ // Convert infix to postfix notation
+ private function _parseFormula($formula, PHPExcel_Cell $pCell = null) {
+ if (($formula = self::_convertMatrixReferences(trim($formula))) === false) {
+ return false;
+ }
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
+ // so we store the parent worksheet so that we can re-attach it when necessary
+ $pCellParent = (!is_null($pCell)) ? $pCell->getParent() : null;
+
+ // Binary Operators
+ // These operators always work on two values
+ // Array key is the operator, the value indicates whether this is a left or right associative operator
+ $operatorAssociativity = array('^' => 0, // Exponentiation
+ '*' => 0, '/' => 0, // Multiplication and Division
+ '+' => 0, '-' => 0, // Addition and Subtraction
+ '&' => 0, // Concatenation
+ '|' => 0, ':' => 0, // Intersect and Range
+ '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison
+ );
+ // Comparison (Boolean) Operators
+ // These operators work on two values, but always return a boolean result
+ $comparisonOperators = array('>', '<', '=', '>=', '<=', '<>');
+
+ // Operator Precedence
+ // This list includes all valid operators, whether binary (including boolean) or unary (such as %)
+ // Array key is the operator, the value is its precedence
+ $operatorPrecedence = array(':' => 8, // Range
+ '|' => 7, // Intersect
+ '~' => 6, // Negation
+ '%' => 5, // Percentage
+ '^' => 4, // Exponentiation
+ '*' => 3, '/' => 3, // Multiplication and Division
+ '+' => 2, '-' => 2, // Addition and Subtraction
+ '&' => 1, // Concatenation
+ '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison
+ );
+
+ $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
+ '|'.self::CALCULATION_REGEXP_NUMBER.
+ '|'.self::CALCULATION_REGEXP_STRING.
+ '|'.self::CALCULATION_REGEXP_OPENBRACE.
+ '|'.self::CALCULATION_REGEXP_CELLREF.
+ '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
+ '|'.self::CALCULATION_REGEXP_ERROR.
+ ')/si';
+
+ // Start with initialisation
+ $index = 0;
+ $stack = new PHPExcel_Token_Stack;
+ $output = array();
+ $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a
+ // - is a negation or + is a positive operator rather than an operation
+ $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand
+ // should be null in a function call
+ // The guts of the lexical parser
+ // Loop through the formula extracting each operator and operand in turn
+ while(True) {
+// echo 'Assessing Expression '.substr($formula, $index).'
';
+ $opCharacter = $formula{$index}; // Get the first character of the value at the current index position
+// echo 'Initial character of expression block is '.$opCharacter.'
';
+ if ((in_array($opCharacter, $comparisonOperators)) && (strlen($formula) > $index) && (in_array($formula{$index+1}, $comparisonOperators))) {
+ $opCharacter .= $formula{++$index};
+// echo 'Initial character of expression block is comparison operator '.$opCharacter.'
';
+ }
+
+ // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
+ $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
+// echo '$isOperandOrFunction is '.(($isOperandOrFunction)?'True':'False').'
';
+
+ if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
+// echo 'Element is a Negation operator
';
+ $stack->push('Unary Operator','~'); // Put a negation on the stack
+ ++$index; // and drop the negation symbol
+ } elseif ($opCharacter == '%' && $expectingOperator) {
+// echo 'Element is a Percentage operator
';
+ $stack->push('Unary Operator','%'); // Put a percentage on the stack
+ ++$index;
+ } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (rather than plus) can be discarded?
+// echo 'Element is a Positive number, not Plus operator
';
+ ++$index; // Drop the redundant plus symbol
+ } elseif (($opCharacter == '~') && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde, because it's legal
+ return $this->_raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression
+
+ } elseif ((in_array($opCharacter, self::$_operators) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
+// echo 'Element with value '.$opCharacter.' is an Operator
';
+ while($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ in_array($o2['value'], self::$_operators) &&
+ @($operatorAssociativity[$opCharacter] ? $operatorPrecedence[$opCharacter] < $operatorPrecedence[$o2['value']] : $operatorPrecedence[$opCharacter] <= $operatorPrecedence[$o2['value']])) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+ $stack->push('Binary Operator',$opCharacter); // Finally put our current operator onto the stack
+ ++$index;
+ $expectingOperator = false;
+
+ } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
+// echo 'Element is a Closing bracket
';
+ $expectingOperand = false;
+ while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
+ if (is_null($o2)) return $this->_raiseFormulaError('Formula Error: Unexpected closing brace ")"');
+ else $output[] = $o2;
+ }
+ $d = $stack->last(2);
+ if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
+ $functionName = $matches[1]; // Get the function name
+// echo 'Closed Function is '.$functionName.'
';
+ $d = $stack->pop();
+ $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack)
+// if ($argumentCount == 0) {
+// echo 'With no arguments
';
+// } elseif ($argumentCount == 1) {
+// echo 'With 1 argument
';
+// } else {
+// echo 'With '.$argumentCount.' arguments
';
+// }
+ $output[] = $d; // Dump the argument count on the output
+ $output[] = $stack->pop(); // Pop the function and push onto the output
+ if (array_key_exists($functionName, self::$_controlFunctions)) {
+// echo 'Built-in function '.$functionName.'
';
+ $expectedArgumentCount = self::$_controlFunctions[$functionName]['argumentCount'];
+ $functionCall = self::$_controlFunctions[$functionName]['functionCall'];
+ } elseif (array_key_exists($functionName, self::$_PHPExcelFunctions)) {
+// echo 'PHPExcel function '.$functionName.'
';
+ $expectedArgumentCount = self::$_PHPExcelFunctions[$functionName]['argumentCount'];
+ $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
+ } else { // did we somehow push a non-function on the stack? this should never happen
+ return $this->_raiseFormulaError("Formula Error: Internal error, non-function on stack");
+ }
+ // Check the argument count
+ $argumentCountError = False;
+ if (is_numeric($expectedArgumentCount)) {
+ if ($expectedArgumentCount < 0) {
+// echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount).'
';
+ if ($argumentCount > abs($expectedArgumentCount)) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
+ }
+ } else {
+// echo '$expectedArgumentCount is numeric '.$expectedArgumentCount.'
';
+ if ($argumentCount != $expectedArgumentCount) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = $expectedArgumentCount;
+ }
+ }
+ } elseif ($expectedArgumentCount != '*') {
+ $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/',$expectedArgumentCount,$argMatch);
+// print_r($argMatch);
+// echo '
';
+ switch ($argMatch[2]) {
+ case '+' :
+ if ($argumentCount < $argMatch[1]) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = $argMatch[1].' or more ';
+ }
+ break;
+ case '-' :
+ if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3];
+ }
+ break;
+ case ',' :
+ if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3];
+ }
+ break;
+ }
+ }
+ if ($argumentCountError) {
+ return $this->_raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, ".$expectedArgumentCountString." expected");
+ }
+ }
+ ++$index;
+
+ } elseif ($opCharacter == ',') { // Is this the separator for function arguments?
+// echo 'Element is a Function argument separator
';
+ while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
+ if (is_null($o2)) return $this->_raiseFormulaError("Formula Error: Unexpected ,");
+ else $output[] = $o2; // pop the argument expression stuff and push onto the output
+ }
+ // If we've a comma when we're expecting an operand, then what we actually have is a null operand;
+ // so push a null onto the stack
+ if (($expectingOperand) || (!$expectingOperator)) {
+ $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL);
+ }
+ // make sure there was a function
+ $d = $stack->last(2);
+ if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches))
+ return $this->_raiseFormulaError("Formula Error: Unexpected ,");
+ $d = $stack->pop();
+ $stack->push($d['type'],++$d['value'],$d['reference']); // increment the argument count
+ $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
+ $expectingOperator = false;
+ $expectingOperand = true;
+ ++$index;
+
+ } elseif ($opCharacter == '(' && !$expectingOperator) {
+// echo 'Element is an Opening Bracket
';
+ $stack->push('Brace', '(');
+ ++$index;
+
+ } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
+ $expectingOperator = true;
+ $expectingOperand = false;
+ $val = $match[1];
+ $length = strlen($val);
+// echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function
';
+
+ if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) {
+ $val = preg_replace('/\s/','',$val);
+// echo 'Element '.$val.' is a Function
';
+ if (array_key_exists(strtoupper($matches[1]), self::$_PHPExcelFunctions) || array_key_exists(strtoupper($matches[1]), self::$_controlFunctions)) { // it's a func
+ $stack->push('Function', strtoupper($val));
+ $ax = preg_match('/^\s*(\s*\))/i', substr($formula, $index+$length), $amatch);
+ if ($ax) {
+ $stack->push('Operand Count for Function '.self::_localeFunc(strtoupper($val)).')', 0);
+ $expectingOperator = true;
+ } else {
+ $stack->push('Operand Count for Function '.self::_localeFunc(strtoupper($val)).')', 1);
+ $expectingOperator = false;
+ }
+ $stack->push('Brace', '(');
+ } else { // it's a var w/ implicit multiplication
+ $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => NULL);
+ }
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) {
+// echo 'Element '.$val.' is a Cell reference
';
+// Watch for this case-change when modifying to allow cell references in different worksheets...
+// Should only be applied to the actual cell column, not the worksheet name
+
+ // If the last entry on the stack was a : operator, then we have a cell range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp['value'] == ':') {
+ // If we have a worksheet reference, then we're playing with a 3D reference
+ if ($matches[2] == '') {
+ // Otherwise, we 'inherit' the worksheet reference from the start cell reference
+ // The start of the cell range reference should be the last entry in $output
+ $startCellRef = $output[count($output)-1]['value'];
+ preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches);
+ if ($startMatches[2] > '') {
+ $val = $startMatches[2].'!'.$val;
+ }
+ }
+ }
+ $cellRef = strtoupper($val);
+
+ $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $cellRef);
+// $expectingOperator = false;
+ } else { // it's a variable, constant, string, number or boolean
+// echo 'Element is a Variable, Constant, String, Number or Boolean
';
+ // If the last entry on the stack was a : operator, then we may have a row or column range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp['value'] == ':') {
+ $startRowColRef = $output[count($output)-1]['value'];
+ $rangeWS1 = '';
+ if (strpos('!',$startRowColRef) !== false) {
+ list($rangeWS1,$startRowColRef) = explode('!',$startRowColRef);
+ }
+ if ($rangeWS1 != '') $rangeWS1 .= '!';
+ $rangeWS2 = $rangeWS1;
+ if (strpos('!',$val) !== false) {
+ list($rangeWS2,$val) = explode('!',$val);
+ }
+ if ($rangeWS2 != '') $rangeWS2 .= '!';
+ if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
+ ($startRowColRef <= 1048576) && ($val <= 1048576)) {
+ // Row range
+ $endRowColRef = (!is_null($pCellParent)) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
+ $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef;
+ $val = $rangeWS2.$endRowColRef.$val;
+ } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
+ (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
+ // Column range
+ $endRowColRef = (!is_null($pCellParent)) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
+ $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1';
+ $val = $rangeWS2.$val.$endRowColRef;
+ }
+ }
+
+ $localeConstant = false;
+ if ($opCharacter == '"') {
+// echo 'Element is a String
';
+ // UnEscape any quotes within the string
+ $val = self::_wrapResult(str_replace('""','"',self::_unwrapResult($val)));
+ } elseif (is_numeric($val)) {
+// echo 'Element is a Number
';
+ if ((strpos($val,'.') !== False) || (stripos($val,'e') !== False) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+// echo 'Casting '.$val.' to float
';
+ $val = (float) $val;
+ } else {
+// echo 'Casting '.$val.' to integer
';
+ $val = (integer) $val;
+ }
+ } elseif (array_key_exists(trim(strtoupper($val)), self::$_ExcelConstants)) {
+ $excelConstant = trim(strtoupper($val));
+// echo 'Element '.$excelConstant.' is an Excel Constant
';
+ $val = self::$_ExcelConstants[$excelConstant];
+ } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$_localeBoolean)) !== false) {
+// echo 'Element '.$localeConstant.' is an Excel Constant
';
+ $val = self::$_ExcelConstants[$localeConstant];
+ }
+ $details = array('type' => 'Value', 'value' => $val, 'reference' => NULL);
+ if ($localeConstant) { $details['localeValue'] = $localeConstant; }
+ $output[] = $details;
+ }
+ $index += $length;
+
+ } elseif ($opCharacter == '$') { // absolute row or column range
+ $index++;
+ } elseif ($opCharacter == ')') { // miscellaneous error checking
+ if ($expectingOperand) {
+ $output[] = array('type' => 'Null Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL);
+ $expectingOperand = false;
+ $expectingOperator = True;
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected ')'");
+ }
+ } elseif (in_array($opCharacter, self::$_operators) && !$expectingOperator) {
+ return $this->_raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
+ } else { // I don't even want to know what you did to get here
+ return $this->_raiseFormulaError("Formula Error: An unexpected error occured");
+ }
+ // Test for end of formula string
+ if ($index == strlen($formula)) {
+ // Did we end with an operator?.
+ // Only valid for the % unary operator
+ if ((in_array($opCharacter, self::$_operators)) && ($opCharacter != '%')) {
+ return $this->_raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
+ } else {
+ break;
+ }
+ }
+ // Ignore white space
+ while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
+ ++$index;
+ }
+ if ($formula{$index} == ' ') {
+ while ($formula{$index} == ' ') {
+ ++$index;
+ }
+ // If we're expecting an operator, but only have a space between the previous and next operands (and both are
+ // Cell References) then we have an INTERSECTION operator
+// echo 'Possible Intersect Operator
';
+ if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/i', substr($formula, $index), $match)) &&
+ ($output[count($output)-1]['type'] == 'Cell Reference')) {
+// echo 'Element is an Intersect Operator
';
+ while($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ in_array($o2['value'], self::$_operators) &&
+ @($operatorAssociativity[$opCharacter] ? $operatorPrecedence[$opCharacter] < $operatorPrecedence[$o2['value']] : $operatorPrecedence[$opCharacter] <= $operatorPrecedence[$o2['value']])) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+ $stack->push('Binary Operator','|'); // Put an Intersect Operator on the stack
+ $expectingOperator = false;
+ }
+ }
+ }
+
+ while (!is_null($op = $stack->pop())) { // pop everything off the stack and push onto output
+ if ($opCharacter['value'] == '(') return $this->_raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
+ $output[] = $op;
+ }
+ return $output;
+ } // function _parseFormula()
+
+
+ // evaluate postfix notation
+ private function _processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null) {
+ if ($tokens == false) return false;
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
+ // so we store the parent worksheet so that we can re-attach it when necessary
+ $pCellParent = (!is_null($pCell)) ? $pCell->getParent() : null;
+ $stack = new PHPExcel_Token_Stack;
+
+ // Loop through each token in turn
+ foreach ($tokens as $tokenData) {
+// print_r($tokenData);
+// echo '
';
+ $token = $tokenData['value'];
+// echo 'Token is '.$token.'
';
+ // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
+ if (in_array($token, self::$_binaryOperators, true)) {
+// echo 'Token is a binary operator
';
+ // We must have two operands, error if we don't
+ if (is_null($operand2Data = $stack->pop())) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ if (is_null($operand1Data = $stack->pop())) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ // Log what we're doing
+ $operand1 = $operand1Data['value'];
+ $operand2 = $operand2Data['value'];
+ if ($token == ':') {
+ $this->_writeDebug('Evaluating Range '.self::_showValue($operand1Data['reference']).$token.self::_showValue($operand2Data['reference']));
+ } else {
+ $this->_writeDebug('Evaluating '.self::_showValue($operand1).' '.$token.' '.self::_showValue($operand2));
+ }
+ // Process the operation in the appropriate manner
+ switch ($token) {
+ // Comparison (Boolean) Operators
+ case '>' : // Greater than
+ case '<' : // Less than
+ case '>=' : // Greater than or Equal to
+ case '<=' : // Less than or Equal to
+ case '=' : // Equality
+ case '<>' : // Inequality
+ $this->_executeBinaryComparisonOperation($cellID,$operand1,$operand2,$token,$stack);
+ break;
+ // Binary Operators
+ case ':' : // Range
+ $sheet1 = $sheet2 = '';
+ if (strpos($operand1Data['reference'],'!') !== false) {
+ list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']);
+ } else {
+ $sheet1 = (!is_null($pCellParent)) ? $pCellParent->getTitle() : '';
+ }
+ if (strpos($operand2Data['reference'],'!') !== false) {
+ list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']);
+ } else {
+ $sheet2 = $sheet1;
+ }
+ if ($sheet1 == $sheet2) {
+ if (is_null($operand1Data['reference'])) {
+ if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
+ $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
+ } elseif (trim($operand1Data['reference']) == '') {
+ $operand1Data['reference'] = $pCell->getColumn().$pCell->getRow();
+ } else {
+ $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
+ }
+ }
+ if (is_null($operand2Data['reference'])) {
+ if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
+ $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
+ } elseif (trim($operand2Data['reference']) == '') {
+ $operand2Data['reference'] = $pCell->getColumn().$pCell->getRow();
+ } else {
+ $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
+ }
+ }
+
+ $oData = array_merge(explode(':',$operand1Data['reference']),explode(':',$operand2Data['reference']));
+ $oCol = $oRow = array();
+ foreach($oData as $oDatum) {
+ $oCR = PHPExcel_Cell::coordinateFromString($oDatum);
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
+ $oRow[] = $oCR[1];
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ if (!is_null($pCellParent)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($sheet1), false);
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $stack->push('Cell Reference',$cellValue,$cellRef);
+ } else {
+ $stack->push('Error',PHPExcel_Calculation_Functions::REF(),NULL);
+ }
+
+ break;
+ case '+' : // Addition
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'plusEquals',$stack);
+ break;
+ case '-' : // Subtraction
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'minusEquals',$stack);
+ break;
+ case '*' : // Multiplication
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayTimesEquals',$stack);
+ break;
+ case '/' : // Division
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayRightDivide',$stack);
+ break;
+ case '^' : // Exponential
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'power',$stack);
+ break;
+ case '&' : // Concatenation
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ if (is_bool($operand1)) {
+ $operand1 = ($operand1) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+ }
+ if (is_bool($operand2)) {
+ $operand2 = ($operand2) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+ }
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ // Ensure that both operands are arrays/matrices
+ self::_checkMatrixOperands($operand1,$operand2,2);
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->concat($operand2);
+ $result = $matrixResult->getArray();
+ } catch (Exception $ex) {
+ $this->_writeDebug('JAMA Matrix Exception: '.$ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ $result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"';
+ }
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ $stack->push('Value',$result);
+ break;
+ case '|' : // Intersect
+ $rowIntersect = array_intersect_key($operand1,$operand2);
+ $cellIntersect = $oCol = $oRow = array();
+ foreach(array_keys($rowIntersect) as $col) {
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
+ $cellIntersect[$col] = array_intersect_key($operand1[$col],$operand2[$col]);
+ foreach($cellIntersect[$col] as $row => $data) {
+ $oRow[] = $row;
+ }
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($cellIntersect));
+ $stack->push('Value',$cellIntersect,$cellRef);
+ break;
+ }
+
+ // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
+ } elseif (($token === '~') || ($token === '%')) {
+// echo 'Token is a unary operator
';
+ if (is_null($arg = $stack->pop())) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ $arg = $arg['value'];
+ if ($token === '~') {
+// echo 'Token is a negation operator
';
+ $this->_writeDebug('Evaluating Negation of '.self::_showValue($arg));
+ $multiplier = -1;
+ } else {
+// echo 'Token is a percentile operator
';
+ $this->_writeDebug('Evaluating Percentile of '.self::_showValue($arg));
+ $multiplier = 0.01;
+ }
+ if (is_array($arg)) {
+ self::_checkMatrixOperands($arg,$multiplier,2);
+ try {
+ $matrix1 = new Matrix($arg);
+ $matrixResult = $matrix1->arrayTimesEquals($multiplier);
+ $result = $matrixResult->getArray();
+ } catch (Exception $ex) {
+ $this->_writeDebug('JAMA Matrix Exception: '.$ex->getMessage());
+ $result = '#VALUE!';
+ }
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ $stack->push('Value',$result);
+ } else {
+ $this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack);
+ }
+
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
+ $cellRef = null;
+// echo 'Element '.$token.' is a Cell reference
';
+ if (isset($matches[8])) {
+// echo 'Reference is a Range of cells
';
+ if (is_null($pCell)) {
+// We can't access the range, so return a REF error
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ } else {
+ $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2],"\"'");
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
';
+ $this->_writeDebug('Evaluating Cell Range '.$cellRef.' in worksheet '.$matches[2]);
+ if (!is_null($pCellParent)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($matches[2]), false);
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_writeDebug('Evaluation Result for cells '.$cellRef.' in worksheet '.$matches[2].' is '.self::_showTypeDetails($cellValue));
+// $cellRef = $matches[2].'!'.$cellRef;
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet
';
+ $this->_writeDebug('Evaluating Cell Range '.$cellRef.' in current worksheet');
+ if (!is_null($pCellParent)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellParent, false);
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_writeDebug('Evaluation Result for cells '.$cellRef.' is '.self::_showTypeDetails($cellValue));
+ }
+ }
+ } else {
+// echo 'Reference is a single Cell
';
+ if (is_null($pCell)) {
+// We can't access the cell, so return a REF error
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ } else {
+ $cellRef = $matches[6].$matches[7];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2],"\"'");
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
';
+ $this->_writeDebug('Evaluating Cell '.$cellRef.' in worksheet '.$matches[2]);
+ if (!is_null($pCellParent)) {
+ if ($pCellParent->getParent()->getSheetByName($matches[2])->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellParent->getParent()->getSheetByName($matches[2]), false);
+ $pCell->attach($pCellParent);
+ } else {
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ }
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_writeDebug('Evaluation Result for cell '.$cellRef.' in worksheet '.$matches[2].' is '.self::_showTypeDetails($cellValue));
+// $cellRef = $matches[2].'!'.$cellRef;
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet
';
+ $this->_writeDebug('Evaluating Cell '.$cellRef.' in current worksheet');
+ if ($pCellParent->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellParent, false);
+ $pCell->attach($pCellParent);
+ } else {
+ $cellValue = NULL;
+ }
+ $this->_writeDebug('Evaluation Result for cell '.$cellRef.' is '.self::_showTypeDetails($cellValue));
+ }
+ }
+ }
+ $stack->push('Value',$cellValue,$cellRef);
+
+ // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
+// echo 'Token is a function
';
+ $functionName = $matches[1];
+ $argCount = $stack->pop();
+ $argCount = $argCount['value'];
+ if ($functionName != 'MKMATRIX') {
+ $this->_writeDebug('Evaluating Function '.self::_localeFunc($functionName).'() with '.(($argCount == 0) ? 'no' : $argCount).' argument'.(($argCount == 1) ? '' : 's'));
+ }
+ if ((array_key_exists($functionName, self::$_PHPExcelFunctions)) || (array_key_exists($functionName, self::$_controlFunctions))) { // function
+ if (array_key_exists($functionName, self::$_PHPExcelFunctions)) {
+ $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$_PHPExcelFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$_PHPExcelFunctions[$functionName]['passCellReference']);
+ } elseif (array_key_exists($functionName, self::$_controlFunctions)) {
+ $functionCall = self::$_controlFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$_controlFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$_controlFunctions[$functionName]['passCellReference']);
+ }
+ // get the arguments for this function
+// echo 'Function '.$functionName.' expects '.$argCount.' arguments
';
+ $args = $argArrayVals = array();
+ for ($i = 0; $i < $argCount; ++$i) {
+ $arg = $stack->pop();
+ $a = $argCount - $i - 1;
+ if (($passByReference) &&
+ (isset(self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
+ (self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) {
+ if (is_null($arg['reference'])) {
+ $args[] = $cellID;
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($cellID); }
+ } else {
+ $args[] = $arg['reference'];
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($arg['reference']); }
+ }
+ } else {
+ $args[] = self::_unwrapResult($arg['value']);
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($arg['value']); }
+ }
+ }
+ // Reverse the order of the arguments
+ krsort($args);
+ if (($passByReference) && ($argCount == 0)) {
+ $args[] = $cellID;
+ $argArrayVals[] = self::_showValue($cellID);
+ }
+// echo 'Arguments are: ';
+// print_r($args);
+// echo '
';
+ if ($functionName != 'MKMATRIX') {
+ krsort($argArrayVals);
+ $this->_writeDebug('Evaluating '. self::_localeFunc($functionName).'( '.implode(self::$_localeArgumentSeparator.' ',$argArrayVals).' )');
+ }
+ // Process each argument in turn, building the return value as an array
+// if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
+// $operand1 = $args[1];
+// $this->_writeDebug('Argument is a matrix: '.self::_showValue($operand1));
+// $result = array();
+// $row = 0;
+// foreach($operand1 as $args) {
+// if (is_array($args)) {
+// foreach($args as $arg) {
+// $this->_writeDebug('Evaluating '.self::_localeFunc($functionName).'( '.self::_showValue($arg).' )');
+// $r = call_user_func_array($functionCall,$arg);
+// $this->_writeDebug('Evaluation Result for '.self::_localeFunc($functionName).'() function call is '.self::_showTypeDetails($r));
+// $result[$row][] = $r;
+// }
+// ++$row;
+// } else {
+// $this->_writeDebug('Evaluating '.self::_localeFunc($functionName).'( '.self::_showValue($args).' )');
+// $r = call_user_func_array($functionCall,$args);
+// $this->_writeDebug('Evaluation Result for '.self::_localeFunc($functionName).'() function call is '.self::_showTypeDetails($r));
+// $result[] = $r;
+// }
+// }
+// } else {
+ // Process the argument with the appropriate function call
+ if ($passCellReference) {
+ $args[] = $pCell;
+ }
+ if (strpos($functionCall,'::') !== false) {
+ $result = call_user_func_array(explode('::',$functionCall),$args);
+ } else {
+ foreach($args as &$arg) {
+ $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
+ }
+ unset($arg);
+ $result = call_user_func_array($functionCall,$args);
+ }
+// }
+ if ($functionName != 'MKMATRIX') {
+ $this->_writeDebug('Evaluation Result for '.self::_localeFunc($functionName).'() function call is '.self::_showTypeDetails($result));
+ }
+ $stack->push('Value',self::_wrapResult($result));
+ }
+
+ } else {
+ // if the token is a number, boolean, string or an Excel error, push it onto the stack
+ if (array_key_exists(strtoupper($token), self::$_ExcelConstants)) {
+ $excelConstant = strtoupper($token);
+// echo 'Token is a PHPExcel constant: '.$excelConstant.'
';
+ $stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]);
+ $this->_writeDebug('Evaluating Constant '.$excelConstant.' as '.self::_showTypeDetails(self::$_ExcelConstants[$excelConstant]));
+ } elseif ((is_numeric($token)) || (is_bool($token)) || (is_null($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
+// echo 'Token is a number, boolean, string, null or an Excel error
';
+ $stack->push('Value',$token);
+ // if the token is a named range, push the named range name onto the stack
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
+// echo 'Token is a named range
';
+ $namedRange = $matches[6];
+// echo 'Named Range is '.$namedRange.'
';
+ $this->_writeDebug('Evaluating Named Range '.$namedRange);
+ $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellParent : null), false);
+ $pCell->attach($pCellParent);
+ $this->_writeDebug('Evaluation Result for named range '.$namedRange.' is '.self::_showTypeDetails($cellValue));
+ $stack->push('Named Range',$cellValue,$namedRange);
+ } else {
+ return $this->_raiseFormulaError("undefined variable '$token'");
+ }
+ }
+ }
+ // when we're out of tokens, the stack should have a single element, the final result
+ if ($stack->count() != 1) return $this->_raiseFormulaError("internal error");
+ $output = $stack->pop();
+ $output = $output['value'];
+
+// if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+// return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
+// }
+ return $output;
+ } // function _processTokenStack()
+
+
+ private function _validateBinaryOperand($cellID,&$operand,&$stack) {
+ // Numbers, matrices and booleans can pass straight through, as they're already valid
+ if (is_string($operand)) {
+ // We only need special validations for the operand if it is a string
+ // Start by stripping off the quotation marks we use to identify true excel string values internally
+ if ($operand > '' && $operand{0} == '"') { $operand = self::_unwrapResult($operand); }
+ // If the string is a numeric value, we treat it as a numeric, so no further testing
+ if (!is_numeric($operand)) {
+ // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
+ if ($operand > '' && $operand{0} == '#') {
+ $stack->push('Value', $operand);
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($operand));
+ return false;
+ } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) {
+ // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
+ $stack->push('Value', '#VALUE!');
+ $this->_writeDebug('Evaluation Result is a '.self::_showTypeDetails('#VALUE!'));
+ return false;
+ }
+ }
+ }
+
+ // return a true if the value of the operand is one that we can use in normal binary operations
+ return true;
+ } // function _validateBinaryOperand()
+
+
+ private function _executeBinaryComparisonOperation($cellID,$operand1,$operand2,$operation,&$stack,$recursingArrays=false) {
+ // If we're dealing with matrix operations, we want a matrix result
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ $result = array();
+ if ((is_array($operand1)) && (!is_array($operand2))) {
+ foreach($operand1 as $x => $operandData) {
+ $this->_writeDebug('Evaluating '.self::_showValue($operandData).' '.$operation.' '.self::_showValue($operand2));
+ $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2,$operation,$stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } elseif ((!is_array($operand1)) && (is_array($operand2))) {
+ foreach($operand2 as $x => $operandData) {
+ $this->_writeDebug('Evaluating '.self::_showValue($operand1).' '.$operation.' '.self::_showValue($operandData));
+ $this->_executeBinaryComparisonOperation($cellID,$operand1,$operandData,$operation,$stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } else {
+ if (!$recursingArrays) { self::_checkMatrixOperands($operand1,$operand2,2); }
+ foreach($operand1 as $x => $operandData) {
+ $this->_writeDebug('Evaluating '.self::_showValue($operandData).' '.$operation.' '.self::_showValue($operand2[$x]));
+ $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,True);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Array',$result);
+ return true;
+ }
+
+ // Simple validate the two operands if they are string values
+ if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') { $operand1 = self::_unwrapResult($operand1); }
+ if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') { $operand2 = self::_unwrapResult($operand2); }
+
+ // execute the necessary operation
+ switch ($operation) {
+ // Greater than
+ case '>':
+ $result = ($operand1 > $operand2);
+ break;
+ // Less than
+ case '<':
+ $result = ($operand1 < $operand2);
+ break;
+ // Equality
+ case '=':
+ $result = ($operand1 == $operand2);
+ break;
+ // Greater than or equal
+ case '>=':
+ $result = ($operand1 >= $operand2);
+ break;
+ // Less than or equal
+ case '<=':
+ $result = ($operand1 <= $operand2);
+ break;
+ // Inequality
+ case '<>':
+ $result = ($operand1 != $operand2);
+ break;
+ }
+
+ // Log the result details
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value',$result);
+ return true;
+ } // function _executeBinaryComparisonOperation()
+
+
+ private function _executeNumericBinaryOperation($cellID,$operand1,$operand2,$operation,$matrixFunction,&$stack) {
+ // Validate the two operands
+ if (!$this->_validateBinaryOperand($cellID,$operand1,$stack)) return false;
+ if (!$this->_validateBinaryOperand($cellID,$operand2,$stack)) return false;
+
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ // Ensure that both operands are arrays/matrices
+ self::_checkMatrixOperands($operand1,$operand2,2);
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->$matrixFunction($operand2);
+ $result = $matrixResult->getArray();
+ } catch (Exception $ex) {
+ $this->_writeDebug('JAMA Matrix Exception: '.$ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ // If we're dealing with non-matrix operations, execute the necessary operation
+ switch ($operation) {
+ // Addition
+ case '+':
+ $result = $operand1+$operand2;
+ break;
+ // Subtraction
+ case '-':
+ $result = $operand1-$operand2;
+ break;
+ // Multiplication
+ case '*':
+ $result = $operand1*$operand2;
+ break;
+ // Division
+ case '/':
+ if ($operand2 == 0) {
+ // Trap for Divide by Zero error
+ $stack->push('Value','#DIV/0!');
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails('#DIV/0!'));
+ return false;
+ } else {
+ $result = $operand1/$operand2;
+ }
+ break;
+ // Power
+ case '^':
+ $result = pow($operand1,$operand2);
+ break;
+ }
+ }
+
+ // Log the result details
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value',$result);
+ return true;
+ } // function _executeNumericBinaryOperation()
+
+
+ private function _writeDebug($message) {
+ // Only write the debug log if logging is enabled
+ if ($this->writeDebugLog) {
+ $this->debugLog[] = implode(' -> ',$this->debugLogStack).' -> '.$message;
+ }
+ } // function _writeDebug()
+
+
+ // trigger an error, but nicely, if need be
+ protected function _raiseFormulaError($errorMessage) {
+ $this->formulaError = $errorMessage;
+ if (!$this->suppressFormulaErrors) throw new Exception($errorMessage);
+ trigger_error($errorMessage, E_USER_ERROR);
+ } // function _raiseFormulaError()
+
+
+ /**
+ * Extract range values
+ *
+ * @param string &$pRange String based range representation
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @throws Exception
+ */
+ public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog=true) {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractCellRange('.$pRange.')
';
+ if (!is_null($pSheet)) {
+// echo 'Passed sheet name is '.$pSheet->getTitle().'
';
+// echo 'Range reference is '.$pRange.'
';
+ if (strpos ($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference
';
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+ $pSheet = $pSheet->getParent()->getSheetByName($worksheetReference[0]);
+// echo 'New sheet name is '.$pSheet->getTitle().'
';
+ $pRange = $worksheetReference[1];
+// echo 'Adjusted Range reference is '.$pRange.'
';
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+ $pRange = $pSheet->getTitle().'!'.$pRange;
+ if (count($aReferences) == 1) {
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ } else {
+ // Extract cell data
+ foreach ($aReferences as $reference) {
+ // Extract range
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference);
+
+ if ($pSheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ }
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function extractCellRange()
+
+
+ /**
+ * Extract range values
+ *
+ * @param string &$pRange String based range representation
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @throws Exception
+ */
+ public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog=true) {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractNamedRange('.$pRange.')
';
+ if (!is_null($pSheet)) {
+// echo 'Current sheet name is '.$pSheet->getTitle().'
';
+// echo 'Range reference is '.$pRange.'
';
+ if (strpos ($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference
';
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+ $pSheet = $pSheet->getParent()->getSheetByName($worksheetReference[0]);
+// echo 'New sheet name is '.$pSheet->getTitle().'
';
+ $pRange = $worksheetReference[1];
+// echo 'Adjusted Range reference is '.$pRange.'
';
+ }
+
+ // Named range?
+ $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet);
+ if (!is_null($namedRange)) {
+ $pSheet = $namedRange->getWorksheet();
+//// echo 'Named Range '.$pRange.' (';
+ $pRange = $namedRange->getRange();
+//// echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'
';
+// if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) {
+// if (!$namedRange->getLocalOnly()) {
+// $pSheet = $namedRange->getWorksheet();
+// } else {
+// return $returnValue;
+// }
+// }
+ } else {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+ if (count($aReferences) == 1) {
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ } else {
+ // Extract cell data
+ foreach ($aReferences as $reference) {
+ // Extract range
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference);
+// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'
';
+ if ($pSheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ }
+ }
+// print_r($returnValue);
+// echo '
';
+ }
+
+ // Return
+ return $returnValue;
+ } // function extractNamedRange()
+
+
+ /**
+ * Is a specific function implemented?
+ *
+ * @param string $pFunction Function Name
+ * @return boolean
+ */
+ public function isImplemented($pFunction = '') {
+ $pFunction = strtoupper ($pFunction);
+ if (isset(self::$_PHPExcelFunctions[$pFunction])) {
+ return (self::$_PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY');
+ } else {
+ return false;
+ }
+ } // function isImplemented()
+
+
+ /**
+ * Get a list of all implemented functions as an array of function objects
+ *
+ * @return array of PHPExcel_Calculation_Function
+ */
+ public function listFunctions() {
+ // Return value
+ $returnValue = array();
+ // Loop functions
+ foreach(self::$_PHPExcelFunctions as $functionName => $function) {
+ if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
+ $returnValue[$functionName] = new PHPExcel_Calculation_Function($function['category'],
+ $functionName,
+ $function['functionCall']
+ );
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function listFunctions()
+
+
+ /**
+ * Get a list of implemented Excel function names
+ *
+ * @return array
+ */
+ public function listFunctionNames() {
+ return array_keys(self::$_PHPExcelFunctions);
+ } // function listFunctionNames()
+
+} // class PHPExcel_Calculation
+
+
+
+
+// for internal use
+class PHPExcel_Token_Stack {
+
+ private $_stack = array();
+ private $_count = 0;
+
+
+ public function count() {
+ return $this->_count;
+ } // function count()
+
+
+ public function push($type,$value,$reference=null) {
+ $this->_stack[$this->_count++] = array('type' => $type,
+ 'value' => $value,
+ 'reference' => $reference
+ );
+ if ($type == 'Function') {
+ $localeFunction = PHPExcel_Calculation::_localeFunc($value);
+ if ($localeFunction != $value) {
+ $this->_stack[($this->_count - 1)]['localeValue'] = $localeFunction;
+ }
+ }
+ } // function push()
+
+
+ public function pop() {
+ if ($this->_count > 0) {
+ return $this->_stack[--$this->_count];
+ }
+ return null;
+ } // function pop()
+
+
+ public function last($n=1) {
+ if ($this->_count-$n < 0) {
+ return null;
+ }
+ return $this->_stack[$this->_count-$n];
+ } // function last()
+
+
+ function __construct() {
+ }
+
+} // class PHPExcel_Token_Stack
diff --git a/Classes/PHPExcel/Calculation/Exception.php b/Classes/PHPExcel/Calculation/Exception.php
new file mode 100644
index 0000000..d674af4
--- /dev/null
+++ b/Classes/PHPExcel/Calculation/Exception.php
@@ -0,0 +1,52 @@
+line = $line;
+ $e->file = $file;
+ throw $e;
+ }
+}
diff --git a/Classes/PHPExcel/Calculation/ExceptionHandler.php b/Classes/PHPExcel/Calculation/ExceptionHandler.php
new file mode 100644
index 0000000..9d6d95d
--- /dev/null
+++ b/Classes/PHPExcel/Calculation/ExceptionHandler.php
@@ -0,0 +1,49 @@
+<";
+ const OPERATORS_POSTFIX = "%";
+
+ /**
+ * Formula
+ *
+ * @var string
+ */
+ private $_formula;
+
+ /**
+ * Tokens
+ *
+ * @var PHPExcel_Calculation_FormulaToken[]
+ */
+ private $_tokens = array();
+
+ /**
+ * Create a new PHPExcel_Calculation_FormulaParser
+ *
+ * @param string $pFormula Formula to parse
+ * @throws Exception
+ */
+ public function __construct($pFormula = '')
+ {
+ // Check parameters
+ if (is_null($pFormula)) {
+ throw new Exception("Invalid parameter passed: formula");
+ }
+
+ // Initialise values
+ $this->_formula = trim($pFormula);
+ // Parse!
+ $this->_parseToTokens();
+ }
+
+ /**
+ * Get Formula
+ *
+ * @return string
+ */
+ public function getFormula() {
+ return $this->_formula;
+ }
+
+ /**
+ * Get Token
+ *
+ * @param int $pId Token id
+ * @return string
+ * @throws Exception
+ */
+ public function getToken($pId = 0) {
+ if (isset($this->_tokens[$pId])) {
+ return $this->_tokens[$pId];
+ } else {
+ throw new Exception("Token with id $pId does not exist.");
+ }
+ }
+
+ /**
+ * Get Token count
+ *
+ * @return string
+ */
+ public function getTokenCount() {
+ return count($this->_tokens);
+ }
+
+ /**
+ * Get Tokens
+ *
+ * @return PHPExcel_Calculation_FormulaToken[]
+ */
+ public function getTokens() {
+ return $this->_tokens;
+ }
+
+ /**
+ * Parse to tokens
+ */
+ private function _parseToTokens() {
+ // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
+ // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
+
+ // Check if the formula has a valid starting =
+ $formulaLength = strlen($this->_formula);
+ if ($formulaLength < 2 || $this->_formula{0} != '=') return;
+
+ // Helper variables
+ $tokens1 = $tokens2 = $stack = array();
+ $inString = $inPath = $inRange = $inError = false;
+ $token = $previousToken = $nextToken = null;
+
+ $index = 1;
+ $value = '';
+
+ $ERRORS = array("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A");
+ $COMPARATORS_MULTI = array(">=", "<=", "<>");
+
+ while ($index < $formulaLength) {
+ // state-dependent character evaluation (order is important)
+
+ // double-quoted strings
+ // embeds are doubled
+ // end marks token
+ if ($inString) {
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->_formula{$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE)) {
+ $value .= PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE;
+ ++$index;
+ } else {
+ $inString = false;
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_TEXT);
+ $value = "";
+ }
+ } else {
+ $value .= $this->_formula{$index};
+ }
+ ++$index;
+ continue;
+ }
+
+ // single-quoted strings (links)
+ // embeds are double
+ // end does not mark a token
+ if ($inPath) {
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->_formula{$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE)) {
+ $value .= PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE;
+ ++$index;
+ } else {
+ $inPath = false;
+ }
+ } else {
+ $value .= $this->_formula{$index};
+ }
+ ++$index;
+ continue;
+ }
+
+ // bracked strings (R1C1 range index or linked workbook name)
+ // no embeds (changed to "()" by Excel)
+ // end does not mark a token
+ if ($inRange) {
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACKET_CLOSE) {
+ $inRange = false;
+ }
+ $value .= $this->_formula{$index};
+ ++$index;
+ continue;
+ }
+
+ // error values
+ // end marks a token, determined from absolute list of values
+ if ($inError) {
+ $value .= $this->_formula{$index};
+ ++$index;
+ if (in_array($value, $ERRORS)) {
+ $inError = false;
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_ERROR);
+ $value = "";
+ }
+ continue;
+ }
+
+ // scientific notation check
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_SN, $this->_formula{$index}) !== false) {
+ if (strlen($value) > 1) {
+ if (preg_match("/^[1-9]{1}(\.[0-9]+)?E{1}$/", $this->_formula{$index}) != 0) {
+ $value .= $this->_formula{$index};
+ ++$index;
+ continue;
+ }
+ }
+ }
+
+ // independent character evaluation (order not important)
+
+ // establish state-dependent character evaluations
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE) {
+ if (strlen($value > 0)) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inString = true;
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE) {
+ if (strlen($value) > 0) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inPath = true;
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACKET_OPEN) {
+ $inRange = true;
+ $value .= PHPExcel_Calculation_FormulaParser::BRACKET_OPEN;
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::ERROR_START) {
+ if (strlen($value) > 0) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inError = true;
+ $value .= PHPExcel_Calculation_FormulaParser::ERROR_START;
+ ++$index;
+ continue;
+ }
+
+ // mark start and end of arrays and array rows
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACE_OPEN) {
+ if (strlen($value) > 0) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAY", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::SEMICOLON) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT);
+ $tokens1[] = $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACE_CLOSE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ // trim white-space
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken("", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE);
+ ++$index;
+ while (($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) && ($index < $formulaLength)) {
+ ++$index;
+ }
+ continue;
+ }
+
+ // multi-character comparators
+ if (($index + 2) <= $formulaLength) {
+ if (in_array(substr($this->_formula, $index, 2), $COMPARATORS_MULTI)) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(substr($this->_formula, $index, 2), PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ $index += 2;
+ continue;
+ }
+ }
+
+ // standard infix operators
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_INFIX, $this->_formula{$index}) !== false) {
+ if (strlen($value) > 0) {
+ $tokens1[] =new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($this->_formula{$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX);
+ ++$index;
+ continue;
+ }
+
+ // standard postfix operators (only one)
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_POSTFIX, $this->_formula{$index}) !== false) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($this->_formula{$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
+ ++$index;
+ continue;
+ }
+
+ // start subexpression or function
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::PAREN_OPEN) {
+ if (strlen($value) > 0) {
+ $tmp = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ $value = "";
+ } else {
+ $tmp = new PHPExcel_Calculation_FormulaToken("", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ }
+ ++$index;
+ continue;
+ }
+
+ // function, subexpression, or array parameters, or operand unions
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::COMMA) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $stack[] = $tmp;
+
+ if ($tmp->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_UNION);
+ } else {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT);
+ }
+ ++$index;
+ continue;
+ }
+
+ // stop subexpression
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::PAREN_CLOSE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ // token accumulation
+ $value .= $this->_formula{$index};
+ ++$index;
+ }
+
+ // dump remaining accumulation
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ }
+
+ // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
+ $tokenCount = count($tokens1);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens1[$i];
+ if (isset($tokens1[$i - 1])) {
+ $previousToken = $tokens1[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens1[$i + 1])) {
+ $nextToken = $tokens1[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if (is_null($token)) {
+ continue;
+ }
+
+ if ($token->getTokenType() != PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE) {
+ $tokens2[] = $token;
+ continue;
+ }
+
+ if (is_null($previousToken)) {
+ continue;
+ }
+
+ if (! (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) ) {
+ continue;
+ }
+
+ if (is_null($nextToken)) {
+ continue;
+ }
+
+ if (! (
+ (($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) ||
+ (($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) ||
+ ($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) ) {
+ continue;
+ }
+
+ $tokens2[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
+ }
+
+ // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
+ // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
+ $this->_tokens = array();
+
+ $tokenCount = count($tokens2);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens2[$i];
+ if (isset($tokens2[$i - 1])) {
+ $previousToken = $tokens2[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens2[$i + 1])) {
+ $nextToken = $tokens2[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if (is_null($token)) {
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "-") {
+ if ($i == 0) {
+ $token->setTokenType(PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ } else if (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ $token->setTokenType(PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "+") {
+ if ($i == 0) {
+ continue;
+ } else if (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ continue;
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (strpos("<>=", substr($token->getValue(), 0, 1)) !== false) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } else if ($token->getValue() == "&") {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND && $token->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (!is_numeric($token->getValue())) {
+ if (strtoupper($token->getValue()) == "TRUE" || strtoupper($token->getValue() == "FALSE")) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_RANGE);
+ }
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NUMBER);
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) {
+ if (strlen($token->getValue() > 0)) {
+ if (substr($token->getValue(), 0, 1) == "@") {
+ $token->setValue(substr($token->getValue(), 1));
+ }
+ }
+ }
+
+ $this->_tokens[] = $token;
+ }
+ }
+}
diff --git a/Classes/PHPExcel/Calculation/FormulaToken.php b/Classes/PHPExcel/Calculation/FormulaToken.php
new file mode 100644
index 0000000..c28e443
--- /dev/null
+++ b/Classes/PHPExcel/Calculation/FormulaToken.php
@@ -0,0 +1,176 @@
+_value = $pValue;
+ $this->_tokenType = $pTokenType;
+ $this->_tokenSubType = $pTokenSubType;
+ }
+
+ /**
+ * Get Value
+ *
+ * @return string
+ */
+ public function getValue() {
+ return $this->_value;
+ }
+
+ /**
+ * Set Value
+ *
+ * @param string $value
+ */
+ public function setValue($value) {
+ $this->_value = $value;
+ }
+
+ /**
+ * Get Token Type (represented by TOKEN_TYPE_*)
+ *
+ * @return string
+ */
+ public function getTokenType() {
+ return $this->_tokenType;
+ }
+
+ /**
+ * Set Token Type
+ *
+ * @param string $value
+ */
+ public function setTokenType($value = PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN) {
+ $this->_tokenType = $value;
+ }
+
+ /**
+ * Get Token SubType (represented by TOKEN_SUBTYPE_*)
+ *
+ * @return string
+ */
+ public function getTokenSubType() {
+ return $this->_tokenSubType;
+ }
+
+ /**
+ * Set Token SubType
+ *
+ * @param string $value
+ */
+ public function setTokenSubType($value = PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ $this->_tokenSubType = $value;
+ }
+}
diff --git a/Classes/PHPExcel/Calculation/Function.php b/Classes/PHPExcel/Calculation/Function.php
new file mode 100644
index 0000000..407fb77
--- /dev/null
+++ b/Classes/PHPExcel/Calculation/Function.php
@@ -0,0 +1,149 @@
+_category = $pCategory;
+ $this->_excelName = $pExcelName;
+ $this->_phpExcelName = $pPHPExcelName;
+ } else {
+ throw new Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Get Category (represented by CATEGORY_*)
+ *
+ * @return string
+ */
+ public function getCategory() {
+ return $this->_category;
+ }
+
+ /**
+ * Set Category (represented by CATEGORY_*)
+ *
+ * @param string $value
+ * @throws Exception
+ */
+ public function setCategory($value = null) {
+ if (!is_null($value)) {
+ $this->_category = $value;
+ } else {
+ throw new Exception("Invalid parameter passed.");
+ }
+ }
+
+ /**
+ * Get Excel name
+ *
+ * @return string
+ */
+ public function getExcelName() {
+ return $this->_excelName;
+ }
+
+ /**
+ * Set Excel name
+ *
+ * @param string $value
+ */
+ public function setExcelName($value) {
+ $this->_excelName = $value;
+ }
+
+ /**
+ * Get PHPExcel name
+ *
+ * @return string
+ */
+ public function getPHPExcelName() {
+ return $this->_phpExcelName;
+ }
+
+ /**
+ * Set PHPExcel name
+ *
+ * @param string $value
+ */
+ public function setPHPExcelName($value) {
+ $this->_phpExcelName = $value;
+ }
+}
diff --git a/Classes/PHPExcel/Calculation/Functions.php b/Classes/PHPExcel/Calculation/Functions.php
new file mode 100644
index 0000000..f8f19fb
--- /dev/null
+++ b/Classes/PHPExcel/Calculation/Functions.php
@@ -0,0 +1,12038 @@
+ '#NULL!',
+ 'divisionbyzero' => '#DIV/0!',
+ 'value' => '#VALUE!',
+ 'reference' => '#REF!',
+ 'name' => '#NAME?',
+ 'num' => '#NUM!',
+ 'na' => '#N/A',
+ 'gettingdata' => '#GETTING_DATA'
+ );
+
+
+ /**
+ * Set the Compatibility Mode
+ *
+ * @access public
+ * @category Function Configuration
+ * @param string $compatibilityMode Compatibility Mode
+ * Permitted values are:
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ * @return boolean (Success or Failure)
+ */
+ public static function setCompatibilityMode($compatibilityMode) {
+ if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
+ ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
+ ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
+ self::$compatibilityMode = $compatibilityMode;
+ return True;
+ }
+ return False;
+ } // function setCompatibilityMode()
+
+
+ /**
+ * Return the current Compatibility Mode
+ *
+ * @access public
+ * @category Function Configuration
+ * @return string Compatibility Mode
+ * Possible Return values are:
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ */
+ public static function getCompatibilityMode() {
+ return self::$compatibilityMode;
+ } // function getCompatibilityMode()
+
+
+ /**
+ * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
+ *
+ * @access public
+ * @category Function Configuration
+ * @param string $returnDateType Return Date Format
+ * Permitted values are:
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
+ * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
+ * @return boolean Success or failure
+ */
+ public static function setReturnDateType($returnDateType) {
+ if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
+ ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
+ ($returnDateType == self::RETURNDATE_EXCEL)) {
+ self::$ReturnDateType = $returnDateType;
+ return True;
+ }
+ return False;
+ } // function setReturnDateType()
+
+
+ /**
+ * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
+ *
+ * @access public
+ * @category Function Configuration
+ * @return string Return Date Format
+ * Possible Return values are:
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
+ * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
+ */
+ public static function getReturnDateType() {
+ return self::$ReturnDateType;
+ } // function getReturnDateType()
+
+
+ /**
+ * DUMMY
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #Not Yet Implemented
+ */
+ public static function DUMMY() {
+ return '#Not Yet Implemented';
+ } // function DUMMY()
+
+
+ /**
+ * NA
+ *
+ * Excel Function:
+ * =NA()
+ *
+ * Returns the error value #N/A
+ * #N/A is the error value that means "no value is available."
+ *
+ * @access public
+ * @category Logical Functions
+ * @return string #N/A!
+ */
+ public static function NA() {
+ return self::$_errorCodes['na'];
+ } // function NA()
+
+
+ /**
+ * NAN
+ *
+ * Returns the error value #NUM!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NUM!
+ */
+ public static function NaN() {
+ return self::$_errorCodes['num'];
+ } // function NAN()
+
+
+ /**
+ * NAME
+ *
+ * Returns the error value #NAME?
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NAME?
+ */
+ public static function NAME() {
+ return self::$_errorCodes['name'];
+ } // function NAME()
+
+
+ /**
+ * REF
+ *
+ * Returns the error value #REF!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #REF!
+ */
+ public static function REF() {
+ return self::$_errorCodes['reference'];
+ } // function REF()
+
+
+ /**
+ * VALUE
+ *
+ * Returns the error value #VALUE!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #VALUE!
+ */
+ public static function VALUE() {
+ return self::$_errorCodes['value'];
+ } // function VALUE()
+
+
+ private static function isMatrixValue($idx) {
+ return ((substr_count($idx,'.') <= 1) || (preg_match('/\.[A-Z]/',$idx) > 0));
+ }
+
+
+ private static function isValue($idx) {
+ return (substr_count($idx,'.') == 0);
+ }
+
+
+ private static function isCellValue($idx) {
+ return (substr_count($idx,'.') > 1);
+ }
+
+
+ /**
+ * LOGICAL_AND
+ *
+ * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
+ *
+ * Excel Function:
+ * =AND(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $arg,... Data values
+ * @return boolean The logical AND of the arguments.
+ */
+ public static function LOGICAL_AND() {
+ // Return value
+ $returnValue = True;
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $argCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $returnValue = $returnValue && $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue = $returnValue && ($arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = strtoupper($arg);
+ if ($arg == 'TRUE') {
+ $arg = 1;
+ } elseif ($arg == 'FALSE') {
+ $arg = 0;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $returnValue = $returnValue && ($arg != 0);
+ }
+ ++$argCount;
+ }
+
+ // Return
+ if ($argCount == 0) {
+ return self::$_errorCodes['value'];
+ }
+ return $returnValue;
+ } // function LOGICAL_AND()
+
+
+ /**
+ * LOGICAL_OR
+ *
+ * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
+ *
+ * Excel Function:
+ * =OR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $arg,... Data values
+ * @return boolean The logical OR of the arguments.
+ */
+ public static function LOGICAL_OR() {
+ // Return value
+ $returnValue = False;
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $argCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $returnValue = $returnValue || $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue = $returnValue || ($arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = strtoupper($arg);
+ if ($arg == 'TRUE') {
+ $arg = 1;
+ } elseif ($arg == 'FALSE') {
+ $arg = 0;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $returnValue = $returnValue || ($arg != 0);
+ }
+ ++$argCount;
+ }
+
+ // Return
+ if ($argCount == 0) {
+ return self::$_errorCodes['value'];
+ }
+ return $returnValue;
+ } // function LOGICAL_OR()
+
+
+ /**
+ * LOGICAL_FALSE
+ *
+ * Returns the boolean FALSE.
+ *
+ * Excel Function:
+ * =FALSE()
+ *
+ * @access public
+ * @category Logical Functions
+ * @return boolean False
+ */
+ public static function LOGICAL_FALSE() {
+ return False;
+ } // function LOGICAL_FALSE()
+
+
+ /**
+ * LOGICAL_TRUE
+ *
+ * Returns the boolean TRUE.
+ *
+ * Excel Function:
+ * =TRUE()
+ *
+ * @access public
+ * @category Logical Functions
+ * @return boolean True
+ */
+ public static function LOGICAL_TRUE() {
+ return True;
+ } // function LOGICAL_TRUE()
+
+
+ /**
+ * LOGICAL_NOT
+ *
+ * Returns the boolean inverse of the argument.
+ *
+ * Excel Function:
+ * =NOT(logical)
+ *
+ * The argument must evaluate to a logical value such as TRUE or FALSE
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+ * @return boolean The boolean inverse of the argument.
+ */
+ public static function LOGICAL_NOT($logical) {
+ $logical = self::flattenSingleValue($logical);
+ if (is_string($logical)) {
+ $logical = strtoupper($logical);
+ if ($logical == 'TRUE') {
+ return False;
+ } elseif ($logical == 'FALSE') {
+ return True;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+
+ return !$logical;
+ } // function LOGICAL_NOT()
+
+
+ /**
+ * STATEMENT_IF
+ *
+ * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
+ *
+ * Excel Function:
+ * =IF(condition[,returnIfTrue[,returnIfFalse]])
+ *
+ * Condition is any value or expression that can be evaluated to TRUE or FALSE.
+ * For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
+ * the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
+ * This argument can use any comparison calculation operator.
+ * ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
+ * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
+ * then the IF function returns the text "Within budget"
+ * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
+ * the logical value TRUE for this argument.
+ * ReturnIfTrue can be another formula.
+ * ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
+ * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
+ * then the IF function returns the text "Over budget".
+ * If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
+ * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
+ * ReturnIfFalse can be another formula.
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $condition Condition to evaluate
+ * @param mixed $returnIfTrue Value to return when condition is true
+ * @param mixed $returnIfFalse Optional value to return when condition is false
+ * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
+ */
+ public static function STATEMENT_IF($condition = true, $returnIfTrue = 0, $returnIfFalse = False) {
+ $condition = (is_null($condition)) ? True : (boolean) self::flattenSingleValue($condition);
+ $returnIfTrue = (is_null($returnIfTrue)) ? 0 : self::flattenSingleValue($returnIfTrue);
+ $returnIfFalse = (is_null($returnIfFalse)) ? False : self::flattenSingleValue($returnIfFalse);
+
+ return ($condition ? $returnIfTrue : $returnIfFalse);
+ } // function STATEMENT_IF()
+
+
+ /**
+ * STATEMENT_IFERROR
+ *
+ * Excel Function:
+ * =IFERROR(testValue,errorpart)
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $testValue Value to check, is also the value returned when no error
+ * @param mixed $errorpart Value to return when testValue is an error condition
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function STATEMENT_IFERROR($testValue = '', $errorpart = '') {
+ $testValue = (is_null($testValue)) ? '' : self::flattenSingleValue($testValue);
+ $errorpart = (is_null($errorpart)) ? '' : self::flattenSingleValue($errorpart);
+
+ return self::STATEMENT_IF(self::IS_ERROR($testValue), $errorpart, $testValue);
+ } // function STATEMENT_IFERROR()
+
+
+ /**
+ * HYPERLINK
+ *
+ * Excel Function:
+ * =HYPERLINK(linkURL,displayName)
+ *
+ * @access public
+ * @category Logical Functions
+ * @param string $linkURL Value to check, is also the value returned when no error
+ * @param string $displayName Value to return when testValue is an error condition
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function HYPERLINK($linkURL = '', $displayName = null, PHPExcel_Cell $pCell = null) {
+ $args = func_get_args();
+ $pCell = array_pop($args);
+
+ $linkURL = (is_null($linkURL)) ? '' : self::flattenSingleValue($linkURL);
+ $displayName = (is_null($displayName)) ? '' : self::flattenSingleValue($displayName);
+
+ if ((!is_object($pCell)) || (trim($linkURL) == '')) {
+ return self::$_errorCodes['reference'];
+ }
+
+ if ((is_object($displayName)) || trim($displayName) == '') {
+ $displayName = $linkURL;
+ }
+
+ $pCell->getHyperlink()->setUrl($linkURL);
+
+ return $displayName;
+ } // function HYPERLINK()
+
+
+ /**
+ * ATAN2
+ *
+ * This function calculates the arc tangent of the two variables x and y. It is similar to
+ * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
+ * to determine the quadrant of the result.
+ * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
+ * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
+ * -pi and pi, excluding -pi.
+ *
+ * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
+ * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
+ *
+ * Excel Function:
+ * ATAN2(xCoordinate,yCoordinate)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $xCoordinate The x-coordinate of the point.
+ * @param float $yCoordinate The y-coordinate of the point.
+ * @return float The inverse tangent of the specified x- and y-coordinates.
+ */
+ public static function REVERSE_ATAN2($xCoordinate, $yCoordinate) {
+ $xCoordinate = (float) self::flattenSingleValue($xCoordinate);
+ $yCoordinate = (float) self::flattenSingleValue($yCoordinate);
+
+ if (($xCoordinate == 0) && ($yCoordinate == 0)) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ return atan2($yCoordinate, $xCoordinate);
+ } // function REVERSE_ATAN2()
+
+
+ /**
+ * LOG_BASE
+ *
+ * Returns the logarithm of a number to a specified base. The default base is 10.
+ *
+ * Excel Function:
+ * LOG(number[,base])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $value The positive real number for which you want the logarithm
+ * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
+ * @return float
+ */
+ public static function LOG_BASE($number, $base=10) {
+ $number = self::flattenSingleValue($number);
+ $base = (is_null($base)) ? 10 : (float) self::flattenSingleValue($base);
+
+ return log($number, $base);
+ } // function LOG_BASE()
+
+
+ /**
+ * SUM
+ *
+ * SUM computes the sum of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * SUM(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUM() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += $arg;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function SUM()
+
+
+ /**
+ * SUMSQ
+ *
+ * SUMSQ returns the sum of the squares of the arguments
+ *
+ * Excel Function:
+ * SUMSQ(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUMSQ() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += ($arg * $arg);
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function SUMSQ()
+
+
+ /**
+ * PRODUCT
+ *
+ * PRODUCT returns the product of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * PRODUCT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function PRODUCT() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue *= $arg;
+ }
+ }
+ }
+
+ // Return
+ if (is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function PRODUCT()
+
+
+ /**
+ * QUOTIENT
+ *
+ * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
+ * and denominator is the divisor.
+ *
+ * Excel Function:
+ * QUOTIENT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function QUOTIENT() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = ($arg == 0) ? 0 : $arg;
+ } else {
+ if (($returnValue == 0) || ($arg == 0)) {
+ $returnValue = 0;
+ } else {
+ $returnValue /= $arg;
+ }
+ }
+ }
+ }
+
+ // Return
+ return intval($returnValue);
+ } // function QUOTIENT()
+
+
+ /**
+ * MIN
+ *
+ * MIN returns the value of the element of the values passed that has the smallest value,
+ * with negative numbers considered smaller than positive numbers.
+ *
+ * Excel Function:
+ * MIN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MIN() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ((is_null($returnValue)) || ($arg < $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MIN()
+
+
+ /**
+ * MINA
+ *
+ * Returns the smallest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MINA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MINA() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if ((is_null($returnValue)) || ($arg < $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MINA()
+
+
+ /**
+ * MINIF
+ *
+ * Returns the minimum value within a range of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * MINIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be checked.
+ * @return float
+ */
+ public static function MINIF($aArgs,$condition,$sumArgs = array()) {
+ // Return value
+ $returnValue = null;
+
+ $aArgs = self::flattenArray($aArgs);
+ $sumArgs = self::flattenArray($sumArgs);
+ if (count($sumArgs) == 0) {
+ $sumArgs = $aArgs;
+ }
+ $condition = self::_ifCondition($condition);
+ // Loop through arguments
+ foreach ($aArgs as $key => $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ if ((is_null($returnValue)) || ($arg < $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function MINIF()
+
+
+ /**
+ * SMALL
+ *
+ * Returns the nth smallest value in a data set. You can use this function to
+ * select a value based on its relative standing.
+ *
+ * Excel Function:
+ * SMALL(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param int $entry Position (ordered from the smallest) in the array or range of data to return
+ * @return float
+ */
+ public static function SMALL() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $count = self::COUNT($mArgs);
+ $entry = floor(--$entry);
+ if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
+ return self::$_errorCodes['num'];
+ }
+ sort($mArgs);
+ return $mArgs[$entry];
+ }
+ return self::$_errorCodes['value'];
+ } // function SMALL()
+
+
+ /**
+ * MAX
+ *
+ * MAX returns the value of the element of the values passed that has the highest value,
+ * with negative numbers considered smaller than positive numbers.
+ *
+ * Excel Function:
+ * MAX(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MAX() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ((is_null($returnValue)) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MAX()
+
+
+ /**
+ * MAXA
+ *
+ * Returns the greatest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MAXA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MAXA() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if ((is_null($returnValue)) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MAXA()
+
+
+ private static function _ifCondition($condition) {
+ $condition = self::flattenSingleValue($condition);
+ if (!in_array($condition{0},array('>', '<', '='))) {
+ if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
+ return '='.$condition;
+ } else {
+ preg_match('/([<>=]+)(.*)/',$condition,$matches);
+ list(,$operator,$operand) = $matches;
+ if (!is_numeric($operand)) { $operand = PHPExcel_Calculation::_wrapResult(strtoupper($operand)); }
+ return $operator.$operand;
+ }
+ } // function _ifCondition()
+
+ /**
+ * MAXIF
+ *
+ * Counts the maximum value within a range of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * MAXIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be checked.
+ * @return float
+ */
+ public static function MAXIF($aArgs,$condition,$sumArgs = array()) {
+ // Return value
+ $returnValue = null;
+
+ $aArgs = self::flattenArray($aArgs);
+ $sumArgs = self::flattenArray($sumArgs);
+ if (count($sumArgs) == 0) {
+ $sumArgs = $aArgs;
+ }
+ $condition = self::_ifCondition($condition);
+ // Loop through arguments
+ foreach ($aArgs as $key => $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ if ((is_null($returnValue)) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function MAXIF()
+
+
+ /**
+ * LARGE
+ *
+ * Returns the nth largest value in a data set. You can use this function to
+ * select a value based on its relative standing.
+ *
+ * Excel Function:
+ * LARGE(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param int $entry Position (ordered from the largest) in the array or range of data to return
+ * @return float
+ *
+ */
+ public static function LARGE() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = floor(array_pop($aArgs));
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $count = self::COUNT($mArgs);
+ $entry = floor(--$entry);
+ if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
+ return self::$_errorCodes['num'];
+ }
+ rsort($mArgs);
+ return $mArgs[$entry];
+ }
+ return self::$_errorCodes['value'];
+ } // function LARGE()
+
+
+ /**
+ * PERCENTILE
+ *
+ * Returns the nth percentile of values in a range..
+ *
+ * Excel Function:
+ * PERCENTILE(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param float $entry Percentile value in the range 0..1, inclusive.
+ * @return float
+ */
+ public static function PERCENTILE() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ if (($entry < 0) || ($entry > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs);
+ $count = self::COUNT($mArgs);
+ $index = $entry * ($count-1);
+ $iBase = floor($index);
+ if ($index == $iBase) {
+ return $mArgs[$index];
+ } else {
+ $iNext = $iBase + 1;
+ $iProportion = $index - $iBase;
+ return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function PERCENTILE()
+
+
+ /**
+ * QUARTILE
+ *
+ * Returns the quartile of a data set.
+ *
+ * Excel Function:
+ * QUARTILE(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param int $entry Quartile value in the range 1..3, inclusive.
+ * @return float
+ */
+ public static function QUARTILE() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = floor(array_pop($aArgs));
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $entry /= 4;
+ if (($entry < 0) || ($entry > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::PERCENTILE($aArgs,$entry);
+ }
+ return self::$_errorCodes['value'];
+ } // function QUARTILE()
+
+
+ /**
+ * COUNT
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * COUNT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return int
+ */
+ public static function COUNT() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNT()
+
+
+ /**
+ * COUNTBLANK
+ *
+ * Counts the number of empty cells within the list of arguments
+ *
+ * Excel Function:
+ * COUNTBLANK(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return int
+ */
+ public static function COUNTBLANK() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a blank cell?
+ if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNTBLANK()
+
+
+ /**
+ * COUNTA
+ *
+ * Counts the number of cells that are not empty within the list of arguments
+ *
+ * Excel Function:
+ * COUNTA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return int
+ */
+ public static function COUNTA() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric, boolean or string value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNTA()
+
+
+ /**
+ * COUNTIF
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * COUNTIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be counted.
+ * @return int
+ */
+ public static function COUNTIF($aArgs,$condition) {
+ // Return value
+ $returnValue = 0;
+
+ $aArgs = self::flattenArray($aArgs);
+ $condition = self::_ifCondition($condition);
+ // Loop through arguments
+ foreach ($aArgs as $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is it a value within our criteria
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNTIF()
+
+
+ /**
+ * SUMIF
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * SUMIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be summed.
+ * @return float
+ */
+ public static function SUMIF($aArgs,$condition,$sumArgs = array()) {
+ // Return value
+ $returnValue = 0;
+
+ $aArgs = self::flattenArray($aArgs);
+ $sumArgs = self::flattenArray($sumArgs);
+ if (count($sumArgs) == 0) {
+ $sumArgs = $aArgs;
+ }
+ $condition = self::_ifCondition($condition);
+ // Loop through arguments
+ foreach ($aArgs as $key => $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is it a value within our criteria
+ $returnValue += $sumArgs[$key];
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function SUMIF()
+
+
+ /**
+ * AVERAGE
+ *
+ * Returns the average (arithmetic mean) of the arguments
+ *
+ * Excel Function:
+ * AVERAGE(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function AVERAGE() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ $returnValue = $aCount = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue += $arg;
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ } else {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ } // function AVERAGE()
+
+
+ /**
+ * AVERAGEA
+ *
+ * Returns the average of its arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * AVERAGEA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function AVERAGEA() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue += $arg;
+ }
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ } else {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ } // function AVERAGEA()
+
+
+ /**
+ * AVERAGEIF
+ *
+ * Returns the average value from a range of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * AVERAGEIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be checked.
+ * @return float
+ */
+ public static function AVERAGEIF($aArgs,$condition,$averageArgs = array()) {
+ // Return value
+ $returnValue = 0;
+
+ $aArgs = self::flattenArray($aArgs);
+ $averageArgs = self::flattenArray($averageArgs);
+ if (count($averageArgs) == 0) {
+ $averageArgs = $aArgs;
+ }
+ $condition = self::_ifCondition($condition);
+ // Loop through arguments
+ $aCount = 0;
+ foreach ($aArgs as $key => $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ if ((is_null($returnValue)) || ($arg > $returnValue)) {
+ $returnValue += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ } else {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ } // function AVERAGEIF()
+
+
+ /**
+ * MEDIAN
+ *
+ * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
+ *
+ * Excel Function:
+ * MEDIAN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MEDIAN() {
+ // Return value
+ $returnValue = self::$_errorCodes['num'];
+
+ $mArgs = array();
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs,SORT_NUMERIC);
+ $mValueCount = $mValueCount / 2;
+ if ($mValueCount == floor($mValueCount)) {
+ $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
+ } else {
+ $mValueCount == floor($mValueCount);
+ $returnValue = $mArgs[$mValueCount];
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function MEDIAN()
+
+
+ //
+ // Special variant of array_count_values that isn't limited to strings and integers,
+ // but can work with floating point numbers as values
+ //
+ private static function _modeCalc($data) {
+ $frequencyArray = array();
+ foreach($data as $datum) {
+ $found = False;
+ foreach($frequencyArray as $key => $value) {
+ if ((string) $value['value'] == (string) $datum) {
+ ++$frequencyArray[$key]['frequency'];
+ $found = True;
+ break;
+ }
+ }
+ if (!$found) {
+ $frequencyArray[] = array('value' => $datum,
+ 'frequency' => 1 );
+ }
+ }
+
+ foreach($frequencyArray as $key => $value) {
+ $frequencyList[$key] = $value['frequency'];
+ $valueList[$key] = $value['value'];
+ }
+ array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
+
+ if ($frequencyArray[0]['frequency'] == 1) {
+ return self::NA();
+ }
+ return $frequencyArray[0]['value'];
+ } // function _modeCalc()
+
+
+ /**
+ * MODE
+ *
+ * Returns the most frequently occurring, or repetitive, value in an array or range of data
+ *
+ * Excel Function:
+ * MODE(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MODE() {
+ // Return value
+ $returnValue = self::NA();
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ if (count($mArgs) > 0) {
+ return self::_modeCalc($mArgs);
+ }
+
+ // Return
+ return $returnValue;
+ } // function MODE()
+
+
+ /**
+ * DEVSQ
+ *
+ * Returns the sum of squares of deviations of data points from their sample mean.
+ *
+ * Excel Function:
+ * DEVSQ(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function DEVSQ() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if ($aMean != self::$_errorCodes['divisionbyzero']) {
+ $aCount = -1;
+ foreach ($aArgs as $k => $arg) {
+ // Is it a numeric value?
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if (is_null($returnValue)) {
+ return self::$_errorCodes['num'];
+ } else {
+ return $returnValue;
+ }
+ }
+ return self::NA();
+ } // function DEVSQ()
+
+
+ /**
+ * AVEDEV
+ *
+ * Returns the average of the absolute deviations of data points from their mean.
+ * AVEDEV is a measure of the variability in a data set.
+ *
+ * Excel Function:
+ * AVEDEV(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function AVEDEV() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if ($aMean != self::$_errorCodes['divisionbyzero']) {
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = abs($arg - $aMean);
+ } else {
+ $returnValue += abs($arg - $aMean);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount == 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ return $returnValue / $aCount;
+ }
+ return self::$_errorCodes['num'];
+ } // function AVEDEV()
+
+
+ /**
+ * GEOMEAN
+ *
+ * Returns the geometric mean of an array or range of positive data. For example, you
+ * can use GEOMEAN to calculate average growth rate given compound interest with
+ * variable rates.
+ *
+ * Excel Function:
+ * GEOMEAN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function GEOMEAN() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ $aMean = self::PRODUCT($aArgs);
+ if (is_numeric($aMean) && ($aMean > 0)) {
+ $aCount = self::COUNT($aArgs) ;
+ if (self::MIN($aArgs) > 0) {
+ return pow($aMean, (1 / $aCount));
+ }
+ }
+ return self::$_errorCodes['num'];
+ } // GEOMEAN()
+
+
+ /**
+ * HARMEAN
+ *
+ * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
+ * arithmetic mean of reciprocals.
+ *
+ * Excel Function:
+ * HARMEAN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function HARMEAN() {
+ // Return value
+ $returnValue = self::NA();
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ if (self::MIN($aArgs) < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ($arg <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ if (is_null($returnValue)) {
+ $returnValue = (1 / $arg);
+ } else {
+ $returnValue += (1 / $arg);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return 1 / ($returnValue / $aCount);
+ } else {
+ return $returnValue;
+ }
+ } // function HARMEAN()
+
+
+ /**
+ * TRIMMEAN
+ *
+ * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
+ * taken by excluding a percentage of data points from the top and bottom tails
+ * of a data set.
+ *
+ * Excel Function:
+ * TRIMEAN(value1[,value2[, ...]],$discard)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param float $discard Percentage to discard
+ * @return float
+ */
+ public static function TRIMMEAN() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $percent = array_pop($aArgs);
+
+ if ((is_numeric($percent)) && (!is_string($percent))) {
+ if (($percent < 0) || ($percent > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $discard = floor(self::COUNT($mArgs) * $percent / 2);
+ sort($mArgs);
+ for ($i=0; $i < $discard; ++$i) {
+ array_pop($mArgs);
+ array_shift($mArgs);
+ }
+ return self::AVERAGE($mArgs);
+ }
+ return self::$_errorCodes['value'];
+ } // function TRIMMEAN()
+
+
+ /**
+ * STDEV
+ *
+ * Estimates standard deviation based on a sample. The standard deviation is a measure of how
+ * widely values are dispersed from the average value (the mean).
+ *
+ * Excel Function:
+ * STDEV(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEV() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = -1;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue >= 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEV()
+
+
+ /**
+ * STDEVA
+ *
+ * Estimates standard deviation based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * STDEVA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEVA() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGEA($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = -1;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue >= 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEVA()
+
+
+ /**
+ * STDEVP
+ *
+ * Calculates standard deviation based on the entire population
+ *
+ * Excel Function:
+ * STDEVP(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEVP() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue >= 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEVP()
+
+
+ /**
+ * STDEVPA
+ *
+ * Calculates standard deviation based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * STDEVPA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEVPA() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGEA($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue >= 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEVPA()
+
+
+ /**
+ * VARFunc
+ *
+ * Estimates variance based on a sample.
+ *
+ * Excel Function:
+ * VAR(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARFunc() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ if (is_bool($arg)) { $arg = (integer) $arg; }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+ return $returnValue;
+ } // function VARFunc()
+
+
+ /**
+ * VARA
+ *
+ * Estimates variance based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARA() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) &&
+ (self::isValue($k))) {
+ return self::$_errorCodes['value'];
+ } elseif ((is_string($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+ return $returnValue;
+ } // function VARA()
+
+
+ /**
+ * VARP
+ *
+ * Calculates variance based on the entire population
+ *
+ * Excel Function:
+ * VARP(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARP() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ if (is_bool($arg)) { $arg = (integer) $arg; }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
+ }
+ return $returnValue;
+ } // function VARP()
+
+
+ /**
+ * VARPA
+ *
+ * Calculates variance based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARPA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARPA() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) &&
+ (self::isValue($k))) {
+ return self::$_errorCodes['value'];
+ } elseif ((is_string($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
+ }
+ return $returnValue;
+ } // function VARPA()
+
+
+ /**
+ * RANK
+ *
+ * Returns the rank of a number in a list of numbers.
+ *
+ * @param number The number whose rank you want to find.
+ * @param array of number An array of, or a reference to, a list of numbers.
+ * @param mixed Order to sort the values in the value set
+ * @return float
+ */
+ public static function RANK($value,$valueSet,$order=0) {
+ $value = self::flattenSingleValue($value);
+ $valueSet = self::flattenArray($valueSet);
+ $order = (is_null($order)) ? 0 : (integer) self::flattenSingleValue($order);
+
+ foreach($valueSet as $key => $valueEntry) {
+ if (!is_numeric($valueEntry)) {
+ unset($valueSet[$key]);
+ }
+ }
+
+ if ($order == 0) {
+ rsort($valueSet,SORT_NUMERIC);
+ } else {
+ sort($valueSet,SORT_NUMERIC);
+ }
+ $pos = array_search($value,$valueSet);
+ if ($pos === False) {
+ return self::$_errorCodes['na'];
+ }
+
+ return ++$pos;
+ } // function RANK()
+
+
+ /**
+ * PERCENTRANK
+ *
+ * Returns the rank of a value in a data set as a percentage of the data set.
+ *
+ * @param array of number An array of, or a reference to, a list of numbers.
+ * @param number The number whose rank you want to find.
+ * @param number The number of significant digits for the returned percentage value.
+ * @return float
+ */
+ public static function PERCENTRANK($valueSet,$value,$significance=3) {
+ $valueSet = self::flattenArray($valueSet);
+ $value = self::flattenSingleValue($value);
+ $significance = (is_null($significance)) ? 3 : (integer) self::flattenSingleValue($significance);
+
+ foreach($valueSet as $key => $valueEntry) {
+ if (!is_numeric($valueEntry)) {
+ unset($valueSet[$key]);
+ }
+ }
+ sort($valueSet,SORT_NUMERIC);
+ $valueCount = count($valueSet);
+ if ($valueCount == 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ $valueAdjustor = $valueCount - 1;
+ if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
+ return self::$_errorCodes['na'];
+ }
+
+ $pos = array_search($value,$valueSet);
+ if ($pos === False) {
+ $pos = 0;
+ $testValue = $valueSet[0];
+ while ($testValue < $value) {
+ $testValue = $valueSet[++$pos];
+ }
+ --$pos;
+ $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
+ }
+
+ return round($pos / $valueAdjustor,$significance);
+ } // function PERCENTRANK()
+
+
+ private static function _checkTrendArrays(&$array1,&$array2) {
+ if (!is_array($array1)) { $array1 = array($array1); }
+ if (!is_array($array2)) { $array2 = array($array2); }
+
+ $array1 = self::flattenArray($array1);
+ $array2 = self::flattenArray($array2);
+ foreach($array1 as $key => $value) {
+ if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
+ unset($array1[$key]);
+ unset($array2[$key]);
+ }
+ }
+ foreach($array2 as $key => $value) {
+ if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
+ unset($array1[$key]);
+ unset($array2[$key]);
+ }
+ }
+ $array1 = array_merge($array1);
+ $array2 = array_merge($array2);
+
+ return True;
+ } // function _checkTrendArrays()
+
+
+ /**
+ * INTERCEPT
+ *
+ * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function INTERCEPT($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getIntersect();
+ } // function INTERCEPT()
+
+
+ /**
+ * RSQ
+ *
+ * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function RSQ($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getGoodnessOfFit();
+ } // function RSQ()
+
+
+ /**
+ * SLOPE
+ *
+ * Returns the slope of the linear regression line through data points in known_y's and known_x's.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function SLOPE($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getSlope();
+ } // function SLOPE()
+
+
+ /**
+ * STEYX
+ *
+ * Returns the standard error of the predicted y-value for each x in the regression.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function STEYX($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getStdevOfResiduals();
+ } // function STEYX()
+
+
+ /**
+ * COVAR
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function COVAR($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getCovariance();
+ } // function COVAR()
+
+
+ /**
+ * CORREL
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function CORREL($yValues,$xValues=null) {
+ if ((is_null($xValues)) || (!is_array($yValues)) || (!is_array($xValues))) {
+ return self::$_errorCodes['value'];
+ }
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getCorrelation();
+ } // function CORREL()
+
+
+ /**
+ * LINEST
+ *
+ * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
+ * and then returns an array that describes the line.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @param boolean A logical value specifying whether to return additional regression statistics.
+ * @return array
+ */
+ public static function LINEST($yValues,$xValues=null,$const=True,$stats=False) {
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+ $stats = (is_null($stats)) ? False : (boolean) self::flattenSingleValue($stats);
+ if (is_null($xValues)) $xValues = range(1,count(self::flattenArray($yValues)));
+
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return 0;
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
+ if ($stats) {
+ return array( array( $bestFitLinear->getSlope(),
+ $bestFitLinear->getSlopeSE(),
+ $bestFitLinear->getGoodnessOfFit(),
+ $bestFitLinear->getF(),
+ $bestFitLinear->getSSRegression(),
+ ),
+ array( $bestFitLinear->getIntersect(),
+ $bestFitLinear->getIntersectSE(),
+ $bestFitLinear->getStdevOfResiduals(),
+ $bestFitLinear->getDFResiduals(),
+ $bestFitLinear->getSSResiduals()
+ )
+ );
+ } else {
+ return array( $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect()
+ );
+ }
+ } // function LINEST()
+
+
+ /**
+ * LOGEST
+ *
+ * Calculates an exponential curve that best fits the X and Y data series,
+ * and then returns an array that describes the line.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @param boolean A logical value specifying whether to return additional regression statistics.
+ * @return array
+ */
+ public static function LOGEST($yValues,$xValues=null,$const=True,$stats=False) {
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+ $stats = (is_null($stats)) ? False : (boolean) self::flattenSingleValue($stats);
+ if (is_null($xValues)) $xValues = range(1,count(self::flattenArray($yValues)));
+
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ foreach($yValues as $value) {
+ if ($value <= 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ }
+
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return 1;
+ }
+
+ $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
+ if ($stats) {
+ return array( array( $bestFitExponential->getSlope(),
+ $bestFitExponential->getSlopeSE(),
+ $bestFitExponential->getGoodnessOfFit(),
+ $bestFitExponential->getF(),
+ $bestFitExponential->getSSRegression(),
+ ),
+ array( $bestFitExponential->getIntersect(),
+ $bestFitExponential->getIntersectSE(),
+ $bestFitExponential->getStdevOfResiduals(),
+ $bestFitExponential->getDFResiduals(),
+ $bestFitExponential->getSSResiduals()
+ )
+ );
+ } else {
+ return array( $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect()
+ );
+ }
+ } // function LOGEST()
+
+
+ /**
+ * FORECAST
+ *
+ * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
+ *
+ * @param float Value of X for which we want to find Y
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function FORECAST($xValue,$yValues,$xValues) {
+ $xValue = self::flattenSingleValue($xValue);
+ if (!is_numeric($xValue)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getValueOfYForX($xValue);
+ } // function FORECAST()
+
+
+ /**
+ * TREND
+ *
+ * Returns values along a linear trend
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param array of mixed Values of X for which we want to find Y
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @return array of float
+ */
+ public static function TREND($yValues,$xValues=array(),$newValues=array(),$const=True) {
+ $yValues = self::flattenArray($yValues);
+ $xValues = self::flattenArray($xValues);
+ $newValues = self::flattenArray($newValues);
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
+ if (count($newValues) == 0) {
+ $newValues = $bestFitLinear->getXValues();
+ }
+
+ $returnArray = array();
+ foreach($newValues as $xValue) {
+ $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
+ }
+
+ return $returnArray;
+ } // function TREND()
+
+
+ /**
+ * GROWTH
+ *
+ * Returns values along a predicted emponential trend
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param array of mixed Values of X for which we want to find Y
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @return array of float
+ */
+ public static function GROWTH($yValues,$xValues=array(),$newValues=array(),$const=True) {
+ $yValues = self::flattenArray($yValues);
+ $xValues = self::flattenArray($xValues);
+ $newValues = self::flattenArray($newValues);
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+
+ $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
+ if (count($newValues) == 0) {
+ $newValues = $bestFitExponential->getXValues();
+ }
+
+ $returnArray = array();
+ foreach($newValues as $xValue) {
+ $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
+ }
+
+ return $returnArray;
+ } // function GROWTH()
+
+
+ private static function _romanCut($num, $n) {
+ return ($num - ($num % $n ) ) / $n;
+ } // function _romanCut()
+
+
+ public static function ROMAN($aValue, $style=0) {
+ $aValue = (integer) self::flattenSingleValue($aValue);
+ $style = (is_null($style)) ? 0 : (integer) self::flattenSingleValue($style);
+ if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
+ return self::$_errorCodes['value'];
+ }
+ if ($aValue == 0) {
+ return '';
+ }
+
+ $mill = Array('', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM');
+ $cent = Array('', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');
+ $tens = Array('', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');
+ $ones = Array('', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
+
+ $roman = '';
+ while ($aValue > 5999) {
+ $roman .= 'M';
+ $aValue -= 1000;
+ }
+ $m = self::_romanCut($aValue, 1000); $aValue %= 1000;
+ $c = self::_romanCut($aValue, 100); $aValue %= 100;
+ $t = self::_romanCut($aValue, 10); $aValue %= 10;
+
+ return $roman.$mill[$m].$cent[$c].$tens[$t].$ones[$aValue];
+ } // function ROMAN()
+
+
+ /**
+ * SUBTOTAL
+ *
+ * Returns a subtotal in a list or database.
+ *
+ * @param int the number 1 to 11 that specifies which function to
+ * use in calculating subtotals within a list.
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function SUBTOTAL() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $subtotal = array_shift($aArgs);
+
+ if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
+ switch($subtotal) {
+ case 1 :
+ return self::AVERAGE($aArgs);
+ break;
+ case 2 :
+ return self::COUNT($aArgs);
+ break;
+ case 3 :
+ return self::COUNTA($aArgs);
+ break;
+ case 4 :
+ return self::MAX($aArgs);
+ break;
+ case 5 :
+ return self::MIN($aArgs);
+ break;
+ case 6 :
+ return self::PRODUCT($aArgs);
+ break;
+ case 7 :
+ return self::STDEV($aArgs);
+ break;
+ case 8 :
+ return self::STDEVP($aArgs);
+ break;
+ case 9 :
+ return self::SUM($aArgs);
+ break;
+ case 10 :
+ return self::VARFunc($aArgs);
+ break;
+ case 11 :
+ return self::VARP($aArgs);
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function SUBTOTAL()
+
+
+ /**
+ * SQRTPI
+ *
+ * Returns the square root of (number * pi).
+ *
+ * @param float $number Number
+ * @return float Square Root of Number * Pi
+ */
+ public static function SQRTPI($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ if ($number < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return sqrt($number * M_PI) ;
+ }
+ return self::$_errorCodes['value'];
+ } // function SQRTPI()
+
+
+ /**
+ * FACT
+ *
+ * Returns the factorial of a number.
+ *
+ * @param float $factVal Factorial Value
+ * @return int Factorial
+ */
+ public static function FACT($factVal) {
+ $factVal = self::flattenSingleValue($factVal);
+
+ if (is_numeric($factVal)) {
+ if ($factVal < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $factLoop = floor($factVal);
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ if ($factVal > $factLoop) {
+ return self::$_errorCodes['num'];
+ }
+ }
+
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ }
+ return $factorial ;
+ }
+ return self::$_errorCodes['value'];
+ } // function FACT()
+
+
+ /**
+ * FACTDOUBLE
+ *
+ * Returns the double factorial of a number.
+ *
+ * @param float $factVal Factorial Value
+ * @return int Double Factorial
+ */
+ public static function FACTDOUBLE($factVal) {
+ $factLoop = floor(self::flattenSingleValue($factVal));
+
+ if (is_numeric($factLoop)) {
+ if ($factVal < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ --$factLoop;
+ }
+ return $factorial ;
+ }
+ return self::$_errorCodes['value'];
+ } // function FACTDOUBLE()
+
+
+ /**
+ * MULTINOMIAL
+ *
+ * Returns the ratio of the factorial of a sum of values to the product of factorials.
+ *
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function MULTINOMIAL() {
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $summer = 0;
+ $divisor = 1;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ if ($arg < 1) {
+ return self::$_errorCodes['num'];
+ }
+ $summer += floor($arg);
+ $divisor *= self::FACT($arg);
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+
+ // Return
+ if ($summer > 0) {
+ $summer = self::FACT($summer);
+ return $summer / $divisor;
+ }
+ return 0;
+ } // function MULTINOMIAL()
+
+
+ /**
+ * CEILING
+ *
+ * Returns number rounded up, away from zero, to the nearest multiple of significance.
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @return float Rounded Number
+ */
+ public static function CEILING($number,$significance=null) {
+ $number = self::flattenSingleValue($number);
+ $significance = self::flattenSingleValue($significance);
+
+ if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
+ $significance = $number/abs($number);
+ }
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if (self::SIGN($number) == self::SIGN($significance)) {
+ if ($significance == 0.0) {
+ return 0;
+ }
+ return ceil($number / $significance) * $significance;
+ } else {
+ return self::$_errorCodes['num'];
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function CEILING()
+
+
+ /**
+ * EVEN
+ *
+ * Returns number rounded up to the nearest even integer.
+ *
+ * @param float $number Number to round
+ * @return int Rounded Number
+ */
+ public static function EVEN($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ $significance = 2 * self::SIGN($number);
+ return self::CEILING($number,$significance);
+ }
+ return self::$_errorCodes['value'];
+ } // function EVEN()
+
+
+ /**
+ * ODD
+ *
+ * Returns number rounded up to the nearest odd integer.
+ *
+ * @param float $number Number to round
+ * @return int Rounded Number
+ */
+ public static function ODD($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ $significance = self::SIGN($number);
+ if ($significance == 0) {
+ return 1;
+ }
+ $result = self::CEILING($number,$significance);
+ if (self::IS_EVEN($result)) {
+ $result += $significance;
+ }
+ return $result;
+ }
+ return self::$_errorCodes['value'];
+ } // function ODD()
+
+
+ /**
+ * INTVALUE
+ *
+ * Casts a floating point value to an integer
+ *
+ * @param float $number Number to cast to an integer
+ * @return integer Integer value
+ */
+ public static function INTVALUE($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ return (int) floor($number);
+ }
+ return self::$_errorCodes['value'];
+ } // function INTVALUE()
+
+
+ /**
+ * ROUNDUP
+ *
+ * Rounds a number up to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function ROUNDUP($number,$digits) {
+ $number = self::flattenSingleValue($number);
+ $digits = self::flattenSingleValue($digits);
+
+ if ((is_numeric($number)) && (is_numeric($digits))) {
+ $significance = pow(10,$digits);
+ if ($number < 0.0) {
+ return floor($number * $significance) / $significance;
+ } else {
+ return ceil($number * $significance) / $significance;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function ROUNDUP()
+
+
+ /**
+ * ROUNDDOWN
+ *
+ * Rounds a number down to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function ROUNDDOWN($number,$digits) {
+ $number = self::flattenSingleValue($number);
+ $digits = self::flattenSingleValue($digits);
+
+ if ((is_numeric($number)) && (is_numeric($digits))) {
+ $significance = pow(10,$digits);
+ if ($number < 0.0) {
+ return ceil($number * $significance) / $significance;
+ } else {
+ return floor($number * $significance) / $significance;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function ROUNDDOWN()
+
+
+ /**
+ * MROUND
+ *
+ * Rounds a number to the nearest multiple of a specified value
+ *
+ * @param float $number Number to round
+ * @param int $multiple Multiple to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function MROUND($number,$multiple) {
+ $number = self::flattenSingleValue($number);
+ $multiple = self::flattenSingleValue($multiple);
+
+ if ((is_numeric($number)) && (is_numeric($multiple))) {
+ if ($multiple == 0) {
+ return 0;
+ }
+ if ((self::SIGN($number)) == (self::SIGN($multiple))) {
+ $multiplier = 1 / $multiple;
+ return round($number * $multiplier) / $multiplier;
+ }
+ return self::$_errorCodes['num'];
+ }
+ return self::$_errorCodes['value'];
+ } // function MROUND()
+
+
+ /**
+ * SIGN
+ *
+ * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
+ * if the number is 0, and -1 if the number is negative.
+ *
+ * @param float $number Number to round
+ * @return int sign value
+ */
+ public static function SIGN($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ if ($number == 0.0) {
+ return 0;
+ }
+ return $number / abs($number);
+ }
+ return self::$_errorCodes['value'];
+ } // function SIGN()
+
+
+ /**
+ * FLOOR
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @return float Rounded Number
+ */
+ public static function FLOOR($number,$significance=null) {
+ $number = self::flattenSingleValue($number);
+ $significance = self::flattenSingleValue($significance);
+
+ if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
+ $significance = $number/abs($number);
+ }
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if ((float) $significance == 0.0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ if (self::SIGN($number) == self::SIGN($significance)) {
+ return floor($number / $significance) * $significance;
+ } else {
+ return self::$_errorCodes['num'];
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function FLOOR()
+
+
+ /**
+ * PERMUT
+ *
+ * Returns the number of permutations for a given number of objects that can be
+ * selected from number objects. A permutation is any set or subset of objects or
+ * events where internal order is significant. Permutations are different from
+ * combinations, for which the internal order is not significant. Use this function
+ * for lottery-style probability calculations.
+ *
+ * @param int $numObjs Number of different objects
+ * @param int $numInSet Number of objects in each permutation
+ * @return int Number of permutations
+ */
+ public static function PERMUT($numObjs,$numInSet) {
+ $numObjs = self::flattenSingleValue($numObjs);
+ $numInSet = self::flattenSingleValue($numInSet);
+
+ if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
+ $numInSet = floor($numInSet);
+ if ($numObjs < $numInSet) {
+ return self::$_errorCodes['num'];
+ }
+ return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet));
+ }
+ return self::$_errorCodes['value'];
+ } // function PERMUT()
+
+
+ /**
+ * COMBIN
+ *
+ * Returns the number of combinations for a given number of items. Use COMBIN to
+ * determine the total possible number of groups for a given number of items.
+ *
+ * @param int $numObjs Number of different objects
+ * @param int $numInSet Number of objects in each combination
+ * @return int Number of combinations
+ */
+ public static function COMBIN($numObjs,$numInSet) {
+ $numObjs = self::flattenSingleValue($numObjs);
+ $numInSet = self::flattenSingleValue($numInSet);
+
+ if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
+ if ($numObjs < $numInSet) {
+ return self::$_errorCodes['num'];
+ } elseif ($numInSet < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
+ }
+ return self::$_errorCodes['value'];
+ } // function COMBIN()
+
+
+ /**
+ * SERIESSUM
+ *
+ * Returns the sum of a power series
+ *
+ * @param float $x Input value to the power series
+ * @param float $n Initial power to which you want to raise $x
+ * @param float $m Step by which to increase $n for each term in the series
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function SERIESSUM() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+
+ $x = array_shift($aArgs);
+ $n = array_shift($aArgs);
+ $m = array_shift($aArgs);
+
+ if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
+ // Calculate
+ $i = 0;
+ foreach($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += $arg * pow($x,$n + ($m * $i++));
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Return
+ return $returnValue;
+ }
+ return self::$_errorCodes['value'];
+ } // function SERIESSUM()
+
+
+ /**
+ * STANDARDIZE
+ *
+ * Returns a normalized value from a distribution characterized by mean and standard_dev.
+ *
+ * @param float $value Value to normalize
+ * @param float $mean Mean Value
+ * @param float $stdDev Standard Deviation
+ * @return float Standardized value
+ */
+ public static function STANDARDIZE($value,$mean,$stdDev) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if ($stdDev <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ return ($value - $mean) / $stdDev ;
+ }
+ return self::$_errorCodes['value'];
+ } // function STANDARDIZE()
+
+
+ //
+ // Private method to return an array of the factors of the input value
+ //
+ private static function _factors($value) {
+ $startVal = floor(sqrt($value));
+
+ $factorArray = array();
+ for ($i = $startVal; $i > 1; --$i) {
+ if (($value % $i) == 0) {
+ $factorArray = array_merge($factorArray,self::_factors($value / $i));
+ $factorArray = array_merge($factorArray,self::_factors($i));
+ if ($i <= sqrt($value)) {
+ break;
+ }
+ }
+ }
+ if (count($factorArray) > 0) {
+ rsort($factorArray);
+ return $factorArray;
+ } else {
+ return array((integer) $value);
+ }
+ } // function _factors()
+
+
+ /**
+ * LCM
+ *
+ * Returns the lowest common multiplier of a series of numbers
+ *
+ * @param $array Values to calculate the Lowest Common Multiplier
+ * @return int Lowest Common Multiplier
+ */
+ public static function LCM() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ $returnValue = 1;
+ $allPoweredFactors = array();
+ foreach($aArgs as $value) {
+ if (!is_numeric($value)) {
+ return self::$_errorCodes['value'];
+ }
+ if ($value == 0) {
+ return 0;
+ } elseif ($value < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $myFactors = self::_factors(floor($value));
+ $myCountedFactors = array_count_values($myFactors);
+ $myPoweredFactors = array();
+ foreach($myCountedFactors as $myCountedFactor => $myCountedPower) {
+ $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor,$myCountedPower);
+ }
+ foreach($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
+ if (array_key_exists($myPoweredValue,$allPoweredFactors)) {
+ if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ } else {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ }
+ }
+ foreach($allPoweredFactors as $allPoweredFactor) {
+ $returnValue *= (integer) $allPoweredFactor;
+ }
+ return $returnValue;
+ } // function LCM()
+
+
+ /**
+ * GCD
+ *
+ * Returns the greatest common divisor of a series of numbers
+ *
+ * @param $array Values to calculate the Greatest Common Divisor
+ * @return int Greatest Common Divisor
+ */
+ public static function GCD() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ $returnValue = 1;
+ $allPoweredFactors = array();
+ foreach($aArgs as $value) {
+ if ($value == 0) {
+ break;
+ }
+ $myFactors = self::_factors($value);
+ $myCountedFactors = array_count_values($myFactors);
+ $allValuesFactors[] = $myCountedFactors;
+ }
+ $allValuesCount = count($allValuesFactors);
+ $mergedArray = $allValuesFactors[0];
+ for ($i=1;$i < $allValuesCount; ++$i) {
+ $mergedArray = array_intersect_key($mergedArray,$allValuesFactors[$i]);
+ }
+ $mergedArrayValues = count($mergedArray);
+ if ($mergedArrayValues == 0) {
+ return $returnValue;
+ } elseif ($mergedArrayValues > 1) {
+ foreach($mergedArray as $mergedKey => $mergedValue) {
+ foreach($allValuesFactors as $highestPowerTest) {
+ foreach($highestPowerTest as $testKey => $testValue) {
+ if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
+ $mergedArray[$mergedKey] = $testValue;
+ $mergedValue = $testValue;
+ }
+ }
+ }
+ }
+
+ $returnValue = 1;
+ foreach($mergedArray as $key => $value) {
+ $returnValue *= pow($key,$value);
+ }
+ return $returnValue;
+ } else {
+ $keys = array_keys($mergedArray);
+ $key = $keys[0];
+ $value = $mergedArray[$key];
+ foreach($allValuesFactors as $testValue) {
+ foreach($testValue as $mergedKey => $mergedValue) {
+ if (($mergedKey == $key) && ($mergedValue < $value)) {
+ $value = $mergedValue;
+ }
+ }
+ }
+ return pow($key,$value);
+ }
+ } // function GCD()
+
+
+ /**
+ * BINOMDIST
+ *
+ * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
+ * a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
+ * when trials are independent, and when the probability of success is constant throughout the
+ * experiment. For example, BINOMDIST can calculate the probability that two of the next three
+ * babies born are male.
+ *
+ * @param float $value Number of successes in trials
+ * @param float $trials Number of trials
+ * @param float $probability Probability of success on each trial
+ * @param boolean $cumulative
+ * @return float
+ *
+ * @todo Cumulative distribution function
+ *
+ */
+ public static function BINOMDIST($value, $trials, $probability, $cumulative) {
+ $value = floor(self::flattenSingleValue($value));
+ $trials = floor(self::flattenSingleValue($trials));
+ $probability = self::flattenSingleValue($probability);
+
+ if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
+ if (($value < 0) || ($value > $trials)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ $summer = 0;
+ for ($i = 0; $i <= $value; ++$i) {
+ $summer += self::COMBIN($trials,$i) * pow($probability,$i) * pow(1 - $probability,$trials - $i);
+ }
+ return $summer;
+ } else {
+ return self::COMBIN($trials,$value) * pow($probability,$value) * pow(1 - $probability,$trials - $value) ;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function BINOMDIST()
+
+
+ /**
+ * NEGBINOMDIST
+ *
+ * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
+ * there will be number_f failures before the number_s-th success, when the constant
+ * probability of a success is probability_s. This function is similar to the binomial
+ * distribution, except that the number of successes is fixed, and the number of trials is
+ * variable. Like the binomial, trials are assumed to be independent.
+ *
+ * @param float $failures Number of Failures
+ * @param float $successes Threshold number of Successes
+ * @param float $probability Probability of success on each trial
+ * @return float
+ *
+ */
+ public static function NEGBINOMDIST($failures, $successes, $probability) {
+ $failures = floor(self::flattenSingleValue($failures));
+ $successes = floor(self::flattenSingleValue($successes));
+ $probability = self::flattenSingleValue($probability);
+
+ if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
+ if (($failures < 0) || ($successes < 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ if (($failures + $successes - 1) <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ }
+ return (self::COMBIN($failures + $successes - 1,$successes - 1)) * (pow($probability,$successes)) * (pow(1 - $probability,$failures)) ;
+ }
+ return self::$_errorCodes['value'];
+ } // function NEGBINOMDIST()
+
+
+ /**
+ * CRITBINOM
+ *
+ * Returns the smallest value for which the cumulative binomial distribution is greater
+ * than or equal to a criterion value
+ *
+ * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
+ *
+ * @param float $trials number of Bernoulli trials
+ * @param float $probability probability of a success on each trial
+ * @param float $alpha criterion value
+ * @return int
+ *
+ * @todo Warning. This implementation differs from the algorithm detailed on the MS
+ * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
+ * This eliminates a potential endless loop error, but may have an adverse affect on the
+ * accuracy of the function (although all my tests have so far returned correct results).
+ *
+ */
+ public static function CRITBINOM($trials, $probability, $alpha) {
+ $trials = floor(self::flattenSingleValue($trials));
+ $probability = self::flattenSingleValue($probability);
+ $alpha = self::flattenSingleValue($alpha);
+
+ if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
+ if ($trials < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($alpha < 0) || ($alpha > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($alpha <= 0.5) {
+ $t = sqrt(log(1 / ($alpha * $alpha)));
+ $trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t));
+ } else {
+ $t = sqrt(log(1 / pow(1 - $alpha,2)));
+ $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
+ }
+ $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
+ if ($Guess < 0) {
+ $Guess = 0;
+ } elseif ($Guess > $trials) {
+ $Guess = $trials;
+ }
+
+ $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
+ $EssentiallyZero = 10e-12;
+
+ $m = floor($trials * $probability);
+ ++$TotalUnscaledProbability;
+ if ($m == $Guess) { ++$UnscaledPGuess; }
+ if ($m <= $Guess) { ++$UnscaledCumPGuess; }
+
+ $PreviousValue = 1;
+ $Done = False;
+ $k = $m + 1;
+ while ((!$Done) && ($k <= $trials)) {
+ $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
+ $TotalUnscaledProbability += $CurrentValue;
+ if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
+ if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
+ if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
+ $PreviousValue = $CurrentValue;
+ ++$k;
+ }
+
+ $PreviousValue = 1;
+ $Done = False;
+ $k = $m - 1;
+ while ((!$Done) && ($k >= 0)) {
+ $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
+ $TotalUnscaledProbability += $CurrentValue;
+ if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
+ if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
+ if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
+ $PreviousValue = $CurrentValue;
+ --$k;
+ }
+
+ $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
+ $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
+
+// $CumPGuessMinus1 = $CumPGuess - $PGuess;
+ $CumPGuessMinus1 = $CumPGuess - 1;
+
+ while (True) {
+ if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
+ return $Guess;
+ } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
+ $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
+ $CumPGuessMinus1 = $CumPGuess;
+ $CumPGuess = $CumPGuess + $PGuessPlus1;
+ $PGuess = $PGuessPlus1;
+ ++$Guess;
+ } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
+ $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
+ $CumPGuess = $CumPGuessMinus1;
+ $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
+ $PGuess = $PGuessMinus1;
+ --$Guess;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function CRITBINOM()
+
+
+ /**
+ * CHIDIST
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param float $value Value for the function
+ * @param float $degrees degrees of freedom
+ * @return float
+ */
+ public static function CHIDIST($value, $degrees) {
+ $value = self::flattenSingleValue($value);
+ $degrees = floor(self::flattenSingleValue($degrees));
+
+ if ((is_numeric($value)) && (is_numeric($degrees))) {
+ if ($degrees < 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($value < 0) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+ return self::$_errorCodes['num'];
+ }
+ return 1 - (self::_incompleteGamma($degrees/2,$value/2) / self::_gamma($degrees/2));
+ }
+ return self::$_errorCodes['value'];
+ } // function CHIDIST()
+
+
+ /**
+ * CHIINV
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param float $probability Probability for the function
+ * @param float $degrees degrees of freedom
+ * @return float
+ */
+ public static function CHIINV($probability, $degrees) {
+ $probability = self::flattenSingleValue($probability);
+ $degrees = floor(self::flattenSingleValue($degrees));
+
+ if ((is_numeric($probability)) && (is_numeric($degrees))) {
+
+ $xLo = 100;
+ $xHi = 0;
+
+ $x = $xNew = 1;
+ $dx = 1;
+ $i = 0;
+
+ while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::CHIDIST($x, $degrees);
+ $error = $result - $probability;
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+ // Avoid division by zero
+ if ($result != 0.0) {
+ $dx = $error / $result;
+ $xNew = $x - $dx;
+ }
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return round($x,12);
+ }
+ return self::$_errorCodes['value'];
+ } // function CHIINV()
+
+
+ /**
+ * EXPONDIST
+ *
+ * Returns the exponential distribution. Use EXPONDIST to model the time between events,
+ * such as how long an automated bank teller takes to deliver cash. For example, you can
+ * use EXPONDIST to determine the probability that the process takes at most 1 minute.
+ *
+ * @param float $value Value of the function
+ * @param float $lambda The parameter value
+ * @param boolean $cumulative
+ * @return float
+ */
+ public static function EXPONDIST($value, $lambda, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $lambda = self::flattenSingleValue($lambda);
+ $cumulative = self::flattenSingleValue($cumulative);
+
+ if ((is_numeric($value)) && (is_numeric($lambda))) {
+ if (($value < 0) || ($lambda < 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return 1 - exp(0-$value*$lambda);
+ } else {
+ return $lambda * exp(0-$value*$lambda);
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function EXPONDIST()
+
+
+ /**
+ * FISHER
+ *
+ * Returns the Fisher transformation at x. This transformation produces a function that
+ * is normally distributed rather than skewed. Use this function to perform hypothesis
+ * testing on the correlation coefficient.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function FISHER($value) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_numeric($value)) {
+ if (($value <= -1) || ($value >= 1)) {
+ return self::$_errorCodes['num'];
+ }
+ return 0.5 * log((1+$value)/(1-$value));
+ }
+ return self::$_errorCodes['value'];
+ } // function FISHER()
+
+
+ /**
+ * FISHERINV
+ *
+ * Returns the inverse of the Fisher transformation. Use this transformation when
+ * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
+ * FISHERINV(y) = x.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function FISHERINV($value) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_numeric($value)) {
+ return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
+ }
+ return self::$_errorCodes['value'];
+ } // function FISHERINV()
+
+
+ // Function cache for _logBeta function
+ private static $_logBetaCache_p = 0.0;
+ private static $_logBetaCache_q = 0.0;
+ private static $_logBetaCache_result = 0.0;
+
+ /**
+ * The natural logarithm of the beta function.
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ * @author Jaco van Kooten
+ */
+ private static function _logBeta($p, $q) {
+ if ($p != self::$_logBetaCache_p || $q != self::$_logBetaCache_q) {
+ self::$_logBetaCache_p = $p;
+ self::$_logBetaCache_q = $q;
+ if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
+ self::$_logBetaCache_result = 0.0;
+ } else {
+ self::$_logBetaCache_result = self::_logGamma($p) + self::_logGamma($q) - self::_logGamma($p + $q);
+ }
+ }
+ return self::$_logBetaCache_result;
+ } // function _logBeta()
+
+
+ /**
+ * Evaluates of continued fraction part of incomplete beta function.
+ * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
+ * @author Jaco van Kooten
+ */
+ private static function _betaFraction($x, $p, $q) {
+ $c = 1.0;
+ $sum_pq = $p + $q;
+ $p_plus = $p + 1.0;
+ $p_minus = $p - 1.0;
+ $h = 1.0 - $sum_pq * $x / $p_plus;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $frac = $h;
+ $m = 1;
+ $delta = 0.0;
+ while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION ) {
+ $m2 = 2 * $m;
+ // even index for d
+ $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < XMININ) {
+ $c = XMININ;
+ }
+ $frac *= $h * $c;
+ // odd index for d
+ $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < XMININ) {
+ $c = XMININ;
+ }
+ $delta = $h * $c;
+ $frac *= $delta;
+ ++$m;
+ }
+ return $frac;
+ } // function _betaFraction()
+
+
+ /**
+ * logGamma function
+ *
+ * @version 1.1
+ * @author Jaco van Kooten
+ *
+ * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
+ *
+ * The natural logarithm of the gamma function.
+ * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
+ * Applied Mathematics Division
+ * Argonne National Laboratory
+ * Argonne, IL 60439
+ *
+ * References:
+ *
+ * - W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
+ * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
+ * - K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
+ * - Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
+ *
+ *
+ *
+ * From the original documentation:
+ *
+ *
+ * This routine calculates the LOG(GAMMA) function for a positive real argument X.
+ * Computation is based on an algorithm outlined in references 1 and 2.
+ * The program uses rational functions that theoretically approximate LOG(GAMMA)
+ * to at least 18 significant decimal digits. The approximation for X > 12 is from
+ * reference 3, while approximations for X < 12.0 are similar to those in reference
+ * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
+ * the compiler, the intrinsic functions, and proper selection of the
+ * machine-dependent constants.
+ *
+ *
+ * Error returns:
+ * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
+ * The computation is believed to be free of underflow and overflow.
+ *
+ * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
+ */
+
+ // Function cache for logGamma
+ private static $_logGammaCache_result = 0.0;
+ private static $_logGammaCache_x = 0.0;
+
+ private static function _logGamma($x) {
+ // Log Gamma related constants
+ static $lg_d1 = -0.5772156649015328605195174;
+ static $lg_d2 = 0.4227843350984671393993777;
+ static $lg_d4 = 1.791759469228055000094023;
+
+ static $lg_p1 = array( 4.945235359296727046734888,
+ 201.8112620856775083915565,
+ 2290.838373831346393026739,
+ 11319.67205903380828685045,
+ 28557.24635671635335736389,
+ 38484.96228443793359990269,
+ 26377.48787624195437963534,
+ 7225.813979700288197698961 );
+ static $lg_p2 = array( 4.974607845568932035012064,
+ 542.4138599891070494101986,
+ 15506.93864978364947665077,
+ 184793.2904445632425417223,
+ 1088204.76946882876749847,
+ 3338152.967987029735917223,
+ 5106661.678927352456275255,
+ 3074109.054850539556250927 );
+ static $lg_p4 = array( 14745.02166059939948905062,
+ 2426813.369486704502836312,
+ 121475557.4045093227939592,
+ 2663432449.630976949898078,
+ 29403789566.34553899906876,
+ 170266573776.5398868392998,
+ 492612579337.743088758812,
+ 560625185622.3951465078242 );
+
+ static $lg_q1 = array( 67.48212550303777196073036,
+ 1113.332393857199323513008,
+ 7738.757056935398733233834,
+ 27639.87074403340708898585,
+ 54993.10206226157329794414,
+ 61611.22180066002127833352,
+ 36351.27591501940507276287,
+ 8785.536302431013170870835 );
+ static $lg_q2 = array( 183.0328399370592604055942,
+ 7765.049321445005871323047,
+ 133190.3827966074194402448,
+ 1136705.821321969608938755,
+ 5267964.117437946917577538,
+ 13467014.54311101692290052,
+ 17827365.30353274213975932,
+ 9533095.591844353613395747 );
+ static $lg_q4 = array( 2690.530175870899333379843,
+ 639388.5654300092398984238,
+ 41355999.30241388052042842,
+ 1120872109.61614794137657,
+ 14886137286.78813811542398,
+ 101680358627.2438228077304,
+ 341747634550.7377132798597,
+ 446315818741.9713286462081 );
+
+ static $lg_c = array( -0.001910444077728,
+ 8.4171387781295e-4,
+ -5.952379913043012e-4,
+ 7.93650793500350248e-4,
+ -0.002777777777777681622553,
+ 0.08333333333333333331554247,
+ 0.0057083835261 );
+
+ // Rough estimate of the fourth root of logGamma_xBig
+ static $lg_frtbig = 2.25e76;
+ static $pnt68 = 0.6796875;
+
+
+ if ($x == self::$_logGammaCache_x) {
+ return self::$_logGammaCache_result;
+ }
+ $y = $x;
+ if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
+ if ($y <= EPS) {
+ $res = -log(y);
+ } elseif ($y <= 1.5) {
+ // ---------------------
+ // EPS .LT. X .LE. 1.5
+ // ---------------------
+ if ($y < $pnt68) {
+ $corr = -log($y);
+ $xm1 = $y;
+ } else {
+ $corr = 0.0;
+ $xm1 = $y - 1.0;
+ }
+ if ($y <= 0.5 || $y >= $pnt68) {
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm1 + $lg_p1[$i];
+ $xden = $xden * $xm1 + $lg_q1[$i];
+ }
+ $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
+ } else {
+ $xm2 = $y - 1.0;
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + $lg_p2[$i];
+ $xden = $xden * $xm2 + $lg_q2[$i];
+ }
+ $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
+ }
+ } elseif ($y <= 4.0) {
+ // ---------------------
+ // 1.5 .LT. X .LE. 4.0
+ // ---------------------
+ $xm2 = $y - 2.0;
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + $lg_p2[$i];
+ $xden = $xden * $xm2 + $lg_q2[$i];
+ }
+ $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
+ } elseif ($y <= 12.0) {
+ // ----------------------
+ // 4.0 .LT. X .LE. 12.0
+ // ----------------------
+ $xm4 = $y - 4.0;
+ $xden = -1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm4 + $lg_p4[$i];
+ $xden = $xden * $xm4 + $lg_q4[$i];
+ }
+ $res = $lg_d4 + $xm4 * ($xnum / $xden);
+ } else {
+ // ---------------------------------
+ // Evaluate for argument .GE. 12.0
+ // ---------------------------------
+ $res = 0.0;
+ if ($y <= $lg_frtbig) {
+ $res = $lg_c[6];
+ $ysq = $y * $y;
+ for ($i = 0; $i < 6; ++$i)
+ $res = $res / $ysq + $lg_c[$i];
+ }
+ $res /= $y;
+ $corr = log($y);
+ $res = $res + log(SQRT2PI) - 0.5 * $corr;
+ $res += $y * ($corr - 1.0);
+ }
+ } else {
+ // --------------------------
+ // Return for bad arguments
+ // --------------------------
+ $res = MAX_VALUE;
+ }
+ // ------------------------------
+ // Final adjustments and return
+ // ------------------------------
+ self::$_logGammaCache_x = $x;
+ self::$_logGammaCache_result = $res;
+ return $res;
+ } // function _logGamma()
+
+
+ /**
+ * Beta function.
+ *
+ * @author Jaco van Kooten
+ *
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ */
+ private static function _beta($p, $q) {
+ if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
+ return 0.0;
+ } else {
+ return exp(self::_logBeta($p, $q));
+ }
+ } // function _beta()
+
+
+ /**
+ * Incomplete beta function
+ *
+ * @author Jaco van Kooten
+ * @author Paul Meagher
+ *
+ * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
+ * @param x require 0<=x<=1
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
+ */
+ private static function _incompleteBeta($x, $p, $q) {
+ if ($x <= 0.0) {
+ return 0.0;
+ } elseif ($x >= 1.0) {
+ return 1.0;
+ } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
+ return 0.0;
+ }
+ $beta_gam = exp((0 - self::_logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
+ if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
+ return $beta_gam * self::_betaFraction($x, $p, $q) / $p;
+ } else {
+ return 1.0 - ($beta_gam * self::_betaFraction(1 - $x, $q, $p) / $q);
+ }
+ } // function _incompleteBeta()
+
+
+ /**
+ * BETADIST
+ *
+ * Returns the beta distribution.
+ *
+ * @param float $value Value at which you want to evaluate the distribution
+ * @param float $alpha Parameter to the distribution
+ * @param float $beta Parameter to the distribution
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function BETADIST($value,$alpha,$beta,$rMin=0,$rMax=1) {
+ $value = self::flattenSingleValue($value);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+ $rMin = self::flattenSingleValue($rMin);
+ $rMax = self::flattenSingleValue($rMax);
+
+ if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
+ if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ $value -= $rMin;
+ $value /= ($rMax - $rMin);
+ return self::_incompleteBeta($value,$alpha,$beta);
+ }
+ return self::$_errorCodes['value'];
+ } // function BETADIST()
+
+
+ /**
+ * BETAINV
+ *
+ * Returns the inverse of the beta distribution.
+ *
+ * @param float $probability Probability at which you want to evaluate the distribution
+ * @param float $alpha Parameter to the distribution
+ * @param float $beta Parameter to the distribution
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function BETAINV($probability,$alpha,$beta,$rMin=0,$rMax=1) {
+ $probability = self::flattenSingleValue($probability);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+ $rMin = self::flattenSingleValue($rMin);
+ $rMax = self::flattenSingleValue($rMax);
+
+ if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
+ if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ $a = 0;
+ $b = 2;
+
+ $i = 0;
+ while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ $guess = ($a + $b) / 2;
+ $result = self::BETADIST($guess, $alpha, $beta);
+ if (($result == $probability) || ($result == 0)) {
+ $b = $a;
+ } elseif ($result > $probability) {
+ $b = $guess;
+ } else {
+ $a = $guess;
+ }
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return round($rMin + $guess * ($rMax - $rMin),12);
+ }
+ return self::$_errorCodes['value'];
+ } // function BETAINV()
+
+
+ //
+ // Private implementation of the incomplete Gamma function
+ //
+ private static function _incompleteGamma($a,$x) {
+ static $max = 32;
+ $summer = 0;
+ for ($n=0; $n<=$max; ++$n) {
+ $divisor = $a;
+ for ($i=1; $i<=$n; ++$i) {
+ $divisor *= ($a + $i);
+ }
+ $summer += (pow($x,$n) / $divisor);
+ }
+ return pow($x,$a) * exp(0-$x) * $summer;
+ } // function _incompleteGamma()
+
+
+ //
+ // Private implementation of the Gamma function
+ //
+ private static function _gamma($data) {
+ if ($data == 0.0) return 0;
+
+ static $p0 = 1.000000000190015;
+ static $p = array ( 1 => 76.18009172947146,
+ 2 => -86.50532032941677,
+ 3 => 24.01409824083091,
+ 4 => -1.231739572450155,
+ 5 => 1.208650973866179e-3,
+ 6 => -5.395239384953e-6
+ );
+
+ $y = $x = $data;
+ $tmp = $x + 5.5;
+ $tmp -= ($x + 0.5) * log($tmp);
+
+ $summer = $p0;
+ for ($j=1;$j<=6;++$j) {
+ $summer += ($p[$j] / ++$y);
+ }
+ return exp(0 - $tmp + log(SQRT2PI * $summer / $x));
+ } // function _gamma()
+
+
+ /**
+ * GAMMADIST
+ *
+ * Returns the gamma distribution.
+ *
+ * @param float $value Value at which you want to evaluate the distribution
+ * @param float $a Parameter to the distribution
+ * @param float $b Parameter to the distribution
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function GAMMADIST($value,$a,$b,$cumulative) {
+ $value = self::flattenSingleValue($value);
+ $a = self::flattenSingleValue($a);
+ $b = self::flattenSingleValue($b);
+
+ if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
+ if (($value < 0) || ($a <= 0) || ($b <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return self::_incompleteGamma($a,$value / $b) / self::_gamma($a);
+ } else {
+ return (1 / (pow($b,$a) * self::_gamma($a))) * pow($value,$a-1) * exp(0-($value / $b));
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function GAMMADIST()
+
+
+ /**
+ * GAMMAINV
+ *
+ * Returns the inverse of the beta distribution.
+ *
+ * @param float $probability Probability at which you want to evaluate the distribution
+ * @param float $alpha Parameter to the distribution
+ * @param float $beta Parameter to the distribution
+ * @return float
+ *
+ */
+ public static function GAMMAINV($probability,$alpha,$beta) {
+ $probability = self::flattenSingleValue($probability);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+
+ if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
+ if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+
+ $xLo = 0;
+ $xHi = $alpha * $beta * 5;
+
+ $x = $xNew = 1;
+ $error = $pdf = 0;
+ $dx = 1024;
+ $i = 0;
+
+ while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $error = self::GAMMADIST($x, $alpha, $beta, True) - $probability;
+ if ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+ $pdf = self::GAMMADIST($x, $alpha, $beta, False);
+ // Avoid division by zero
+ if ($pdf != 0.0) {
+ $dx = $error / $pdf;
+ $xNew = $x - $dx;
+ }
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return $x;
+ }
+ return self::$_errorCodes['value'];
+ } // function GAMMAINV()
+
+
+ /**
+ * GAMMALN
+ *
+ * Returns the natural logarithm of the gamma function.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function GAMMALN($value) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_numeric($value)) {
+ if ($value <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ return log(self::_gamma($value));
+ }
+ return self::$_errorCodes['value'];
+ } // function GAMMALN()
+
+
+ /**
+ * NORMDIST
+ *
+ * Returns the normal distribution for the specified mean and standard deviation. This
+ * function has a very wide range of applications in statistics, including hypothesis
+ * testing.
+ *
+ * @param float $value
+ * @param float $mean Mean Value
+ * @param float $stdDev Standard Deviation
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function NORMDIST($value, $mean, $stdDev, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if ($stdDev < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return 0.5 * (1 + self::_erfVal(($value - $mean) / ($stdDev * sqrt(2))));
+ } else {
+ return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean,2) / (2 * ($stdDev * $stdDev))));
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function NORMDIST()
+
+
+ /**
+ * NORMSDIST
+ *
+ * Returns the standard normal cumulative distribution function. The distribution has
+ * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
+ * table of standard normal curve areas.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function NORMSDIST($value) {
+ $value = self::flattenSingleValue($value);
+
+ return self::NORMDIST($value, 0, 1, True);
+ } // function NORMSDIST()
+
+
+ /**
+ * LOGNORMDIST
+ *
+ * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
+ * with parameters mean and standard_dev.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function LOGNORMDIST($value, $mean, $stdDev) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::NORMSDIST((log($value) - $mean) / $stdDev);
+ }
+ return self::$_errorCodes['value'];
+ } // function LOGNORMDIST()
+
+
+ /***************************************************************************
+ * inverse_ncdf.php
+ * -------------------
+ * begin : Friday, January 16, 2004
+ * copyright : (C) 2004 Michael Nickerson
+ * email : nickersonm@yahoo.com
+ *
+ ***************************************************************************/
+ private static function _inverse_ncdf($p) {
+ // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
+ // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
+ // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
+ // I have not checked the accuracy of this implementation. Be aware that PHP
+ // will truncate the coeficcients to 14 digits.
+
+ // You have permission to use and distribute this function freely for
+ // whatever purpose you want, but please show common courtesy and give credit
+ // where credit is due.
+
+ // Input paramater is $p - probability - where 0 < p < 1.
+
+ // Coefficients in rational approximations
+ static $a = array( 1 => -3.969683028665376e+01,
+ 2 => 2.209460984245205e+02,
+ 3 => -2.759285104469687e+02,
+ 4 => 1.383577518672690e+02,
+ 5 => -3.066479806614716e+01,
+ 6 => 2.506628277459239e+00
+ );
+
+ static $b = array( 1 => -5.447609879822406e+01,
+ 2 => 1.615858368580409e+02,
+ 3 => -1.556989798598866e+02,
+ 4 => 6.680131188771972e+01,
+ 5 => -1.328068155288572e+01
+ );
+
+ static $c = array( 1 => -7.784894002430293e-03,
+ 2 => -3.223964580411365e-01,
+ 3 => -2.400758277161838e+00,
+ 4 => -2.549732539343734e+00,
+ 5 => 4.374664141464968e+00,
+ 6 => 2.938163982698783e+00
+ );
+
+ static $d = array( 1 => 7.784695709041462e-03,
+ 2 => 3.224671290700398e-01,
+ 3 => 2.445134137142996e+00,
+ 4 => 3.754408661907416e+00
+ );
+
+ // Define lower and upper region break-points.
+ $p_low = 0.02425; //Use lower region approx. below this
+ $p_high = 1 - $p_low; //Use upper region approx. above this
+
+ if (0 < $p && $p < $p_low) {
+ // Rational approximation for lower region.
+ $q = sqrt(-2 * log($p));
+ return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
+ (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ } elseif ($p_low <= $p && $p <= $p_high) {
+ // Rational approximation for central region.
+ $q = $p - 0.5;
+ $r = $q * $q;
+ return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
+ ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
+ } elseif ($p_high < $p && $p < 1) {
+ // Rational approximation for upper region.
+ $q = sqrt(-2 * log(1 - $p));
+ return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
+ (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ }
+ // If 0 < p < 1, return a null value
+ return self::$_errorCodes['null'];
+ } // function _inverse_ncdf()
+
+
+ private static function _inverse_ncdf2($prob) {
+ // Approximation of inverse standard normal CDF developed by
+ // B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
+
+ $a1 = 2.50662823884;
+ $a2 = -18.61500062529;
+ $a3 = 41.39119773534;
+ $a4 = -25.44106049637;
+
+ $b1 = -8.4735109309;
+ $b2 = 23.08336743743;
+ $b3 = -21.06224101826;
+ $b4 = 3.13082909833;
+
+ $c1 = 0.337475482272615;
+ $c2 = 0.976169019091719;
+ $c3 = 0.160797971491821;
+ $c4 = 2.76438810333863E-02;
+ $c5 = 3.8405729373609E-03;
+ $c6 = 3.951896511919E-04;
+ $c7 = 3.21767881768E-05;
+ $c8 = 2.888167364E-07;
+ $c9 = 3.960315187E-07;
+
+ $y = $prob - 0.5;
+ if (abs($y) < 0.42) {
+ $z = ($y * $y);
+ $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
+ } else {
+ if ($y > 0) {
+ $z = log(-log(1 - $prob));
+ } else {
+ $z = log(-log($prob));
+ }
+ $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
+ if ($y < 0) {
+ $z = -$z;
+ }
+ }
+ return $z;
+ } // function _inverse_ncdf2()
+
+
+ private static function _inverse_ncdf3($p) {
+ // ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
+ // Produces the normal deviate Z corresponding to a given lower
+ // tail area of P; Z is accurate to about 1 part in 10**16.
+ //
+ // This is a PHP version of the original FORTRAN code that can
+ // be found at http://lib.stat.cmu.edu/apstat/
+ $split1 = 0.425;
+ $split2 = 5;
+ $const1 = 0.180625;
+ $const2 = 1.6;
+
+ // coefficients for p close to 0.5
+ $a0 = 3.3871328727963666080;
+ $a1 = 1.3314166789178437745E+2;
+ $a2 = 1.9715909503065514427E+3;
+ $a3 = 1.3731693765509461125E+4;
+ $a4 = 4.5921953931549871457E+4;
+ $a5 = 6.7265770927008700853E+4;
+ $a6 = 3.3430575583588128105E+4;
+ $a7 = 2.5090809287301226727E+3;
+
+ $b1 = 4.2313330701600911252E+1;
+ $b2 = 6.8718700749205790830E+2;
+ $b3 = 5.3941960214247511077E+3;
+ $b4 = 2.1213794301586595867E+4;
+ $b5 = 3.9307895800092710610E+4;
+ $b6 = 2.8729085735721942674E+4;
+ $b7 = 5.2264952788528545610E+3;
+
+ // coefficients for p not close to 0, 0.5 or 1.
+ $c0 = 1.42343711074968357734;
+ $c1 = 4.63033784615654529590;
+ $c2 = 5.76949722146069140550;
+ $c3 = 3.64784832476320460504;
+ $c4 = 1.27045825245236838258;
+ $c5 = 2.41780725177450611770E-1;
+ $c6 = 2.27238449892691845833E-2;
+ $c7 = 7.74545014278341407640E-4;
+
+ $d1 = 2.05319162663775882187;
+ $d2 = 1.67638483018380384940;
+ $d3 = 6.89767334985100004550E-1;
+ $d4 = 1.48103976427480074590E-1;
+ $d5 = 1.51986665636164571966E-2;
+ $d6 = 5.47593808499534494600E-4;
+ $d7 = 1.05075007164441684324E-9;
+
+ // coefficients for p near 0 or 1.
+ $e0 = 6.65790464350110377720;
+ $e1 = 5.46378491116411436990;
+ $e2 = 1.78482653991729133580;
+ $e3 = 2.96560571828504891230E-1;
+ $e4 = 2.65321895265761230930E-2;
+ $e5 = 1.24266094738807843860E-3;
+ $e6 = 2.71155556874348757815E-5;
+ $e7 = 2.01033439929228813265E-7;
+
+ $f1 = 5.99832206555887937690E-1;
+ $f2 = 1.36929880922735805310E-1;
+ $f3 = 1.48753612908506148525E-2;
+ $f4 = 7.86869131145613259100E-4;
+ $f5 = 1.84631831751005468180E-5;
+ $f6 = 1.42151175831644588870E-7;
+ $f7 = 2.04426310338993978564E-15;
+
+ $q = $p - 0.5;
+
+ // computation for p close to 0.5
+ if (abs($q) <= split1) {
+ $R = $const1 - $q * $q;
+ $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
+ ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
+ } else {
+ if ($q < 0) {
+ $R = $p;
+ } else {
+ $R = 1 - $p;
+ }
+ $R = pow(-log($R),2);
+
+ // computation for p not close to 0, 0.5 or 1.
+ If ($R <= $split2) {
+ $R = $R - $const2;
+ $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
+ ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
+ } else {
+ // computation for p near 0 or 1.
+ $R = $R - $split2;
+ $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
+ ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
+ }
+ if ($q < 0) {
+ $z = -$z;
+ }
+ }
+ return $z;
+ } // function _inverse_ncdf3()
+
+
+ /**
+ * NORMINV
+ *
+ * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
+ *
+ * @param float $value
+ * @param float $mean Mean Value
+ * @param float $stdDev Standard Deviation
+ * @return float
+ *
+ */
+ public static function NORMINV($probability,$mean,$stdDev) {
+ $probability = self::flattenSingleValue($probability);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($stdDev < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return (self::_inverse_ncdf($probability) * $stdDev) + $mean;
+ }
+ return self::$_errorCodes['value'];
+ } // function NORMINV()
+
+
+ /**
+ * NORMSINV
+ *
+ * Returns the inverse of the standard normal cumulative distribution
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function NORMSINV($value) {
+ return self::NORMINV($value, 0, 1);
+ } // function NORMSINV()
+
+
+ /**
+ * LOGINV
+ *
+ * Returns the inverse of the normal cumulative distribution
+ *
+ * @param float $value
+ * @return float
+ *
+ * @todo Try implementing P J Acklam's refinement algorithm for greater
+ * accuracy if I can get my head round the mathematics
+ * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
+ */
+ public static function LOGINV($probability, $mean, $stdDev) {
+ $probability = self::flattenSingleValue($probability);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ return exp($mean + $stdDev * self::NORMSINV($probability));
+ }
+ return self::$_errorCodes['value'];
+ } // function LOGINV()
+
+
+ /**
+ * HYPGEOMDIST
+ *
+ * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
+ * sample successes, given the sample size, population successes, and population size.
+ *
+ * @param float $sampleSuccesses Number of successes in the sample
+ * @param float $sampleNumber Size of the sample
+ * @param float $populationSuccesses Number of successes in the population
+ * @param float $populationNumber Population size
+ * @return float
+ *
+ */
+ public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {
+ $sampleSuccesses = floor(self::flattenSingleValue($sampleSuccesses));
+ $sampleNumber = floor(self::flattenSingleValue($sampleNumber));
+ $populationSuccesses = floor(self::flattenSingleValue($populationSuccesses));
+ $populationNumber = floor(self::flattenSingleValue($populationNumber));
+
+ if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
+ if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::COMBIN($populationSuccesses,$sampleSuccesses) *
+ self::COMBIN($populationNumber - $populationSuccesses,$sampleNumber - $sampleSuccesses) /
+ self::COMBIN($populationNumber,$sampleNumber);
+ }
+ return self::$_errorCodes['value'];
+ } // function HYPGEOMDIST()
+
+
+ /**
+ * TDIST
+ *
+ * Returns the probability of Student's T distribution.
+ *
+ * @param float $value Value for the function
+ * @param float $degrees degrees of freedom
+ * @param float $tails number of tails (1 or 2)
+ * @return float
+ */
+ public static function TDIST($value, $degrees, $tails) {
+ $value = self::flattenSingleValue($value);
+ $degrees = floor(self::flattenSingleValue($degrees));
+ $tails = floor(self::flattenSingleValue($tails));
+
+ if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
+ if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
+ return self::$_errorCodes['num'];
+ }
+ // tdist, which finds the probability that corresponds to a given value
+ // of t with k degrees of freedom. This algorithm is translated from a
+ // pascal function on p81 of "Statistical Computing in Pascal" by D
+ // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
+ // London). The above Pascal algorithm is itself a translation of the
+ // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
+ // Laboratory as reported in (among other places) "Applied Statistics
+ // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
+ // Horwood Ltd.; W. Sussex, England).
+ $tterm = $degrees;
+ $ttheta = atan2($value,sqrt($tterm));
+ $tc = cos($ttheta);
+ $ts = sin($ttheta);
+ $tsum = 0;
+
+ if (($degrees % 2) == 1) {
+ $ti = 3;
+ $tterm = $tc;
+ } else {
+ $ti = 2;
+ $tterm = 1;
+ }
+
+ $tsum = $tterm;
+ while ($ti < $degrees) {
+ $tterm *= $tc * $tc * ($ti - 1) / $ti;
+ $tsum += $tterm;
+ $ti += 2;
+ }
+ $tsum *= $ts;
+ if (($degrees % 2) == 1) { $tsum = M_2DIVPI * ($tsum + $ttheta); }
+ $tValue = 0.5 * (1 + $tsum);
+ if ($tails == 1) {
+ return 1 - abs($tValue);
+ } else {
+ return 1 - abs((1 - $tValue) - $tValue);
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function TDIST()
+
+
+ /**
+ * TINV
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param float $probability Probability for the function
+ * @param float $degrees degrees of freedom
+ * @return float
+ */
+ public static function TINV($probability, $degrees) {
+ $probability = self::flattenSingleValue($probability);
+ $degrees = floor(self::flattenSingleValue($degrees));
+
+ if ((is_numeric($probability)) && (is_numeric($degrees))) {
+ $xLo = 100;
+ $xHi = 0;
+
+ $x = $xNew = 1;
+ $dx = 1;
+ $i = 0;
+
+ while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::TDIST($x, $degrees, 2);
+ $error = $result - $probability;
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+ // Avoid division by zero
+ if ($result != 0.0) {
+ $dx = $error / $result;
+ $xNew = $x - $dx;
+ }
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return round($x,12);
+ }
+ return self::$_errorCodes['value'];
+ } // function TINV()
+
+
+ /**
+ * CONFIDENCE
+ *
+ * Returns the confidence interval for a population mean
+ *
+ * @param float $alpha
+ * @param float $stdDev Standard Deviation
+ * @param float $size
+ * @return float
+ *
+ */
+ public static function CONFIDENCE($alpha,$stdDev,$size) {
+ $alpha = self::flattenSingleValue($alpha);
+ $stdDev = self::flattenSingleValue($stdDev);
+ $size = floor(self::flattenSingleValue($size));
+
+ if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
+ if (($alpha <= 0) || ($alpha >= 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($stdDev <= 0) || ($size < 1)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
+ }
+ return self::$_errorCodes['value'];
+ } // function CONFIDENCE()
+
+
+ /**
+ * POISSON
+ *
+ * Returns the Poisson distribution. A common application of the Poisson distribution
+ * is predicting the number of events over a specific time, such as the number of
+ * cars arriving at a toll plaza in 1 minute.
+ *
+ * @param float $value
+ * @param float $mean Mean Value
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function POISSON($value, $mean, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+
+ if ((is_numeric($value)) && (is_numeric($mean))) {
+ if (($value <= 0) || ($mean <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ $summer = 0;
+ for ($i = 0; $i <= floor($value); ++$i) {
+ $summer += pow($mean,$i) / self::FACT($i);
+ }
+ return exp(0-$mean) * $summer;
+ } else {
+ return (exp(0-$mean) * pow($mean,$value)) / self::FACT($value);
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function POISSON()
+
+
+ /**
+ * WEIBULL
+ *
+ * Returns the Weibull distribution. Use this distribution in reliability
+ * analysis, such as calculating a device's mean time to failure.
+ *
+ * @param float $value
+ * @param float $alpha Alpha Parameter
+ * @param float $beta Beta Parameter
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function WEIBULL($value, $alpha, $beta, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+
+ if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
+ if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return 1 - exp(0 - pow($value / $beta,$alpha));
+ } else {
+ return ($alpha / pow($beta,$alpha)) * pow($value,$alpha - 1) * exp(0 - pow($value / $beta,$alpha));
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function WEIBULL()
+
+
+ /**
+ * ZTEST
+ *
+ * Returns the Weibull distribution. Use this distribution in reliability
+ * analysis, such as calculating a device's mean time to failure.
+ *
+ * @param float $value
+ * @param float $alpha Alpha Parameter
+ * @param float $beta Beta Parameter
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function ZTEST($dataSet, $m0, $sigma=null) {
+ $dataSet = self::flattenArrayIndexed($dataSet);
+ $m0 = self::flattenSingleValue($m0);
+ $sigma = self::flattenSingleValue($sigma);
+
+ if (is_null($sigma)) {
+ $sigma = self::STDEV($dataSet);
+ }
+ $n = count($dataSet);
+
+ return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0)/($sigma/SQRT($n)));
+ } // function ZTEST()
+
+
+ /**
+ * SKEW
+ *
+ * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
+ * of a distribution around its mean. Positive skewness indicates a distribution with an
+ * asymmetric tail extending toward more positive values. Negative skewness indicates a
+ * distribution with an asymmetric tail extending toward more negative values.
+ *
+ * @param array Data Series
+ * @return float
+ */
+ public static function SKEW() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $mean = self::AVERAGE($aArgs);
+ $stdDev = self::STDEV($aArgs);
+
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += pow((($arg - $mean) / $stdDev),3) ;
+ ++$count;
+ }
+ }
+ }
+
+ // Return
+ if ($count > 2) {
+ return $summer * ($count / (($count-1) * ($count-2)));
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function SKEW()
+
+
+ /**
+ * KURT
+ *
+ * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
+ * or flatness of a distribution compared with the normal distribution. Positive
+ * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
+ * relatively flat distribution.
+ *
+ * @param array Data Series
+ * @return float
+ */
+ public static function KURT() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $mean = self::AVERAGE($aArgs);
+ $stdDev = self::STDEV($aArgs);
+
+ if ($stdDev > 0) {
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += pow((($arg - $mean) / $stdDev),4) ;
+ ++$count;
+ }
+ }
+ }
+
+ // Return
+ if ($count > 3) {
+ return $summer * ($count * ($count+1) / (($count-1) * ($count-2) * ($count-3))) - (3 * pow($count-1,2) / (($count-2) * ($count-3)));
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function KURT()
+
+
+ /**
+ * RAND
+ *
+ * @param int $min Minimal value
+ * @param int $max Maximal value
+ * @return int Random number
+ */
+ public static function RAND($min = 0, $max = 0) {
+ $min = self::flattenSingleValue($min);
+ $max = self::flattenSingleValue($max);
+
+ if ($min == 0 && $max == 0) {
+ return (rand(0,10000000)) / 10000000;
+ } else {
+ return rand($min, $max);
+ }
+ } // function RAND()
+
+
+ /**
+ * MOD
+ *
+ * @param int $a Dividend
+ * @param int $b Divisor
+ * @return int Remainder
+ */
+ public static function MOD($a = 1, $b = 1) {
+ $a = self::flattenSingleValue($a);
+ $b = self::flattenSingleValue($b);
+
+ if ($b == 0.0) {
+ return self::$_errorCodes['divisionbyzero'];
+ } elseif (($a < 0.0) && ($b > 0.0)) {
+ return $b - fmod(abs($a),$b);
+ } elseif (($a > 0.0) && ($b < 0.0)) {
+ return $b + fmod($a,abs($b));
+ }
+
+ return fmod($a,$b);
+ } // function MOD()
+
+
+ /**
+ * CHARACTER
+ *
+ * @param string $character Value
+ * @return int
+ */
+ public static function CHARACTER($character) {
+ $character = self::flattenSingleValue($character);
+
+ if ((!is_numeric($character)) || ($character < 0)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding(''.intval($character).';', 'UTF-8', 'HTML-ENTITIES');
+ } else {
+ return chr(intval($character));
+ }
+ }
+
+
+ private static function _uniord($c) {
+ if (ord($c{0}) >=0 && ord($c{0}) <= 127)
+ return ord($c{0});
+ if (ord($c{0}) >= 192 && ord($c{0}) <= 223)
+ return (ord($c{0})-192)*64 + (ord($c{1})-128);
+ if (ord($c{0}) >= 224 && ord($c{0}) <= 239)
+ return (ord($c{0})-224)*4096 + (ord($c{1})-128)*64 + (ord($c{2})-128);
+ if (ord($c{0}) >= 240 && ord($c{0}) <= 247)
+ return (ord($c{0})-240)*262144 + (ord($c{1})-128)*4096 + (ord($c{2})-128)*64 + (ord($c{3})-128);
+ if (ord($c{0}) >= 248 && ord($c{0}) <= 251)
+ return (ord($c{0})-248)*16777216 + (ord($c{1})-128)*262144 + (ord($c{2})-128)*4096 + (ord($c{3})-128)*64 + (ord($c{4})-128);
+ if (ord($c{0}) >= 252 && ord($c{0}) <= 253)
+ return (ord($c{0})-252)*1073741824 + (ord($c{1})-128)*16777216 + (ord($c{2})-128)*262144 + (ord($c{3})-128)*4096 + (ord($c{4})-128)*64 + (ord($c{5})-128);
+ if (ord($c{0}) >= 254 && ord($c{0}) <= 255) //error
+ return self::$_errorCodes['value'];
+ return 0;
+ } // function _uniord()
+
+ /**
+ * ASCIICODE
+ *
+ * @param string $character Value
+ * @return int
+ */
+ public static function ASCIICODE($characters) {
+ $characters = self::flattenSingleValue($characters);
+ if (is_bool($characters)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $characters = (int) $characters;
+ } else {
+ if ($characters) {
+ $characters = 'True';
+ } else {
+ $characters = 'False';
+ }
+ }
+ }
+
+ $character = $characters;
+ if ((function_exists('mb_strlen')) && (function_exists('mb_substr'))) {
+ if (mb_strlen($characters, 'UTF-8') > 1) { $character = mb_substr($characters, 0, 1, 'UTF-8'); }
+ return self::_uniord($character);
+ } else {
+ if (strlen($characters) > 0) { $character = substr($characters, 0, 1); }
+ return ord($character);
+ }
+ } // function ASCIICODE()
+
+
+ /**
+ * CONCATENATE
+ *
+ * @return string
+ */
+ public static function CONCATENATE() {
+ // Return value
+ $returnValue = '';
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ if (is_bool($arg)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $arg = (int) $arg;
+ } else {
+ if ($arg) {
+ $arg = 'TRUE';
+ } else {
+ $arg = 'FALSE';
+ }
+ }
+ }
+ $returnValue .= $arg;
+ }
+
+ // Return
+ return $returnValue;
+ } // function CONCATENATE()
+
+
+ /**
+ * STRINGLENGTH
+ *
+ * @param string $value Value
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function STRINGLENGTH($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($value, 'UTF-8');
+ } else {
+ return strlen($value);
+ }
+ } // function STRINGLENGTH()
+
+
+ /**
+ * SEARCHSENSITIVE
+ *
+ * @param string $needle The string to look for
+ * @param string $haystack The string in which to look
+ * @param int $offset Offset within $haystack
+ * @return string
+ */
+ public static function SEARCHSENSITIVE($needle,$haystack,$offset=1) {
+ $needle = self::flattenSingleValue($needle);
+ $haystack = self::flattenSingleValue($haystack);
+ $offset = self::flattenSingleValue($offset);
+
+ if (!is_bool($needle)) {
+ if (is_bool($haystack)) {
+ $haystack = ($haystack) ? 'TRUE' : 'FALSE';
+ }
+
+ if (($offset > 0) && (strlen($haystack) > $offset)) {
+ if (function_exists('mb_strpos')) {
+ $pos = mb_strpos($haystack, $needle, --$offset,'UTF-8');
+ } else {
+ $pos = strpos($haystack, $needle, --$offset);
+ }
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function SEARCHSENSITIVE()
+
+
+ /**
+ * SEARCHINSENSITIVE
+ *
+ * @param string $needle The string to look for
+ * @param string $haystack The string in which to look
+ * @param int $offset Offset within $haystack
+ * @return string
+ */
+ public static function SEARCHINSENSITIVE($needle,$haystack,$offset=1) {
+ $needle = self::flattenSingleValue($needle);
+ $haystack = self::flattenSingleValue($haystack);
+ $offset = self::flattenSingleValue($offset);
+
+ if (!is_bool($needle)) {
+ if (is_bool($haystack)) {
+ $haystack = ($haystack) ? 'TRUE' : 'FALSE';
+ }
+
+ if (($offset > 0) && (strlen($haystack) > $offset)) {
+ if (function_exists('mb_stripos')) {
+ $pos = mb_stripos($haystack, $needle, --$offset,'UTF-8');
+ } else {
+ $pos = stripos($haystack, $needle, --$offset);
+ }
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function SEARCHINSENSITIVE()
+
+
+ /**
+ * LEFT
+ *
+ * @param string $value Value
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function LEFT($value = '', $chars = 1) {
+ $value = self::flattenSingleValue($value);
+ $chars = self::flattenSingleValue($chars);
+
+ if ($chars < 0) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_substr')) {
+ return mb_substr($value, 0, $chars, 'UTF-8');
+ } else {
+ return substr($value, 0, $chars);
+ }
+ } // function LEFT()
+
+
+ /**
+ * RIGHT
+ *
+ * @param string $value Value
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function RIGHT($value = '', $chars = 1) {
+ $value = self::flattenSingleValue($value);
+ $chars = self::flattenSingleValue($chars);
+
+ if ($chars < 0) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if ((function_exists('mb_substr')) && (function_exists('mb_strlen'))) {
+ return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
+ } else {
+ return substr($value, strlen($value) - $chars);
+ }
+ } // function RIGHT()
+
+
+ /**
+ * MID
+ *
+ * @param string $value Value
+ * @param int $start Start character
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function MID($value = '', $start = 1, $chars = null) {
+ $value = self::flattenSingleValue($value);
+ $start = self::flattenSingleValue($start);
+ $chars = self::flattenSingleValue($chars);
+
+ if (($start < 1) || ($chars < 0)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_substr')) {
+ return mb_substr($value, --$start, $chars, 'UTF-8');
+ } else {
+ return substr($value, --$start, $chars);
+ }
+ } // function MID()
+
+
+ /**
+ * REPLACE
+ *
+ * @param string $value Value
+ * @param int $start Start character
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function REPLACE($oldText = '', $start = 1, $chars = null, $newText) {
+ $oldText = self::flattenSingleValue($oldText);
+ $start = self::flattenSingleValue($start);
+ $chars = self::flattenSingleValue($chars);
+ $newText = self::flattenSingleValue($newText);
+
+ $left = self::LEFT($oldText,$start-1);
+ $right = self::RIGHT($oldText,self::STRINGLENGTH($oldText)-($start+$chars)+1);
+
+ return $left.$newText.$right;
+ } // function REPLACE()
+
+
+ /**
+ * SUBSTITUTE
+ *
+ * @param string $text Value
+ * @param string $fromText From Value
+ * @param string $toText To Value
+ * @param integer $instance Instance Number
+ * @return string
+ */
+ public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0) {
+ $text = self::flattenSingleValue($text);
+ $fromText = self::flattenSingleValue($fromText);
+ $toText = self::flattenSingleValue($toText);
+ $instance = floor(self::flattenSingleValue($instance));
+
+ if ($instance == 0) {
+ if(function_exists('mb_str_replace')) {
+ return mb_str_replace($fromText,$toText,$text);
+ } else {
+ return str_replace($fromText,$toText,$text);
+ }
+ } else {
+ $pos = -1;
+ while($instance > 0) {
+ if (function_exists('mb_strpos')) {
+ $pos = mb_strpos($text, $fromText, $pos+1, 'UTF-8');
+ } else {
+ $pos = strpos($text, $fromText, $pos+1);
+ }
+ if ($pos === false) {
+ break;
+ }
+ --$instance;
+ }
+ if ($pos !== false) {
+ if (function_exists('mb_strlen')) {
+ return self::REPLACE($text,++$pos,mb_strlen($fromText, 'UTF-8'),$toText);
+ } else {
+ return self::REPLACE($text,++$pos,strlen($fromText),$toText);
+ }
+ }
+ }
+
+ return $left.$newText.$right;
+ } // function SUBSTITUTE()
+
+
+ /**
+ * RETURNSTRING
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function RETURNSTRING($testValue = '') {
+ $testValue = self::flattenSingleValue($testValue);
+
+ if (is_string($testValue)) {
+ return $testValue;
+ }
+ return Null;
+ } // function RETURNSTRING()
+
+
+ /**
+ * FIXEDFORMAT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function FIXEDFORMAT($value,$decimals=2,$no_commas=false) {
+ $value = self::flattenSingleValue($value);
+ $decimals = self::flattenSingleValue($decimals);
+ $no_commas = self::flattenSingleValue($no_commas);
+
+ $valueResult = round($value,$decimals);
+ if ($decimals < 0) { $decimals = 0; }
+ if (!$no_commas) {
+ $valueResult = number_format($valueResult,$decimals);
+ }
+
+ return (string) $valueResult;
+ } // function FIXEDFORMAT()
+
+
+ /**
+ * TEXTFORMAT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function TEXTFORMAT($value,$format) {
+ $value = self::flattenSingleValue($value);
+ $format = self::flattenSingleValue($format);
+
+ if ((is_string($value)) && (!is_numeric($value)) && PHPExcel_Shared_Date::isDateTimeFormatCode($format)) {
+ $value = self::DATEVALUE($value);
+ }
+
+ return (string) PHPExcel_Style_NumberFormat::toFormattedString($value,$format);
+ } // function TEXTFORMAT()
+
+
+ /**
+ * TRIMSPACES
+ *
+ * @param mixed $value Value to check
+ * @return string
+ */
+ public static function TRIMSPACES($stringValue = '') {
+ $stringValue = self::flattenSingleValue($stringValue);
+
+ if (is_string($stringValue) || is_numeric($stringValue)) {
+ return trim(preg_replace('/ +/',' ',$stringValue));
+ }
+ return Null;
+ } // function TRIMSPACES()
+
+
+ private static $_invalidChars = Null;
+
+ /**
+ * TRIMNONPRINTABLE
+ *
+ * @param mixed $value Value to check
+ * @return string
+ */
+ public static function TRIMNONPRINTABLE($stringValue = '') {
+ $stringValue = self::flattenSingleValue($stringValue);
+
+ if (is_bool($stringValue)) {
+ $stringValue = ($stringValue) ? 'TRUE' : 'FALSE';
+ }
+
+ if (self::$_invalidChars == Null) {
+ self::$_invalidChars = range(chr(0),chr(31));
+ }
+
+ if (is_string($stringValue) || is_numeric($stringValue)) {
+ return str_replace(self::$_invalidChars,'',trim($stringValue,"\x00..\x1F"));
+ }
+ return Null;
+ } // function TRIMNONPRINTABLE()
+
+
+ /**
+ * ERROR_TYPE
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function ERROR_TYPE($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ $i = 1;
+ foreach(self::$_errorCodes as $errorCode) {
+ if ($value == $errorCode) {
+ return $i;
+ }
+ ++$i;
+ }
+ return self::$_errorCodes['na'];
+ } // function ERROR_TYPE()
+
+
+ /**
+ * IS_BLANK
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_BLANK($value=null) {
+ if (!is_null($value)) {
+ $value = self::flattenSingleValue($value);
+ }
+
+ return is_null($value);
+ } // function IS_BLANK()
+
+
+ /**
+ * IS_ERR
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ERR($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return self::IS_ERROR($value) && (!self::IS_NA($value));
+ } // function IS_ERR()
+
+
+ /**
+ * IS_ERROR
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ERROR($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return in_array($value, array_values(self::$_errorCodes));
+ } // function IS_ERROR()
+
+
+ /**
+ * IS_NA
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NA($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return ($value === self::$_errorCodes['na']);
+ } // function IS_NA()
+
+
+ /**
+ * IS_EVEN
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_EVEN($value = 0) {
+ $value = self::flattenSingleValue($value);
+
+ if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return self::$_errorCodes['value'];
+ }
+ return ($value % 2 == 0);
+ } // function IS_EVEN()
+
+
+ /**
+ * IS_ODD
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ODD($value = null) {
+ $value = self::flattenSingleValue($value);
+
+ if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return self::$_errorCodes['value'];
+ }
+ return (abs($value) % 2 == 1);
+ } // function IS_ODD()
+
+
+ /**
+ * IS_NUMBER
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NUMBER($value = 0) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_string($value)) {
+ return False;
+ }
+ return is_numeric($value);
+ } // function IS_NUMBER()
+
+
+ /**
+ * IS_LOGICAL
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_LOGICAL($value = true) {
+ $value = self::flattenSingleValue($value);
+
+ return is_bool($value);
+ } // function IS_LOGICAL()
+
+
+ /**
+ * IS_TEXT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_TEXT($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return is_string($value);
+ } // function IS_TEXT()
+
+
+ /**
+ * IS_NONTEXT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NONTEXT($value = '') {
+ return !self::IS_TEXT($value);
+ } // function IS_NONTEXT()
+
+
+ /**
+ * VERSION
+ *
+ * @return string Version information
+ */
+ public static function VERSION() {
+ return 'PHPExcel ##VERSION##, ##DATE##';
+ } // function VERSION()
+
+
+ /**
+ * DATE
+ *
+ * @param long $year
+ * @param long $month
+ * @param long $day
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATE($year = 0, $month = 1, $day = 1) {
+ $year = (integer) self::flattenSingleValue($year);
+ $month = (integer) self::flattenSingleValue($month);
+ $day = (integer) self::flattenSingleValue($day);
+
+ $baseYear = PHPExcel_Shared_Date::getExcelCalendar();
+ // Validate parameters
+ if ($year < ($baseYear-1900)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((($baseYear-1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (($year < $baseYear) && ($year >= ($baseYear-1900))) {
+ $year += 1900;
+ }
+
+ if ($month < 1) {
+ // Handle year/month adjustment if month < 1
+ --$month;
+ $year += ceil($month / 12) - 1;
+ $month = 13 - abs($month % 12);
+ } elseif ($month > 12) {
+ // Handle year/month adjustment if month > 12
+ $year += floor($month / 12);
+ $month = ($month % 12);
+ }
+
+ // Re-validate the year parameter after adjustments
+ if (($year < $baseYear) || ($year >= 10000)) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
+ break;
+ }
+ } // function DATE()
+
+
+ /**
+ * TIME
+ *
+ * @param long $hour
+ * @param long $minute
+ * @param long $second
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIME($hour = 0, $minute = 0, $second = 0) {
+ $hour = self::flattenSingleValue($hour);
+ $minute = self::flattenSingleValue($minute);
+ $second = self::flattenSingleValue($second);
+
+ if ($hour == '') { $hour = 0; }
+ if ($minute == '') { $minute = 0; }
+ if ($second == '') { $second = 0; }
+
+ if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
+ return self::$_errorCodes['value'];
+ }
+ $hour = (integer) $hour;
+ $minute = (integer) $minute;
+ $second = (integer) $second;
+
+ if ($second < 0) {
+ $minute += floor($second / 60);
+ $second = 60 - abs($second % 60);
+ if ($second == 60) { $second = 0; }
+ } elseif ($second >= 60) {
+ $minute += floor($second / 60);
+ $second = $second % 60;
+ }
+ if ($minute < 0) {
+ $hour += floor($minute / 60);
+ $minute = 60 - abs($minute % 60);
+ if ($minute == 60) { $minute = 0; }
+ } elseif ($minute >= 60) {
+ $hour += floor($minute / 60);
+ $minute = $minute % 60;
+ }
+
+ if ($hour > 23) {
+ $hour = $hour % 24;
+ } elseif ($hour < 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : $date = 0;
+ $calendar = PHPExcel_Shared_Date::getExcelCalendar();
+ if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900) {
+ $date = 1;
+ }
+ return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::FormattedPHPToExcel(1970, 1, 1, $hour-1, $minute, $second)); // -2147468400; // -2147472000 + 3600
+ break;
+ case self::RETURNDATE_PHP_OBJECT : $dayAdjust = 0;
+ if ($hour < 0) {
+ $dayAdjust = floor($hour / 24);
+ $hour = 24 - abs($hour % 24);
+ if ($hour == 24) { $hour = 0; }
+ } elseif ($hour >= 24) {
+ $dayAdjust = floor($hour / 24);
+ $hour = $hour % 24;
+ }
+ $phpDateObject = new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
+ if ($dayAdjust != 0) {
+ $phpDateObject->modify($dayAdjust.' days');
+ }
+ return $phpDateObject;
+ break;
+ }
+ } // function TIME()
+
+
+ /**
+ * DATEVALUE
+ *
+ * @param string $dateValue
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATEVALUE($dateValue = 1) {
+ $dateValue = trim(self::flattenSingleValue($dateValue),'"');
+ // Strip any ordinals because they're allowed in Excel (English only)
+ $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui','$1$3',$dateValue);
+ // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
+ $dateValue = str_replace(array('/','.','-',' '),array(' ',' ',' ',' '),$dateValue);
+
+ $yearFound = false;
+ $t1 = explode(' ',$dateValue);
+ foreach($t1 as &$t) {
+ if ((is_numeric($t)) && ($t > 31)) {
+ if ($yearFound) {
+ return self::$_errorCodes['value'];
+ } else {
+ if ($t < 100) { $t += 1900; }
+ $yearFound = true;
+ }
+ }
+ }
+ if ((count($t1) == 1) && (strpos($t,':') != false)) {
+ // We've been fed a time value without any date
+ return 0.0;
+ } elseif (count($t1) == 2) {
+ // We only have two parts of the date: either day/month or month/year
+ if ($yearFound) {
+ array_unshift($t1,1);
+ } else {
+ array_push($t1,date('Y'));
+ }
+ }
+ unset($t);
+ $dateValue = implode(' ',$t1);
+
+ $PHPDateArray = date_parse($dateValue);
+ if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
+ $testVal1 = strtok($dateValue,'- ');
+ if ($testVal1 !== False) {
+ $testVal2 = strtok('- ');
+ if ($testVal2 !== False) {
+ $testVal3 = strtok('- ');
+ if ($testVal3 === False) {
+ $testVal3 = strftime('%Y');
+ }
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $PHPDateArray = date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
+ if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
+ $PHPDateArray = date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
+ if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ }
+
+ if (($PHPDateArray !== False) && ($PHPDateArray['error_count'] == 0)) {
+ // Execute function
+ if ($PHPDateArray['year'] == '') { $PHPDateArray['year'] = strftime('%Y'); }
+ if ($PHPDateArray['month'] == '') { $PHPDateArray['month'] = strftime('%m'); }
+ if ($PHPDateArray['day'] == '') { $PHPDateArray['day'] = strftime('%d'); }
+ $excelDateValue = floor(PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']));
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function DATEVALUE()
+
+
+ /**
+ * _getDateValue
+ *
+ * @param string $dateValue
+ * @return mixed Excel date/time serial value, or string if error
+ */
+ private static function _getDateValue($dateValue) {
+ if (!is_numeric($dateValue)) {
+ if ((is_string($dateValue)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
+ return self::$_errorCodes['value'];
+ }
+ if ((is_object($dateValue)) && ($dateValue instanceof PHPExcel_Shared_Date::$dateTimeObjectType)) {
+ $dateValue = PHPExcel_Shared_Date::PHPToExcel($dateValue);
+ } else {
+ $saveReturnDateType = self::getReturnDateType();
+ self::setReturnDateType(self::RETURNDATE_EXCEL);
+ $dateValue = self::DATEVALUE($dateValue);
+ self::setReturnDateType($saveReturnDateType);
+ }
+ }
+ return $dateValue;
+ } // function _getDateValue()
+
+
+ /**
+ * TIMEVALUE
+ *
+ * @param string $timeValue
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIMEVALUE($timeValue) {
+ $timeValue = trim(self::flattenSingleValue($timeValue),'"');
+ $timeValue = str_replace(array('/','.'),array('-','-'),$timeValue);
+
+ $PHPDateArray = date_parse($timeValue);
+ if (($PHPDateArray !== False) && ($PHPDateArray['error_count'] == 0)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);
+ } else {
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(1900,1,1,$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']) - 1;
+ }
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) $phpDateValue = PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+25569) - 3600;;
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function TIMEVALUE()
+
+
+ /**
+ * _getTimeValue
+ *
+ * @param string $timeValue
+ * @return mixed Excel date/time serial value, or string if error
+ */
+ private static function _getTimeValue($timeValue) {
+ $saveReturnDateType = self::getReturnDateType();
+ self::setReturnDateType(self::RETURNDATE_EXCEL);
+ $timeValue = self::TIMEVALUE($timeValue);
+ self::setReturnDateType($saveReturnDateType);
+ return $timeValue;
+ } // function _getTimeValue()
+
+
+ /**
+ * DATETIMENOW
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATETIMENOW() {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = False;
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) time();
+ break;
+ case self::RETURNDATE_PHP_OBJECT : $retValue = new DateTime();
+ break;
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ } // function DATETIMENOW()
+
+
+ /**
+ * DATENOW
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATENOW() {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = False;
+ $excelDateTime = floor(PHPExcel_Shared_Date::PHPToExcel(time()));
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : $retValue = (float) $excelDateTime;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime) - 3600;
+ break;
+ case self::RETURNDATE_PHP_OBJECT : $retValue = PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
+ break;
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ } // function DATENOW()
+
+
+ private static function _isLeapYear($year) {
+ return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
+ } // function _isLeapYear()
+
+
+ private static function _dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS) {
+ if ($startDay == 31) {
+ --$startDay;
+ } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::_isLeapYear($startYear))))) {
+ $startDay = 30;
+ }
+ if ($endDay == 31) {
+ if ($methodUS && $startDay != 30) {
+ $endDay = 1;
+ if ($endMonth == 12) {
+ ++$endYear;
+ $endMonth = 1;
+ } else {
+ ++$endMonth;
+ }
+ } else {
+ $endDay = 30;
+ }
+ }
+
+ return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
+ } // function _dateDiff360()
+
+
+ /**
+ * DAYS360
+ *
+ * @param long $startDate Excel date serial value or a standard date string
+ * @param long $endDate Excel date serial value or a standard date string
+ * @param boolean $method US or European Method
+ * @return long PHP date/time serial
+ */
+ public static function DAYS360($startDate = 0, $endDate = 0, $method = false) {
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Execute function
+ $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
+ $startDay = $PHPStartDateObject->format('j');
+ $startMonth = $PHPStartDateObject->format('n');
+ $startYear = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ $endDay = $PHPEndDateObject->format('j');
+ $endMonth = $PHPEndDateObject->format('n');
+ $endYear = $PHPEndDateObject->format('Y');
+
+ return self::_dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
+ } // function DAYS360()
+
+
+ /**
+ * DATEDIF
+ *
+ * @param long $startDate Excel date serial value or a standard date string
+ * @param long $endDate Excel date serial value or a standard date string
+ * @param string $unit
+ * @return long Interval between the dates
+ */
+ public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') {
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+ $unit = strtoupper(self::flattenSingleValue($unit));
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Validate parameters
+ if ($startDate >= $endDate) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $difference = $endDate - $startDate;
+
+ $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
+ $startDays = $PHPStartDateObject->format('j');
+ $startMonths = $PHPStartDateObject->format('n');
+ $startYears = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ $endDays = $PHPEndDateObject->format('j');
+ $endMonths = $PHPEndDateObject->format('n');
+ $endYears = $PHPEndDateObject->format('Y');
+
+ $retVal = self::$_errorCodes['num'];
+ switch ($unit) {
+ case 'D':
+ $retVal = intval($difference);
+ break;
+ case 'M':
+ $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
+ // We're only interested in full months
+ if ($endDays < $startDays) {
+ --$retVal;
+ }
+ break;
+ case 'Y':
+ $retVal = intval($endYears - $startYears);
+ // We're only interested in full months
+ if ($endMonths < $startMonths) {
+ --$retVal;
+ } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
+ --$retVal;
+ }
+ break;
+ case 'MD':
+ if ($endDays < $startDays) {
+ $retVal = $endDays;
+ $PHPEndDateObject->modify('-'.$endDays.' days');
+ $adjustDays = $PHPEndDateObject->format('j');
+ if ($adjustDays > $startDays) {
+ $retVal += ($adjustDays - $startDays);
+ }
+ } else {
+ $retVal = $endDays - $startDays;
+ }
+ break;
+ case 'YM':
+ $retVal = intval($endMonths - $startMonths);
+ if ($retVal < 0) $retVal = 12 + $retVal;
+ // We're only interested in full months
+ if ($endDays < $startDays) {
+ --$retVal;
+ }
+ break;
+ case 'YD':
+ $retVal = intval($difference);
+ if ($endYears > $startYears) {
+ while ($endYears > $startYears) {
+ $PHPEndDateObject->modify('-1 year');
+ $endYears = $PHPEndDateObject->format('Y');
+ }
+ $retVal = $PHPEndDateObject->format('z') - $PHPStartDateObject->format('z');
+ if ($retVal < 0) { $retVal += 365; }
+ }
+ break;
+ }
+ return $retVal;
+ } // function DATEDIF()
+
+
+ /**
+ * YEARFRAC
+ *
+ * Calculates the fraction of the year represented by the number of whole days between two dates (the start_date and the
+ * end_date). Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or obligations
+ * to assign to a specific term.
+ *
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
+ * @param integer $method Method used for the calculation
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float fraction of the year
+ */
+ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) {
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+ $method = self::flattenSingleValue($method);
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
+ switch($method) {
+ case 0 :
+ return self::DAYS360($startDate,$endDate) / 360;
+ break;
+ case 1 :
+ $days = self::DATEDIF($startDate,$endDate);
+ $startYear = self::YEAR($startDate);
+ $endYear = self::YEAR($endDate);
+ $years = $endYear - $startYear + 1;
+ $leapDays = 0;
+ if ($years == 1) {
+ if (self::_isLeapYear($endYear)) {
+ $startMonth = self::MONTHOFYEAR($startDate);
+ $endMonth = self::MONTHOFYEAR($endDate);
+ $endDay = self::DAYOFMONTH($endDate);
+ if (($startMonth < 3) ||
+ (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
+ $leapDays += 1;
+ }
+ }
+ } else {
+ for($year = $startYear; $year <= $endYear; ++$year) {
+ if ($year == $startYear) {
+ $startMonth = self::MONTHOFYEAR($startDate);
+ $startDay = self::DAYOFMONTH($startDate);
+ if ($startMonth < 3) {
+ $leapDays += (self::_isLeapYear($year)) ? 1 : 0;
+ }
+ } elseif($year == $endYear) {
+ $endMonth = self::MONTHOFYEAR($endDate);
+ $endDay = self::DAYOFMONTH($endDate);
+ if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
+ $leapDays += (self::_isLeapYear($year)) ? 1 : 0;
+ }
+ } else {
+ $leapDays += (self::_isLeapYear($year)) ? 1 : 0;
+ }
+ }
+ if ($years == 2) {
+ if (($leapDays == 0) && (self::_isLeapYear($startYear)) && ($days > 365)) {
+ $leapDays = 1;
+ } elseif ($days < 366) {
+ $years = 1;
+ }
+ }
+ $leapDays /= $years;
+ }
+ return $days / (365 + $leapDays);
+ break;
+ case 2 :
+ return self::DATEDIF($startDate,$endDate) / 360;
+ break;
+ case 3 :
+ return self::DATEDIF($startDate,$endDate) / 365;
+ break;
+ case 4 :
+ return self::DAYS360($startDate,$endDate,True) / 360;
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function YEARFRAC()
+
+
+ /**
+ * NETWORKDAYS
+ *
+ * @param mixed Start date
+ * @param mixed End date
+ * @param array of mixed Optional Date Series
+ * @return long Interval between the dates
+ */
+ public static function NETWORKDAYS($startDate,$endDate) {
+ // Retrieve the mandatory start and end date that are referenced in the function definition
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+ // Flush the mandatory start and end date that are referenced in the function definition, and get the optional days
+ $dateArgs = self::flattenArray(func_get_args());
+ array_shift($dateArgs);
+ array_shift($dateArgs);
+
+ // Validate the start and end dates
+ if (is_string($startDate = $sDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ $startDate = (float) floor($startDate);
+ if (is_string($endDate = $eDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+ $endDate = (float) floor($endDate);
+
+ if ($sDate > $eDate) {
+ $startDate = $eDate;
+ $endDate = $sDate;
+ }
+
+ // Execute function
+ $startDoW = 6 - self::DAYOFWEEK($startDate,2);
+ if ($startDoW < 0) { $startDoW = 0; }
+ $endDoW = self::DAYOFWEEK($endDate,2);
+ if ($endDoW >= 6) { $endDoW = 0; }
+
+ $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
+ $partWeekDays = $endDoW + $startDoW;
+ if ($partWeekDays > 5) {
+ $partWeekDays -= 5;
+ }
+
+ // Test any extra holiday parameters
+ $holidayCountedArray = array();
+ foreach ($dateArgs as $holidayDate) {
+ if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
+ --$partWeekDays;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+
+ if ($sDate > $eDate) {
+ return 0 - ($wholeWeekDays + $partWeekDays);
+ }
+ return $wholeWeekDays + $partWeekDays;
+ } // function NETWORKDAYS()
+
+
+ /**
+ * WORKDAY
+ *
+ * @param mixed Start date
+ * @param mixed number of days for adjustment
+ * @param array of mixed Optional Date Series
+ * @return long Interval between the dates
+ */
+ public static function WORKDAY($startDate,$endDays) {
+ // Retrieve the mandatory start date and days that are referenced in the function definition
+ $startDate = self::flattenSingleValue($startDate);
+ $endDays = (int) self::flattenSingleValue($endDays);
+ // Flush the mandatory start date and days that are referenced in the function definition, and get the optional days
+ $dateArgs = self::flattenArray(func_get_args());
+ array_shift($dateArgs);
+ array_shift($dateArgs);
+
+ if ((is_string($startDate = self::_getDateValue($startDate))) || (!is_numeric($endDays))) {
+ return self::$_errorCodes['value'];
+ }
+ $startDate = (float) floor($startDate);
+ // If endDays is 0, we always return startDate
+ if ($endDays == 0) { return $startDate; }
+
+ $decrementing = ($endDays < 0) ? True : False;
+
+ // Adjust the start date if it falls over a weekend
+
+ $startDoW = self::DAYOFWEEK($startDate,3);
+ if (self::DAYOFWEEK($startDate,3) >= 5) {
+ $startDate += ($decrementing) ? -$startDoW + 4: 7 - $startDoW;
+ ($decrementing) ? $endDays++ : $endDays--;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + (intval($endDays / 5) * 7) + ($endDays % 5);
+
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::DAYOFWEEK($endDate,3);
+ if ($endDoW >= 5) {
+ $endDate += ($decrementing) ? -$endDoW + 4: 7 - $endDoW;
+ }
+
+ // Test any extra holiday parameters
+ if (count($dateArgs) > 0) {
+ $holidayCountedArray = $holidayDates = array();
+ foreach ($dateArgs as $holidayDate) {
+ if ((!is_null($holidayDate)) && (trim($holidayDate) > '')) {
+ if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (self::DAYOFWEEK($holidayDate,3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ }
+ if ($decrementing) {
+ rsort($holidayDates, SORT_NUMERIC);
+ } else {
+ sort($holidayDates, SORT_NUMERIC);
+ }
+ foreach ($holidayDates as $holidayDate) {
+ if ($decrementing) {
+ if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
+ if (!in_array($holidayDate,$holidayCountedArray)) {
+ --$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ } else {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if (!in_array($holidayDate,$holidayCountedArray)) {
+ ++$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::DAYOFWEEK($endDate,3);
+ if ($endDoW >= 5) {
+ $endDate += ($decrementing) ? -$endDoW + 4: 7 - $endDoW;
+ }
+
+ }
+ }
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $endDate;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ break;
+ }
+ } // function WORKDAY()
+
+
+ /**
+ * DAYOFMONTH
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Day
+ */
+ public static function DAYOFMONTH($dateValue = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue == 0.0) {
+ return 0;
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('j');
+ } // function DAYOFMONTH()
+
+
+ /**
+ * DAYOFWEEK
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Day
+ */
+ public static function DAYOFWEEK($dateValue = 1, $style = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $style = floor(self::flattenSingleValue($style));
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $DoW = $PHPDateObject->format('w');
+
+ $firstDay = 1;
+ switch ($style) {
+ case 1: ++$DoW;
+ break;
+ case 2: if ($DoW == 0) { $DoW = 7; }
+ break;
+ case 3: if ($DoW == 0) { $DoW = 7; }
+ $firstDay = 0;
+ --$DoW;
+ break;
+ default:
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_EXCEL) {
+ // Test for Excel's 1900 leap year, and introduce the error as required
+ if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
+ --$DoW;
+ if ($DoW < $firstDay) {
+ $DoW += 7;
+ }
+ }
+ }
+
+ return (int) $DoW;
+ } // function DAYOFWEEK()
+
+
+ /**
+ * WEEKOFYEAR
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @param boolean $method Week begins on Sunday or Monday
+ * @return int Week Number
+ */
+ public static function WEEKOFYEAR($dateValue = 1, $method = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $method = floor(self::flattenSingleValue($method));
+
+ if (!is_numeric($method)) {
+ return self::$_errorCodes['value'];
+ } elseif (($method < 1) || ($method > 2)) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $dayOfYear = $PHPDateObject->format('z');
+ $dow = $PHPDateObject->format('w');
+ $PHPDateObject->modify('-'.$dayOfYear.' days');
+ $dow = $PHPDateObject->format('w');
+ $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
+ $dayOfYear -= $daysInFirstWeek;
+ $weekOfYear = ceil($dayOfYear / 7) + 1;
+
+ return (int) $weekOfYear;
+ } // function WEEKOFYEAR()
+
+
+ /**
+ * MONTHOFYEAR
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Month
+ */
+ public static function MONTHOFYEAR($dateValue = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('n');
+ } // function MONTHOFYEAR()
+
+
+ /**
+ * YEAR
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Year
+ */
+ public static function YEAR($dateValue = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('Y');
+ } // function YEAR()
+
+
+ /**
+ * HOUROFDAY
+ *
+ * @param mixed $timeValue Excel time serial value or a standard time string
+ * @return int Hour
+ */
+ public static function HOUROFDAY($timeValue = 0) {
+ $timeValue = self::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue,'/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $timeValue = self::_getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue,1);
+ } elseif ($timeValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('G',$timeValue);
+ } // function HOUROFDAY()
+
+
+ /**
+ * MINUTEOFHOUR
+ *
+ * @param long $timeValue Excel time serial value or a standard time string
+ * @return int Minute
+ */
+ public static function MINUTEOFHOUR($timeValue = 0) {
+ $timeValue = $timeTester = self::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue,'/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $timeValue = self::_getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue,1);
+ } elseif ($timeValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('i',$timeValue);
+ } // function MINUTEOFHOUR()
+
+
+ /**
+ * SECONDOFMINUTE
+ *
+ * @param long $timeValue Excel time serial value or a standard time string
+ * @return int Second
+ */
+ public static function SECONDOFMINUTE($timeValue = 0) {
+ $timeValue = self::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue,'/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $timeValue = self::_getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue,1);
+ } elseif ($timeValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('s',$timeValue);
+ } // function SECONDOFMINUTE()
+
+
+ private static function _adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0) {
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $oMonth = (int) $PHPDateObject->format('m');
+ $oYear = (int) $PHPDateObject->format('Y');
+
+ $adjustmentMonthsString = (string) $adjustmentMonths;
+ if ($adjustmentMonths > 0) {
+ $adjustmentMonthsString = '+'.$adjustmentMonths;
+ }
+ if ($adjustmentMonths != 0) {
+ $PHPDateObject->modify($adjustmentMonthsString.' months');
+ }
+ $nMonth = (int) $PHPDateObject->format('m');
+ $nYear = (int) $PHPDateObject->format('Y');
+
+ $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
+ if ($monthDiff != $adjustmentMonths) {
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-'.$adjustDays.' days';
+ $PHPDateObject->modify($adjustDaysString);
+ }
+ return $PHPDateObject;
+ } // function _adjustDateByMonths()
+
+
+ /**
+ * EDATE
+ *
+ * Returns the serial number that represents the date that is the indicated number of months before or after a specified date
+ * (the start_date). Use EDATE to calculate maturity dates or due dates that fall on the same day of the month as the date of issue.
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @param int $adjustmentMonths Number of months to adjust by
+ * @return long Excel date serial value
+ */
+ public static function EDATE($dateValue = 1, $adjustmentMonths = 0) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
+
+ if (!is_numeric($adjustmentMonths)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Execute function
+ $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths);
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
+ break;
+ }
+ } // function EDATE()
+
+
+ /**
+ * EOMONTH
+ *
+ * Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.
+ * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @param int $adjustmentMonths Number of months to adjust by
+ * @return long Excel date serial value
+ */
+ public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
+
+ if (!is_numeric($adjustmentMonths)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Execute function
+ $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths+1);
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-'.$adjustDays.' days';
+ $PHPDateObject->modify($adjustDaysString);
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
+ break;
+ }
+ } // function EOMONTH()
+
+
+ /**
+ * TRUNC
+ *
+ * Truncates value to the number of fractional digits by number_digits.
+ *
+ * @param float $value
+ * @param int $number_digits
+ * @return float Truncated value
+ */
+ public static function TRUNC($value = 0, $number_digits = 0) {
+ $value = self::flattenSingleValue($value);
+ $number_digits = self::flattenSingleValue($number_digits);
+
+ // Validate parameters
+ if ($number_digits < 0) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Truncate
+ if ($number_digits > 0) {
+ $value = $value * pow(10, $number_digits);
+ }
+ $value = intval($value);
+ if ($number_digits > 0) {
+ $value = $value / pow(10, $number_digits);
+ }
+
+ // Return
+ return $value;
+ } // function TRUNC()
+
+ /**
+ * POWER
+ *
+ * Computes x raised to the power y.
+ *
+ * @param float $x
+ * @param float $y
+ * @return float
+ */
+ public static function POWER($x = 0, $y = 2) {
+ $x = self::flattenSingleValue($x);
+ $y = self::flattenSingleValue($y);
+
+ // Validate parameters
+ if ($x == 0 && $y <= 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ // Return
+ return pow($x, $y);
+ } // function POWER()
+
+
+ private static function _nbrConversionFormat($xVal,$places) {
+ if (!is_null($places)) {
+ if (strlen($xVal) <= $places) {
+ return substr(str_pad($xVal,$places,'0',STR_PAD_LEFT),-10);
+ } else {
+ return self::$_errorCodes['num'];
+ }
+ }
+
+ return substr($xVal,-10);
+ } // function _nbrConversionFormat()
+
+
+ /**
+ * BINTODEC
+ *
+ * Return a binary value as Decimal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function BINTODEC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
+ return self::$_errorCodes['num'];
+ }
+ if (strlen($x) > 10) {
+ return self::$_errorCodes['num'];
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ $x = substr($x,-9);
+ return '-'.(512-bindec($x));
+ }
+ return bindec($x);
+ } // function BINTODEC()
+
+
+ /**
+ * BINTOHEX
+ *
+ * Return a binary value as Hex.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function BINTOHEX($x, $places=null) {
+ $x = floor(self::flattenSingleValue($x));
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
+ return self::$_errorCodes['num'];
+ }
+ if (strlen($x) > 10) {
+ return self::$_errorCodes['num'];
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ return str_repeat('F',8).substr(strtoupper(dechex(bindec(substr($x,-9)))),-2);
+ }
+ $hexVal = (string) strtoupper(dechex(bindec($x)));
+
+ return self::_nbrConversionFormat($hexVal,$places);
+ } // function BINTOHEX()
+
+
+ /**
+ * BINTOOCT
+ *
+ * Return a binary value as Octal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function BINTOOCT($x, $places=null) {
+ $x = floor(self::flattenSingleValue($x));
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
+ return self::$_errorCodes['num'];
+ }
+ if (strlen($x) > 10) {
+ return self::$_errorCodes['num'];
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ return str_repeat('7',7).substr(strtoupper(decoct(bindec(substr($x,-9)))),-3);
+ }
+ $octVal = (string) decoct(bindec($x));
+
+ return self::_nbrConversionFormat($octVal,$places);
+ } // function BINTOOCT()
+
+
+ /**
+ * DECTOBIN
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function DECTOBIN($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) floor($x);
+ $r = decbin($x);
+ if (strlen($r) == 32) {
+ // Two's Complement
+ $r = substr($r,-10);
+ } elseif (strlen($r) > 11) {
+ return self::$_errorCodes['num'];
+ }
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function DECTOBIN()
+
+
+ /**
+ * DECTOOCT
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function DECTOOCT($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) floor($x);
+ $r = decoct($x);
+ if (strlen($r) == 11) {
+ // Two's Complement
+ $r = substr($r,-10);
+ }
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function DECTOOCT()
+
+
+ /**
+ * DECTOHEX
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function DECTOHEX($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) floor($x);
+ $r = strtoupper(dechex($x));
+ if (strlen($r) == 8) {
+ // Two's Complement
+ $r = 'FF'.$r;
+ }
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function DECTOHEX()
+
+
+ /**
+ * HEXTOBIN
+ *
+ * Return a hex value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function HEXTOBIN($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
+ return self::$_errorCodes['num'];
+ }
+ $binVal = decbin(hexdec($x));
+
+ return substr(self::_nbrConversionFormat($binVal,$places),-10);
+ } // function HEXTOBIN()
+
+
+ /**
+ * HEXTOOCT
+ *
+ * Return a hex value as octal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function HEXTOOCT($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
+ return self::$_errorCodes['num'];
+ }
+ $octVal = decoct(hexdec($x));
+
+ return self::_nbrConversionFormat($octVal,$places);
+ } // function HEXTOOCT()
+
+
+ /**
+ * HEXTODEC
+ *
+ * Return a hex value as octal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function HEXTODEC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
+ return self::$_errorCodes['num'];
+ }
+ return hexdec($x);
+ } // function HEXTODEC()
+
+
+ /**
+ * OCTTOBIN
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function OCTTOBIN($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
+ return self::$_errorCodes['num'];
+ }
+ $r = decbin(octdec($x));
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function OCTTOBIN()
+
+
+ /**
+ * OCTTODEC
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function OCTTODEC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
+ return self::$_errorCodes['num'];
+ }
+ return octdec($x);
+ } // function OCTTODEC()
+
+
+ /**
+ * OCTTOHEX
+ *
+ * Return an octal value as hex.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function OCTTOHEX($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
+ return self::$_errorCodes['num'];
+ }
+ $hexVal = strtoupper(dechex(octdec($x)));
+
+ return self::_nbrConversionFormat($hexVal,$places);
+ } // function OCTTOHEX()
+
+
+ public static function _parseComplex($complexNumber) {
+ $workString = (string) $complexNumber;
+
+ $realNumber = $imaginary = 0;
+ // Extract the suffix, if there is one
+ $suffix = substr($workString,-1);
+ if (!is_numeric($suffix)) {
+ $workString = substr($workString,0,-1);
+ } else {
+ $suffix = '';
+ }
+
+ // Split the input into its Real and Imaginary components
+ $leadingSign = 0;
+ if (strlen($workString) > 0) {
+ $leadingSign = (($workString{0} == '+') || ($workString{0} == '-')) ? 1 : 0;
+ }
+ $power = '';
+ $realNumber = strtok($workString, '+-');
+ if (strtoupper(substr($realNumber,-1)) == 'E') {
+ $power = strtok('+-');
+ ++$leadingSign;
+ }
+
+ $realNumber = substr($workString,0,strlen($realNumber)+strlen($power)+$leadingSign);
+
+ if ($suffix != '') {
+ $imaginary = substr($workString,strlen($realNumber));
+
+ if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
+ $imaginary = $realNumber.'1';
+ $realNumber = '0';
+ } else if ($imaginary == '') {
+ $imaginary = $realNumber;
+ $realNumber = '0';
+ } elseif (($imaginary == '+') || ($imaginary == '-')) {
+ $imaginary .= '1';
+ }
+ }
+
+ $complexArray = array( 'real' => $realNumber,
+ 'imaginary' => $imaginary,
+ 'suffix' => $suffix
+ );
+
+ return $complexArray;
+ } // function _parseComplex()
+
+
+ private static function _cleanComplex($complexNumber) {
+ if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
+ if ($complexNumber{0} == '0') $complexNumber = substr($complexNumber,1);
+ if ($complexNumber{0} == '.') $complexNumber = '0'.$complexNumber;
+ if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
+ return $complexNumber;
+ }
+
+
+ /**
+ * COMPLEX
+ *
+ * returns a complex number of the form x + yi or x + yj.
+ *
+ * @param float $realNumber
+ * @param float $imaginary
+ * @param string $suffix
+ * @return string
+ */
+ public static function COMPLEX($realNumber=0.0, $imaginary=0.0, $suffix='i') {
+ $realNumber = (is_null($realNumber)) ? 0.0 : (float) self::flattenSingleValue($realNumber);
+ $imaginary = (is_null($imaginary)) ? 0.0 : (float) self::flattenSingleValue($imaginary);
+ $suffix = (is_null($suffix)) ? 'i' : self::flattenSingleValue($suffix);
+
+ if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
+ (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))) {
+ if ($suffix == '') $suffix = 'i';
+ if ($realNumber == 0.0) {
+ if ($imaginary == 0.0) {
+ return (string) '0';
+ } elseif ($imaginary == 1.0) {
+ return (string) $suffix;
+ } elseif ($imaginary == -1.0) {
+ return (string) '-'.$suffix;
+ }
+ return (string) $imaginary.$suffix;
+ } elseif ($imaginary == 0.0) {
+ return (string) $realNumber;
+ } elseif ($imaginary == 1.0) {
+ return (string) $realNumber.'+'.$suffix;
+ } elseif ($imaginary == -1.0) {
+ return (string) $realNumber.'-'.$suffix;
+ }
+ if ($imaginary > 0) { $imaginary = (string) '+'.$imaginary; }
+ return (string) $realNumber.$imaginary.$suffix;
+ }
+ return self::$_errorCodes['value'];
+ } // function COMPLEX()
+
+
+ /**
+ * IMAGINARY
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return real
+ */
+ public static function IMAGINARY($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ return $parsedComplex['imaginary'];
+ } // function IMAGINARY()
+
+
+ /**
+ * IMREAL
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return real
+ */
+ public static function IMREAL($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ return $parsedComplex['real'];
+ } // function IMREAL()
+
+
+ /**
+ * IMABS
+ *
+ * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return real
+ */
+ public static function IMABS($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ return sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
+ } // function IMABS()
+
+
+ /**
+ * IMARGUMENT
+ *
+ * Returns the argument theta of a complex number, i.e. the angle in radians from the real axis to the representation of the number in polar coordinates.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMARGUMENT($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['real'] == 0.0) {
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return 0.0;
+ } elseif($parsedComplex['imaginary'] < 0.0) {
+ return M_PI / -2;
+ } else {
+ return M_PI / 2;
+ }
+ } elseif ($parsedComplex['real'] > 0.0) {
+ return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
+ } elseif ($parsedComplex['imaginary'] < 0.0) {
+ return 0 - (M_PI - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
+ } else {
+ return M_PI - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
+ }
+ } // function IMARGUMENT()
+
+
+ /**
+ * IMCONJUGATE
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMCONJUGATE($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return $parsedComplex['real'];
+ } else {
+ return self::_cleanComplex(self::COMPLEX($parsedComplex['real'], 0 - $parsedComplex['imaginary'], $parsedComplex['suffix']));
+ }
+ } // function IMCONJUGATE()
+
+
+ /**
+ * IMCOS
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMCOS($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return cos($parsedComplex['real']);
+ } else {
+ return self::IMCONJUGATE(self::COMPLEX(cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']));
+ }
+ } // function IMCOS()
+
+
+ /**
+ * IMSIN
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMSIN($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return sin($parsedComplex['real']);
+ } else {
+ return self::COMPLEX(sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']);
+ }
+ } // function IMSIN()
+
+
+ /**
+ * IMSQRT
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMSQRT($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ $theta = self::IMARGUMENT($complexNumber);
+ $d1 = cos($theta / 2);
+ $d2 = sin($theta / 2);
+ $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($d1 * $r,$d2 * $r);
+ } else {
+ return self::COMPLEX($d1 * $r,$d2 * $r,$parsedComplex['suffix']);
+ }
+ } // function IMSQRT()
+
+
+ /**
+ * IMLN
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMLN($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return self::$_errorCodes['num'];
+ }
+
+ $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
+ $t = self::IMARGUMENT($complexNumber);
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($logR,$t);
+ } else {
+ return self::COMPLEX($logR,$t,$parsedComplex['suffix']);
+ }
+ } // function IMLN()
+
+
+ /**
+ * IMLOG10
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMLOG10($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return self::$_errorCodes['num'];
+ } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return log10($parsedComplex['real']);
+ }
+
+ return self::IMPRODUCT(log10(EULER),self::IMLN($complexNumber));
+ } // function IMLOG10()
+
+
+ /**
+ * IMLOG2
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMLOG2($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return self::$_errorCodes['num'];
+ } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return log($parsedComplex['real'],2);
+ }
+
+ return self::IMPRODUCT(log(EULER,2),self::IMLN($complexNumber));
+ } // function IMLOG2()
+
+
+ /**
+ * IMEXP
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMEXP($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return '1';
+ }
+
+ $e = exp($parsedComplex['real']);
+ $eX = $e * cos($parsedComplex['imaginary']);
+ $eY = $e * sin($parsedComplex['imaginary']);
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($eX,$eY);
+ } else {
+ return self::COMPLEX($eX,$eY,$parsedComplex['suffix']);
+ }
+ } // function IMEXP()
+
+
+ /**
+ * IMPOWER
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMPOWER($complexNumber,$realNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+ $realNumber = self::flattenSingleValue($realNumber);
+
+ if (!is_numeric($realNumber)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
+ $rPower = pow($r,$realNumber);
+ $theta = self::IMARGUMENT($complexNumber) * $realNumber;
+ if ($theta == 0) {
+ return 1;
+ } elseif ($parsedComplex['imaginary'] == 0.0) {
+ return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
+ } else {
+ return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
+ }
+ } // function IMPOWER()
+
+
+ /**
+ * IMDIV
+ *
+ * Returns the quotient of two complex numbers in x + yi or x + yj text format.
+ *
+ * @param string $complexDividend
+ * @param string $complexDivisor
+ * @return real
+ */
+ public static function IMDIV($complexDividend,$complexDivisor) {
+ $complexDividend = self::flattenSingleValue($complexDividend);
+ $complexDivisor = self::flattenSingleValue($complexDivisor);
+
+ $parsedComplexDividend = self::_parseComplex($complexDividend);
+ if (!is_array($parsedComplexDividend)) {
+ return $parsedComplexDividend;
+ }
+
+ $parsedComplexDivisor = self::_parseComplex($complexDivisor);
+ if (!is_array($parsedComplexDivisor)) {
+ return $parsedComplexDividend;
+ }
+
+ if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
+ ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])) {
+ return self::$_errorCodes['num'];
+ }
+ if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
+ $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
+ }
+
+ $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
+ $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
+ $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
+
+ $r = $d1/$d3;
+ $i = $d2/$d3;
+
+ if ($i > 0.0) {
+ return self::_cleanComplex($r.'+'.$i.$parsedComplexDivisor['suffix']);
+ } elseif ($i < 0.0) {
+ return self::_cleanComplex($r.$i.$parsedComplexDivisor['suffix']);
+ } else {
+ return $r;
+ }
+ } // function IMDIV()
+
+
+ /**
+ * IMSUB
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber1
+ * @param string $complexNumber2
+ * @return real
+ */
+ public static function IMSUB($complexNumber1,$complexNumber2) {
+ $complexNumber1 = self::flattenSingleValue($complexNumber1);
+ $complexNumber2 = self::flattenSingleValue($complexNumber2);
+
+ $parsedComplex1 = self::_parseComplex($complexNumber1);
+ if (!is_array($parsedComplex1)) {
+ return $parsedComplex1;
+ }
+
+ $parsedComplex2 = self::_parseComplex($complexNumber2);
+ if (!is_array($parsedComplex2)) {
+ return $parsedComplex2;
+ }
+
+ if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
+ ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])) {
+ return self::$_errorCodes['num'];
+ } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
+ $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
+ }
+
+ $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
+ $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
+
+ return self::COMPLEX($d1,$d2,$parsedComplex1['suffix']);
+ } // function IMSUB()
+
+
+ /**
+ * IMSUM
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * @param array of mixed Data Series
+ * @return real
+ */
+ public static function IMSUM() {
+ // Return value
+ $returnValue = self::_parseComplex('0');
+ $activeSuffix = '';
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ $parsedComplex = self::_parseComplex($arg);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($activeSuffix == '') {
+ $activeSuffix = $parsedComplex['suffix'];
+ } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
+ return self::$_errorCodes['value'];
+ }
+
+ $returnValue['real'] += $parsedComplex['real'];
+ $returnValue['imaginary'] += $parsedComplex['imaginary'];
+ }
+
+ if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
+ return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
+ } // function IMSUM()
+
+
+ /**
+ * IMPRODUCT
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * @param array of mixed Data Series
+ * @return real
+ */
+ public static function IMPRODUCT() {
+ // Return value
+ $returnValue = self::_parseComplex('1');
+ $activeSuffix = '';
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ $parsedComplex = self::_parseComplex($arg);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ $workValue = $returnValue;
+ if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
+ $activeSuffix = $parsedComplex['suffix'];
+ } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
+ return self::$_errorCodes['num'];
+ }
+ $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
+ $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
+ }
+
+ if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
+ return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
+ } // function IMPRODUCT()
+
+
+ private static $_conversionUnits = array( 'g' => array( 'Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => True ),
+ 'sg' => array( 'Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => False ),
+ 'lbm' => array( 'Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => False ),
+ 'u' => array( 'Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => True ),
+ 'ozm' => array( 'Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => False ),
+ 'm' => array( 'Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => True ),
+ 'mi' => array( 'Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => False ),
+ 'Nmi' => array( 'Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => False ),
+ 'in' => array( 'Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => False ),
+ 'ft' => array( 'Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => False ),
+ 'yd' => array( 'Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => False ),
+ 'ang' => array( 'Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => True ),
+ 'Pica' => array( 'Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => False ),
+ 'yr' => array( 'Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => False ),
+ 'day' => array( 'Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => False ),
+ 'hr' => array( 'Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => False ),
+ 'mn' => array( 'Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => False ),
+ 'sec' => array( 'Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => True ),
+ 'Pa' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
+ 'p' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
+ 'atm' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
+ 'at' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
+ 'mmHg' => array( 'Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => True ),
+ 'N' => array( 'Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => True ),
+ 'dyn' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
+ 'dy' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
+ 'lbf' => array( 'Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => False ),
+ 'J' => array( 'Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => True ),
+ 'e' => array( 'Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => True ),
+ 'c' => array( 'Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => True ),
+ 'cal' => array( 'Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => True ),
+ 'eV' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
+ 'ev' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
+ 'HPh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
+ 'hh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
+ 'Wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
+ 'wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
+ 'flb' => array( 'Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => False ),
+ 'BTU' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
+ 'btu' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
+ 'HP' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
+ 'h' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
+ 'W' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
+ 'w' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
+ 'T' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => True ),
+ 'ga' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => True ),
+ 'C' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
+ 'cel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
+ 'F' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
+ 'fah' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
+ 'K' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
+ 'kel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
+ 'tsp' => array( 'Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => False ),
+ 'tbs' => array( 'Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => False ),
+ 'oz' => array( 'Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => False ),
+ 'cup' => array( 'Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => False ),
+ 'pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
+ 'us_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
+ 'uk_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => False ),
+ 'qt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => False ),
+ 'gal' => array( 'Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => False ),
+ 'l' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True ),
+ 'lt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True )
+ );
+
+ private static $_conversionMultipliers = array( 'Y' => array( 'multiplier' => 1E24, 'name' => 'yotta' ),
+ 'Z' => array( 'multiplier' => 1E21, 'name' => 'zetta' ),
+ 'E' => array( 'multiplier' => 1E18, 'name' => 'exa' ),
+ 'P' => array( 'multiplier' => 1E15, 'name' => 'peta' ),
+ 'T' => array( 'multiplier' => 1E12, 'name' => 'tera' ),
+ 'G' => array( 'multiplier' => 1E9, 'name' => 'giga' ),
+ 'M' => array( 'multiplier' => 1E6, 'name' => 'mega' ),
+ 'k' => array( 'multiplier' => 1E3, 'name' => 'kilo' ),
+ 'h' => array( 'multiplier' => 1E2, 'name' => 'hecto' ),
+ 'e' => array( 'multiplier' => 1E1, 'name' => 'deka' ),
+ 'd' => array( 'multiplier' => 1E-1, 'name' => 'deci' ),
+ 'c' => array( 'multiplier' => 1E-2, 'name' => 'centi' ),
+ 'm' => array( 'multiplier' => 1E-3, 'name' => 'milli' ),
+ 'u' => array( 'multiplier' => 1E-6, 'name' => 'micro' ),
+ 'n' => array( 'multiplier' => 1E-9, 'name' => 'nano' ),
+ 'p' => array( 'multiplier' => 1E-12, 'name' => 'pico' ),
+ 'f' => array( 'multiplier' => 1E-15, 'name' => 'femto' ),
+ 'a' => array( 'multiplier' => 1E-18, 'name' => 'atto' ),
+ 'z' => array( 'multiplier' => 1E-21, 'name' => 'zepto' ),
+ 'y' => array( 'multiplier' => 1E-24, 'name' => 'yocto' )
+ );
+
+ private static $_unitConversions = array( 'Mass' => array( 'g' => array( 'g' => 1.0,
+ 'sg' => 6.85220500053478E-05,
+ 'lbm' => 2.20462291469134E-03,
+ 'u' => 6.02217000000000E+23,
+ 'ozm' => 3.52739718003627E-02
+ ),
+ 'sg' => array( 'g' => 1.45938424189287E+04,
+ 'sg' => 1.0,
+ 'lbm' => 3.21739194101647E+01,
+ 'u' => 8.78866000000000E+27,
+ 'ozm' => 5.14782785944229E+02
+ ),
+ 'lbm' => array( 'g' => 4.5359230974881148E+02,
+ 'sg' => 3.10810749306493E-02,
+ 'lbm' => 1.0,
+ 'u' => 2.73161000000000E+26,
+ 'ozm' => 1.60000023429410E+01
+ ),
+ 'u' => array( 'g' => 1.66053100460465E-24,
+ 'sg' => 1.13782988532950E-28,
+ 'lbm' => 3.66084470330684E-27,
+ 'u' => 1.0,
+ 'ozm' => 5.85735238300524E-26
+ ),
+ 'ozm' => array( 'g' => 2.83495152079732E+01,
+ 'sg' => 1.94256689870811E-03,
+ 'lbm' => 6.24999908478882E-02,
+ 'u' => 1.70725600000000E+25,
+ 'ozm' => 1.0
+ )
+ ),
+ 'Distance' => array( 'm' => array( 'm' => 1.0,
+ 'mi' => 6.21371192237334E-04,
+ 'Nmi' => 5.39956803455724E-04,
+ 'in' => 3.93700787401575E+01,
+ 'ft' => 3.28083989501312E+00,
+ 'yd' => 1.09361329797891E+00,
+ 'ang' => 1.00000000000000E+10,
+ 'Pica' => 2.83464566929116E+03
+ ),
+ 'mi' => array( 'm' => 1.60934400000000E+03,
+ 'mi' => 1.0,
+ 'Nmi' => 8.68976241900648E-01,
+ 'in' => 6.33600000000000E+04,
+ 'ft' => 5.28000000000000E+03,
+ 'yd' => 1.76000000000000E+03,
+ 'ang' => 1.60934400000000E+13,
+ 'Pica' => 4.56191999999971E+06
+ ),
+ 'Nmi' => array( 'm' => 1.85200000000000E+03,
+ 'mi' => 1.15077944802354E+00,
+ 'Nmi' => 1.0,
+ 'in' => 7.29133858267717E+04,
+ 'ft' => 6.07611548556430E+03,
+ 'yd' => 2.02537182785694E+03,
+ 'ang' => 1.85200000000000E+13,
+ 'Pica' => 5.24976377952723E+06
+ ),
+ 'in' => array( 'm' => 2.54000000000000E-02,
+ 'mi' => 1.57828282828283E-05,
+ 'Nmi' => 1.37149028077754E-05,
+ 'in' => 1.0,
+ 'ft' => 8.33333333333333E-02,
+ 'yd' => 2.77777777686643E-02,
+ 'ang' => 2.54000000000000E+08,
+ 'Pica' => 7.19999999999955E+01
+ ),
+ 'ft' => array( 'm' => 3.04800000000000E-01,
+ 'mi' => 1.89393939393939E-04,
+ 'Nmi' => 1.64578833693305E-04,
+ 'in' => 1.20000000000000E+01,
+ 'ft' => 1.0,
+ 'yd' => 3.33333333223972E-01,
+ 'ang' => 3.04800000000000E+09,
+ 'Pica' => 8.63999999999946E+02
+ ),
+ 'yd' => array( 'm' => 9.14400000300000E-01,
+ 'mi' => 5.68181818368230E-04,
+ 'Nmi' => 4.93736501241901E-04,
+ 'in' => 3.60000000118110E+01,
+ 'ft' => 3.00000000000000E+00,
+ 'yd' => 1.0,
+ 'ang' => 9.14400000300000E+09,
+ 'Pica' => 2.59200000085023E+03
+ ),
+ 'ang' => array( 'm' => 1.00000000000000E-10,
+ 'mi' => 6.21371192237334E-14,
+ 'Nmi' => 5.39956803455724E-14,
+ 'in' => 3.93700787401575E-09,
+ 'ft' => 3.28083989501312E-10,
+ 'yd' => 1.09361329797891E-10,
+ 'ang' => 1.0,
+ 'Pica' => 2.83464566929116E-07
+ ),
+ 'Pica' => array( 'm' => 3.52777777777800E-04,
+ 'mi' => 2.19205948372629E-07,
+ 'Nmi' => 1.90484761219114E-07,
+ 'in' => 1.38888888888898E-02,
+ 'ft' => 1.15740740740748E-03,
+ 'yd' => 3.85802469009251E-04,
+ 'ang' => 3.52777777777800E+06,
+ 'Pica' => 1.0
+ )
+ ),
+ 'Time' => array( 'yr' => array( 'yr' => 1.0,
+ 'day' => 365.25,
+ 'hr' => 8766.0,
+ 'mn' => 525960.0,
+ 'sec' => 31557600.0
+ ),
+ 'day' => array( 'yr' => 2.73785078713210E-03,
+ 'day' => 1.0,
+ 'hr' => 24.0,
+ 'mn' => 1440.0,
+ 'sec' => 86400.0
+ ),
+ 'hr' => array( 'yr' => 1.14077116130504E-04,
+ 'day' => 4.16666666666667E-02,
+ 'hr' => 1.0,
+ 'mn' => 60.0,
+ 'sec' => 3600.0
+ ),
+ 'mn' => array( 'yr' => 1.90128526884174E-06,
+ 'day' => 6.94444444444444E-04,
+ 'hr' => 1.66666666666667E-02,
+ 'mn' => 1.0,
+ 'sec' => 60.0
+ ),
+ 'sec' => array( 'yr' => 3.16880878140289E-08,
+ 'day' => 1.15740740740741E-05,
+ 'hr' => 2.77777777777778E-04,
+ 'mn' => 1.66666666666667E-02,
+ 'sec' => 1.0
+ )
+ ),
+ 'Pressure' => array( 'Pa' => array( 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923299998193E-06,
+ 'at' => 9.86923299998193E-06,
+ 'mmHg' => 7.50061707998627E-03
+ ),
+ 'p' => array( 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923299998193E-06,
+ 'at' => 9.86923299998193E-06,
+ 'mmHg' => 7.50061707998627E-03
+ ),
+ 'atm' => array( 'Pa' => 1.01324996583000E+05,
+ 'p' => 1.01324996583000E+05,
+ 'atm' => 1.0,
+ 'at' => 1.0,
+ 'mmHg' => 760.0
+ ),
+ 'at' => array( 'Pa' => 1.01324996583000E+05,
+ 'p' => 1.01324996583000E+05,
+ 'atm' => 1.0,
+ 'at' => 1.0,
+ 'mmHg' => 760.0
+ ),
+ 'mmHg' => array( 'Pa' => 1.33322363925000E+02,
+ 'p' => 1.33322363925000E+02,
+ 'atm' => 1.31578947368421E-03,
+ 'at' => 1.31578947368421E-03,
+ 'mmHg' => 1.0
+ )
+ ),
+ 'Force' => array( 'N' => array( 'N' => 1.0,
+ 'dyn' => 1.0E+5,
+ 'dy' => 1.0E+5,
+ 'lbf' => 2.24808923655339E-01
+ ),
+ 'dyn' => array( 'N' => 1.0E-5,
+ 'dyn' => 1.0,
+ 'dy' => 1.0,
+ 'lbf' => 2.24808923655339E-06
+ ),
+ 'dy' => array( 'N' => 1.0E-5,
+ 'dyn' => 1.0,
+ 'dy' => 1.0,
+ 'lbf' => 2.24808923655339E-06
+ ),
+ 'lbf' => array( 'N' => 4.448222,
+ 'dyn' => 4.448222E+5,
+ 'dy' => 4.448222E+5,
+ 'lbf' => 1.0
+ )
+ ),
+ 'Energy' => array( 'J' => array( 'J' => 1.0,
+ 'e' => 9.99999519343231E+06,
+ 'c' => 2.39006249473467E-01,
+ 'cal' => 2.38846190642017E-01,
+ 'eV' => 6.24145700000000E+18,
+ 'ev' => 6.24145700000000E+18,
+ 'HPh' => 3.72506430801000E-07,
+ 'hh' => 3.72506430801000E-07,
+ 'Wh' => 2.77777916238711E-04,
+ 'wh' => 2.77777916238711E-04,
+ 'flb' => 2.37304222192651E+01,
+ 'BTU' => 9.47815067349015E-04,
+ 'btu' => 9.47815067349015E-04
+ ),
+ 'e' => array( 'J' => 1.00000048065700E-07,
+ 'e' => 1.0,
+ 'c' => 2.39006364353494E-08,
+ 'cal' => 2.38846305445111E-08,
+ 'eV' => 6.24146000000000E+11,
+ 'ev' => 6.24146000000000E+11,
+ 'HPh' => 3.72506609848824E-14,
+ 'hh' => 3.72506609848824E-14,
+ 'Wh' => 2.77778049754611E-11,
+ 'wh' => 2.77778049754611E-11,
+ 'flb' => 2.37304336254586E-06,
+ 'BTU' => 9.47815522922962E-11,
+ 'btu' => 9.47815522922962E-11
+ ),
+ 'c' => array( 'J' => 4.18399101363672E+00,
+ 'e' => 4.18398900257312E+07,
+ 'c' => 1.0,
+ 'cal' => 9.99330315287563E-01,
+ 'eV' => 2.61142000000000E+19,
+ 'ev' => 2.61142000000000E+19,
+ 'HPh' => 1.55856355899327E-06,
+ 'hh' => 1.55856355899327E-06,
+ 'Wh' => 1.16222030532950E-03,
+ 'wh' => 1.16222030532950E-03,
+ 'flb' => 9.92878733152102E+01,
+ 'BTU' => 3.96564972437776E-03,
+ 'btu' => 3.96564972437776E-03
+ ),
+ 'cal' => array( 'J' => 4.18679484613929E+00,
+ 'e' => 4.18679283372801E+07,
+ 'c' => 1.00067013349059E+00,
+ 'cal' => 1.0,
+ 'eV' => 2.61317000000000E+19,
+ 'ev' => 2.61317000000000E+19,
+ 'HPh' => 1.55960800463137E-06,
+ 'hh' => 1.55960800463137E-06,
+ 'Wh' => 1.16299914807955E-03,
+ 'wh' => 1.16299914807955E-03,
+ 'flb' => 9.93544094443283E+01,
+ 'BTU' => 3.96830723907002E-03,
+ 'btu' => 3.96830723907002E-03
+ ),
+ 'eV' => array( 'J' => 1.60219000146921E-19,
+ 'e' => 1.60218923136574E-12,
+ 'c' => 3.82933423195043E-20,
+ 'cal' => 3.82676978535648E-20,
+ 'eV' => 1.0,
+ 'ev' => 1.0,
+ 'HPh' => 5.96826078912344E-26,
+ 'hh' => 5.96826078912344E-26,
+ 'Wh' => 4.45053000026614E-23,
+ 'wh' => 4.45053000026614E-23,
+ 'flb' => 3.80206452103492E-18,
+ 'BTU' => 1.51857982414846E-22,
+ 'btu' => 1.51857982414846E-22
+ ),
+ 'ev' => array( 'J' => 1.60219000146921E-19,
+ 'e' => 1.60218923136574E-12,
+ 'c' => 3.82933423195043E-20,
+ 'cal' => 3.82676978535648E-20,
+ 'eV' => 1.0,
+ 'ev' => 1.0,
+ 'HPh' => 5.96826078912344E-26,
+ 'hh' => 5.96826078912344E-26,
+ 'Wh' => 4.45053000026614E-23,
+ 'wh' => 4.45053000026614E-23,
+ 'flb' => 3.80206452103492E-18,
+ 'BTU' => 1.51857982414846E-22,
+ 'btu' => 1.51857982414846E-22
+ ),
+ 'HPh' => array( 'J' => 2.68451741316170E+06,
+ 'e' => 2.68451612283024E+13,
+ 'c' => 6.41616438565991E+05,
+ 'cal' => 6.41186757845835E+05,
+ 'eV' => 1.67553000000000E+25,
+ 'ev' => 1.67553000000000E+25,
+ 'HPh' => 1.0,
+ 'hh' => 1.0,
+ 'Wh' => 7.45699653134593E+02,
+ 'wh' => 7.45699653134593E+02,
+ 'flb' => 6.37047316692964E+07,
+ 'BTU' => 2.54442605275546E+03,
+ 'btu' => 2.54442605275546E+03
+ ),
+ 'hh' => array( 'J' => 2.68451741316170E+06,
+ 'e' => 2.68451612283024E+13,
+ 'c' => 6.41616438565991E+05,
+ 'cal' => 6.41186757845835E+05,
+ 'eV' => 1.67553000000000E+25,
+ 'ev' => 1.67553000000000E+25,
+ 'HPh' => 1.0,
+ 'hh' => 1.0,
+ 'Wh' => 7.45699653134593E+02,
+ 'wh' => 7.45699653134593E+02,
+ 'flb' => 6.37047316692964E+07,
+ 'BTU' => 2.54442605275546E+03,
+ 'btu' => 2.54442605275546E+03
+ ),
+ 'Wh' => array( 'J' => 3.59999820554720E+03,
+ 'e' => 3.59999647518369E+10,
+ 'c' => 8.60422069219046E+02,
+ 'cal' => 8.59845857713046E+02,
+ 'eV' => 2.24692340000000E+22,
+ 'ev' => 2.24692340000000E+22,
+ 'HPh' => 1.34102248243839E-03,
+ 'hh' => 1.34102248243839E-03,
+ 'Wh' => 1.0,
+ 'wh' => 1.0,
+ 'flb' => 8.54294774062316E+04,
+ 'BTU' => 3.41213254164705E+00,
+ 'btu' => 3.41213254164705E+00
+ ),
+ 'wh' => array( 'J' => 3.59999820554720E+03,
+ 'e' => 3.59999647518369E+10,
+ 'c' => 8.60422069219046E+02,
+ 'cal' => 8.59845857713046E+02,
+ 'eV' => 2.24692340000000E+22,
+ 'ev' => 2.24692340000000E+22,
+ 'HPh' => 1.34102248243839E-03,
+ 'hh' => 1.34102248243839E-03,
+ 'Wh' => 1.0,
+ 'wh' => 1.0,
+ 'flb' => 8.54294774062316E+04,
+ 'BTU' => 3.41213254164705E+00,
+ 'btu' => 3.41213254164705E+00
+ ),
+ 'flb' => array( 'J' => 4.21400003236424E-02,
+ 'e' => 4.21399800687660E+05,
+ 'c' => 1.00717234301644E-02,
+ 'cal' => 1.00649785509554E-02,
+ 'eV' => 2.63015000000000E+17,
+ 'ev' => 2.63015000000000E+17,
+ 'HPh' => 1.56974211145130E-08,
+ 'hh' => 1.56974211145130E-08,
+ 'Wh' => 1.17055614802000E-05,
+ 'wh' => 1.17055614802000E-05,
+ 'flb' => 1.0,
+ 'BTU' => 3.99409272448406E-05,
+ 'btu' => 3.99409272448406E-05
+ ),
+ 'BTU' => array( 'J' => 1.05505813786749E+03,
+ 'e' => 1.05505763074665E+10,
+ 'c' => 2.52165488508168E+02,
+ 'cal' => 2.51996617135510E+02,
+ 'eV' => 6.58510000000000E+21,
+ 'ev' => 6.58510000000000E+21,
+ 'HPh' => 3.93015941224568E-04,
+ 'hh' => 3.93015941224568E-04,
+ 'Wh' => 2.93071851047526E-01,
+ 'wh' => 2.93071851047526E-01,
+ 'flb' => 2.50369750774671E+04,
+ 'BTU' => 1.0,
+ 'btu' => 1.0,
+ ),
+ 'btu' => array( 'J' => 1.05505813786749E+03,
+ 'e' => 1.05505763074665E+10,
+ 'c' => 2.52165488508168E+02,
+ 'cal' => 2.51996617135510E+02,
+ 'eV' => 6.58510000000000E+21,
+ 'ev' => 6.58510000000000E+21,
+ 'HPh' => 3.93015941224568E-04,
+ 'hh' => 3.93015941224568E-04,
+ 'Wh' => 2.93071851047526E-01,
+ 'wh' => 2.93071851047526E-01,
+ 'flb' => 2.50369750774671E+04,
+ 'BTU' => 1.0,
+ 'btu' => 1.0,
+ )
+ ),
+ 'Power' => array( 'HP' => array( 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45701000000000E+02,
+ 'w' => 7.45701000000000E+02
+ ),
+ 'h' => array( 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45701000000000E+02,
+ 'w' => 7.45701000000000E+02
+ ),
+ 'W' => array( 'HP' => 1.34102006031908E-03,
+ 'h' => 1.34102006031908E-03,
+ 'W' => 1.0,
+ 'w' => 1.0
+ ),
+ 'w' => array( 'HP' => 1.34102006031908E-03,
+ 'h' => 1.34102006031908E-03,
+ 'W' => 1.0,
+ 'w' => 1.0
+ )
+ ),
+ 'Magnetism' => array( 'T' => array( 'T' => 1.0,
+ 'ga' => 10000.0
+ ),
+ 'ga' => array( 'T' => 0.0001,
+ 'ga' => 1.0
+ )
+ ),
+ 'Liquid' => array( 'tsp' => array( 'tsp' => 1.0,
+ 'tbs' => 3.33333333333333E-01,
+ 'oz' => 1.66666666666667E-01,
+ 'cup' => 2.08333333333333E-02,
+ 'pt' => 1.04166666666667E-02,
+ 'us_pt' => 1.04166666666667E-02,
+ 'uk_pt' => 8.67558516821960E-03,
+ 'qt' => 5.20833333333333E-03,
+ 'gal' => 1.30208333333333E-03,
+ 'l' => 4.92999408400710E-03,
+ 'lt' => 4.92999408400710E-03
+ ),
+ 'tbs' => array( 'tsp' => 3.00000000000000E+00,
+ 'tbs' => 1.0,
+ 'oz' => 5.00000000000000E-01,
+ 'cup' => 6.25000000000000E-02,
+ 'pt' => 3.12500000000000E-02,
+ 'us_pt' => 3.12500000000000E-02,
+ 'uk_pt' => 2.60267555046588E-02,
+ 'qt' => 1.56250000000000E-02,
+ 'gal' => 3.90625000000000E-03,
+ 'l' => 1.47899822520213E-02,
+ 'lt' => 1.47899822520213E-02
+ ),
+ 'oz' => array( 'tsp' => 6.00000000000000E+00,
+ 'tbs' => 2.00000000000000E+00,
+ 'oz' => 1.0,
+ 'cup' => 1.25000000000000E-01,
+ 'pt' => 6.25000000000000E-02,
+ 'us_pt' => 6.25000000000000E-02,
+ 'uk_pt' => 5.20535110093176E-02,
+ 'qt' => 3.12500000000000E-02,
+ 'gal' => 7.81250000000000E-03,
+ 'l' => 2.95799645040426E-02,
+ 'lt' => 2.95799645040426E-02
+ ),
+ 'cup' => array( 'tsp' => 4.80000000000000E+01,
+ 'tbs' => 1.60000000000000E+01,
+ 'oz' => 8.00000000000000E+00,
+ 'cup' => 1.0,
+ 'pt' => 5.00000000000000E-01,
+ 'us_pt' => 5.00000000000000E-01,
+ 'uk_pt' => 4.16428088074541E-01,
+ 'qt' => 2.50000000000000E-01,
+ 'gal' => 6.25000000000000E-02,
+ 'l' => 2.36639716032341E-01,
+ 'lt' => 2.36639716032341E-01
+ ),
+ 'pt' => array( 'tsp' => 9.60000000000000E+01,
+ 'tbs' => 3.20000000000000E+01,
+ 'oz' => 1.60000000000000E+01,
+ 'cup' => 2.00000000000000E+00,
+ 'pt' => 1.0,
+ 'us_pt' => 1.0,
+ 'uk_pt' => 8.32856176149081E-01,
+ 'qt' => 5.00000000000000E-01,
+ 'gal' => 1.25000000000000E-01,
+ 'l' => 4.73279432064682E-01,
+ 'lt' => 4.73279432064682E-01
+ ),
+ 'us_pt' => array( 'tsp' => 9.60000000000000E+01,
+ 'tbs' => 3.20000000000000E+01,
+ 'oz' => 1.60000000000000E+01,
+ 'cup' => 2.00000000000000E+00,
+ 'pt' => 1.0,
+ 'us_pt' => 1.0,
+ 'uk_pt' => 8.32856176149081E-01,
+ 'qt' => 5.00000000000000E-01,
+ 'gal' => 1.25000000000000E-01,
+ 'l' => 4.73279432064682E-01,
+ 'lt' => 4.73279432064682E-01
+ ),
+ 'uk_pt' => array( 'tsp' => 1.15266000000000E+02,
+ 'tbs' => 3.84220000000000E+01,
+ 'oz' => 1.92110000000000E+01,
+ 'cup' => 2.40137500000000E+00,
+ 'pt' => 1.20068750000000E+00,
+ 'us_pt' => 1.20068750000000E+00,
+ 'uk_pt' => 1.0,
+ 'qt' => 6.00343750000000E-01,
+ 'gal' => 1.50085937500000E-01,
+ 'l' => 5.68260698087162E-01,
+ 'lt' => 5.68260698087162E-01
+ ),
+ 'qt' => array( 'tsp' => 1.92000000000000E+02,
+ 'tbs' => 6.40000000000000E+01,
+ 'oz' => 3.20000000000000E+01,
+ 'cup' => 4.00000000000000E+00,
+ 'pt' => 2.00000000000000E+00,
+ 'us_pt' => 2.00000000000000E+00,
+ 'uk_pt' => 1.66571235229816E+00,
+ 'qt' => 1.0,
+ 'gal' => 2.50000000000000E-01,
+ 'l' => 9.46558864129363E-01,
+ 'lt' => 9.46558864129363E-01
+ ),
+ 'gal' => array( 'tsp' => 7.68000000000000E+02,
+ 'tbs' => 2.56000000000000E+02,
+ 'oz' => 1.28000000000000E+02,
+ 'cup' => 1.60000000000000E+01,
+ 'pt' => 8.00000000000000E+00,
+ 'us_pt' => 8.00000000000000E+00,
+ 'uk_pt' => 6.66284940919265E+00,
+ 'qt' => 4.00000000000000E+00,
+ 'gal' => 1.0,
+ 'l' => 3.78623545651745E+00,
+ 'lt' => 3.78623545651745E+00
+ ),
+ 'l' => array( 'tsp' => 2.02840000000000E+02,
+ 'tbs' => 6.76133333333333E+01,
+ 'oz' => 3.38066666666667E+01,
+ 'cup' => 4.22583333333333E+00,
+ 'pt' => 2.11291666666667E+00,
+ 'us_pt' => 2.11291666666667E+00,
+ 'uk_pt' => 1.75975569552166E+00,
+ 'qt' => 1.05645833333333E+00,
+ 'gal' => 2.64114583333333E-01,
+ 'l' => 1.0,
+ 'lt' => 1.0
+ ),
+ 'lt' => array( 'tsp' => 2.02840000000000E+02,
+ 'tbs' => 6.76133333333333E+01,
+ 'oz' => 3.38066666666667E+01,
+ 'cup' => 4.22583333333333E+00,
+ 'pt' => 2.11291666666667E+00,
+ 'us_pt' => 2.11291666666667E+00,
+ 'uk_pt' => 1.75975569552166E+00,
+ 'qt' => 1.05645833333333E+00,
+ 'gal' => 2.64114583333333E-01,
+ 'l' => 1.0,
+ 'lt' => 1.0
+ )
+ )
+ );
+
+
+ /**
+ * getConversionGroups
+ *
+ * @return array
+ */
+ public static function getConversionGroups() {
+ $conversionGroups = array();
+ foreach(self::$_conversionUnits as $conversionUnit) {
+ $conversionGroups[] = $conversionUnit['Group'];
+ }
+ return array_merge(array_unique($conversionGroups));
+ } // function getConversionGroups()
+
+
+ /**
+ * getConversionGroupUnits
+ *
+ * @return array
+ */
+ public static function getConversionGroupUnits($group = NULL) {
+ $conversionGroups = array();
+ foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
+ if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
+ $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
+ }
+ }
+ return $conversionGroups;
+ } // function getConversionGroupUnits()
+
+
+ /**
+ * getConversionGroupUnitDetails
+ *
+ * @return array
+ */
+ public static function getConversionGroupUnitDetails($group = NULL) {
+ $conversionGroups = array();
+ foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
+ if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
+ $conversionGroups[$conversionGroup['Group']][] = array( 'unit' => $conversionUnit,
+ 'description' => $conversionGroup['Unit Name']
+ );
+ }
+ }
+ return $conversionGroups;
+ } // function getConversionGroupUnitDetails()
+
+
+ /**
+ * getConversionGroups
+ *
+ * @return array
+ */
+ public static function getConversionMultipliers() {
+ return self::$_conversionMultipliers;
+ } // function getConversionGroups()
+
+
+ /**
+ * CONVERTUOM
+ *
+ * @param float $value
+ * @param string $fromUOM
+ * @param string $toUOM
+ * @return float
+ */
+ public static function CONVERTUOM($value, $fromUOM, $toUOM) {
+ $value = self::flattenSingleValue($value);
+ $fromUOM = self::flattenSingleValue($fromUOM);
+ $toUOM = self::flattenSingleValue($toUOM);
+
+ if (!is_numeric($value)) {
+ return self::$_errorCodes['value'];
+ }
+ $fromMultiplier = 1;
+ if (isset(self::$_conversionUnits[$fromUOM])) {
+ $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
+ } else {
+ $fromMultiplier = substr($fromUOM,0,1);
+ $fromUOM = substr($fromUOM,1);
+ if (isset(self::$_conversionMultipliers[$fromMultiplier])) {
+ $fromMultiplier = self::$_conversionMultipliers[$fromMultiplier]['multiplier'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ if ((isset(self::$_conversionUnits[$fromUOM])) && (self::$_conversionUnits[$fromUOM]['AllowPrefix'])) {
+ $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ }
+ $value *= $fromMultiplier;
+
+ $toMultiplier = 1;
+ if (isset(self::$_conversionUnits[$toUOM])) {
+ $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
+ } else {
+ $toMultiplier = substr($toUOM,0,1);
+ $toUOM = substr($toUOM,1);
+ if (isset(self::$_conversionMultipliers[$toMultiplier])) {
+ $toMultiplier = self::$_conversionMultipliers[$toMultiplier]['multiplier'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ if ((isset(self::$_conversionUnits[$toUOM])) && (self::$_conversionUnits[$toUOM]['AllowPrefix'])) {
+ $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ }
+ if ($unitGroup1 != $unitGroup2) {
+ return self::$_errorCodes['na'];
+ }
+
+ if ($fromUOM == $toUOM) {
+ return 1.0;
+ } elseif ($unitGroup1 == 'Temperature') {
+ if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
+ if (($toUOM == 'F') || ($toUOM == 'fah')) {
+ return 1.0;
+ } else {
+ $value = (($value - 32) / 1.8);
+ if (($toUOM == 'K') || ($toUOM == 'kel')) {
+ $value += 273.15;
+ }
+ return $value;
+ }
+ } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
+ (($toUOM == 'K') || ($toUOM == 'kel'))) {
+ return 1.0;
+ } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
+ (($toUOM == 'C') || ($toUOM == 'cel'))) {
+ return 1.0;
+ }
+ if (($toUOM == 'F') || ($toUOM == 'fah')) {
+ if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
+ $value -= 273.15;
+ }
+ return ($value * 1.8) + 32;
+ }
+ if (($toUOM == 'C') || ($toUOM == 'cel')) {
+ return $value - 273.15;
+ }
+ return $value + 273.15;
+ }
+ return ($value * self::$_unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
+ } // function CONVERTUOM()
+
+
+ /**
+ * BESSELI
+ *
+ * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments
+ *
+ * @param float $x
+ * @param float $n
+ * @return int
+ */
+ public static function BESSELI($x, $n) {
+ $x = (is_null($x)) ? 0.0 : self::flattenSingleValue($x);
+ $n = (is_null($n)) ? 0.0 : self::flattenSingleValue($n);
+
+ if ((is_numeric($x)) && (is_numeric($n))) {
+ $n = floor($n);
+ if ($n < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $f_2_PI = 2 * M_PI;
+
+ if (abs($x) <= 30) {
+ $fTerm = pow($x / 2, $n) / self::FACT($n);
+ $nK = 1;
+ $fResult = $fTerm;
+ $fSqrX = ($x * $x) / 4;
+ do {
+ $fTerm *= $fSqrX;
+ $fTerm /= ($nK * ($nK + $n));
+ $fResult += $fTerm;
+ } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
+ } else {
+ $fXAbs = abs($x);
+ $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
+ if (($n && 1) && ($x < 0)) {
+ $fResult = -$fResult;
+ }
+ }
+ return $fResult;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELI()
+
+
+ /**
+ * BESSELJ
+ *
+ * Returns the Bessel function
+ *
+ * @param float $x
+ * @param float $n
+ * @return int
+ */
+ public static function BESSELJ($x, $n) {
+ $x = (is_null($x)) ? 0.0 : self::flattenSingleValue($x);
+ $n = (is_null($n)) ? 0.0 : self::flattenSingleValue($n);
+
+ if ((is_numeric($x)) && (is_numeric($n))) {
+ $n = floor($n);
+ if ($n < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $f_PI_DIV_2 = M_PI / 2;
+ $f_PI_DIV_4 = M_PI / 4;
+
+ $fResult = 0;
+ if (abs($x) <= 30) {
+ $fTerm = pow($x / 2, $n) / self::FACT($n);
+ $nK = 1;
+ $fResult = $fTerm;
+ $fSqrX = ($x * $x) / -4;
+ do {
+ $fTerm *= $fSqrX;
+ $fTerm /= ($nK * ($nK + $n));
+ $fResult += $fTerm;
+ } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
+ } else {
+ $fXAbs = abs($x);
+ $fResult = sqrt(M_2DIVPI / $fXAbs) * cos($fXAbs - $n * $f_PI_DIV_2 - $f_PI_DIV_4);
+ if (($n && 1) && ($x < 0)) {
+ $fResult = -$fResult;
+ }
+ }
+ return $fResult;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELJ()
+
+
+ private static function _Besselk0($fNum) {
+ if ($fNum <= 2) {
+ $fNum2 = $fNum * 0.5;
+ $y = ($fNum2 * $fNum2);
+ $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
+ (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
+ (0.10750e-3 + $y * 0.74e-5))))));
+ } else {
+ $y = 2 / $fNum;
+ $fRet = exp(-$fNum) / sqrt($fNum) *
+ (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
+ (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
+ }
+ return $fRet;
+ } // function _Besselk0()
+
+
+ private static function _Besselk1($fNum) {
+ if ($fNum <= 2) {
+ $fNum2 = $fNum * 0.5;
+ $y = ($fNum2 * $fNum2);
+ $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
+ (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
+ } else {
+ $y = 2 / $fNum;
+ $fRet = exp(-$fNum) / sqrt($fNum) *
+ (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
+ (0.325614e-2 + $y * (-0.68245e-3)))))));
+ }
+ return $fRet;
+ } // function _Besselk1()
+
+
+ /**
+ * BESSELK
+ *
+ * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.
+ *
+ * @param float $x
+ * @param float $ord
+ * @return float
+ */
+ public static function BESSELK($x, $ord) {
+ $x = (is_null($x)) ? 0.0 : self::flattenSingleValue($x);
+ $ord = (is_null($ord)) ? 0.0 : self::flattenSingleValue($ord);
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ if (($ord < 0) || ($x == 0.0)) {
+ return self::$_errorCodes['num'];
+ }
+
+ switch(floor($ord)) {
+ case 0 : return self::_Besselk0($x);
+ break;
+ case 1 : return self::_Besselk1($x);
+ break;
+ default : $fTox = 2 / $x;
+ $fBkm = self::_Besselk0($x);
+ $fBk = self::_Besselk1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fBkp = $fBkm + $n * $fTox * $fBk;
+ $fBkm = $fBk;
+ $fBk = $fBkp;
+ }
+ }
+ return $fBk;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELK()
+
+
+ private static function _Bessely0($fNum) {
+ if ($fNum < 8.0) {
+ $y = ($fNum * $fNum);
+ $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
+ $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
+ $fRet = $f1 / $f2 + M_2DIVPI * self::BESSELJ($fNum, 0) * log($fNum);
+ } else {
+ $z = 8.0 / $fNum;
+ $y = ($z * $z);
+ $xx = $fNum - 0.785398164;
+ $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
+ $fRet = sqrt(M_2DIVPI / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
+ }
+ return $fRet;
+ } // function _Bessely0()
+
+
+ private static function _Bessely1($fNum) {
+ if ($fNum < 8.0) {
+ $y = ($fNum * $fNum);
+ $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
+ (-0.4237922726e7 + $y * 0.8511937935e4)))));
+ $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
+ (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+ $fRet = $f1 / $f2 + M_2DIVPI * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
+ } else {
+ $z = 8.0 / $fNum;
+ $y = ($z * $z);
+ $xx = $fNum - 2.356194491;
+ $f1 = 1 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e6))));
+ $f2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y * (-0.88228987e-6 + $y * 0.105787412e-6)));
+ $fRet = sqrt(M_2DIVPI / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
+ #i12430# ...but this seems to work much better.
+// $fRet = sqrt(M_2DIVPI / $fNum) * sin($fNum - 2.356194491);
+ }
+ return $fRet;
+ } // function _Bessely1()
+
+
+ /**
+ * BESSELY
+ *
+ * Returns the Bessel function, which is also called the Weber function or the Neumann function.
+ *
+ * @param float $x
+ * @param float $n
+ * @return int
+ */
+ public static function BESSELY($x, $ord) {
+ $x = (is_null($x)) ? 0.0 : self::flattenSingleValue($x);
+ $ord = (is_null($ord)) ? 0.0 : self::flattenSingleValue($ord);
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ if (($ord < 0) || ($x == 0.0)) {
+ return self::$_errorCodes['num'];
+ }
+
+ switch(floor($ord)) {
+ case 0 : return self::_Bessely0($x);
+ break;
+ case 1 : return self::_Bessely1($x);
+ break;
+ default: $fTox = 2 / $x;
+ $fBym = self::_Bessely0($x);
+ $fBy = self::_Bessely1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fByp = $n * $fTox * $fBy - $fBym;
+ $fBym = $fBy;
+ $fBy = $fByp;
+ }
+ }
+ return $fBy;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELY()
+
+
+ /**
+ * DELTA
+ *
+ * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
+ *
+ * @param float $a
+ * @param float $b
+ * @return int
+ */
+ public static function DELTA($a, $b=0) {
+ $a = self::flattenSingleValue($a);
+ $b = self::flattenSingleValue($b);
+
+ return (int) ($a == $b);
+ } // function DELTA()
+
+
+ /**
+ * GESTEP
+ *
+ * Returns 1 if number = step; returns 0 (zero) otherwise
+ *
+ * @param float $number
+ * @param float $step
+ * @return int
+ */
+ public static function GESTEP($number, $step=0) {
+ $number = self::flattenSingleValue($number);
+ $step = self::flattenSingleValue($step);
+
+ return (int) ($number >= $step);
+ } // function GESTEP()
+
+
+ //
+ // Private method to calculate the erf value
+ //
+ private static $_two_sqrtpi = 1.128379167095512574;
+
+ private static function _erfVal($x) {
+ if (abs($x) > 2.2) {
+ return 1 - self::_erfcVal($x);
+ }
+ $sum = $term = $x;
+ $xsqr = ($x * $x);
+ $j = 1;
+ do {
+ $term *= $xsqr / $j;
+ $sum -= $term / (2 * $j + 1);
+ ++$j;
+ $term *= $xsqr / $j;
+ $sum += $term / (2 * $j + 1);
+ ++$j;
+ if ($sum == 0.0) {
+ break;
+ }
+ } while (abs($term / $sum) > PRECISION);
+ return self::$_two_sqrtpi * $sum;
+ } // function _erfVal()
+
+
+ /**
+ * ERF
+ *
+ * Returns the error function integrated between lower_limit and upper_limit
+ *
+ * @param float $lower lower bound for integrating ERF
+ * @param float $upper upper bound for integrating ERF.
+ * If omitted, ERF integrates between zero and lower_limit
+ * @return int
+ */
+ public static function ERF($lower, $upper = null) {
+ $lower = self::flattenSingleValue($lower);
+ $upper = self::flattenSingleValue($upper);
+
+ if (is_numeric($lower)) {
+ if ($lower < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if (is_null($upper)) {
+ return self::_erfVal($lower);
+ }
+ if (is_numeric($upper)) {
+ if ($upper < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return self::_erfVal($upper) - self::_erfVal($lower);
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function ERF()
+
+
+ //
+ // Private method to calculate the erfc value
+ //
+ private static $_one_sqrtpi = 0.564189583547756287;
+
+ private static function _erfcVal($x) {
+ if (abs($x) < 2.2) {
+ return 1 - self::_erfVal($x);
+ }
+ if ($x < 0) {
+ return 2 - self::erfc(-$x);
+ }
+ $a = $n = 1;
+ $b = $c = $x;
+ $d = ($x * $x) + 0.5;
+ $q1 = $q2 = $b / $d;
+ $t = 0;
+ do {
+ $t = $a * $n + $b * $x;
+ $a = $b;
+ $b = $t;
+ $t = $c * $n + $d * $x;
+ $c = $d;
+ $d = $t;
+ $n += 0.5;
+ $q1 = $q2;
+ $q2 = $b / $d;
+ } while ((abs($q1 - $q2) / $q2) > PRECISION);
+ return self::$_one_sqrtpi * exp(-$x * $x) * $q2;
+ } // function _erfcVal()
+
+
+ /**
+ * ERFC
+ *
+ * Returns the complementary ERF function integrated between x and infinity
+ *
+ * @param float $x The lower bound for integrating ERF
+ * @return int
+ */
+ public static function ERFC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_numeric($x)) {
+ if ($x < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return self::_erfcVal($x);
+ }
+ return self::$_errorCodes['value'];
+ } // function ERFC()
+
+
+ /**
+ * LOWERCASE
+ *
+ * Converts a string value to upper case.
+ *
+ * @param string $mixedCaseString
+ * @return string
+ */
+ public static function LOWERCASE($mixedCaseString) {
+ $mixedCaseString = self::flattenSingleValue($mixedCaseString);
+
+ if (is_bool($mixedCaseString)) {
+ $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_convert_case')) {
+ return mb_convert_case($mixedCaseString, MB_CASE_LOWER, 'UTF-8');
+ } else {
+ return strtoupper($mixedCaseString);
+ }
+ } // function LOWERCASE()
+
+
+ /**
+ * UPPERCASE
+ *
+ * Converts a string value to upper case.
+ *
+ * @param string $mixedCaseString
+ * @return string
+ */
+ public static function UPPERCASE($mixedCaseString) {
+ $mixedCaseString = self::flattenSingleValue($mixedCaseString);
+
+ if (is_bool($mixedCaseString)) {
+ $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_convert_case')) {
+ return mb_convert_case($mixedCaseString, MB_CASE_UPPER, 'UTF-8');
+ } else {
+ return strtoupper($mixedCaseString);
+ }
+ } // function UPPERCASE()
+
+
+ /**
+ * PROPERCASE
+ *
+ * Converts a string value to upper case.
+ *
+ * @param string $mixedCaseString
+ * @return string
+ */
+ public static function PROPERCASE($mixedCaseString) {
+ $mixedCaseString = self::flattenSingleValue($mixedCaseString);
+
+ if (is_bool($mixedCaseString)) {
+ $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_convert_case')) {
+ return mb_convert_case($mixedCaseString, MB_CASE_TITLE, 'UTF-8');
+ } else {
+ return ucwords($mixedCaseString);
+ }
+ } // function PROPERCASE()
+
+
+ /**
+ * DOLLAR
+ *
+ * This function converts a number to text using currency format, with the decimals rounded to the specified place.
+ * The format used is $#,##0.00_);($#,##0.00)..
+ *
+ * @param float $value The value to format
+ * @param int $decimals The number of digits to display to the right of the decimal point.
+ * If decimals is negative, number is rounded to the left of the decimal point.
+ * If you omit decimals, it is assumed to be 2
+ * @return string
+ */
+ public static function DOLLAR($value = 0, $decimals = 2) {
+ $value = self::flattenSingleValue($value);
+ $decimals = is_null($decimals) ? 0 : self::flattenSingleValue($decimals);
+
+ // Validate parameters
+ if (!is_numeric($value) || !is_numeric($decimals)) {
+ return self::$_errorCodes['num'];
+ }
+ $decimals = floor($decimals);
+
+ if ($decimals > 0) {
+ return money_format('%.'.$decimals.'n',$value);
+ } else {
+ $round = pow(10,abs($decimals));
+ if ($value < 0) { $round = 0-$round; }
+ $value = self::MROUND($value,$round);
+ // The implementation of money_format used if the standard PHP function is not available can't handle decimal places of 0,
+ // so we display to 1 dp and chop off that character and the decimal separator using substr
+ return substr(money_format('%.1n',$value),0,-2);
+ }
+ } // function DOLLAR()
+
+
+ /**
+ * DOLLARDE
+ *
+ * Converts a dollar price expressed as an integer part and a fraction part into a dollar price expressed as a decimal number.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * @param float $fractional_dollar Fractional Dollar
+ * @param int $fraction Fraction
+ * @return float
+ */
+ public static function DOLLARDE($fractional_dollar = Null, $fraction = 0) {
+ $fractional_dollar = self::flattenSingleValue($fractional_dollar);
+ $fraction = (int)self::flattenSingleValue($fraction);
+
+ // Validate parameters
+ if (is_null($fractional_dollar) || $fraction < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if ($fraction == 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $dollars = floor($fractional_dollar);
+ $cents = fmod($fractional_dollar,1);
+ $cents /= $fraction;
+ $cents *= pow(10,ceil(log10($fraction)));
+ return $dollars + $cents;
+ } // function DOLLARDE()
+
+
+ /**
+ * DOLLARFR
+ *
+ * Converts a dollar price expressed as a decimal number into a dollar price expressed as a fraction.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * @param float $decimal_dollar Decimal Dollar
+ * @param int $fraction Fraction
+ * @return float
+ */
+ public static function DOLLARFR($decimal_dollar = Null, $fraction = 0) {
+ $decimal_dollar = self::flattenSingleValue($decimal_dollar);
+ $fraction = (int)self::flattenSingleValue($fraction);
+
+ // Validate parameters
+ if (is_null($decimal_dollar) || $fraction < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if ($fraction == 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $dollars = floor($decimal_dollar);
+ $cents = fmod($decimal_dollar,1);
+ $cents *= $fraction;
+ $cents *= pow(10,-ceil(log10($fraction)));
+ return $dollars + $cents;
+ } // function DOLLARFR()
+
+
+ /**
+ * EFFECT
+ *
+ * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.
+ *
+ * @param float $nominal_rate Nominal interest rate
+ * @param int $npery Number of compounding payments per year
+ * @return float
+ */
+ public static function EFFECT($nominal_rate = 0, $npery = 0) {
+ $nominal_rate = self::flattenSingleValue($nominal_rate);
+ $npery = (int)self::flattenSingleValue($npery);
+
+ // Validate parameters
+ if ($nominal_rate <= 0 || $npery < 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ return pow((1 + $nominal_rate / $npery), $npery) - 1;
+ } // function EFFECT()
+
+
+ /**
+ * NOMINAL
+ *
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
+ *
+ * @param float $effect_rate Effective interest rate
+ * @param int $npery Number of compounding payments per year
+ * @return float
+ */
+ public static function NOMINAL($effect_rate = 0, $npery = 0) {
+ $effect_rate = self::flattenSingleValue($effect_rate);
+ $npery = (int)self::flattenSingleValue($npery);
+
+ // Validate parameters
+ if ($effect_rate <= 0 || $npery < 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
+ } // function NOMINAL()
+
+
+ /**
+ * PV
+ *
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pmt Periodic payment (annuity)
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = self::flattenSingleValue($nper);
+ $pmt = self::flattenSingleValue($pmt);
+ $fv = self::flattenSingleValue($fv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
+ } else {
+ return -$fv - $pmt * $nper;
+ }
+ } // function PV()
+
+
+ /**
+ * FV
+ *
+ * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pmt Periodic payment (annuity)
+ * @param float $pv Present Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = self::flattenSingleValue($nper);
+ $pmt = self::flattenSingleValue($pmt);
+ $pv = self::flattenSingleValue($pv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
+ } else {
+ return -$pv - $pmt * $nper;
+ }
+ } // function FV()
+
+
+ /**
+ * FVSCHEDULE
+ *
+ */
+ public static function FVSCHEDULE($principal, $schedule) {
+ $principal = self::flattenSingleValue($principal);
+ $schedule = self::flattenArray($schedule);
+
+ foreach($schedule as $n) {
+ $principal *= 1 + $n;
+ }
+
+ return $principal;
+ } // function FVSCHEDULE()
+
+
+ /**
+ * PMT
+ *
+ * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
+ } else {
+ return (-$pv - $fv) / $nper;
+ }
+ } // function PMT()
+
+
+ /**
+ * NPER
+ *
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $pmt Periodic payment (annuity)
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $pmt = self::flattenSingleValue($pmt);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ if ($pmt == 0 && $pv == 0) {
+ return self::$_errorCodes['num'];
+ }
+ return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+ } else {
+ if ($pmt == 0) {
+ return self::$_errorCodes['num'];
+ }
+ return (-$pv -$fv) / $pmt;
+ }
+ } // function NPER()
+
+
+
+ private static function _interestAndPrincipal($rate=0, $per=0, $nper=0, $pv=0, $fv=0, $type=0) {
+ $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
+ $capital = $pv;
+ for ($i = 1; $i<= $per; ++$i) {
+ $interest = ($type && $i == 1)? 0 : -$capital * $rate;
+ $principal = $pmt - $interest;
+ $capital += $principal;
+ }
+ return array($interest, $principal);
+ } // function _interestAndPrincipal()
+
+
+ /**
+ * IPMT
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $per Period for which we want to find the interest
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $per = (int) self::flattenSingleValue($per);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($per <= 0 || $per > $nper) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[0];
+ } // function IPMT()
+
+
+ /**
+ * CUMIPMT
+ *
+ * Returns the cumulative interest paid on a loan between start_period and end_period.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param int start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param int end The last period in the calculation.
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $start = (int) self::flattenSingleValue($start);
+ $end = (int) self::flattenSingleValue($end);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($start < 1 || $start > $end) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $interest = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
+ }
+
+ return $interest;
+ } // function CUMIPMT()
+
+
+ /**
+ * PPMT
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $per Period for which we want to find the interest
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $per = (int) self::flattenSingleValue($per);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($per <= 0 || $per > $nper) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[1];
+ } // function PPMT()
+
+
+ /**
+ * CUMPRINC
+ *
+ * Returns the cumulative principal paid on a loan between start_period and end_period.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param int start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param int end The last period in the calculation.
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $start = (int) self::flattenSingleValue($start);
+ $end = (int) self::flattenSingleValue($end);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($start < 1 || $start > $end) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $principal = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
+ }
+
+ return $principal;
+ } // function CUMPRINC()
+
+
+ /**
+ * ISPMT
+ *
+ * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
+ *
+ * Excel Function:
+ * =ISPMT(interest_rate, period, number_payments, PV)
+ *
+ * interest_rate is the interest rate for the investment
+ *
+ * period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
+ *
+ * number_payments is the number of payments for the annuity
+ *
+ * PV is the loan amount or present value of the payments
+ */
+ public static function ISPMT() {
+ // Return value
+ $returnValue = 0;
+
+ // Get the parameters
+ $aArgs = self::flattenArray(func_get_args());
+ $interestRate = array_shift($aArgs);
+ $period = array_shift($aArgs);
+ $numberPeriods = array_shift($aArgs);
+ $principleRemaining = array_shift($aArgs);
+
+ // Calculate
+ $principlePayment = ($principleRemaining * 1.0) / ($numberPeriods * 1.0);
+ for($i=0; $i <= $period; ++$i) {
+ $returnValue = $interestRate * $principleRemaining * -1;
+ $principleRemaining -= $principlePayment;
+ // principle needs to be 0 after the last payment, don't let floating point screw it up
+ if($i == $numberPeriods) {
+ $returnValue = 0;
+ }
+ }
+ return($returnValue);
+ } // function ISPMT()
+
+
+ /**
+ * NPV
+ *
+ * Returns the Net Present Value of a cash flow series given a discount rate.
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ */
+ public static function NPV() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $rate = array_shift($aArgs);
+ for ($i = 1; $i <= count($aArgs); ++$i) {
+ // Is it a numeric value?
+ if (is_numeric($aArgs[$i - 1])) {
+ $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function NPV()
+
+
+ /**
+ * XNPV
+ *
+ * Returns the net present value for a schedule of cash flows that is not necessarily periodic.
+ * To calculate the net present value for a series of cash flows that is periodic, use the NPV function.
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ */
+ public static function XNPV($rate, $values, $dates) {
+ if ((!is_array($values)) || (!is_array($dates))) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $dates = self::flattenArray($dates);
+ $valCount = count($values);
+ if ($valCount != count($dates)) return self::$_errorCodes['num'];
+
+ $xnpv = 0.0;
+ for ($i = 0; $i < $valCount; ++$i) {
+ $xnpv += $values[$i] / pow(1 + $rate, self::DATEDIF($dates[0],$dates[$i],'d') / 365);
+ }
+ return (is_finite($xnpv) ? $xnpv : self::$_errorCodes['value']);
+ } // function XNPV()
+
+
+ public static function IRR($values, $guess = 0.1) {
+ if (!is_array($values)) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $guess = self::flattenSingleValue($guess);
+
+ // create an initial range, with a root somewhere between 0 and guess
+ $x1 = 0.0;
+ $x2 = $guess;
+ $f1 = self::NPV($x1, $values);
+ $f2 = self::NPV($x2, $values);
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ if (($f1 * $f2) < 0.0) break;
+ if (abs($f1) < abs($f2)) {
+ $f1 = self::NPV($x1 += 1.6 * ($x1 - $x2), $values);
+ } else {
+ $f2 = self::NPV($x2 += 1.6 * ($x2 - $x1), $values);
+ }
+ }
+ if (($f1 * $f2) > 0.0) return self::$_errorCodes['value'];
+
+ $f = self::NPV($x1, $values);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::NPV($x_mid, $values);
+ if ($f_mid <= 0.0) $rtb = $x_mid;
+ if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) return $x_mid;
+ }
+ return self::$_errorCodes['value'];
+ } // function IRR()
+
+
+ public static function MIRR($values, $finance_rate, $reinvestment_rate) {
+ if (!is_array($values)) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $finance_rate = self::flattenSingleValue($finance_rate);
+ $reinvestment_rate = self::flattenSingleValue($reinvestment_rate);
+ $n = count($values);
+
+ $rr = 1.0 + $reinvestment_rate;
+ $fr = 1.0 + $finance_rate;
+
+ $npv_pos = $npv_neg = 0.0;
+ foreach($values as $i => $v) {
+ if ($v >= 0) {
+ $npv_pos += $v / pow($rr, $i);
+ } else {
+ $npv_neg += $v / pow($fr, $i);
+ }
+ }
+
+ if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvestment_rate <= -1)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $mirr = pow((-$npv_pos * pow($rr, $n))
+ / ($npv_neg * ($rr)), (1.0 / ($n - 1))) - 1.0;
+
+ return (is_finite($mirr) ? $mirr : self::$_errorCodes['value']);
+ } // function MIRR()
+
+
+ public static function XIRR($values, $dates, $guess = 0.1) {
+ if ((!is_array($values)) && (!is_array($dates))) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $dates = self::flattenArray($dates);
+ $guess = self::flattenSingleValue($guess);
+ if (count($values) != count($dates)) return self::$_errorCodes['num'];
+
+ // create an initial range, with a root somewhere between 0 and guess
+ $x1 = 0.0;
+ $x2 = $guess;
+ $f1 = self::XNPV($x1, $values, $dates);
+ $f2 = self::XNPV($x2, $values, $dates);
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ if (($f1 * $f2) < 0.0) break;
+ if (abs($f1) < abs($f2)) {
+ $f1 = self::XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
+ } else {
+ $f2 = self::XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
+ }
+ }
+ if (($f1 * $f2) > 0.0) return self::$_errorCodes['value'];
+
+ $f = self::XNPV($x1, $values, $dates);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::XNPV($x_mid, $values, $dates);
+ if ($f_mid <= 0.0) $rtb = $x_mid;
+ if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) return $x_mid;
+ }
+ return self::$_errorCodes['value'];
+ }
+
+
+ /**
+ * RATE
+ *
+ **/
+ public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
+ $nper = (int) self::flattenSingleValue($nper);
+ $pmt = self::flattenSingleValue($pmt);
+ $pv = self::flattenSingleValue($pv);
+ $fv = (is_null($fv)) ? 0.0 : self::flattenSingleValue($fv);
+ $type = (is_null($type)) ? 0 : (int) self::flattenSingleValue($type);
+ $guess = (is_null($guess)) ? 0.1 : self::flattenSingleValue($guess);
+
+ $rate = $guess;
+ if (abs($rate) < FINANCIAL_PRECISION) {
+ $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
+ } else {
+ $f = exp($nper * log(1 + $rate));
+ $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+ }
+ $y0 = $pv + $pmt * $nper + $fv;
+ $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+
+ // find root by secant method
+ $i = $x0 = 0.0;
+ $x1 = $rate;
+ while ((abs($y0 - $y1) > FINANCIAL_PRECISION) && ($i < FINANCIAL_MAX_ITERATIONS)) {
+ $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
+ $x0 = $x1;
+ $x1 = $rate;
+
+ if (abs($rate) < FINANCIAL_PRECISION) {
+ $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
+ } else {
+ $f = exp($nper * log(1 + $rate));
+ $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+ }
+
+ $y0 = $y1;
+ $y1 = $y;
+ ++$i;
+ }
+ return $rate;
+ } // function RATE()
+
+
+ /**
+ * DB
+ *
+ * Returns the depreciation of an asset for a specified period using the fixed-declining balance method.
+ * This form of depreciation is used if you want to get a higher depreciation value at the beginning of the depreciation
+ * (as opposed to linear depreciation). The depreciation value is reduced with every depreciation period by the
+ * depreciation already deducted from the initial cost.
+ *
+ * @param float cost Initial cost of the asset.
+ * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
+ * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
+ * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
+ * @param float month Number of months in the first year. If month is omitted, it defaults to 12.
+ * @return float
+ */
+ public static function DB($cost, $salvage, $life, $period, $month=12) {
+ $cost = (float) self::flattenSingleValue($cost);
+ $salvage = (float) self::flattenSingleValue($salvage);
+ $life = (int) self::flattenSingleValue($life);
+ $period = (int) self::flattenSingleValue($period);
+ $month = (int) self::flattenSingleValue($month);
+
+ // Validate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
+ if ($cost == 0) {
+ return 0.0;
+ } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
+ return self::$_errorCodes['num'];
+ }
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ $previousDepreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ if ($per == 1) {
+ $depreciation = $cost * $fixedDepreciationRate * $month / 12;
+ } elseif ($per == ($life + 1)) {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
+ } else {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
+ }
+ $previousDepreciation += $depreciation;
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $depreciation = round($depreciation,2);
+ }
+ return $depreciation;
+ }
+ return self::$_errorCodes['value'];
+ } // function DB()
+
+
+ /**
+ * DDB
+ *
+ * Returns the depreciation of an asset for a specified period using the double-declining balance method or some other method you specify.
+ *
+ * @param float cost Initial cost of the asset.
+ * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
+ * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
+ * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
+ * @param float factor The rate at which the balance declines.
+ * If factor is omitted, it is assumed to be 2 (the double-declining balance method).
+ * @return float
+ */
+ public static function DDB($cost, $salvage, $life, $period, $factor=2.0) {
+ $cost = (float) self::flattenSingleValue($cost);
+ $salvage = (float) self::flattenSingleValue($salvage);
+ $life = (int) self::flattenSingleValue($life);
+ $period = (int) self::flattenSingleValue($period);
+ $factor = (float) self::flattenSingleValue($factor);
+
+ // Validate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
+ if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
+ return self::$_errorCodes['num'];
+ }
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ $previousDepreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ $depreciation = min( ($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation) );
+ $previousDepreciation += $depreciation;
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $depreciation = round($depreciation,2);
+ }
+ return $depreciation;
+ }
+ return self::$_errorCodes['value'];
+ } // function DDB()
+
+
+ private static function _daysPerYear($year,$basis) {
+ switch ($basis) {
+ case 0 :
+ case 2 :
+ case 4 :
+ $daysPerYear = 360;
+ break;
+ case 3 :
+ $daysPerYear = 365;
+ break;
+ case 1 :
+ if (self::_isLeapYear($year)) {
+ $daysPerYear = 366;
+ } else {
+ $daysPerYear = 365;
+ }
+ break;
+ default :
+ return self::$_errorCodes['num'];
+ }
+ return $daysPerYear;
+ } // function _daysPerYear()
+
+
+ /**
+ * ACCRINT
+ *
+ * Returns the discount rate for a security.
+ *
+ * @param mixed issue The security's issue date.
+ * @param mixed firstinter The security's first interest date.
+ * @param mixed settlement The security's settlement date.
+ * @param float rate The security's annual coupon rate.
+ * @param float par The security's par value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function ACCRINT($issue, $firstinter, $settlement, $rate, $par=1000, $frequency=1, $basis=0) {
+ $issue = self::flattenSingleValue($issue);
+ $firstinter = self::flattenSingleValue($firstinter);
+ $settlement = self::flattenSingleValue($settlement);
+ $rate = (float) self::flattenSingleValue($rate);
+ $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
+ $frequency = (is_null($frequency)) ? 1 : (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($rate)) && (is_numeric($par))) {
+ if (($rate <= 0) || ($par <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+
+ return $par * $rate * $daysBetweenIssueAndSettlement;
+ }
+ return self::$_errorCodes['value'];
+ } // function ACCRINT()
+
+
+ /**
+ * ACCRINTM
+ *
+ * Returns the discount rate for a security.
+ *
+ * @param mixed issue The security's issue date.
+ * @param mixed settlement The security's settlement date.
+ * @param float rate The security's annual coupon rate.
+ * @param float par The security's par value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function ACCRINTM($issue, $settlement, $rate, $par=1000, $basis=0) {
+ $issue = self::flattenSingleValue($issue);
+ $settlement = self::flattenSingleValue($settlement);
+ $rate = (float) self::flattenSingleValue($rate);
+ $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($rate)) && (is_numeric($par))) {
+ if (($rate <= 0) || ($par <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ return $par * $rate * $daysBetweenIssueAndSettlement;
+ }
+ return self::$_errorCodes['value'];
+ } // function ACCRINTM()
+
+
+ public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
+ $cost = self::flattenSingleValue($cost);
+ $purchased = self::flattenSingleValue($purchased);
+ $firstPeriod = self::flattenSingleValue($firstPeriod);
+ $salvage = self::flattenSingleValue($salvage);
+ $period = floor(self::flattenSingleValue($period));
+ $rate = self::flattenSingleValue($rate);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ $fUsePer = 1.0 / $rate;
+
+ if ($fUsePer < 3.0) {
+ $amortiseCoeff = 1.0;
+ } elseif ($fUsePer < 5.0) {
+ $amortiseCoeff = 1.5;
+ } elseif ($fUsePer <= 6.0) {
+ $amortiseCoeff = 2.0;
+ } else {
+ $amortiseCoeff = 2.5;
+ }
+
+ $rate *= $amortiseCoeff;
+// $fNRate = floor((self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost) + 0.5);
+ $fNRate = round(self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost,0);
+ $cost -= $fNRate;
+ $fRest = $cost - $salvage;
+
+ for ($n = 0; $n < $period; ++$n) {
+// $fNRate = floor(($rate * $cost) + 0.5);
+ $fNRate = round($rate * $cost,0);
+ $fRest -= $fNRate;
+
+ if ($fRest < 0.0) {
+ switch ($period - $n) {
+ case 0 :
+ case 1 :
+// return floor(($cost * 0.5) + 0.5);
+ return round($cost * 0.5,0);
+ break;
+ default : return 0.0;
+ break;
+ }
+ }
+ $cost -= $fNRate;
+ }
+ return $fNRate;
+ } // function AMORDEGRC()
+
+
+ public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
+ $cost = self::flattenSingleValue($cost);
+ $purchased = self::flattenSingleValue($purchased);
+ $firstPeriod = self::flattenSingleValue($firstPeriod);
+ $salvage = self::flattenSingleValue($salvage);
+ $period = self::flattenSingleValue($period);
+ $rate = self::flattenSingleValue($rate);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ $fOneRate = $cost * $rate;
+ $fCostDelta = $cost - $salvage;
+ // Note, quirky variation for leap years on the YEARFRAC for this function
+ $purchasedYear = self::YEAR($purchased);
+ $yearFrac = self::YEARFRAC($purchased, $firstPeriod, $basis);
+ if (($basis == 1) && ($yearFrac < 1) && (self::_isLeapYear($purchasedYear))) {
+ $yearFrac *= 365 / 366;
+ }
+ $f0Rate = $yearFrac * $rate * $cost;
+ $nNumOfFullPeriods = intval(($cost - $salvage - $f0Rate) / $fOneRate);
+
+ if ($period == 0) {
+ return $f0Rate;
+ } elseif ($period <= $nNumOfFullPeriods) {
+ return $fOneRate;
+ } elseif ($period == ($nNumOfFullPeriods + 1)) {
+ return ($fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate);
+ } else {
+ return 0.0;
+ }
+ } // function AMORLINC()
+
+
+ private static function _lastDayOfMonth($testDate) {
+ $date = clone $testDate;
+ $date->modify('+1 day');
+ return ($date->format('d') == 1);
+ } // function _lastDayOfMonth()
+
+ private static function _firstDayOfMonth($testDate) {
+ $date = clone $testDate;
+ return ($date->format('d') == 1);
+ } // function _lastDayOfMonth()
+
+ private static function _coupFirstPeriodDate($settlement, $maturity, $frequency, $next) {
+ $months = 12 / $frequency;
+
+ $result = PHPExcel_Shared_Date::ExcelToPHPObject($maturity);
+ $eom = self::_lastDayOfMonth($result);
+
+ while ($settlement < PHPExcel_Shared_Date::PHPToExcel($result)) {
+ $result->modify('-'.$months.' months');
+ }
+ if ($next) {
+ $result->modify('+'.$months.' months');
+ }
+
+ if ($eom) {
+ $result->modify('-1 day');
+ }
+
+ return PHPExcel_Shared_Date::PHPToExcel($result);
+ } // function _coupFirstPeriodDate()
+
+
+ private static function _validFrequency($frequency) {
+ if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
+ return true;
+ }
+ if ((self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) &&
+ (($frequency == 6) || ($frequency == 12))) {
+ return true;
+ }
+ return false;
+ } // function _validFrequency()
+
+ public static function COUPDAYS($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ switch ($basis) {
+ case 3: // Actual/365
+ return 365 / $frequency;
+ case 1: // Actual/actual
+ if ($frequency == 1) {
+ $daysPerYear = self::_daysPerYear(self::YEAR($maturity),$basis);
+ return ($daysPerYear / $frequency);
+ } else {
+ $prev = self::_coupFirstPeriodDate($settlement, $maturity, $frequency, False);
+ $next = self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);
+ return ($next - $prev);
+ }
+ default: // US (NASD) 30/360, Actual/360 or European 30/360
+ return 360 / $frequency;
+ }
+ return self::$_errorCodes['value'];
+ } // function COUPDAYS()
+
+
+ public static function COUPDAYBS($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ $prev = self::_coupFirstPeriodDate($settlement, $maturity, $frequency, False);
+
+ return self::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
+ } // function COUPDAYBS()
+
+
+ public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ $next = self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);
+
+ return self::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
+ } // function COUPDAYSNC()
+
+
+ public static function COUPNCD($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ return self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);
+ } // function COUPNCD()
+
+
+ public static function COUPPCD($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ return self::_coupFirstPeriodDate($settlement, $maturity, $frequency, False);
+ } // function COUPPCD()
+
+
+ public static function COUPNUM($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ $settlement = self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis) * 365;
+
+ switch ($frequency) {
+ case 1: // annual payments
+ return ceil($daysBetweenSettlementAndMaturity / 360);
+ case 2: // half-yearly
+ return ceil($daysBetweenSettlementAndMaturity / 180);
+ case 4: // quarterly
+ return ceil($daysBetweenSettlementAndMaturity / 90);
+ case 6: // bimonthly
+ return ceil($daysBetweenSettlementAndMaturity / 60);
+ case 12: // monthly
+ return ceil($daysBetweenSettlementAndMaturity / 30);
+ }
+ return self::$_errorCodes['value'];
+ } // function COUPNUM()
+
+
+ public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $rate = (float) self::flattenSingleValue($rate);
+ $yield = (float) self::flattenSingleValue($yield);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $frequency = (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ if (is_string($settlement = self::_getDateValue($settlement))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::_validFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return self::$_errorCodes['num'];
+ }
+
+ $dsc = self::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
+ $e = self::COUPDAYS($settlement, $maturity, $frequency, $basis);
+ $n = self::COUPNUM($settlement, $maturity, $frequency, $basis);
+ $a = self::COUPDAYBS($settlement, $maturity, $frequency, $basis);
+
+ $baseYF = 1.0 + ($yield / $frequency);
+ $rfp = 100 * ($rate / $frequency);
+ $de = $dsc / $e;
+
+ $result = $redemption / pow($baseYF, (--$n + $de));
+ for($k = 0; $k <= $n; ++$k) {
+ $result += $rfp / (pow($baseYF, ($k + $de)));
+ }
+ $result -= $rfp * ($a / $e);
+
+ return $result;
+ } // function PRICE()
+
+
+ /**
+ * DISC
+ *
+ * Returns the discount rate for a security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int price The security's price per $100 face value.
+ * @param int redemption the security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function DISC($settlement, $maturity, $price, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $price = (float) self::flattenSingleValue($price);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($price <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return ((1 - $price / $redemption) / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function DISC()
+
+
+ /**
+ * PRICEDISC
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int discount The security's discount rate.
+ * @param int redemption The security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $discount = (float) self::flattenSingleValue($discount);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($discount <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function PRICEDISC()
+
+
+ /**
+ * PRICEMAT
+ *
+ * Returns the price per $100 face value of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed issue The security's issue date.
+ * @param int rate The security's interest rate at date of issue.
+ * @param int yield The security's annual yield.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $issue = self::flattenSingleValue($issue);
+ $rate = self::flattenSingleValue($rate);
+ $yield = self::flattenSingleValue($yield);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($rate) && is_numeric($yield)) {
+ if (($rate <= 0) || ($yield <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
+ (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
+ (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100));
+ }
+ return self::$_errorCodes['value'];
+ } // function PRICEMAT()
+
+
+ /**
+ * RECEIVED
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int investment The amount invested in the security.
+ * @param int discount The security's discount rate.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $investment = (float) self::flattenSingleValue($investment);
+ $discount = (float) self::flattenSingleValue($discount);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
+ if (($investment <= 0) || ($discount <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $investment / ( 1 - ($discount * $daysBetweenSettlementAndMaturity));
+ }
+ return self::$_errorCodes['value'];
+ } // function RECEIVED()
+
+
+ /**
+ * INTRATE
+ *
+ * Returns the interest rate for a fully invested security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int investment The amount invested in the security.
+ * @param int redemption The amount to be received at maturity.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $investment = (float) self::flattenSingleValue($investment);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($investment <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function INTRATE()
+
+
+ /**
+ * TBILLEQ
+ *
+ * Returns the bond-equivalent yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int discount The Treasury bill's discount rate.
+ * @return float
+ */
+ public static function TBILLEQ($settlement, $maturity, $discount) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $discount = self::flattenSingleValue($discount);
+
+ // Use TBILLPRICE for validation
+ $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
+ if (is_string($testValue)) {
+ return $testValue;
+ }
+
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
+ } else {
+ $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
+ }
+
+ return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ } // function TBILLEQ()
+
+
+ /**
+ * TBILLPRICE
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int discount The Treasury bill's discount rate.
+ * @return float
+ */
+ public static function TBILLPRICE($settlement, $maturity, $discount) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $discount = self::flattenSingleValue($discount);
+
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Validate
+ if (is_numeric($discount)) {
+ if ($discount <= 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ } else {
+ $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
+ }
+
+ if ($daysBetweenSettlementAndMaturity > 360) {
+ return self::$_errorCodes['num'];
+ }
+
+ $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
+ if ($price <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ return $price;
+ }
+ return self::$_errorCodes['value'];
+ } // function TBILLPRICE()
+
+
+ /**
+ * TBILLYIELD
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int price The Treasury bill's price per $100 face value.
+ * @return float
+ */
+ public static function TBILLYIELD($settlement, $maturity, $price) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $price = self::flattenSingleValue($price);
+
+ // Validate
+ if (is_numeric($price)) {
+ if ($price <= 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ } else {
+ $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
+ }
+
+ if ($daysBetweenSettlementAndMaturity > 360) {
+ return self::$_errorCodes['num'];
+ }
+
+ return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function TBILLYIELD()
+
+
+ /**
+ * SLN
+ *
+ * Returns the straight-line depreciation of an asset for one period
+ *
+ * @param cost Initial cost of the asset
+ * @param salvage Value at the end of the depreciation
+ * @param life Number of periods over which the asset is depreciated
+ * @return float
+ */
+ public static function SLN($cost, $salvage, $life) {
+ $cost = self::flattenSingleValue($cost);
+ $salvage = self::flattenSingleValue($salvage);
+ $life = self::flattenSingleValue($life);
+
+ // Calculate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
+ if ($life < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return ($cost - $salvage) / $life;
+ }
+ return self::$_errorCodes['value'];
+ } // function SLN()
+
+
+ /**
+ * YIELDMAT
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed issue The security's issue date.
+ * @param int rate The security's interest rate at date of issue.
+ * @param int price The security's price per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $issue = self::flattenSingleValue($issue);
+ $rate = self::flattenSingleValue($rate);
+ $price = self::flattenSingleValue($price);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($rate) && is_numeric($price)) {
+ if (($rate <= 0) || ($price <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
+ ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function YIELDMAT()
+
+
+ /**
+ * YIELDDISC
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int price The security's price per $100 face value.
+ * @param int redemption The security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $price = self::flattenSingleValue($price);
+ $redemption = self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($price) && is_numeric($redemption)) {
+ if (($price <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity,$basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function YIELDDISC()
+
+
+ /**
+ * CELL_ADDRESS
+ *
+ * Creates a cell address as text, given specified row and column numbers.
+ *
+ * @param row Row number to use in the cell reference
+ * @param column Column number to use in the cell reference
+ * @param relativity Flag indicating the type of reference to return
+ * 1 or omitted Absolute
+ * 2 Absolute row; relative column
+ * 3 Relative row; absolute column
+ * 4 Relative
+ * @param referenceStyle A logical value that specifies the A1 or R1C1 reference style.
+ * TRUE or omitted CELL_ADDRESS returns an A1-style reference
+ * FALSE CELL_ADDRESS returns an R1C1-style reference
+ * @param sheetText Optional Name of worksheet to use
+ * @return string
+ */
+ public static function CELL_ADDRESS($row, $column, $relativity=1, $referenceStyle=True, $sheetText='') {
+ $row = self::flattenSingleValue($row);
+ $column = self::flattenSingleValue($column);
+ $relativity = self::flattenSingleValue($relativity);
+ $sheetText = self::flattenSingleValue($sheetText);
+
+ if (($row < 1) || ($column < 1)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if ($sheetText > '') {
+ if (strpos($sheetText,' ') !== False) { $sheetText = "'".$sheetText."'"; }
+ $sheetText .='!';
+ }
+ if ((!is_bool($referenceStyle)) || $referenceStyle) {
+ $rowRelative = $columnRelative = '$';
+ $column = PHPExcel_Cell::stringFromColumnIndex($column-1);
+ if (($relativity == 2) || ($relativity == 4)) { $columnRelative = ''; }
+ if (($relativity == 3) || ($relativity == 4)) { $rowRelative = ''; }
+ return $sheetText.$columnRelative.$column.$rowRelative.$row;
+ } else {
+ if (($relativity == 2) || ($relativity == 4)) { $column = '['.$column.']'; }
+ if (($relativity == 3) || ($relativity == 4)) { $row = '['.$row.']'; }
+ return $sheetText.'R'.$row.'C'.$column;
+ }
+ } // function CELL_ADDRESS()
+
+
+ /**
+ * COLUMN
+ *
+ * Returns the column number of the given cell reference
+ * If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.
+ * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
+ * reference of the cell in which the COLUMN function appears; otherwise this function returns 0.
+ *
+ * @param cellAddress A reference to a range of cells for which you want the column numbers
+ * @return integer or array of integer
+ */
+ public static function COLUMN($cellAddress=Null) {
+ if (is_null($cellAddress) || trim($cellAddress) === '') { return 0; }
+
+ if (is_array($cellAddress)) {
+ foreach($cellAddress as $columnKey => $value) {
+ $columnKey = preg_replace('/[^a-z]/i','',$columnKey);
+ return (integer) PHPExcel_Cell::columnIndexFromString($columnKey);
+ }
+ } else {
+ if (strpos($cellAddress,'!') !== false) {
+ list($sheet,$cellAddress) = explode('!',$cellAddress);
+ }
+ if (strpos($cellAddress,':') !== false) {
+ list($startAddress,$endAddress) = explode(':',$cellAddress);
+ $startAddress = preg_replace('/[^a-z]/i','',$startAddress);
+ $endAddress = preg_replace('/[^a-z]/i','',$endAddress);
+ $returnValue = array();
+ do {
+ $returnValue[] = (integer) PHPExcel_Cell::columnIndexFromString($startAddress);
+ } while ($startAddress++ != $endAddress);
+ return $returnValue;
+ } else {
+ $cellAddress = preg_replace('/[^a-z]/i','',$cellAddress);
+ return (integer) PHPExcel_Cell::columnIndexFromString($cellAddress);
+ }
+ }
+ } // function COLUMN()
+
+
+ /**
+ * COLUMNS
+ *
+ * Returns the number of columns in an array or reference.
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
+ * @return integer
+ */
+ public static function COLUMNS($cellAddress=Null) {
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return 1;
+ } elseif (!is_array($cellAddress)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $x = array_keys($cellAddress);
+ $x = array_shift($x);
+ $isMatrix = (is_numeric($x));
+ list($columns,$rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $rows;
+ } else {
+ return $columns;
+ }
+ } // function COLUMNS()
+
+
+ /**
+ * ROW
+ *
+ * Returns the row number of the given cell reference
+ * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.
+ * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
+ * reference of the cell in which the ROW function appears; otherwise this function returns 0.
+ *
+ * @param cellAddress A reference to a range of cells for which you want the row numbers
+ * @return integer or array of integer
+ */
+ public static function ROW($cellAddress=Null) {
+ if (is_null($cellAddress) || trim($cellAddress) === '') { return 0; }
+
+ if (is_array($cellAddress)) {
+ foreach($cellAddress as $columnKey => $rowValue) {
+ foreach($rowValue as $rowKey => $cellValue) {
+ return (integer) preg_replace('/[^0-9]/i','',$rowKey);
+ }
+ }
+ } else {
+ if (strpos($cellAddress,'!') !== false) {
+ list($sheet,$cellAddress) = explode('!',$cellAddress);
+ }
+ if (strpos($cellAddress,':') !== false) {
+ list($startAddress,$endAddress) = explode(':',$cellAddress);
+ $startAddress = preg_replace('/[^0-9]/','',$startAddress);
+ $endAddress = preg_replace('/[^0-9]/','',$endAddress);
+ $returnValue = array();
+ do {
+ $returnValue[][] = (integer) $startAddress;
+ } while ($startAddress++ != $endAddress);
+ return $returnValue;
+ } else {
+ list($cellAddress) = explode(':',$cellAddress);
+ return (integer) preg_replace('/[^0-9]/','',$cellAddress);
+ }
+ }
+ } // function ROW()
+
+
+ /**
+ * ROWS
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @return integer
+ */
+ public static function ROWS($cellAddress=Null) {
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return 1;
+ } elseif (!is_array($cellAddress)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $i = array_keys($cellAddress);
+ $isMatrix = (is_numeric(array_shift($i)));
+ list($columns,$rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $columns;
+ } else {
+ return $rows;
+ }
+ } // function ROWS()
+
+
+ /**
+ * INDIRECT
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @return integer
+ */
+ public static function INDIRECT($cellAddress=Null, PHPExcel_Cell $pCell = null) {
+ $cellAddress = self::flattenSingleValue($cellAddress);
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return self::REF();
+ }
+
+ $cellAddress1 = $cellAddress;
+ $cellAddress2 = NULL;
+ if (strpos($cellAddress,':') !== false) {
+ list($cellAddress1,$cellAddress2) = explode(':',$cellAddress);
+ }
+
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress1, $matches)) ||
+ ((!is_null($cellAddress2)) && (!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress2, $matches)))) {
+ return self::REF();
+ }
+
+ if (strpos($cellAddress,'!') !== false) {
+ list($sheetName,$cellAddress) = explode('!',$cellAddress);
+ $pSheet = $pCell->getParent()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getParent();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, False);
+ } // function INDIRECT()
+
+
+ /**
+ * OFFSET
+ *
+ * Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
+ * The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and
+ * the number of columns to be returned.
+ *
+ * @param cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
+ * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
+ * @param rows The number of rows, up or down, that you want the upper-left cell to refer to.
+ * Using 5 as the rows argument specifies that the upper-left cell in the reference is
+ * five rows below reference. Rows can be positive (which means below the starting reference)
+ * or negative (which means above the starting reference).
+ * @param cols The number of columns, to the left or right, that you want the upper-left cell of the result
+ * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
+ * reference is five columns to the right of reference. Cols can be positive (which means
+ * to the right of the starting reference) or negative (which means to the left of the
+ * starting reference).
+ * @param height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
+ * @param width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
+ * @return string A reference to a cell or range of cells
+ */
+ public static function OFFSET($cellAddress=Null,$rows=0,$columns=0,$height=null,$width=null) {
+ $rows = self::flattenSingleValue($rows);
+ $columns = self::flattenSingleValue($columns);
+ $height = self::flattenSingleValue($height);
+ $width = self::flattenSingleValue($width);
+ if ($cellAddress == Null) {
+ return 0;
+ }
+
+ $args = func_get_args();
+ $pCell = array_pop($args);
+ if (!is_object($pCell)) {
+ return self::$_errorCodes['reference'];
+ }
+
+ $sheetName = null;
+ if (strpos($cellAddress,"!")) {
+ list($sheetName,$cellAddress) = explode("!",$cellAddress);
+ }
+ if (strpos($cellAddress,":")) {
+ list($startCell,$endCell) = explode(":",$cellAddress);
+ } else {
+ $startCell = $endCell = $cellAddress;
+ }
+ list($startCellColumn,$startCellRow) = PHPExcel_Cell::coordinateFromString($startCell);
+ list($endCellColumn,$endCellRow) = PHPExcel_Cell::coordinateFromString($endCell);
+
+ $startCellRow += $rows;
+ $startCellColumn = PHPExcel_Cell::columnIndexFromString($startCellColumn) - 1;
+ $startCellColumn += $columns;
+
+ if (($startCellRow <= 0) || ($startCellColumn < 0)) {
+ return self::$_errorCodes['reference'];
+ }
+ $endCellColumn = PHPExcel_Cell::columnIndexFromString($endCellColumn) - 1;
+ if (($width != null) && (!is_object($width))) {
+ $endCellColumn = $startCellColumn + $width - 1;
+ } else {
+ $endCellColumn += $columns;
+ }
+ $startCellColumn = PHPExcel_Cell::stringFromColumnIndex($startCellColumn);
+
+ if (($height != null) && (!is_object($height))) {
+ $endCellRow = $startCellRow + $height - 1;
+ } else {
+ $endCellRow += $rows;
+ }
+
+ if (($endCellRow <= 0) || ($endCellColumn < 0)) {
+ return self::$_errorCodes['reference'];
+ }
+ $endCellColumn = PHPExcel_Cell::stringFromColumnIndex($endCellColumn);
+
+ $cellAddress = $startCellColumn.$startCellRow;
+ if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
+ $cellAddress .= ':'.$endCellColumn.$endCellRow;
+ }
+
+ if ($sheetName !== null) {
+ $pSheet = $pCell->getParent()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getParent();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, False);
+ } // function OFFSET()
+
+
+ public static function CHOOSE() {
+ $chooseArgs = func_get_args();
+ $chosenEntry = self::flattenArray(array_shift($chooseArgs));
+ $entryCount = count($chooseArgs) - 1;
+
+ if(is_array($chosenEntry)) {
+ $chosenEntry = array_shift($chosenEntry);
+ }
+ if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
+ --$chosenEntry;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $chosenEntry = floor($chosenEntry);
+ if (($chosenEntry <= 0) || ($chosenEntry > $entryCount)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_array($chooseArgs[$chosenEntry])) {
+ return self::flattenArray($chooseArgs[$chosenEntry]);
+ } else {
+ return $chooseArgs[$chosenEntry];
+ }
+ } // function CHOOSE()
+
+
+ /**
+ * MATCH
+ *
+ * The MATCH function searches for a specified item in a range of cells
+ *
+ * @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.
+ * @return integer The relative position of the found item
+ */
+ public static function MATCH($lookup_value, $lookup_array, $match_type=1) {
+ $lookup_array = self::flattenArray($lookup_array);
+ $lookup_value = self::flattenSingleValue($lookup_value);
+ $match_type = (is_null($match_type)) ? 1 : (int) self::flattenSingleValue($match_type);
+ // MATCH is not case sensitive
+ $lookup_value = strtolower($lookup_value);
+
+ // lookup_value type has to be number, text, or logical values
+ if ((!is_numeric($lookup_value)) && (!is_string($lookup_value)) && (!is_bool($lookup_value))) {
+ return self::$_errorCodes['na'];
+ }
+
+ // match_type is 0, 1 or -1
+ if (($match_type !== 0) && ($match_type !== -1) && ($match_type !== 1)) {
+ return self::$_errorCodes['na'];
+ }
+
+ // lookup_array should not be empty
+ $lookupArraySize = count($lookup_array);
+ if ($lookupArraySize <= 0) {
+ return self::$_errorCodes['na'];
+ }
+
+ // lookup_array should contain only number, text, or logical values, or empty (null) cells
+ foreach($lookup_array as $i => $lookupArrayValue) {
+ // check the type of the value
+ if ((!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) &&
+ (!is_bool($lookupArrayValue)) && (!is_null($lookupArrayValue))) {
+ return self::$_errorCodes['na'];
+ }
+ // convert strings to lowercase for case-insensitive testing
+ if (is_string($lookupArrayValue)) {
+ $lookup_array[$i] = strtolower($lookupArrayValue);
+ }
+ if ((is_null($lookupArrayValue)) && (($match_type == 1) || ($match_type == -1))) {
+ $lookup_array = array_slice($lookup_array,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);
+ }
+
+ // **
+ // find the match
+ // **
+ // loop on the cells
+// var_dump($lookup_array);
+// echo '
';
+ foreach($lookup_array as $i => $lookupArrayValue) {
+ if (($match_type == 0) && ($lookupArrayValue == $lookup_value)) {
+ // exact match
+ return ++$i;
+ } elseif (($match_type == -1) && ($lookupArrayValue <= $lookup_value)) {
+// echo '$i = '.$i.' => ';
+// var_dump($lookupArrayValue);
+// echo '
';
+// echo 'Keyset = ';
+// var_dump($keySet);
+// echo '
';
+ $i = array_search($i,$keySet);
+// echo '$i='.$i.'
';
+ // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value
+ if ($i < 1){
+ // 1st cell was allready smaller than the lookup_value
+ break;
+ } else {
+ // the previous cell was the match
+ return $keySet[$i-1]+1;
+ }
+ } elseif (($match_type == 1) && ($lookupArrayValue >= $lookup_value)) {
+// echo '$i = '.$i.' => ';
+// var_dump($lookupArrayValue);
+// echo '
';
+// echo 'Keyset = ';
+// var_dump($keySet);
+// echo '
';
+ $i = array_search($i,$keySet);
+// echo '$i='.$i.'
';
+ // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value
+ if ($i < 1){
+ // 1st cell was allready bigger than the lookup_value
+ break;
+ } else {
+ // the previous cell was the match
+ return $keySet[$i-1]+1;
+ }
+ }
+ }
+
+ // unsuccessful in finding a match, return #N/A error value
+ return self::$_errorCodes['na'];
+ } // function MATCH()
+
+
+ /**
+ * INDEX
+ *
+ * Uses an index to choose a value from a reference or array
+ * implemented: Return the value of a specified cell or array of cells Array form
+ * not implemented: Return a reference to specified cells Reference form
+ *
+ * @param range_array a range of cells or an array constant
+ * @param row_num selects the row in array from which to return a value. If row_num is omitted, column_num is required.
+ * @param column_num selects the column in array from which to return a value. If column_num is omitted, row_num is required.
+ */
+ public static function INDEX($arrayValues,$rowNum = 0,$columnNum = 0) {
+
+ if (($rowNum < 0) || ($columnNum < 0)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (!is_array($arrayValues)) {
+ return self::$_errorCodes['reference'];
+ }
+
+ $rowKeys = array_keys($arrayValues);
+ $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
+
+ if ($columnNum > count($columnKeys)) {
+ return self::$_errorCodes['value'];
+ } elseif ($columnNum == 0) {
+ if ($rowNum == 0) {
+ return $arrayValues;
+ }
+ $rowNum = $rowKeys[--$rowNum];
+ $returnArray = array();
+ foreach($arrayValues as $arrayColumn) {
+ if (is_array($arrayColumn)) {
+ if (isset($arrayColumn[$rowNum])) {
+ $returnArray[] = $arrayColumn[$rowNum];
+ } else {
+ return $arrayValues[$rowNum];
+ }
+ } else {
+ return $arrayValues[$rowNum];
+ }
+ }
+ return $returnArray;
+ }
+ $columnNum = $columnKeys[--$columnNum];
+ if ($rowNum > count($rowKeys)) {
+ return self::$_errorCodes['value'];
+ } elseif ($rowNum == 0) {
+ return $arrayValues[$columnNum];
+ }
+ $rowNum = $rowKeys[--$rowNum];
+
+ return $arrayValues[$rowNum][$columnNum];
+ } // function INDEX()
+
+
+ /**
+ * N
+ *
+ * Returns a value converted to a number
+ *
+ * @param value The value you want converted
+ * @return number N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number That number
+ * A date The serial number of that date
+ * TRUE 1
+ * FALSE 0
+ * An error value The error value
+ * Anything else 0
+ */
+ public static function N($value) {
+ while (is_array($value)) {
+ $value = array_shift($value);
+ }
+
+ switch (gettype($value)) {
+ case 'double' :
+ case 'float' :
+ case 'integer' :
+ return $value;
+ break;
+ case 'boolean' :
+ return (integer) $value;
+ break;
+ case 'string' :
+ // Errors
+ if ((strlen($value) > 0) && ($value{0} == '#')) {
+ return $value;
+ }
+ break;
+ }
+ return 0;
+ } // function N()
+
+
+ /**
+ * TYPE
+ *
+ * Returns a number that identifies the type of a value
+ *
+ * @param value The value you want tested
+ * @return number N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number 1
+ * Text 2
+ * Logical Value 4
+ * An error value 16
+ * Array or Matrix 64
+ */
+ public static function TYPE($value) {
+ $value = self::flattenArrayIndexed($value);
+ if (is_array($value) && (count($value) > 1)) {
+ $a = array_keys($value);
+ $a = array_pop($a);
+ // Range of cells is an error
+ if (self::isCellValue($a)) {
+ return 16;
+ // Test for Matrix
+ } elseif (self::isMatrixValue($a)) {
+ return 64;
+ }
+ } elseif(count($value) == 0) {
+ // Empty Cell
+ return 1;
+ }
+ $value = self::flattenSingleValue($value);
+
+ if ((is_float($value)) || (is_int($value))) {
+ return 1;
+ } elseif(is_bool($value)) {
+ return 4;
+ } elseif(is_array($value)) {
+ return 64;
+ break;
+ } elseif(is_string($value)) {
+ // Errors
+ if ((strlen($value) > 0) && ($value{0} == '#')) {
+ return 16;
+ }
+ return 2;
+ }
+ return 0;
+ } // function TYPE()
+
+
+ /**
+ * SYD
+ *
+ * Returns the sum-of-years' digits depreciation of an asset for a specified period.
+ *
+ * @param cost Initial cost of the asset
+ * @param salvage Value at the end of the depreciation
+ * @param life Number of periods over which the asset is depreciated
+ * @param period Period
+ * @return float
+ */
+ public static function SYD($cost, $salvage, $life, $period) {
+ $cost = self::flattenSingleValue($cost);
+ $salvage = self::flattenSingleValue($salvage);
+ $life = self::flattenSingleValue($life);
+ $period = self::flattenSingleValue($period);
+
+ // Calculate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
+ if (($life < 1) || ($period > $life)) {
+ return self::$_errorCodes['num'];
+ }
+ return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
+ }
+ return self::$_errorCodes['value'];
+ } // function SYD()
+
+
+ /**
+ * TRANSPOSE
+ *
+ * @param array $matrixData A matrix of values
+ * @return array
+ *
+ * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.
+ */
+ public static function TRANSPOSE($matrixData) {
+ $returnMatrix = array();
+ if (!is_array($matrixData)) { $matrixData = array(array($matrixData)); }
+
+ $column = 0;
+ foreach($matrixData as $matrixRow) {
+ $row = 0;
+ foreach($matrixRow as $matrixCell) {
+ $returnMatrix[$row][$column] = $matrixCell;
+ ++$row;
+ }
+ ++$column;
+ }
+ return $returnMatrix;
+ } // function TRANSPOSE()
+
+
+ /**
+ * MMULT
+ *
+ * @param array $matrixData1 A matrix of values
+ * @param array $matrixData2 A matrix of values
+ * @return array
+ */
+ public static function MMULT($matrixData1,$matrixData2) {
+ $matrixAData = $matrixBData = array();
+ if (!is_array($matrixData1)) { $matrixData1 = array(array($matrixData1)); }
+ if (!is_array($matrixData2)) { $matrixData2 = array(array($matrixData2)); }
+
+ $rowA = 0;
+ foreach($matrixData1 as $matrixRow) {
+ $columnA = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixAData[$rowA][$columnA] = $matrixCell;
+ ++$columnA;
+ }
+ ++$rowA;
+ }
+ try {
+ $matrixA = new Matrix($matrixAData);
+ $rowB = 0;
+ foreach($matrixData2 as $matrixRow) {
+ $columnB = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixBData[$rowB][$columnB] = $matrixCell;
+ ++$columnB;
+ }
+ ++$rowB;
+ }
+ $matrixB = new Matrix($matrixBData);
+
+ if (($rowA != $columnB) || ($rowB != $columnA)) {
+ return self::$_errorCodes['value'];
+ }
+
+ return $matrixA->times($matrixB)->getArray();
+ } catch (Exception $ex) {
+ return self::$_errorCodes['value'];
+ }
+ } // function MMULT()
+
+
+ /**
+ * MINVERSE
+ *
+ * @param array $matrixValues A matrix of values
+ * @return array
+ */
+ public static function MINVERSE($matrixValues) {
+ $matrixData = array();
+ if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
+
+ $row = $maxColumn = 0;
+ foreach($matrixValues as $matrixRow) {
+ $column = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixData[$column][$row] = $matrixCell;
+ ++$column;
+ }
+ if ($column > $maxColumn) { $maxColumn = $column; }
+ ++$row;
+ }
+ if ($row != $maxColumn) { return self::$_errorCodes['value']; }
+
+ try {
+ $matrix = new Matrix($matrixData);
+ return $matrix->inverse()->getArray();
+ } catch (Exception $ex) {
+ return self::$_errorCodes['value'];
+ }
+ } // function MINVERSE()
+
+
+ /**
+ * MDETERM
+ *
+ * @param array $matrixValues A matrix of values
+ * @return float
+ */
+ public static function MDETERM($matrixValues) {
+ $matrixData = array();
+ if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
+
+ $row = $maxColumn = 0;
+ foreach($matrixValues as $matrixRow) {
+ $column = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixData[$column][$row] = $matrixCell;
+ ++$column;
+ }
+ if ($column > $maxColumn) { $maxColumn = $column; }
+ ++$row;
+ }
+ if ($row != $maxColumn) { return self::$_errorCodes['value']; }
+
+ try {
+ $matrix = new Matrix($matrixData);
+ return $matrix->det();
+ } catch (Exception $ex) {
+ return self::$_errorCodes['value'];
+ }
+ } // function MDETERM()
+
+
+ /**
+ * SUMPRODUCT
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMPRODUCT() {
+ $arrayList = func_get_args();
+
+ $wrkArray = self::flattenArray(array_shift($arrayList));
+ $wrkCellCount = count($wrkArray);
+
+ foreach($arrayList as $matrixData) {
+ $array2 = self::flattenArray($matrixData);
+ $count = count($array2);
+ if ($wrkCellCount != $count) {
+ return self::$_errorCodes['value'];
+ }
+
+ foreach ($array2 as $i => $val) {
+ if (((is_numeric($wrkArray[$i])) && (!is_string($wrkArray[$i]))) &&
+ ((is_numeric($val)) && (!is_string($val)))) {
+ $wrkArray[$i] *= $val;
+ }
+ }
+ }
+
+ return array_sum($wrkArray);
+ } // function SUMPRODUCT()
+
+
+ /**
+ * SUMX2MY2
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMX2MY2($matrixData1,$matrixData2) {
+ $array1 = self::flattenArray($matrixData1);
+ $array2 = self::flattenArray($matrixData2);
+ $count1 = count($array1);
+ $count2 = count($array2);
+ if ($count1 < $count2) {
+ $count = $count1;
+ } else {
+ $count = $count2;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
+ }
+ }
+
+ return $result;
+ } // function SUMX2MY2()
+
+
+ /**
+ * SUMX2PY2
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMX2PY2($matrixData1,$matrixData2) {
+ $array1 = self::flattenArray($matrixData1);
+ $array2 = self::flattenArray($matrixData2);
+ $count1 = count($array1);
+ $count2 = count($array2);
+ if ($count1 < $count2) {
+ $count = $count1;
+ } else {
+ $count = $count2;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
+ }
+ }
+
+ return $result;
+ } // function SUMX2PY2()
+
+
+ /**
+ * SUMXMY2
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMXMY2($matrixData1,$matrixData2) {
+ $array1 = self::flattenArray($matrixData1);
+ $array2 = self::flattenArray($matrixData2);
+ $count1 = count($array1);
+ $count2 = count($array2);
+ if ($count1 < $count2) {
+ $count = $count1;
+ } else {
+ $count = $count2;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
+ }
+ }
+
+ return $result;
+ } // function SUMXMY2()
+
+
+ private static function _vlookupSort($a,$b) {
+ $f = array_keys($a);
+ $firstColumn = array_shift($f);
+ if (strtolower($a[$firstColumn]) == strtolower($b[$firstColumn])) {
+ return 0;
+ }
+ return (strtolower($a[$firstColumn]) < strtolower($b[$firstColumn])) ? -1 : 1;
+ } // function _vlookupSort()
+
+
+ /**
+ * VLOOKUP
+ * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value in the same row based on the index_number.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_array The range of cells being searched
+ * @param index_number The column number in table_array from which the matching value must be returned. The first column is 1.
+ * @param not_exact_match Determines if you are looking for an exact match based on lookup_value.
+ * @return mixed The value of the found cell
+ */
+ public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match=true) {
+ $lookup_value = self::flattenSingleValue($lookup_value);
+ $index_number = self::flattenSingleValue($index_number);
+ $not_exact_match = self::flattenSingleValue($not_exact_match);
+
+ // index_number must be greater than or equal to 1
+ if ($index_number < 1) {
+ return self::$_errorCodes['value'];
+ }
+
+ // index_number must be less than or equal to the number of columns in lookup_array
+ if ((!is_array($lookup_array)) || (count($lookup_array) < 1)) {
+ return self::$_errorCodes['reference'];
+ } else {
+ $f = array_keys($lookup_array);
+ $firstRow = array_pop($f);
+ if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
+ return self::$_errorCodes['reference'];
+ } else {
+ $columnKeys = array_keys($lookup_array[$firstRow]);
+ $returnColumn = $columnKeys[--$index_number];
+ $firstColumn = array_shift($columnKeys);
+ }
+ }
+
+ if (!$not_exact_match) {
+ uasort($lookup_array,array('self','_vlookupSort'));
+ }
+
+ $rowNumber = $rowValue = False;
+ foreach($lookup_array as $rowKey => $rowData) {
+ if (strtolower($rowData[$firstColumn]) > strtolower($lookup_value)) {
+ break;
+ }
+ $rowNumber = $rowKey;
+ $rowValue = $rowData[$firstColumn];
+ }
+
+ if ($rowNumber !== false) {
+ if ((!$not_exact_match) && ($rowValue != $lookup_value)) {
+ // if an exact match is required, we have what we need to return an appropriate response
+ return self::$_errorCodes['na'];
+ } else {
+ // otherwise return the appropriate value
+ return $lookup_array[$rowNumber][$returnColumn];
+ }
+ }
+
+ return self::$_errorCodes['na'];
+ } // function VLOOKUP()
+
+
+ /**
+ * LOOKUP
+ * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_vector The range of cells being searched
+ * @param result_vector The column from which the matching value must be returned
+ * @return mixed The value of the found cell
+ */
+ public static function LOOKUP($lookup_value, $lookup_vector, $result_vector=null) {
+ $lookup_value = self::flattenSingleValue($lookup_value);
+
+ if (!is_array($lookup_vector)) {
+ return self::$_errorCodes['na'];
+ }
+ $lookupRows = count($lookup_vector);
+ $l = array_keys($lookup_vector);
+ $l = array_shift($l);
+ $lookupColumns = count($lookup_vector[$l]);
+ if ((($lookupRows == 1) && ($lookupColumns > 1)) || (($lookupRows == 2) && ($lookupColumns != 2))) {
+ $lookup_vector = self::TRANSPOSE($lookup_vector);
+ $lookupRows = count($lookup_vector);
+ $l = array_keys($lookup_vector);
+ $lookupColumns = count($lookup_vector[array_shift($l)]);
+ }
+
+ if (is_null($result_vector)) {
+ $result_vector = $lookup_vector;
+ }
+ $resultRows = count($result_vector);
+ $l = array_keys($result_vector);
+ $l = array_shift($l);
+ $resultColumns = count($result_vector[$l]);
+ if ((($resultRows == 1) && ($resultColumns > 1)) || (($resultRows == 2) && ($resultColumns != 2))) {
+ $result_vector = self::TRANSPOSE($result_vector);
+ $resultRows = count($result_vector);
+ $r = array_keys($result_vector);
+ $resultColumns = count($result_vector[array_shift($r)]);
+ }
+
+ if ($lookupRows == 2) {
+ $result_vector = array_pop($lookup_vector);
+ $lookup_vector = array_shift($lookup_vector);
+ }
+ if ($lookupColumns != 2) {
+ foreach($lookup_vector as &$value) {
+ if (is_array($value)) {
+ $k = array_keys($value);
+ $key1 = $key2 = array_shift($k);
+ $key2++;
+ $dataValue1 = $value[$key1];
+ } else {
+ $key1 = 0;
+ $key2 = 1;
+ $dataValue1 = $value;
+ }
+ $dataValue2 = array_shift($result_vector);
+ if (is_array($dataValue2)) {
+ $dataValue2 = array_shift($dataValue2);
+ }
+ $value = array($key1 => $dataValue1, $key2 => $dataValue2);
+ }
+ unset($value);
+ }
+
+ return self::VLOOKUP($lookup_value,$lookup_vector,2);
+ } // function LOOKUP()
+
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array
+ *
+ * @param array $array Array to be flattened
+ * @return array Flattened array
+ */
+ public static function flattenArray($array) {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = array();
+ foreach ($array as $value) {
+ if (is_array($value)) {
+ foreach ($value as $val) {
+ if (is_array($val)) {
+ foreach ($val as $v) {
+ $arrayValues[] = $v;
+ }
+ } else {
+ $arrayValues[] = $val;
+ }
+ }
+ } else {
+ $arrayValues[] = $value;
+ }
+ }
+
+ return $arrayValues;
+ } // function flattenArray()
+
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing
+ *
+ * @param array $array Array to be flattened
+ * @return array Flattened array
+ */
+ public static function flattenArrayIndexed($array) {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = array();
+ foreach ($array as $k1 => $value) {
+ if (is_array($value)) {
+ foreach ($value as $k2 => $val) {
+ if (is_array($val)) {
+ foreach ($val as $k3 => $v) {
+ $arrayValues[$k1.'.'.$k2.'.'.$k3] = $v;
+ }
+ } else {
+ $arrayValues[$k1.'.'.$k2] = $val;
+ }
+ }
+ } else {
+ $arrayValues[$k1] = $value;
+ }
+ }
+
+ return $arrayValues;
+ } // function flattenArrayIndexed()
+
+
+ /**
+ * Convert an array to a single scalar value by extracting the first element
+ *
+ * @param mixed $value Array or scalar value
+ * @return mixed
+ */
+ public static function flattenSingleValue($value = '') {
+ while (is_array($value)) {
+ $value = array_pop($value);
+ }
+
+ return $value;
+ } // function flattenSingleValue()
+
+} // class PHPExcel_Calculation_Functions
+
+
+//
+// There are a few mathematical functions that aren't available on all versions of PHP for all platforms
+// These functions aren't available in Windows implementations of PHP prior to version 5.3.0
+// So we test if they do exist for this version of PHP/operating platform; and if not we create them
+//
+if (!function_exists('acosh')) {
+ function acosh($x) {
+ return 2 * log(sqrt(($x + 1) / 2) + sqrt(($x - 1) / 2));
+ } // function acosh()
+}
+
+if (!function_exists('asinh')) {
+ function asinh($x) {
+ return log($x + sqrt(1 + $x * $x));
+ } // function asinh()
+}
+
+if (!function_exists('atanh')) {
+ function atanh($x) {
+ return (log(1 + $x) - log(1 - $x)) / 2;
+ } // function atanh()
+}
+
+if (!function_exists('money_format')) {
+ function money_format($format, $number) {
+ $regex = array( '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?(?:#([0-9]+))?',
+ '(?:\.([0-9]+))?([in%])/'
+ );
+ $regex = implode('', $regex);
+ if (setlocale(LC_MONETARY, null) == '') {
+ setlocale(LC_MONETARY, '');
+ }
+ $locale = localeconv();
+ $number = floatval($number);
+ if (!preg_match($regex, $format, $fmatch)) {
+ trigger_error("No format specified or invalid format", E_USER_WARNING);
+ return $number;
+ }
+ $flags = array( 'fillchar' => preg_match('/\=(.)/', $fmatch[1], $match) ? $match[1] : ' ',
+ 'nogroup' => preg_match('/\^/', $fmatch[1]) > 0,
+ 'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ? $match[0] : '+',
+ 'nosimbol' => preg_match('/\!/', $fmatch[1]) > 0,
+ 'isleft' => preg_match('/\-/', $fmatch[1]) > 0
+ );
+ $width = trim($fmatch[2]) ? (int)$fmatch[2] : 0;
+ $left = trim($fmatch[3]) ? (int)$fmatch[3] : 0;
+ $right = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits'];
+ $conversion = $fmatch[5];
+ $positive = true;
+ if ($number < 0) {
+ $positive = false;
+ $number *= -1;
+ }
+ $letter = $positive ? 'p' : 'n';
+ $prefix = $suffix = $cprefix = $csuffix = $signal = '';
+ if (!$positive) {
+ $signal = $locale['negative_sign'];
+ switch (true) {
+ case $locale['n_sign_posn'] == 0 || $flags['usesignal'] == '(':
+ $prefix = '(';
+ $suffix = ')';
+ break;
+ case $locale['n_sign_posn'] == 1:
+ $prefix = $signal;
+ break;
+ case $locale['n_sign_posn'] == 2:
+ $suffix = $signal;
+ break;
+ case $locale['n_sign_posn'] == 3:
+ $cprefix = $signal;
+ break;
+ case $locale['n_sign_posn'] == 4:
+ $csuffix = $signal;
+ break;
+ }
+ }
+ if (!$flags['nosimbol']) {
+ $currency = $cprefix;
+ $currency .= ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']);
+ $currency .= $csuffix;
+ $currency = iconv('ISO-8859-1','UTF-8',$currency);
+ } else {
+ $currency = '';
+ }
+ $space = $locale["{$letter}_sep_by_space"] ? ' ' : '';
+
+ $number = number_format($number, $right, $locale['mon_decimal_point'], $flags['nogroup'] ? '' : $locale['mon_thousands_sep'] );
+ $number = explode($locale['mon_decimal_point'], $number);
+
+ $n = strlen($prefix) + strlen($currency);
+ if ($left > 0 && $left > $n) {
+ if ($flags['isleft']) {
+ $number[0] .= str_repeat($flags['fillchar'], $left - $n);
+ } else {
+ $number[0] = str_repeat($flags['fillchar'], $left - $n) . $number[0];
+ }
+ }
+ $number = implode($locale['mon_decimal_point'], $number);
+ if ($locale["{$letter}_cs_precedes"]) {
+ $number = $prefix . $currency . $space . $number . $suffix;
+ } else {
+ $number = $prefix . $number . $space . $currency . $suffix;
+ }
+ if ($width > 0) {
+ $number = str_pad($number, $width, $flags['fillchar'], $flags['isleft'] ? STR_PAD_RIGHT : STR_PAD_LEFT);
+ }
+ $format = str_replace($fmatch[0], $number, $format);
+ return $format;
+ } // function money_format()
+}
+
+
+//
+// Strangely, PHP doesn't have a mb_str_replace multibyte function
+// As we'll only ever use this function with UTF-8 characters, we can simply "hard-code" the character set
+//
+if ((!function_exists('mb_str_replace')) &&
+ (function_exists('mb_substr')) && (function_exists('mb_strlen')) && (function_exists('mb_strpos'))) {
+ function mb_str_replace($search, $replace, $subject) {
+ if(is_array($subject)) {
+ $ret = array();
+ foreach($subject as $key => $val) {
+ $ret[$key] = mb_str_replace($search, $replace, $val);
+ }
+ return $ret;
+ }
+
+ foreach((array) $search as $key => $s) {
+ if($s == '') {
+ continue;
+ }
+ $r = !is_array($replace) ? $replace : (array_key_exists($key, $replace) ? $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'), 65535, 'UTF-8');
+ $pos = mb_strpos($subject, $s, $pos + mb_strlen($r, 'UTF-8'), 'UTF-8');
+ }
+ }
+ return $subject;
+ }
+}
diff --git a/Classes/PHPExcel/Calculation/functionlist.txt b/Classes/PHPExcel/Calculation/functionlist.txt
new file mode 100644
index 0000000..67dbd49
--- /dev/null
+++ b/Classes/PHPExcel/Calculation/functionlist.txt
@@ -0,0 +1,351 @@
+ABS
+ACCRINT
+ACCRINTM
+ACOS
+ACOSH
+ADDRESS
+AMORDEGRC
+AMORLINC
+AND
+AREAS
+ASC
+ASIN
+ASINH
+ATAN
+ATAN2
+ATANH
+AVEDEV
+AVERAGE
+AVERAGEA
+AVERAGEIF
+AVERAGEIFS
+BAHTTEXT
+BESSELI
+BESSELJ
+BESSELK
+BESSELY
+BETADIST
+BETAINV
+BIN2DEC
+BIN2HEX
+BIN2OCT
+BINOMDIST
+CEILING
+CELL
+CHAR
+CHIDIST
+CHIINV
+CHITEST
+CHOOSE
+CLEAN
+CODE
+COLUMN
+COLUMNS
+COMBIN
+COMPLEX
+CONCATENATE
+CONFIDENCE
+CONVERT
+CORREL
+COS
+COSH
+COUNT
+COUNTA
+COUNTBLANK
+COUNTIF
+COUNTIFS
+COUPDAYBS
+COUPDAYBS
+COUPDAYSNC
+COUPNCD
+COUPNUM
+COUPPCD
+COVAR
+CRITBINOM
+CUBEKPIMEMBER
+CUBEMEMBER
+CUBEMEMBERPROPERTY
+CUBERANKEDMEMBER
+CUBESET
+CUBESETCOUNT
+CUBEVALUE
+CUMIPMT
+CUMPRINC
+DATE
+DATEDIF
+DATEVALUE
+DAVERAGE
+DAY
+DAYS360
+DB
+DCOUNT
+DCOUNTA
+DDB
+DEC2BIN
+DEC2HEX
+DEC2OCT
+DEGREES
+DELTA
+DEVSQ
+DGET
+DISC
+DMAX
+DMIN
+DOLLAR
+DOLLARDE
+DOLLARFR
+DPRODUCT
+DSTDEV
+DSTDEVP
+DSUM
+DURATION
+DVAR
+DVARP
+EDATE
+EFFECT
+EOMONTH
+ERF
+ERFC
+ERROR.TYPE
+EVEN
+EXACT
+EXP
+EXPONDIST
+FACT
+FACTDOUBLE
+FALSE
+FDIST
+FIND
+FINDB
+FINV
+FISHER
+FISHERINV
+FIXED
+FLOOR
+FORECAST
+FREQUENCY
+FTEST
+FV
+FVSCHEDULE
+GAMAMDIST
+GAMMAINV
+GAMMALN
+GCD
+GEOMEAN
+GESTEP
+GETPIVOTDATA
+GROWTH
+HARMEAN
+HEX2BIN
+HEX2OCT
+HLOOKUP
+HOUR
+HYPERLINK
+HYPGEOMDIST
+IF
+IFERROR
+IMABS
+IMAGINARY
+IMARGUMENT
+IMCONJUGATE
+IMCOS
+IMEXP
+IMLN
+IMLOG10
+IMLOG2
+IMPOWER
+IMPRODUCT
+IMREAL
+IMSIN
+IMSQRT
+IMSUB
+IMSUM
+INDEX
+INDIRECT
+INFO
+INT
+INTERCEPT
+INTRATE
+IPMT
+IRR
+ISBLANK
+ISERR
+ISERROR
+ISEVEN
+ISLOGICAL
+ISNA
+ISNONTEXT
+ISNUMBER
+ISODD
+ISPMT
+ISREF
+ISTEXT
+JIS
+KURT
+LARGE
+LCM
+LEFT
+LEFTB
+LEN
+LENB
+LINEST
+LN
+LOG
+LOG10
+LOGEST
+LOGINV
+LOGNORMDIST
+LOOKUP
+LOWER
+MATCH
+MAX
+MAXA
+MDETERM
+MDURATION
+MEDIAN
+MID
+MIDB
+MIN
+MINA
+MINUTE
+MINVERSE
+MIRR
+MMULT
+MOD
+MODE
+MONTH
+MROUND
+MULTINOMIAL
+N
+NA
+NEGBINOMDIST
+NETWORKDAYS
+NOMINAL
+NORMDIST
+NORMINV
+NORMSDIST
+NORMSINV
+NOT
+NOW
+NPER
+NPV
+OCT2BIN
+OCT2DEC
+OCT2HEX
+ODD
+ODDFPRICE
+ODDFYIELD
+ODDLPRICE
+ODDLYIELD
+OFFSET
+OR
+PEARSON
+PERCENTILE
+PERCENTRANK
+PERMUT
+PHONETIC
+PI
+PMT
+POISSON
+POWER
+PPMT
+PRICE
+PRICEDISC
+PRICEMAT
+PROB
+PRODUCT
+PROPER
+PV
+QUARTILE
+QUOTIENT
+RADIANS
+RAND
+RANDBETWEEN
+RANK
+RATE
+RECEIVED
+REPLACE
+REPLACEB
+REPT
+RIGHT
+RIGHTB
+ROMAN
+ROUND
+ROUNDDOWN
+ROUNDUP
+ROW
+ROWS
+RSQ
+RTD
+SEARCH
+SEARCHB
+SECOND
+SERIESSUM
+SIGN
+SIN
+SINH
+SKEW
+SLN
+SLOPE
+SMALL
+SQRT
+SQRTPI
+STANDARDIZE
+STDEV
+STDEVA
+STDEVP
+STDEVPA
+STEYX
+SUBSTITUTE
+SUBTOTAL
+SUM
+SUMIF
+SUMIFS
+SUMPRODUCT
+SUMSQ
+SUMX2MY2
+SUMX2PY2
+SUMXMY2
+SYD
+T
+TAN
+TANH
+TBILLEQ
+TBILLPRICE
+TBILLYIELD
+TDIST
+TEXT
+TIME
+TIMEVALUE
+TINV
+TODAY
+TRANSPOSE
+TREND
+TRIM
+TRIMMEAN
+TRUE
+TRUNC
+TTEST
+TYPE
+UPPER
+USDOLLAR
+VALUE
+VAR
+VARA
+VARP
+VARPA
+VDB
+VERSION
+VLOOKUP
+WEEKDAY
+WEEKNUM
+WEIBULL
+WORKDAY
+XIRR
+XNPV
+YEAR
+YEARFRAC
+YIELD
+YIELDDISC
+YIELDMAT
+ZTEST
diff --git a/Classes/PHPExcel/Cell.php b/Classes/PHPExcel/Cell.php
new file mode 100644
index 0000000..feaa765
--- /dev/null
+++ b/Classes/PHPExcel/Cell.php
@@ -0,0 +1,834 @@
+_parent->getCellCacheController()->updateCacheData($this);
+ return $this;
+ }
+
+ public function detach() {
+ $this->_parent = null;
+ }
+
+ public function attach($parent) {
+ $this->_parent = $parent;
+ }
+
+
+ /**
+ * Create a new Cell
+ *
+ * @param string $pColumn
+ * @param int $pRow
+ * @param mixed $pValue
+ * @param string $pDataType
+ * @param PHPExcel_Worksheet $pSheet
+ * @throws Exception
+ */
+ public function __construct($pColumn = 'A', $pRow = 1, $pValue = null, $pDataType = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // Initialise cell coordinate
+ $this->_column = strtoupper($pColumn);
+ $this->_row = $pRow;
+
+ // Initialise cell value
+ $this->_value = $pValue;
+
+ // Set worksheet
+ $this->_parent = $pSheet;
+
+ // Set datatype?
+ if (!is_null($pDataType)) {
+ $this->_dataType = $pDataType;
+ } else {
+ if (!self::getValueBinder()->bindValue($this, $pValue)) {
+ throw new Exception("Value could not be bound to cell.");
+ }
+ }
+
+ // set default index to cellXf
+ $this->_xfIndex = 0;
+ }
+
+ /**
+ * Get cell coordinate column
+ *
+ * @return string
+ */
+ public function getColumn()
+ {
+ return $this->_column;
+ }
+
+ /**
+ * Get cell coordinate row
+ *
+ * @return int
+ */
+ public function getRow()
+ {
+ return $this->_row;
+ }
+
+ /**
+ * Get cell coordinate
+ *
+ * @return string
+ */
+ public function getCoordinate()
+ {
+ return $this->_column . $this->_row;
+ }
+
+ /**
+ * Get cell value
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * Set cell value
+ *
+ * This clears the cell formula.
+ *
+ * @param mixed $pValue Value
+ * @return PHPExcel_Cell
+ */
+ public function setValue($pValue = null)
+ {
+ if (!self::getValueBinder()->bindValue($this, $pValue)) {
+ throw new Exception("Value could not be bound to cell.");
+ }
+ return $this;
+ }
+
+ /**
+ * Set cell value (with explicit data type given)
+ *
+ * @param mixed $pValue Value
+ * @param string $pDataType Explicit data type
+ * @return PHPExcel_Cell
+ * @throws Exception
+ */
+ public function setValueExplicit($pValue = null, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING)
+ {
+ // set the value according to data type
+ switch ($pDataType) {
+ case PHPExcel_Cell_DataType::TYPE_STRING:
+ case PHPExcel_Cell_DataType::TYPE_NULL:
+ case PHPExcel_Cell_DataType::TYPE_INLINE:
+ $this->_value = PHPExcel_Cell_DataType::checkString($pValue);
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_NUMERIC:
+ $this->_value = (float)$pValue;
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_FORMULA:
+ $this->_value = (string)$pValue;
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_BOOL:
+ $this->_value = (bool)$pValue;
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_ERROR:
+ $this->_value = PHPExcel_Cell_DataType::checkErrorCode($pValue);
+ break;
+
+ default:
+ throw new Exception('Invalid datatype: ' . $pDataType);
+ break;
+ }
+
+ // set the datatype
+ $this->_dataType = $pDataType;
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Get calculated cell value
+ *
+ * @return mixed
+ */
+ public function getCalculatedValue($resetLog=true)
+ {
+// echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().'
';
+ if ($this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ try {
+// echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value
';
+ $result = PHPExcel_Calculation::getInstance()->calculateCellValue($this,$resetLog);
+// echo $this->getCoordinate().' calculation result is '.$result.'
';
+ } catch ( Exception $ex ) {
+// echo 'Calculation Exception: '.$ex->getMessage().'
';
+ $result = '#N/A';
+ throw(new Exception($this->getParent()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage()));
+ }
+
+ if ($result === '#Not Yet Implemented') {
+// echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().'
';
+ return $this->_calculatedValue; // Fallback if calculation engine does not support the formula.
+ }
+// echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().'
';
+ return $result;
+ }
+
+ if (is_null($this->_value)) {
+// echo 'Cell '.$this->getCoordinate().' has no value, formula or otherwise
';
+ return null;
+ }
+// echo 'Cell value for '.$this->getCoordinate().' is not a formula: Returning data value of '.$this->_value.'
';
+ return $this->_value;
+ }
+
+ /**
+ * Set calculated value (used for caching)
+ *
+ * @param mixed $pValue Value
+ * @return PHPExcel_Cell
+ */
+ public function setCalculatedValue($pValue = null)
+ {
+ if (!is_null($pValue)) {
+ $this->_calculatedValue = $pValue;
+ }
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Get old calculated value (cached)
+ *
+ * @return mixed
+ */
+ public function getOldCalculatedValue()
+ {
+ return $this->_calculatedValue;
+ }
+
+ /**
+ * Get cell data type
+ *
+ * @return string
+ */
+ public function getDataType()
+ {
+ return $this->_dataType;
+ }
+
+ /**
+ * Set cell data type
+ *
+ * @param string $pDataType
+ * @return PHPExcel_Cell
+ */
+ public function setDataType($pDataType = PHPExcel_Cell_DataType::TYPE_STRING)
+ {
+ $this->_dataType = $pDataType;
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Has Data validation?
+ *
+ * @return boolean
+ */
+ public function hasDataValidation()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
+ }
+
+ return $this->_parent->dataValidationExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Data validation
+ *
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function getDataValidation()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
+ }
+
+ return $this->_parent->getDataValidation($this->getCoordinate());
+ }
+
+ /**
+ * Set Data validation
+ *
+ * @param PHPExcel_Cell_DataValidation $pDataValidation
+ * @throws Exception
+ * @return PHPExcel_Cell
+ */
+ public function setDataValidation(PHPExcel_Cell_DataValidation $pDataValidation = null)
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
+ }
+
+ $this->_parent->setDataValidation($this->getCoordinate(), $pDataValidation);
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Has Hyperlink
+ *
+ * @return boolean
+ */
+ public function hasHyperlink()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
+ }
+
+ return $this->_parent->hyperlinkExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Hyperlink
+ *
+ * @throws Exception
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function getHyperlink()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
+ }
+
+ return $this->_parent->getHyperlink($this->getCoordinate());
+ }
+
+ /**
+ * Set Hyperlink
+ *
+ * @param PHPExcel_Cell_Hyperlink $pHyperlink
+ * @throws Exception
+ * @return PHPExcel_Cell
+ */
+ public function setHyperlink(PHPExcel_Cell_Hyperlink $pHyperlink = null)
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
+ }
+
+ $this->_parent->setHyperlink($this->getCoordinate(), $pHyperlink);
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Get parent
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getParent() {
+ return $this->_parent;
+ }
+
+ /**
+ * Re-bind parent
+ *
+ * @param PHPExcel_Worksheet $parent
+ * @return PHPExcel_Cell
+ */
+ public function rebindParent(PHPExcel_Worksheet $parent) {
+ $this->_parent = $parent;
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Is cell in a specific range?
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return boolean
+ */
+ public function isInRange($pRange = 'A1:A1')
+ {
+ list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange);
+
+ // Translate properties
+ $myColumn = PHPExcel_Cell::columnIndexFromString($this->getColumn()) - 1;
+ $myRow = $this->getRow();
+
+ // Verify if cell is in range
+ return (($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
+ ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow)
+ );
+ }
+
+ /**
+ * Coordinate from string
+ *
+ * @param string $pCoordinateString
+ * @return array Array containing column and row (indexes 0 and 1)
+ * @throws Exception
+ */
+ public static function coordinateFromString($pCoordinateString = 'A1')
+ {
+ if (strpos($pCoordinateString,':') !== false) {
+ throw new Exception('Cell coordinate string can not be a range of cells.');
+ } else if ($pCoordinateString == '') {
+ throw new Exception('Cell coordinate can not be zero-length string.');
+ } else if (preg_match("/([$]?[A-Z]+)([$]?\d+)/", $pCoordinateString, $matches)) {
+ list(, $column, $row) = $matches;
+ return array($column, $row);
+ } else {
+ throw new Exception('Invalid cell coordinate.');
+ }
+ }
+
+ /**
+ * Make string coordinate absolute
+ *
+ * @param string $pCoordinateString
+ * @return string Absolute coordinate
+ * @throws Exception
+ */
+ public static function absoluteCoordinate($pCoordinateString = 'A1')
+ {
+ if (strpos($pCoordinateString,':') === false && strpos($pCoordinateString,',') === false) {
+ // Create absolute coordinate
+ list($column, $row) = PHPExcel_Cell::coordinateFromString($pCoordinateString);
+ return '$' . $column . '$' . $row;
+ } else {
+ throw new Exception("Coordinate string should not be a cell range.");
+ }
+ }
+
+ /**
+ * Split range into coordinate strings
+ *
+ * @param string $pRange
+ * @return array Array containg one or more arrays containing one or two coordinate strings
+ */
+ public static function splitRange($pRange = 'A1:A1')
+ {
+ $exploded = explode(',', $pRange);
+ for ($i = 0; $i < count($exploded); ++$i) {
+ $exploded[$i] = explode(':', $exploded[$i]);
+ }
+ return $exploded;
+ }
+
+ /**
+ * Build range from coordinate strings
+ *
+ * @param array $pRange Array containg one or more arrays containing one or two coordinate strings
+ * @return string String representation of $pRange
+ * @throws Exception
+ */
+ public static function buildRange($pRange)
+ {
+ // Verify range
+ if (!is_array($pRange) || count($pRange) == 0 || !is_array($pRange[0])) {
+ throw new Exception('Range does not contain any information.');
+ }
+
+ // Build range
+ $imploded = array();
+ for ($i = 0; $i < count($pRange); ++$i) {
+ $pRange[$i] = implode(':', $pRange[$i]);
+ }
+ $imploded = implode(',', $pRange);
+
+ return $imploded;
+ }
+
+ /**
+ * Calculate range boundaries
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return array Range coordinates (Start Cell, End Cell) where Start Cell and End Cell are arrays (Column Number, Row Number)
+ */
+ public static function rangeBoundaries($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Extract range
+ if (strpos($pRange, ':') === false) {
+ $rangeA = $rangeB = $pRange;
+ } else {
+ list($rangeA, $rangeB) = explode(':', $pRange);
+ }
+
+ // Calculate range outer borders
+ $rangeStart = PHPExcel_Cell::coordinateFromString($rangeA);
+ $rangeEnd = PHPExcel_Cell::coordinateFromString($rangeB);
+
+ // Translate column into index
+ $rangeStart[0] = PHPExcel_Cell::columnIndexFromString($rangeStart[0]);
+ $rangeEnd[0] = PHPExcel_Cell::columnIndexFromString($rangeEnd[0]);
+
+ return array($rangeStart, $rangeEnd);
+ }
+
+ /**
+ * Calculate range dimension
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return array Range dimension (width, height)
+ */
+ public static function rangeDimension($pRange = 'A1:A1')
+ {
+ // Calculate range outer borders
+ list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange);
+
+ return array( ($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1) );
+ }
+
+ /**
+ * Calculate range boundaries
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return array Range boundaries (staring Column, starting Row, Final Column, Final Row)
+ */
+ public static function getRangeBoundaries($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Extract range
+ if (strpos($pRange, ':') === false) {
+ $rangeA = $pRange;
+ $rangeB = $pRange;
+ } else {
+ list($rangeA, $rangeB) = explode(':', $pRange);
+ }
+
+ return array( self::coordinateFromString($rangeA), self::coordinateFromString($rangeB));
+ }
+
+ /**
+ * Column index from string
+ *
+ * @param string $pString
+ * @return int Column index (base 1 !!!)
+ * @throws Exception
+ */
+ public static function columnIndexFromString($pString = 'A')
+ {
+ static $lookup = array(
+ 'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8, 'I' => 9, 'J' => 10, 'K' => 11, 'L' => 12, 'M' => 13,
+ 'N' => 14, 'O' => 15, 'P' => 16, 'Q' => 17, 'R' => 18, 'S' => 19, 'T' => 20, 'U' => 21, 'V' => 22, 'W' => 23, 'X' => 24, 'Y' => 25, 'Z' => 26
+ );
+
+ if (isset($lookup[$pString]))
+ return $lookup[$pString];
+
+ // Convert to uppercase
+ $pString = strtoupper($pString);
+
+ $strLen = strlen($pString);
+ // Convert column to integer
+ if ($strLen == 1) {
+ return (ord($pString{0}) - 64);
+ } elseif ($strLen == 2) {
+ return $result = ((1 + (ord($pString{0}) - 65)) * 26) + (ord($pString{1}) - 64);
+ } elseif ($strLen == 3) {
+ return ((1 + (ord($pString{0}) - 65)) * 676) + ((1 + (ord($pString{1}) - 65)) * 26) + (ord($pString{2}) - 64);
+ } else {
+ throw new Exception("Column string index can not be " . ($strLen != 0 ? "longer than 3 characters" : "empty") . ".");
+ }
+ }
+
+ /**
+ * String from columnindex
+ *
+ * @param int $pColumnIndex Column index (base 0 !!!)
+ * @return string
+ */
+ public static function stringFromColumnIndex($pColumnIndex = 0)
+ {
+ // Determine column string
+ if ($pColumnIndex < 26) {
+ return chr(65 + $pColumnIndex);
+ }
+ return PHPExcel_Cell::stringFromColumnIndex((int)($pColumnIndex / 26) -1).chr(65 + $pColumnIndex%26) ;
+ }
+
+ /**
+ * Extract all cell references in range
+ *
+ * @param string $pRange Range (e.g. A1 or A1:A10 or A1:A10 A100:A1000)
+ * @return array Array containing single cell references
+ */
+ public static function extractAllCellReferencesInRange($pRange = 'A1') {
+ // Returnvalue
+ $returnValue = array();
+
+ // Explode spaces
+ $aExplodeSpaces = explode(' ', str_replace('$', '', strtoupper($pRange)));
+ foreach ($aExplodeSpaces as $explodedSpaces) {
+ // Single cell?
+ if (strpos($explodedSpaces,':') === false && strpos($explodedSpaces,',') === false) {
+ $col = 'A';
+ $row = 1;
+ list($col, $row) = PHPExcel_Cell::coordinateFromString($explodedSpaces);
+
+ if (strlen($col) <= 2) {
+ $returnValue[] = $explodedSpaces;
+ }
+
+ continue;
+ }
+
+ // Range...
+ $range = PHPExcel_Cell::splitRange($explodedSpaces);
+ for ($i = 0; $i < count($range); ++$i) {
+ // Single cell?
+ if (count($range[$i]) == 1) {
+ $col = 'A';
+ $row = 1;
+ list($col, $row) = PHPExcel_Cell::coordinateFromString($range[$i]);
+
+ if (strlen($col) <= 2) {
+ $returnValue[] = $explodedSpaces;
+ }
+ }
+
+ // Range...
+ $rangeStart = $rangeEnd = '';
+ $startingCol = $startingRow = $endingCol = $endingRow = 0;
+
+ list($rangeStart, $rangeEnd) = $range[$i];
+ list($startingCol, $startingRow) = PHPExcel_Cell::coordinateFromString($rangeStart);
+ list($endingCol, $endingRow) = PHPExcel_Cell::coordinateFromString($rangeEnd);
+
+ // Conversions...
+ $startingCol = PHPExcel_Cell::columnIndexFromString($startingCol);
+ $endingCol = PHPExcel_Cell::columnIndexFromString($endingCol);
+
+ // Current data
+ $currentCol = --$startingCol;
+ $currentRow = $startingRow;
+
+ // Loop cells
+ while ($currentCol < $endingCol) {
+ $loopColumn = PHPExcel_Cell::stringFromColumnIndex($currentCol);
+ while ($currentRow <= $endingRow) {
+ $returnValue[] = $loopColumn.$currentRow;
+ ++$currentRow;
+ }
+ ++$currentCol;
+ $currentRow = $startingRow;
+ }
+ }
+ }
+
+ // Return value
+ return $returnValue;
+ }
+
+ /**
+ * Compare 2 cells
+ *
+ * @param PHPExcel_Cell $a Cell a
+ * @param PHPExcel_Cell $a Cell b
+ * @return int Result of comparison (always -1 or 1, never zero!)
+ */
+ public static function compareCells(PHPExcel_Cell $a, PHPExcel_Cell $b)
+ {
+ if ($a->_row < $b->_row) {
+ return -1;
+ } elseif ($a->_row > $b->_row) {
+ return 1;
+ } elseif (PHPExcel_Cell::columnIndexFromString($a->_column) < PHPExcel_Cell::columnIndexFromString($b->_column)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Get value binder to use
+ *
+ * @return PHPExcel_Cell_IValueBinder
+ */
+ public static function getValueBinder() {
+ if (is_null(self::$_valueBinder)) {
+ self::$_valueBinder = new PHPExcel_Cell_DefaultValueBinder();
+ }
+
+ return self::$_valueBinder;
+ }
+
+ /**
+ * Set value binder to use
+ *
+ * @param PHPExcel_Cell_IValueBinder $binder
+ * @throws Exception
+ */
+ public static function setValueBinder(PHPExcel_Cell_IValueBinder $binder = null) {
+ if (is_null($binder)) {
+ throw new Exception("A PHPExcel_Cell_IValueBinder is required for PHPExcel to function correctly.");
+ }
+
+ self::$_valueBinder = $binder;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if ((is_object($value)) && ($key != '_parent')) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ /**
+ * Get index to cellXf
+ *
+ * @return int
+ */
+ public function getXfIndex()
+ {
+ return $this->_xfIndex;
+ }
+
+ /**
+ * Set index to cellXf
+ *
+ * @param int $pValue
+ * @return PHPExcel_Cell
+ */
+ public function setXfIndex($pValue = 0)
+ {
+ $this->_xfIndex = $pValue;
+
+ return $this->notifyCacheController();
+ }
+
+
+ public function setFormulaAttributes($pAttributes)
+ {
+ $this->_formulaAttributes = $pAttributes;
+ return $this;
+ }
+
+ public function getFormulaAttributes()
+ {
+ return $this->_formulaAttributes;
+ }
+
+}
+
diff --git a/Classes/PHPExcel/Cell/AdvancedValueBinder.php b/Classes/PHPExcel/Cell/AdvancedValueBinder.php
new file mode 100644
index 0000000..6d0423f
--- /dev/null
+++ b/Classes/PHPExcel/Cell/AdvancedValueBinder.php
@@ -0,0 +1,127 @@
+setValueExplicit( (float)str_replace('%', '', $value) / 100, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_PERCENTAGE );
+
+ return true;
+ }
+
+ // Check for time without seconds e.g. '9:45', '09:45'
+ if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
+ list($h, $m) = explode(':', $value);
+ $days = $h / 24 + $m / 1440;
+
+ // Convert value to number
+ $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME3 );
+
+ return true;
+ }
+
+ // Check for time with seconds '9:45:59', '09:45:59'
+ if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
+ list($h, $m, $s) = explode(':', $value);
+ $days = $h / 24 + $m / 1440 + $s / 86400;
+
+ // Convert value to number
+ $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4 );
+
+ return true;
+ }
+
+ // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
+ if (($v = PHPExcel_Shared_Date::stringToExcel($value)) !== false) {
+ // Convert value to Excel date
+ $cell->setValueExplicit($v, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style. Either there is a time part or not. Look for ':'
+ if (strpos($value, ':') !== false) {
+ $formatCode = 'yyyy-mm-dd h:mm';
+ } else {
+ $formatCode = 'yyyy-mm-dd';
+ }
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode($formatCode);
+
+ return true;
+ }
+
+ // Check for newline character "\n"
+ if (strpos($value, "\n") !== false) {
+ $value = PHPExcel_Shared_String::SanitizeUTF8($value);
+ $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getAlignment()->setWrapText(true);
+
+ return true;
+ }
+ }
+
+ // Not bound yet? Use parent...
+ return parent::bindValue($cell, $value);
+ }
+}
diff --git a/Classes/PHPExcel/Cell/DataType.php b/Classes/PHPExcel/Cell/DataType.php
new file mode 100644
index 0000000..944dba9
--- /dev/null
+++ b/Classes/PHPExcel/Cell/DataType.php
@@ -0,0 +1,113 @@
+ 0, '#DIV/0!' => 1, '#VALUE!' => 2, '#REF!' => 3, '#NAME?' => 4, '#NUM!' => 5, '#N/A' => 6);
+
+ /**
+ * Get list of error codes
+ *
+ * @return array
+ */
+ public static function getErrorCodes() {
+ return self::$_errorCodes;
+ }
+
+ /**
+ * DataType for value
+ *
+ * @deprecated Replaced by PHPExcel_Cell_IValueBinder infrastructure
+ * @param mixed $pValue
+ * @return int
+ */
+ public static function dataTypeForValue($pValue = null) {
+ return PHPExcel_Cell_DefaultValueBinder::dataTypeForValue($pValue);
+ }
+
+ /**
+ * Check a string that it satisfies Excel requirements
+ *
+ * @param mixed Value to sanitize to an Excel string
+ * @return mixed Sanitized value
+ */
+ public static function checkString($pValue = null)
+ {
+ if ($pValue instanceof PHPExcel_RichText) {
+ // TODO: Sanitize Rich-Text string (max. character count is 32,767)
+ return $pValue;
+ }
+
+ // string must never be longer than 32,767 characters, truncate if necessary
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 32767);
+
+ // we require that newline is represented as "\n" in core, not as "\r\n" or "\r"
+ $pValue = str_replace(array("\r\n", "\r"), "\n", $pValue);
+
+ return $pValue;
+ }
+
+ /**
+ * Check a value that it is a valid error code
+ *
+ * @param mixed Value to sanitize to an Excel error code
+ * @return string Sanitized value
+ */
+ public static function checkErrorCode($pValue = null)
+ {
+ $pValue = (string)$pValue;
+
+ if ( !array_key_exists($pValue, self::$_errorCodes) ) {
+ $pValue = '#NULL!';
+ }
+
+ return $pValue;
+ }
+
+}
diff --git a/Classes/PHPExcel/Cell/DataValidation.php b/Classes/PHPExcel/Cell/DataValidation.php
new file mode 100644
index 0000000..cdb02ba
--- /dev/null
+++ b/Classes/PHPExcel/Cell/DataValidation.php
@@ -0,0 +1,474 @@
+_formula1 = '';
+ $this->_formula2 = '';
+ $this->_type = PHPExcel_Cell_DataValidation::TYPE_NONE;
+ $this->_errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP;
+ $this->_operator = '';
+ $this->_allowBlank = false;
+ $this->_showDropDown = false;
+ $this->_showInputMessage = false;
+ $this->_showErrorMessage = false;
+ $this->_errorTitle = '';
+ $this->_error = '';
+ $this->_promptTitle = '';
+ $this->_prompt = '';
+ }
+
+ /**
+ * Get Formula 1
+ *
+ * @return string
+ */
+ public function getFormula1() {
+ return $this->_formula1;
+ }
+
+ /**
+ * Set Formula 1
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setFormula1($value = '') {
+ $this->_formula1 = $value;
+ return $this;
+ }
+
+ /**
+ * Get Formula 2
+ *
+ * @return string
+ */
+ public function getFormula2() {
+ return $this->_formula2;
+ }
+
+ /**
+ * Set Formula 2
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setFormula2($value = '') {
+ $this->_formula2 = $value;
+ return $this;
+ }
+
+ /**
+ * Get Type
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->_type;
+ }
+
+ /**
+ * Set Type
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setType($value = PHPExcel_Cell_DataValidation::TYPE_NONE) {
+ $this->_type = $value;
+ return $this;
+ }
+
+ /**
+ * Get Error style
+ *
+ * @return string
+ */
+ public function getErrorStyle() {
+ return $this->_errorStyle;
+ }
+
+ /**
+ * Set Error style
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setErrorStyle($value = PHPExcel_Cell_DataValidation::STYLE_STOP) {
+ $this->_errorStyle = $value;
+ return $this;
+ }
+
+ /**
+ * Get Operator
+ *
+ * @return string
+ */
+ public function getOperator() {
+ return $this->_operator;
+ }
+
+ /**
+ * Set Operator
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setOperator($value = '') {
+ $this->_operator = $value;
+ return $this;
+ }
+
+ /**
+ * Get Allow Blank
+ *
+ * @return boolean
+ */
+ public function getAllowBlank() {
+ return $this->_allowBlank;
+ }
+
+ /**
+ * Set Allow Blank
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setAllowBlank($value = false) {
+ $this->_allowBlank = $value;
+ return $this;
+ }
+
+ /**
+ * Get Show DropDown
+ *
+ * @return boolean
+ */
+ public function getShowDropDown() {
+ return $this->_showDropDown;
+ }
+
+ /**
+ * Set Show DropDown
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setShowDropDown($value = false) {
+ $this->_showDropDown = $value;
+ return $this;
+ }
+
+ /**
+ * Get Show InputMessage
+ *
+ * @return boolean
+ */
+ public function getShowInputMessage() {
+ return $this->_showInputMessage;
+ }
+
+ /**
+ * Set Show InputMessage
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setShowInputMessage($value = false) {
+ $this->_showInputMessage = $value;
+ return $this;
+ }
+
+ /**
+ * Get Show ErrorMessage
+ *
+ * @return boolean
+ */
+ public function getShowErrorMessage() {
+ return $this->_showErrorMessage;
+ }
+
+ /**
+ * Set Show ErrorMessage
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setShowErrorMessage($value = false) {
+ $this->_showErrorMessage = $value;
+ return $this;
+ }
+
+ /**
+ * Get Error title
+ *
+ * @return string
+ */
+ public function getErrorTitle() {
+ return $this->_errorTitle;
+ }
+
+ /**
+ * Set Error title
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setErrorTitle($value = '') {
+ $this->_errorTitle = $value;
+ return $this;
+ }
+
+ /**
+ * Get Error
+ *
+ * @return string
+ */
+ public function getError() {
+ return $this->_error;
+ }
+
+ /**
+ * Set Error
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setError($value = '') {
+ $this->_error = $value;
+ return $this;
+ }
+
+ /**
+ * Get Prompt title
+ *
+ * @return string
+ */
+ public function getPromptTitle() {
+ return $this->_promptTitle;
+ }
+
+ /**
+ * Set Prompt title
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setPromptTitle($value = '') {
+ $this->_promptTitle = $value;
+ return $this;
+ }
+
+ /**
+ * Get Prompt
+ *
+ * @return string
+ */
+ public function getPrompt() {
+ return $this->_prompt;
+ }
+
+ /**
+ * Set Prompt
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setPrompt($value = '') {
+ $this->_prompt = $value;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_formula1
+ . $this->_formula2
+ . $this->_type = PHPExcel_Cell_DataValidation::TYPE_NONE
+ . $this->_errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP
+ . $this->_operator
+ . ($this->_allowBlank ? 't' : 'f')
+ . ($this->_showDropDown ? 't' : 'f')
+ . ($this->_showInputMessage ? 't' : 'f')
+ . ($this->_showErrorMessage ? 't' : 'f')
+ . $this->_errorTitle
+ . $this->_error
+ . $this->_promptTitle
+ . $this->_prompt
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/Cell/DefaultValueBinder.php b/Classes/PHPExcel/Cell/DefaultValueBinder.php
new file mode 100644
index 0000000..3672bba
--- /dev/null
+++ b/Classes/PHPExcel/Cell/DefaultValueBinder.php
@@ -0,0 +1,96 @@
+setValueExplicit( $value, PHPExcel_Cell_DataType::dataTypeForValue($value) );
+
+ // Done!
+ return true;
+ }
+
+ /**
+ * DataType for value
+ *
+ * @param mixed $pValue
+ * @return int
+ */
+ public static function dataTypeForValue($pValue = null) {
+ // Match the value against a few data types
+ if (is_null($pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_NULL;
+
+ } elseif ($pValue === '') {
+ return PHPExcel_Cell_DataType::TYPE_STRING;
+
+ } elseif ($pValue instanceof PHPExcel_RichText) {
+ return PHPExcel_Cell_DataType::TYPE_STRING;
+
+ } elseif ($pValue{0} === '=' && strlen($pValue) > 1) {
+ return PHPExcel_Cell_DataType::TYPE_FORMULA;
+
+ } elseif (is_bool($pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_BOOL;
+
+ } elseif (is_float($pValue) || is_int($pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_NUMERIC;
+
+ } elseif (preg_match('/^\-?([0-9]+\\.?[0-9]*|[0-9]*\\.?[0-9]+)$/', $pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_NUMERIC;
+
+ } elseif (is_string($pValue) && array_key_exists($pValue, PHPExcel_Cell_DataType::getErrorCodes())) {
+ return PHPExcel_Cell_DataType::TYPE_ERROR;
+
+ } else {
+ return PHPExcel_Cell_DataType::TYPE_STRING;
+
+ }
+ }
+}
diff --git a/Classes/PHPExcel/Cell/Hyperlink.php b/Classes/PHPExcel/Cell/Hyperlink.php
new file mode 100644
index 0000000..e5734f0
--- /dev/null
+++ b/Classes/PHPExcel/Cell/Hyperlink.php
@@ -0,0 +1,127 @@
+_url = $pUrl;
+ $this->_tooltip = $pTooltip;
+ }
+
+ /**
+ * Get URL
+ *
+ * @return string
+ */
+ public function getUrl() {
+ return $this->_url;
+ }
+
+ /**
+ * Set URL
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function setUrl($value = '') {
+ $this->_url = $value;
+ return $this;
+ }
+
+ /**
+ * Get tooltip
+ *
+ * @return string
+ */
+ public function getTooltip() {
+ return $this->_tooltip;
+ }
+
+ /**
+ * Set tooltip
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function setTooltip($value = '') {
+ $this->_tooltip = $value;
+ return $this;
+ }
+
+ /**
+ * Is this hyperlink internal? (to another sheet)
+ *
+ * @return boolean
+ */
+ public function isInternal() {
+ return strpos($this->_url, 'sheet://') !== false;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_url
+ . $this->_tooltip
+ . __CLASS__
+ );
+ }
+}
diff --git a/Classes/PHPExcel/Cell/IValueBinder.php b/Classes/PHPExcel/Cell/IValueBinder.php
new file mode 100644
index 0000000..67bbed0
--- /dev/null
+++ b/Classes/PHPExcel/Cell/IValueBinder.php
@@ -0,0 +1,46 @@
+_author = 'Author';
+ $this->_text = new PHPExcel_RichText();
+ $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1');
+ }
+
+ /**
+ * Get Author
+ *
+ * @return string
+ */
+ public function getAuthor() {
+ return $this->_author;
+ }
+
+ /**
+ * Set Author
+ *
+ * @param string $pValue
+ * @return PHPExcel_Comment
+ */
+ public function setAuthor($pValue = '') {
+ $this->_author = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Rich text comment
+ *
+ * @return PHPExcel_RichText
+ */
+ public function getText() {
+ return $this->_text;
+ }
+
+ /**
+ * Set Rich text comment
+ *
+ * @param PHPExcel_RichText $pValue
+ * @return PHPExcel_Comment
+ */
+ public function setText(PHPExcel_RichText $pValue) {
+ $this->_text = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get comment width (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getWidth() {
+ return $this->_width;
+ }
+
+ /**
+ * Set comment width (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setWidth($value = '96pt') {
+ $this->_width = $value;
+ return $this;
+ }
+
+ /**
+ * Get comment height (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getHeight() {
+ return $this->_height;
+ }
+
+ /**
+ * Set comment height (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setHeight($value = '55.5pt') {
+ $this->_height = $value;
+ return $this;
+ }
+
+ /**
+ * Get left margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getMarginLeft() {
+ return $this->_marginLeft;
+ }
+
+ /**
+ * Set left margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setMarginLeft($value = '59.25pt') {
+ $this->_marginLeft = $value;
+ return $this;
+ }
+
+ /**
+ * Get top margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getMarginTop() {
+ return $this->_marginTop;
+ }
+
+ /**
+ * Set top margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setMarginTop($value = '1.5pt') {
+ $this->_marginTop = $value;
+ return $this;
+ }
+
+ /**
+ * Is the comment visible by default?
+ *
+ * @return boolean
+ */
+ public function getVisible() {
+ return $this->_visible;
+ }
+
+ /**
+ * Set comment default visibility
+ *
+ * @param boolean $value
+ * @return PHPExcel_Comment
+ */
+ public function setVisible($value = false) {
+ $this->_visible = $value;
+ return $this;
+ }
+
+ /**
+ * Get fill color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getFillColor() {
+ return $this->_fillColor;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_author
+ . $this->_text->getHashCode()
+ . $this->_width
+ . $this->_height
+ . $this->_marginLeft
+ . $this->_marginTop
+ . ($this->_visible ? 1 : 0)
+ . $this->_fillColor->getHashCode()
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/DocumentProperties.php b/Classes/PHPExcel/DocumentProperties.php
new file mode 100644
index 0000000..b43022c
--- /dev/null
+++ b/Classes/PHPExcel/DocumentProperties.php
@@ -0,0 +1,366 @@
+_creator = 'Unknown Creator';
+ $this->_lastModifiedBy = $this->_creator;
+ $this->_created = time();
+ $this->_modified = time();
+ $this->_title = "Untitled Spreadsheet";
+ $this->_subject = '';
+ $this->_description = '';
+ $this->_keywords = '';
+ $this->_category = '';
+ $this->_manager = '';
+ $this->_company = 'Microsoft Corporation';
+ }
+
+ /**
+ * Get Creator
+ *
+ * @return string
+ */
+ public function getCreator() {
+ return $this->_creator;
+ }
+
+ /**
+ * Set Creator
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCreator($pValue = '') {
+ $this->_creator = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Last Modified By
+ *
+ * @return string
+ */
+ public function getLastModifiedBy() {
+ return $this->_lastModifiedBy;
+ }
+
+ /**
+ * Set Last Modified By
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setLastModifiedBy($pValue = '') {
+ $this->_lastModifiedBy = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Created
+ *
+ * @return datetime
+ */
+ public function getCreated() {
+ return $this->_created;
+ }
+
+ /**
+ * Set Created
+ *
+ * @param datetime $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCreated($pValue = null) {
+ if (is_null($pValue)) {
+ $pValue = time();
+ }
+ $this->_created = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Modified
+ *
+ * @return datetime
+ */
+ public function getModified() {
+ return $this->_modified;
+ }
+
+ /**
+ * Set Modified
+ *
+ * @param datetime $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setModified($pValue = null) {
+ if (is_null($pValue)) {
+ $pValue = time();
+ }
+ $this->_modified = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Title
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->_title;
+ }
+
+ /**
+ * Set Title
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setTitle($pValue = '') {
+ $this->_title = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Description
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->_description;
+ }
+
+ /**
+ * Set Description
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setDescription($pValue = '') {
+ $this->_description = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Subject
+ *
+ * @return string
+ */
+ public function getSubject() {
+ return $this->_subject;
+ }
+
+ /**
+ * Set Subject
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setSubject($pValue = '') {
+ $this->_subject = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Keywords
+ *
+ * @return string
+ */
+ public function getKeywords() {
+ return $this->_keywords;
+ }
+
+ /**
+ * Set Keywords
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setKeywords($pValue = '') {
+ $this->_keywords = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Category
+ *
+ * @return string
+ */
+ public function getCategory() {
+ return $this->_category;
+ }
+
+ /**
+ * Set Category
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCategory($pValue = '') {
+ $this->_category = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Company
+ *
+ * @return string
+ */
+ public function getCompany() {
+ return $this->_company;
+ }
+
+ /**
+ * Set Company
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCompany($pValue = '') {
+ $this->_company = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Manager
+ *
+ * @return string
+ */
+ public function getManager() {
+ return $this->_manager;
+ }
+
+ /**
+ * Set Manager
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setManager($pValue = '') {
+ $this->_manager = $pValue;
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/DocumentSecurity.php b/Classes/PHPExcel/DocumentSecurity.php
new file mode 100644
index 0000000..70eec2a
--- /dev/null
+++ b/Classes/PHPExcel/DocumentSecurity.php
@@ -0,0 +1,218 @@
+_lockRevision = false;
+ $this->_lockStructure = false;
+ $this->_lockWindows = false;
+ $this->_revisionsPassword = '';
+ $this->_workbookPassword = '';
+ }
+
+ /**
+ * Is some sort of dcument security enabled?
+ *
+ * @return boolean
+ */
+ function isSecurityEnabled() {
+ return $this->_lockRevision ||
+ $this->_lockStructure ||
+ $this->_lockWindows;
+ }
+
+ /**
+ * Get LockRevision
+ *
+ * @return boolean
+ */
+ function getLockRevision() {
+ return $this->_lockRevision;
+ }
+
+ /**
+ * Set LockRevision
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setLockRevision($pValue = false) {
+ $this->_lockRevision = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get LockStructure
+ *
+ * @return boolean
+ */
+ function getLockStructure() {
+ return $this->_lockStructure;
+ }
+
+ /**
+ * Set LockStructure
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setLockStructure($pValue = false) {
+ $this->_lockStructure = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get LockWindows
+ *
+ * @return boolean
+ */
+ function getLockWindows() {
+ return $this->_lockWindows;
+ }
+
+ /**
+ * Set LockWindows
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setLockWindows($pValue = false) {
+ $this->_lockWindows = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get RevisionsPassword (hashed)
+ *
+ * @return string
+ */
+ function getRevisionsPassword() {
+ return $this->_revisionsPassword;
+ }
+
+ /**
+ * Set RevisionsPassword
+ *
+ * @param string $pValue
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setRevisionsPassword($pValue = '', $pAlreadyHashed = false) {
+ if (!$pAlreadyHashed) {
+ $pValue = PHPExcel_Shared_PasswordHasher::hashPassword($pValue);
+ }
+ $this->_revisionsPassword = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get WorkbookPassword (hashed)
+ *
+ * @return string
+ */
+ function getWorkbookPassword() {
+ return $this->_workbookPassword;
+ }
+
+ /**
+ * Set WorkbookPassword
+ *
+ * @param string $pValue
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setWorkbookPassword($pValue = '', $pAlreadyHashed = false) {
+ if (!$pAlreadyHashed) {
+ $pValue = PHPExcel_Shared_PasswordHasher::hashPassword($pValue);
+ }
+ $this->_workbookPassword = $pValue;
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/HashTable.php b/Classes/PHPExcel/HashTable.php
new file mode 100644
index 0000000..23824d8
--- /dev/null
+++ b/Classes/PHPExcel/HashTable.php
@@ -0,0 +1,200 @@
+addFromSource($pSource);
+ }
+ }
+
+ /**
+ * Add HashTable items from source
+ *
+ * @param PHPExcel_IComparable[] $pSource Source array to create HashTable from
+ * @throws Exception
+ */
+ public function addFromSource($pSource = null) {
+ // Check if an array was passed
+ if ($pSource == null) {
+ return;
+ } else if (!is_array($pSource)) {
+ throw new Exception('Invalid array parameter passed.');
+ }
+
+ foreach ($pSource as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Add HashTable item
+ *
+ * @param PHPExcel_IComparable $pSource Item to add
+ * @throws Exception
+ */
+ public function add(PHPExcel_IComparable $pSource = null) {
+ if (!isset($this->_items[ $pSource->getHashCode() ])) {
+ $this->_items[ $pSource->getHashCode() ] = $pSource;
+ $this->_keyMap[ count($this->_items) - 1 ] = $pSource->getHashCode();
+ }
+ }
+
+ /**
+ * Remove HashTable item
+ *
+ * @param PHPExcel_IComparable $pSource Item to remove
+ * @throws Exception
+ */
+ public function remove(PHPExcel_IComparable $pSource = null) {
+ if (isset($this->_items[ $pSource->getHashCode() ])) {
+ unset($this->_items[ $pSource->getHashCode() ]);
+
+ $deleteKey = -1;
+ foreach ($this->_keyMap as $key => $value) {
+ if ($deleteKey >= 0) {
+ $this->_keyMap[$key - 1] = $value;
+ }
+
+ if ($value == $pSource->getHashCode()) {
+ $deleteKey = $key;
+ }
+ }
+ unset($this->_keyMap[ count($this->_keyMap) - 1 ]);
+ }
+ }
+
+ /**
+ * Clear HashTable
+ *
+ */
+ public function clear() {
+ $this->_items = array();
+ $this->_keyMap = array();
+ }
+
+ /**
+ * Count
+ *
+ * @return int
+ */
+ public function count() {
+ return count($this->_items);
+ }
+
+ /**
+ * Get index for hash code
+ *
+ * @param string $pHashCode
+ * @return int Index
+ */
+ public function getIndexForHashCode($pHashCode = '') {
+ return array_search($pHashCode, $this->_keyMap);
+ }
+
+ /**
+ * Get by index
+ *
+ * @param int $pIndex
+ * @return PHPExcel_IComparable
+ *
+ */
+ public function getByIndex($pIndex = 0) {
+ if (isset($this->_keyMap[$pIndex])) {
+ return $this->getByHashCode( $this->_keyMap[$pIndex] );
+ }
+
+ return null;
+ }
+
+ /**
+ * Get by hashcode
+ *
+ * @param string $pHashCode
+ * @return PHPExcel_IComparable
+ *
+ */
+ public function getByHashCode($pHashCode = '') {
+ if (isset($this->_items[$pHashCode])) {
+ return $this->_items[$pHashCode];
+ }
+
+ return null;
+ }
+
+ /**
+ * HashTable to array
+ *
+ * @return PHPExcel_IComparable[]
+ */
+ public function toArray() {
+ return $this->_items;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/IComparable.php b/Classes/PHPExcel/IComparable.php
new file mode 100644
index 0000000..d68201d
--- /dev/null
+++ b/Classes/PHPExcel/IComparable.php
@@ -0,0 +1,43 @@
+ 'IWriter', 'path' => 'PHPExcel/Writer/{0}.php', 'class' => 'PHPExcel_Writer_{0}' ),
+ array( 'type' => 'IReader', 'path' => 'PHPExcel/Reader/{0}.php', 'class' => 'PHPExcel_Reader_{0}' )
+ );
+
+ /**
+ * Autoresolve classes
+ *
+ * @var array
+ * @access private
+ * @static
+ */
+ private static $_autoResolveClasses = array(
+ 'Excel2007',
+ 'Excel5',
+ 'Excel2003XML',
+ 'OOCalc',
+ 'SYLK',
+ 'Serialized',
+ 'CSV',
+ );
+
+ /**
+ * Private constructor for PHPExcel_IOFactory
+ */
+ private function __construct() { }
+
+ /**
+ * Get search locations
+ *
+ * @static
+ * @access public
+ * @return array
+ */
+ public static function getSearchLocations() {
+ return self::$_searchLocations;
+ } // function getSearchLocations()
+
+ /**
+ * Set search locations
+ *
+ * @static
+ * @access public
+ * @param array $value
+ * @throws Exception
+ */
+ public static function setSearchLocations($value) {
+ if (is_array($value)) {
+ self::$_searchLocations = $value;
+ } else {
+ throw new Exception('Invalid parameter passed.');
+ }
+ } // function setSearchLocations()
+
+ /**
+ * Add search location
+ *
+ * @static
+ * @access public
+ * @param string $type Example: IWriter
+ * @param string $location Example: PHPExcel/Writer/{0}.php
+ * @param string $classname Example: PHPExcel_Writer_{0}
+ */
+ public static function addSearchLocation($type = '', $location = '', $classname = '') {
+ self::$_searchLocations[] = array( 'type' => $type, 'path' => $location, 'class' => $classname );
+ } // function addSearchLocation()
+
+ /**
+ * Create PHPExcel_Writer_IWriter
+ *
+ * @static
+ * @access public
+ * @param PHPExcel $phpExcel
+ * @param string $writerType Example: Excel2007
+ * @return PHPExcel_Writer_IWriter
+ * @throws Exception
+ */
+ public static function createWriter(PHPExcel $phpExcel, $writerType = '') {
+ // Search type
+ $searchType = 'IWriter';
+
+ // Include class
+ foreach (self::$_searchLocations as $searchLocation) {
+ if ($searchLocation['type'] == $searchType) {
+ $className = str_replace('{0}', $writerType, $searchLocation['class']);
+ $classFile = str_replace('{0}', $writerType, $searchLocation['path']);
+
+ $instance = new $className($phpExcel);
+ if (!is_null($instance)) {
+ return $instance;
+ }
+ }
+ }
+
+ // Nothing found...
+ throw new Exception("No $searchType found for type $writerType");
+ } // function createWriter()
+
+ /**
+ * Create PHPExcel_Reader_IReader
+ *
+ * @static
+ * @access public
+ * @param string $readerType Example: Excel2007
+ * @return PHPExcel_Reader_IReader
+ * @throws Exception
+ */
+ public static function createReader($readerType = '') {
+ // Search type
+ $searchType = 'IReader';
+
+ // Include class
+ foreach (self::$_searchLocations as $searchLocation) {
+ if ($searchLocation['type'] == $searchType) {
+ $className = str_replace('{0}', $readerType, $searchLocation['class']);
+ $classFile = str_replace('{0}', $readerType, $searchLocation['path']);
+
+ $instance = new $className();
+ if (!is_null($instance)) {
+ return $instance;
+ }
+ }
+ }
+
+ // Nothing found...
+ throw new Exception("No $searchType found for type $readerType");
+ } // function createReader()
+
+ /**
+ * Loads PHPExcel from file using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @static
+ * @access public
+ * @param string $pFileName
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public static function load($pFilename) {
+ $reader = self::createReaderForFile($pFilename);
+ return $reader->load($pFilename);
+ } // function load()
+
+ /**
+ * Identify file type using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @static
+ * @access public
+ * @param string $pFileName
+ * @return string
+ * @throws Exception
+ */
+ public static function identify($pFilename) {
+ $reader = self::createReaderForFile($pFilename);
+ $className = get_class($reader);
+ $classType = explode('_',$className);
+ unset($reader);
+ return array_pop($classType);
+ } // function identify()
+
+ /**
+ * Create PHPExcel_Reader_IReader for file using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @static
+ * @access public
+ * @param string $pFileName
+ * @return PHPExcel_Reader_IReader
+ * @throws Exception
+ */
+ public static function createReaderForFile($pFilename) {
+
+ // First, lucky guess by inspecting file extension
+ $pathinfo = pathinfo($pFilename);
+
+ if (isset($pathinfo['extension'])) {
+ switch (strtolower($pathinfo['extension'])) {
+ case 'xlsx':
+ $reader = self::createReader('Excel2007');
+ break;
+ case 'xls':
+ $reader = self::createReader('Excel5');
+ break;
+ case 'ods':
+ $reader = self::createReader('OOCalc');
+ break;
+ case 'slk':
+ $reader = self::createReader('SYLK');
+ break;
+ case 'xml':
+ $reader = self::createReader('Excel2003XML');
+ break;
+ case 'csv':
+ // Do nothing
+ // We must not try to use CSV reader since it loads
+ // all files including Excel files etc.
+ break;
+ default:
+ break;
+ }
+
+ // Let's see if we are lucky
+ if (isset($reader) && $reader->canRead($pFilename)) {
+ return $reader;
+ }
+
+ }
+
+ // If we reach here then "lucky guess" didn't give any result
+
+ // Try loading using self::$_autoResolveClasses
+ foreach (self::$_autoResolveClasses as $autoResolveClass) {
+ $reader = self::createReader($autoResolveClass);
+ if ($reader->canRead($pFilename)) {
+ return $reader;
+ }
+ }
+
+ } // function createReaderForFile()
+}
diff --git a/Classes/PHPExcel/NamedRange.php b/Classes/PHPExcel/NamedRange.php
new file mode 100644
index 0000000..3008747
--- /dev/null
+++ b/Classes/PHPExcel/NamedRange.php
@@ -0,0 +1,245 @@
+_worksheet)
+ *
+ * @var bool
+ */
+ private $_localOnly;
+
+ /**
+ * Scope
+ *
+ * @var PHPExcel_Worksheet
+ */
+ private $_scope;
+
+ /**
+ * Create a new NamedRange
+ *
+ * @param string $pName
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @param string $pRange
+ * @param bool $pLocalOnly
+ * @param PHPExcel_Worksheet|null $pScope Scope. Only applies when $pLocalOnly = true. Null for global scope.
+ */
+ public function __construct($pName = null, PHPExcel_Worksheet $pWorksheet, $pRange = 'A1', $pLocalOnly = false, $pScope = null)
+ {
+ // Validate data
+ if (is_null($pName) || is_null($pWorksheet)|| is_null($pRange)) {
+ throw new Exception('Parameters can not be null.');
+ }
+
+ // Set local members
+ $this->_name = $pName;
+ $this->_worksheet = $pWorksheet;
+ $this->_range = $pRange;
+ $this->_localOnly = $pLocalOnly;
+ $this->_scope = ($pLocalOnly == true) ?
+ (($pScope == null) ? $pWorksheet : $pScope) : null;
+ }
+
+ /**
+ * Get name
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->_name;
+ }
+
+ /**
+ * Set name
+ *
+ * @param string $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setName($value = null) {
+ if (!is_null($value)) {
+ // Old title
+ $oldTitle = $this->_name;
+
+ // Re-attach
+ if (!is_null($this->_worksheet)) {
+ $this->_worksheet->getParent()->removeNamedRange($this->_name,$this->_worksheet);
+ }
+ $this->_name = $value;
+
+ if (!is_null($this->_worksheet)) {
+ $this->_worksheet->getParent()->addNamedRange($this);
+ }
+
+ // New title
+ $newTitle = $this->_name;
+ PHPExcel_ReferenceHelper::getInstance()->updateNamedFormulas($this->_worksheet->getParent(), $oldTitle, $newTitle);
+ }
+ return $this;
+ }
+
+ /**
+ * Get worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getWorksheet() {
+ return $this->_worksheet;
+ }
+
+ /**
+ * Set worksheet
+ *
+ * @param PHPExcel_Worksheet $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setWorksheet(PHPExcel_Worksheet $value = null) {
+ if (!is_null($value)) {
+ $this->_worksheet = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Get range
+ *
+ * @return string
+ */
+ public function getRange() {
+ return $this->_range;
+ }
+
+ /**
+ * Set range
+ *
+ * @param string $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setRange($value = null) {
+ if (!is_null($value)) {
+ $this->_range = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Get localOnly
+ *
+ * @return bool
+ */
+ public function getLocalOnly() {
+ return $this->_localOnly;
+ }
+
+ /**
+ * Set localOnly
+ *
+ * @param bool $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setLocalOnly($value = false) {
+ $this->_localOnly = $value;
+ $this->_scope = $value ? $this->_worksheet : null;
+ return $this;
+ }
+
+ /**
+ * Get scope
+ *
+ * @return PHPExcel_Worksheet|null
+ */
+ public function getScope() {
+ return $this->_scope;
+ }
+
+ /**
+ * Set scope
+ *
+ * @param PHPExcel_Worksheet|null $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setScope(PHPExcel_Worksheet $value = null) {
+ $this->_scope = $value;
+ $this->_localOnly = ($value == null) ? false : true;
+ return $this;
+ }
+
+ /**
+ * Resolve a named range to a regular cell range
+ *
+ * @param string $pNamedRange Named range
+ * @param PHPExcel_Worksheet|null $pSheet Scope. Use null for global scope
+ * @return PHPExcel_NamedRange
+ */
+ public static function resolveRange($pNamedRange = '', PHPExcel_Worksheet $pSheet) {
+ return $pSheet->getParent()->getNamedRange($pNamedRange, $pSheet);
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/Reader/CSV.php b/Classes/PHPExcel/Reader/CSV.php
new file mode 100644
index 0000000..adc8a68
--- /dev/null
+++ b/Classes/PHPExcel/Reader/CSV.php
@@ -0,0 +1,351 @@
+_inputEncoding = 'UTF-8';
+ $this->_delimiter = ',';
+ $this->_enclosure = '"';
+ $this->_lineEnding = PHP_EOL;
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ } // function __construct()
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @access public
+ * @param string $pFileName
+ * @return boolean
+ * @throws Exception
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ return true;
+ } // function canRead()
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @access public
+ * @param string $pFilename
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ } // function load()
+
+ /**
+ * Read filter
+ *
+ * @access public
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ } // function getReadFilter()
+
+ /**
+ * Set read filter
+ *
+ * @access public
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ } // function setReadFilter()
+
+ /**
+ * Set input encoding
+ *
+ * @access public
+ * @param string $pValue Input encoding
+ */
+ public function setInputEncoding($pValue = 'UTF-8')
+ {
+ $this->_inputEncoding = $pValue;
+ return $this;
+ } // function setInputEncoding()
+
+ /**
+ * Get input encoding
+ *
+ * @access public
+ * @return string
+ */
+ public function getInputEncoding()
+ {
+ return $this->_inputEncoding;
+ } // function getInputEncoding()
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @access public
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Create new PHPExcel
+ while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) {
+ $objPHPExcel->createSheet();
+ }
+ $objPHPExcel->setActiveSheetIndex( $this->_sheetIndex );
+
+ // Open file
+ $fileHandle = fopen($pFilename, 'r');
+ if ($fileHandle === false) {
+ throw new Exception("Could not open file $pFilename for reading.");
+ }
+
+ // Skip BOM, if any
+ switch ($this->_inputEncoding) {
+ case 'UTF-8':
+ fgets($fileHandle, 4) == "\xEF\xBB\xBF" ?
+ fseek($fileHandle, 3) : fseek($fileHandle, 0);
+ break;
+ default:
+ break;
+ }
+
+ // Loop through file
+ $currentRow = 0;
+ $rowData = array();
+ while (($rowData = fgetcsv($fileHandle, 0, $this->_delimiter, $this->_enclosure)) !== FALSE) {
+ ++$currentRow;
+ $rowDataCount = count($rowData);
+ for ($i = 0; $i < $rowDataCount; ++$i) {
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
+ if ($rowData[$i] != '' && $this->_readFilter->readCell($columnLetter, $currentRow)) {
+ // Unescape enclosures
+ $rowData[$i] = str_replace("\\" . $this->_enclosure, $this->_enclosure, $rowData[$i]);
+ $rowData[$i] = str_replace($this->_enclosure . $this->_enclosure, $this->_enclosure, $rowData[$i]);
+
+ // Convert encoding if necessary
+ if ($this->_inputEncoding !== 'UTF-8') {
+ $rowData[$i] = PHPExcel_Shared_String::ConvertEncoding($rowData[$i], 'UTF-8', $this->_inputEncoding);
+ }
+
+ // Set cell value
+ $objPHPExcel->getActiveSheet()->getCell($columnLetter . $currentRow)->setValue($rowData[$i]);
+ }
+ }
+ }
+
+ // Close file
+ fclose($fileHandle);
+
+ // Return
+ return $objPHPExcel;
+ } // function loadIntoExisting()
+
+ /**
+ * Get delimiter
+ *
+ * @access public
+ * @return string
+ */
+ public function getDelimiter() {
+ return $this->_delimiter;
+ } // function getDelimiter()
+
+ /**
+ * Set delimiter
+ *
+ * @access public
+ * @param string $pValue Delimiter, defaults to ,
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setDelimiter($pValue = ',') {
+ $this->_delimiter = $pValue;
+ return $this;
+ } // function setDelimiter()
+
+ /**
+ * Get enclosure
+ *
+ * @access public
+ * @return string
+ */
+ public function getEnclosure() {
+ return $this->_enclosure;
+ } // function getEnclosure()
+
+ /**
+ * Set enclosure
+ *
+ * @access public
+ * @param string $pValue Enclosure, defaults to "
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setEnclosure($pValue = '"') {
+ if ($pValue == '') {
+ $pValue = '"';
+ }
+ $this->_enclosure = $pValue;
+ return $this;
+ } // function setEnclosure()
+
+ /**
+ * Get line ending
+ *
+ * @access public
+ * @return string
+ */
+ public function getLineEnding() {
+ return $this->_lineEnding;
+ } // function getLineEnding()
+
+ /**
+ * Set line ending
+ *
+ * @access public
+ * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setLineEnding($pValue = PHP_EOL) {
+ $this->_lineEnding = $pValue;
+ return $this;
+ } // function setLineEnding()
+
+ /**
+ * Get sheet index
+ *
+ * @access public
+ * @return int
+ */
+ public function getSheetIndex() {
+ return $this->_sheetIndex;
+ } // function getSheetIndex()
+
+ /**
+ * Set sheet index
+ *
+ * @access public
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setSheetIndex($pValue = 0) {
+ $this->_sheetIndex = $pValue;
+ return $this;
+ } // function setSheetIndex()
+}
diff --git a/Classes/PHPExcel/Reader/DefaultReadFilter.php b/Classes/PHPExcel/Reader/DefaultReadFilter.php
new file mode 100644
index 0000000..f19a058
--- /dev/null
+++ b/Classes/PHPExcel/Reader/DefaultReadFilter.php
@@ -0,0 +1,64 @@
+_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadDataOnly($pValue = false) {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_Excel2003XML
+ */
+ public function __construct() {
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+
+// Office xmlns:o="urn:schemas-microsoft-com:office:office"
+// Excel xmlns:x="urn:schemas-microsoft-com:office:excel"
+// XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
+// Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
+// XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
+// XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
+// MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset"
+// Rowset xmlns:z="#RowsetSchema"
+//
+
+ $signature = array(
+ '',
+ ''
+ );
+
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Read sample data (first 2 KB will do)
+ $fh = fopen($pFilename, 'r');
+ $data = fread($fh, 2048);
+ fclose($fh);
+
+ $headers = explode("\n",$data);
+ $valid = true;
+ foreach($signature as $key => $match) {
+ if (isset($headers[$key])) {
+ $line = trim(rtrim($headers[$key], "\r\n"));
+ if ($line != $match) {
+ $valid = false;
+ break;
+ }
+ } else {
+ $valid = false;
+ break;
+ }
+ }
+
+ return $valid;
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ }
+
+ private static function identifyFixedStyleValue($styleList,&$styleAttributeValue) {
+ $styleAttributeValue = strtolower($styleAttributeValue);
+ foreach($styleList as $style) {
+ if ($styleAttributeValue == strtolower($style)) {
+ $styleAttributeValue = $style;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * pixel units to excel width units(units of 1/256th of a character width)
+ * @param pxs
+ * @return
+ */
+ private static function _pixel2WidthUnits($pxs) {
+ $UNIT_OFFSET_MAP = array(0, 36, 73, 109, 146, 182, 219);
+
+ $widthUnits = 256 * ($pxs / 7);
+ $widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
+ return $widthUnits;
+ }
+
+ /**
+ * excel width units(units of 1/256th of a character width) to pixel units
+ * @param widthUnits
+ * @return
+ */
+ private static function _widthUnits2Pixel($widthUnits) {
+ $pixels = ($widthUnits / 256) * 7;
+ $offsetWidthUnits = $widthUnits % 256;
+ $pixels += round($offsetWidthUnits / (256 / 7));
+ return $pixels;
+ }
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ $fromFormats = array('\-', '\ ');
+ $toFormats = array('-', ' ');
+
+ $underlineStyles = array (
+ PHPExcel_Style_Font::UNDERLINE_NONE,
+ PHPExcel_Style_Font::UNDERLINE_DOUBLE,
+ PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING,
+ PHPExcel_Style_Font::UNDERLINE_SINGLE,
+ PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING
+ );
+ $verticalAlignmentStyles = array (
+ PHPExcel_Style_Alignment::VERTICAL_BOTTOM,
+ PHPExcel_Style_Alignment::VERTICAL_TOP,
+ PHPExcel_Style_Alignment::VERTICAL_CENTER,
+ PHPExcel_Style_Alignment::VERTICAL_JUSTIFY
+ );
+ $horizontalAlignmentStyles = array (
+ PHPExcel_Style_Alignment::HORIZONTAL_GENERAL,
+ PHPExcel_Style_Alignment::HORIZONTAL_LEFT,
+ PHPExcel_Style_Alignment::HORIZONTAL_RIGHT,
+ PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
+ PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+ PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY
+ );
+
+
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ $xml = simplexml_load_file($pFilename);
+ $namespaces = $xml->getNamespaces(true);
+// echo '';
+// print_r($namespaces);
+// echo '
';
+//
+// echo '';
+// print_r($xml);
+// echo '
';
+//
+ $docProps = $objPHPExcel->getProperties();
+ foreach($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
+ switch ($propertyName) {
+ case 'Title' :
+ $docProps->setTitle($propertyValue);
+ break;
+ case 'Subject' :
+ $docProps->setSubject($propertyValue);
+ break;
+ case 'Author' :
+ $docProps->setCreator($propertyValue);
+ break;
+ case 'Created' :
+ $creationDate = strtotime($propertyValue);
+ $docProps->setCreated($creationDate);
+ break;
+ case 'LastAuthor' :
+ $docProps->setLastModifiedBy($propertyValue);
+ break;
+ case 'Company' :
+ $docProps->setCompany($propertyValue);
+ break;
+ case 'Category' :
+ $docProps->setCategory($propertyValue);
+ break;
+ case 'Keywords' :
+ $docProps->setKeywords($propertyValue);
+ break;
+ case 'Description' :
+ $docProps->setDescription($propertyValue);
+ break;
+ }
+ }
+
+
+ foreach($xml->Styles[0] as $style) {
+ $style_ss = $style->attributes($namespaces['ss']);
+ $styleID = (string) $style_ss['ID'];
+// echo 'Style ID = '.$styleID.'
';
+ if ($styleID == 'Default') {
+ $this->_styles['Default'] = array();
+ } else {
+ $this->_styles[$styleID] = $this->_styles['Default'];
+ }
+ foreach ($style as $styleType => $styleData) {
+ $styleAttributes = $styleData->attributes($namespaces['ss']);
+// echo $styleType.'
';
+ switch ($styleType) {
+ case 'Alignment' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.'
';
+ $styleAttributeValue = (string) $styleAttributeValue;
+ switch ($styleAttributeKey) {
+ case 'Vertical' :
+ if (self::identifyFixedStyleValue($verticalAlignmentStyles,$styleAttributeValue)) {
+ $this->_styles[$styleID]['alignment']['vertical'] = $styleAttributeValue;
+ }
+ break;
+ case 'Horizontal' :
+ if (self::identifyFixedStyleValue($horizontalAlignmentStyles,$styleAttributeValue)) {
+ $this->_styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue;
+ }
+ break;
+ case 'WrapText' :
+ $this->_styles[$styleID]['alignment']['wrap'] = true;
+ break;
+ }
+ }
+ break;
+ case 'Borders' :
+ foreach($styleData->Border as $borderStyle) {
+ $borderAttributes = $borderStyle->attributes($namespaces['ss']);
+ $thisBorder = array();
+ foreach($borderAttributes as $borderStyleKey => $borderStyleValue) {
+// echo $borderStyleKey.' = '.$borderStyleValue.'
';
+ switch ($borderStyleKey) {
+ case 'LineStyle' :
+ $thisBorder['style'] = PHPExcel_Style_Border::BORDER_MEDIUM;
+// $thisBorder['style'] = $borderStyleValue;
+ break;
+ case 'Weight' :
+// $thisBorder['style'] = $borderStyleValue;
+ break;
+ case 'Position' :
+ $borderPosition = strtolower($borderStyleValue);
+ break;
+ case 'Color' :
+ $borderColour = substr($borderStyleValue,1);
+ $thisBorder['color']['rgb'] = $borderColour;
+ break;
+ }
+ }
+ if (count($thisBorder) > 0) {
+ if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) {
+ $this->_styles[$styleID]['borders'][$borderPosition] = $thisBorder;
+ }
+ }
+ }
+ break;
+ case 'Font' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.'
';
+ $styleAttributeValue = (string) $styleAttributeValue;
+ switch ($styleAttributeKey) {
+ case 'FontName' :
+ $this->_styles[$styleID]['font']['name'] = $styleAttributeValue;
+ break;
+ case 'Size' :
+ $this->_styles[$styleID]['font']['size'] = $styleAttributeValue;
+ break;
+ case 'Color' :
+ $this->_styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue,1);
+ break;
+ case 'Bold' :
+ $this->_styles[$styleID]['font']['bold'] = true;
+ break;
+ case 'Italic' :
+ $this->_styles[$styleID]['font']['italic'] = true;
+ break;
+ case 'Underline' :
+ if (self::identifyFixedStyleValue($underlineStyles,$styleAttributeValue)) {
+ $this->_styles[$styleID]['font']['underline'] = $styleAttributeValue;
+ }
+ break;
+ }
+ }
+ break;
+ case 'Interior' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.'
';
+ switch ($styleAttributeKey) {
+ case 'Color' :
+ $this->_styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue,1);
+ break;
+ }
+ }
+ break;
+ case 'NumberFormat' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.'
';
+ $styleAttributeValue = str_replace($fromFormats,$toFormats,$styleAttributeValue);
+ switch ($styleAttributeValue) {
+ case 'Short Date' :
+ $styleAttributeValue = 'dd/mm/yyyy';
+ break;
+ }
+ if ($styleAttributeValue > '') {
+ $this->_styles[$styleID]['numberformat']['code'] = $styleAttributeValue;
+ }
+ }
+ break;
+ case 'Protection' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.'
';
+ }
+ break;
+ }
+ }
+// print_r($this->_styles[$styleID]);
+// echo '
';
+ }
+// echo '
';
+
+ $worksheetID = 0;
+ foreach($xml->Worksheet as $worksheet) {
+ $worksheet_ss = $worksheet->attributes($namespaces['ss']);
+ if ((isset($this->_loadSheetsOnly)) && (isset($worksheet_ss['Name'])) &&
+ (!in_array($worksheet_ss['Name'], $this->_loadSheetsOnly))) {
+ continue;
+ }
+
+ // Create new Worksheet
+ $objPHPExcel->createSheet();
+ $objPHPExcel->setActiveSheetIndex($worksheetID);
+ if (isset($worksheet_ss['Name'])) {
+ $worksheetName = (string) $worksheet_ss['Name'];
+ $objPHPExcel->getActiveSheet()->setTitle($worksheetName);
+ }
+
+ $columnID = 'A';
+ foreach($worksheet->Table->Column as $columnData) {
+ $columnData_ss = $columnData->attributes($namespaces['ss']);
+ if (isset($columnData_ss['Index'])) {
+ $columnID = PHPExcel_Cell::stringFromColumnIndex($columnData_ss['Index']-1);
+ }
+ if (isset($columnData_ss['Width'])) {
+ $columnWidth = $columnData_ss['Width'];
+// echo 'Setting column width for '.$columnID.' to '.$columnWidth.'
';
+ $objPHPExcel->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
+ }
+ ++$columnID;
+ }
+
+ $rowID = 1;
+ foreach($worksheet->Table->Row as $rowData) {
+ $row_ss = $rowData->attributes($namespaces['ss']);
+ if (isset($row_ss['Index'])) {
+ $rowID = (integer) $row_ss['Index'];
+ }
+// echo 'Row '.$rowID.'
';
+ if (isset($row_ss['StyleID'])) {
+ $rowStyle = $row_ss['StyleID'];
+ }
+ if (isset($row_ss['Height'])) {
+ $rowHeight = $row_ss['Height'];
+// echo 'Setting row height to '.$rowHeight.'
';
+ $objPHPExcel->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight);
+ }
+ $columnID = 'A';
+ foreach($rowData->Cell as $cell) {
+
+ $cell_ss = $cell->attributes($namespaces['ss']);
+ if (isset($cell_ss['Index'])) {
+ $columnID = PHPExcel_Cell::stringFromColumnIndex($cell_ss['Index']-1);
+ }
+ $cellRange = $columnID.$rowID;
+
+ if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
+ $columnTo = $columnID;
+ if (isset($cell_ss['MergeAcross'])) {
+ $columnTo = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cell_ss['MergeAcross'] -1);
+ }
+ $rowTo = $rowID;
+ if (isset($cell_ss['MergeDown'])) {
+ $rowTo = $rowTo + $cell_ss['MergeDown'];
+ }
+ $cellRange .= ':'.$columnTo.$rowTo;
+ $objPHPExcel->getActiveSheet()->mergeCells($cellRange);
+ }
+
+ $hasCalculatedValue = false;
+ $cellDataFormula = '';
+ if (isset($cell_ss['Formula'])) {
+ $cellDataFormula = $cell_ss['Formula'];
+ // added this as a check for array formulas
+ if (isset($cell_ss['ArrayRange'])) {
+ $cellDataCSEFormula = $cell_ss['ArrayRange'];
+// echo "found an array formula at ".$columnID.$rowID."
";
+ }
+ $hasCalculatedValue = true;
+ }
+ if (isset($cell->Data)) {
+ $cellValue = $cellData = $cell->Data;
+ $type = PHPExcel_Cell_DataType::TYPE_NULL;
+ $cellData_ss = $cellData->attributes($namespaces['ss']);
+ if (isset($cellData_ss['Type'])) {
+ $cellDataType = $cellData_ss['Type'];
+ switch ($cellDataType) {
+ /*
+ const TYPE_STRING = 's';
+ const TYPE_FORMULA = 'f';
+ const TYPE_NUMERIC = 'n';
+ const TYPE_BOOL = 'b';
+ const TYPE_NULL = 's';
+ const TYPE_INLINE = 'inlineStr';
+ const TYPE_ERROR = 'e';
+ */
+ case 'String' :
+ $type = PHPExcel_Cell_DataType::TYPE_STRING;
+ break;
+ case 'Number' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $cellValue = (float) $cellValue;
+ if (floor($cellValue) == $cellValue) {
+ $cellValue = (integer) $cellValue;
+ }
+ break;
+ case 'Boolean' :
+ $type = PHPExcel_Cell_DataType::TYPE_BOOL;
+ $cellValue = ($cellValue != 0);
+ break;
+ case 'DateTime' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $cellValue = PHPExcel_Shared_Date::PHPToExcel(strtotime($cellValue));
+ break;
+ case 'Error' :
+ $type = PHPExcel_Cell_DataType::TYPE_ERROR;
+ break;
+ }
+ }
+ if ($hasCalculatedValue) {
+ $type = PHPExcel_Cell_DataType::TYPE_FORMULA;
+ $columnNumber = PHPExcel_Cell::columnIndexFromString($columnID);
+ // Convert R1C1 style references to A1 style references (but only when not quoted)
+ $temp = explode('"',$cellDataFormula);
+ foreach($temp as $key => &$value) {
+ // Only replace in alternate array entries (i.e. non-quoted blocks)
+ if (($key % 2) == 0) {
+ preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/',$value, $cellReferences,PREG_SET_ORDER+PREG_OFFSET_CAPTURE);
+ // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
+ // through the formula from left to right. Reversing means that we work right to left.through
+ // the formula
+ $cellReferences = array_reverse($cellReferences);
+ // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
+ // then modify the formula to use that new reference
+ foreach($cellReferences as $cellReference) {
+ $rowReference = $cellReference[2][0];
+ // Empty R reference is the current row
+ if ($rowReference == '') $rowReference = $rowID;
+ // Bracketed R references are relative to the current row
+ if ($rowReference{0} == '[') $rowReference = $rowID + trim($rowReference,'[]');
+ $columnReference = $cellReference[4][0];
+ // Empty C reference is the current column
+ if ($columnReference == '') $columnReference = $columnNumber;
+ // Bracketed C references are relative to the current column
+ if ($columnReference{0} == '[') $columnReference = $columnNumber + trim($columnReference,'[]');
+ $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference;
+ $value = substr_replace($value,$A1CellReference,$cellReference[0][1],strlen($cellReference[0][0]));
+ }
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $cellDataFormula = implode('"',$temp);
+ }
+
+// echo 'Cell '.$columnID.$rowID.' is a '.$type.' with a value of '.(($hasCalculatedValue) ? $cellDataFormula : $cellValue).'
';
+//
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue),$type);
+ if ($hasCalculatedValue) {
+// echo 'Forumla result is '.$cellValue.'
';
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setCalculatedValue($cellValue);
+ }
+ }
+ if (isset($cell_ss['StyleID'])) {
+ $style = (string) $cell_ss['StyleID'];
+// echo 'Cell style for '.$columnID.$rowID.' is '.$style.'
';
+ if ((isset($this->_styles[$style])) && (count($this->_styles[$style]) > 0)) {
+// echo 'Cell '.$columnID.$rowID.'
';
+// print_r($this->_styles[$style]);
+// echo '
';
+ if (!$objPHPExcel->getActiveSheet()->cellExists($columnID.$rowID)) {
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValue(NULL);
+ }
+ $objPHPExcel->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->_styles[$style]);
+ }
+ }
+ ++$columnID;
+ }
+ ++$rowID;
+ }
+ ++$worksheetID;
+ }
+
+ // Return
+ return $objPHPExcel;
+ }
+
+ /**
+ * Get sheet index
+ *
+ * @return int
+ */
+ public function getSheetIndex() {
+ return $this->_sheetIndex;
+ }
+
+ /**
+ * Set sheet index
+ *
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Reader_Excel2003XML
+ */
+ public function setSheetIndex($pValue = 0) {
+ $this->_sheetIndex = $pValue;
+ return $this;
+ }
+}
diff --git a/Classes/PHPExcel/Reader/Excel2007.php b/Classes/PHPExcel/Reader/Excel2007.php
new file mode 100644
index 0000000..69e7ece
--- /dev/null
+++ b/Classes/PHPExcel/Reader/Excel2007.php
@@ -0,0 +1,1656 @@
+_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadDataOnly($pValue = false) {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_Excel2007 instance
+ */
+ public function __construct() {
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ $this->_referenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if zip class exists
+ if (!class_exists('ZipArchive')) {
+ return false;
+ }
+
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Load file
+ $zip = new ZipArchive;
+ if ($zip->open($pFilename) === true) {
+ // check if it is an OOXML archive
+ $rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels"));
+
+ $zip->close();
+
+ return ($rels !== false);
+ }
+
+ return false;
+ }
+
+ private function _castToBool($c) {
+// echo 'Initial Cast to Boolean
';
+ $value = isset($c->v) ? (string) $c->v : null;
+ if ($value == '0') {
+ $value = false;
+ } elseif ($value == '1') {
+ $value = true;
+ } else {
+ $value = (bool)$c->v;
+ }
+ return $value;
+ } // function _castToBool()
+
+ private function _castToError($c) {
+// echo 'Initial Cast to Error
';
+ return isset($c->v) ? (string) $c->v : null;;
+ } // function _castToError()
+
+ private function _castToString($c) {
+// echo 'Initial Cast to String
';
+ return isset($c->v) ? (string) $c->v : null;;
+ } // function _castToString()
+
+ private function _castToFormula($c,$r,&$cellDataType,&$value,&$calculatedValue,&$sharedFormulas,$castBaseType) {
+// echo 'Formula
';
+// echo '$c->f is '.$c->f.'
';
+ $cellDataType = 'f';
+ $value = "={$c->f}";
+ $calculatedValue = $this->$castBaseType($c);
+
+ // Shared formula?
+ if (isset($c->f['t']) && strtolower((string)$c->f['t']) == 'shared') {
+// echo 'SHARED FORMULA
';
+ $instance = (string)$c->f['si'];
+
+// echo 'Instance ID = '.$instance.'
';
+//
+// echo 'Shared Formula Array:';
+// print_r($sharedFormulas);
+// echo '
';
+ if (!isset($sharedFormulas[(string)$c->f['si']])) {
+// echo 'SETTING NEW SHARED FORMULA
';
+// echo 'Master is '.$r.'
';
+// echo 'Formula is '.$value.'
';
+ $sharedFormulas[$instance] = array( 'master' => $r,
+ 'formula' => $value
+ );
+// echo 'New Shared Formula Array:';
+// print_r($sharedFormulas);
+// echo '
';
+ } else {
+// echo 'GETTING SHARED FORMULA
';
+// echo 'Master is '.$sharedFormulas[$instance]['master'].'
';
+// echo 'Formula is '.$sharedFormulas[$instance]['formula'].'
';
+ $master = PHPExcel_Cell::coordinateFromString($sharedFormulas[$instance]['master']);
+ $current = PHPExcel_Cell::coordinateFromString($r);
+
+ $difference = array(0, 0);
+ $difference[0] = PHPExcel_Cell::columnIndexFromString($current[0]) - PHPExcel_Cell::columnIndexFromString($master[0]);
+ $difference[1] = $current[1] - $master[1];
+
+ $value = $this->_referenceHelper->updateFormulaReferences( $sharedFormulas[$instance]['formula'],
+ 'A1',
+ $difference[0],
+ $difference[1]
+ );
+// echo 'Adjusted Formula is '.$value.'
';
+ }
+ }
+ }
+
+ public function _getFromZipArchive(ZipArchive $archive, $fileName = '')
+ {
+ // Root-relative paths
+ if (strpos($fileName, '//') !== false)
+ {
+ $fileName = substr($fileName, strpos($fileName, '//') + 1);
+ }
+ $fileName = PHPExcel_Shared_File::realpath($fileName);
+
+ // Apache POI fixes
+ $contents = $archive->getFromName($fileName);
+ if ($contents === false)
+ {
+ $contents = $archive->getFromName(substr($fileName, 1));
+ }
+
+ /*
+ if (strpos($contents, 'removeSheetByIndex(0);
+ if (!$this->_readDataOnly) {
+ $excel->removeCellStyleXfByIndex(0); // remove the default style
+ $excel->removeCellXfByIndex(0); // remove the default style
+ }
+ $zip = new ZipArchive;
+ $zip->open($pFilename);
+
+ $rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ foreach ($rels->Relationship as $rel) {
+ switch ($rel["Type"]) {
+ case "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties":
+ $xmlCore = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}"));
+ if (is_object($xmlCore)) {
+ $xmlCore->registerXPathNamespace("dc", "http://purl.org/dc/elements/1.1/");
+ $xmlCore->registerXPathNamespace("dcterms", "http://purl.org/dc/terms/");
+ $xmlCore->registerXPathNamespace("cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
+ $docProps = $excel->getProperties();
+ $docProps->setCreator((string) self::array_item($xmlCore->xpath("dc:creator")));
+ $docProps->setLastModifiedBy((string) self::array_item($xmlCore->xpath("cp:lastModifiedBy")));
+ $docProps->setCreated(strtotime(self::array_item($xmlCore->xpath("dcterms:created")))); //! respect xsi:type
+ $docProps->setModified(strtotime(self::array_item($xmlCore->xpath("dcterms:modified")))); //! respect xsi:type
+ $docProps->setTitle((string) self::array_item($xmlCore->xpath("dc:title")));
+ $docProps->setDescription((string) self::array_item($xmlCore->xpath("dc:description")));
+ $docProps->setSubject((string) self::array_item($xmlCore->xpath("dc:subject")));
+ $docProps->setKeywords((string) self::array_item($xmlCore->xpath("cp:keywords")));
+ $docProps->setCategory((string) self::array_item($xmlCore->xpath("cp:category")));
+ }
+ break;
+
+ case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties":
+ $xmlCore = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}"));
+ if (is_object($xmlCore)) {
+ $docProps = $excel->getProperties();
+ if (isset($xmlCore->Company))
+ $docProps->setCompany((string) $xmlCore->Company);
+ if (isset($xmlCore->Manager))
+ $docProps->setManager((string) $xmlCore->Manager);
+ }
+ break;
+
+ case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties":
+ $xmlCore = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}"));
+ if (is_object($xmlCore)) {
+ $xmlCore->registerXPathNamespace("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
+ $docProps = $excel->getProperties();
+ }
+ break;
+
+ case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument":
+ $dir = dirname($rel["Target"]);
+ $relsWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/_rels/" . basename($rel["Target"]) . ".rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $relsWorkbook->registerXPathNamespace("rel", "http://schemas.openxmlformats.org/package/2006/relationships");
+
+ $sharedStrings = array();
+ $xpath = self::array_item($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
+ $xmlStrings = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/$xpath[Target]")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ if (isset($xmlStrings) && isset($xmlStrings->si)) {
+ foreach ($xmlStrings->si as $val) {
+ if (isset($val->t)) {
+ $sharedStrings[] = PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $val->t );
+ } elseif (isset($val->r)) {
+ $sharedStrings[] = $this->_parseRichText($val);
+ }
+ }
+ }
+
+ $worksheets = array();
+ foreach ($relsWorkbook->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet") {
+ $worksheets[(string) $ele["Id"]] = $ele["Target"];
+ }
+ }
+
+ $styles = array();
+ $cellStyles = array();
+ $xpath = self::array_item($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
+ $xmlStyles = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/$xpath[Target]")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ $numFmts = null;
+ if ($xmlStyles && $xmlStyles->numFmts[0]) {
+ $numFmts = $xmlStyles->numFmts[0];
+ }
+ if (isset($numFmts) && !is_null($numFmts)) {
+ $numFmts->registerXPathNamespace("sml", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ }
+ if (!$this->_readDataOnly && $xmlStyles) {
+ foreach ($xmlStyles->cellXfs->xf as $xf) {
+ $numFmt = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
+
+ if ($xf["numFmtId"]) {
+ if (isset($numFmts)) {
+ $tmpNumFmt = self::array_item($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+
+ if (isset($tmpNumFmt["formatCode"])) {
+ $numFmt = (string) $tmpNumFmt["formatCode"];
+ }
+ }
+
+ if ((int)$xf["numFmtId"] < 164) {
+ $numFmt = PHPExcel_Style_NumberFormat::builtInFormatCode((int)$xf["numFmtId"]);
+ }
+ }
+ //$numFmt = str_replace('mm', 'i', $numFmt);
+ //$numFmt = str_replace('h', 'H', $numFmt);
+
+ $style = (object) array(
+ "numFmt" => $numFmt,
+ "font" => $xmlStyles->fonts->font[intval($xf["fontId"])],
+ "fill" => $xmlStyles->fills->fill[intval($xf["fillId"])],
+ "border" => $xmlStyles->borders->border[intval($xf["borderId"])],
+ "alignment" => $xf->alignment,
+ "protection" => $xf->protection,
+ );
+ $styles[] = $style;
+
+ // add style to cellXf collection
+ $objStyle = new PHPExcel_Style;
+ $this->_readStyle($objStyle, $style);
+ $excel->addCellXf($objStyle);
+ }
+
+ foreach ($xmlStyles->cellStyleXfs->xf as $xf) {
+ $numFmt = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
+ if ($numFmts && $xf["numFmtId"]) {
+ $tmpNumFmt = self::array_item($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+ if (isset($tmpNumFmt["formatCode"])) {
+ $numFmt = (string) $tmpNumFmt["formatCode"];
+ } else if ((int)$xf["numFmtId"] < 165) {
+ $numFmt = PHPExcel_Style_NumberFormat::builtInFormatCode((int)$xf["numFmtId"]);
+ }
+ }
+
+ $cellStyle = (object) array(
+ "numFmt" => $numFmt,
+ "font" => $xmlStyles->fonts->font[intval($xf["fontId"])],
+ "fill" => $xmlStyles->fills->fill[intval($xf["fillId"])],
+ "border" => $xmlStyles->borders->border[intval($xf["borderId"])],
+ "alignment" => $xf->alignment,
+ "protection" => $xf->protection,
+ );
+ $cellStyles[] = $cellStyle;
+
+ // add style to cellStyleXf collection
+ $objStyle = new PHPExcel_Style;
+ $this->_readStyle($objStyle, $cellStyle);
+ $excel->addCellStyleXf($objStyle);
+ }
+ }
+
+ $dxfs = array();
+ if (!$this->_readDataOnly && $xmlStyles) {
+ if ($xmlStyles->dxfs) {
+ foreach ($xmlStyles->dxfs->dxf as $dxf) {
+ $style = new PHPExcel_Style;
+ $this->_readStyle($style, $dxf);
+ $dxfs[] = $style;
+ }
+ }
+
+ if ($xmlStyles->cellStyles)
+ {
+ foreach ($xmlStyles->cellStyles->cellStyle as $cellStyle) {
+ if (intval($cellStyle['builtinId']) == 0) {
+ if (isset($cellStyles[intval($cellStyle['xfId'])])) {
+ // Set default style
+ $style = new PHPExcel_Style;
+ $this->_readStyle($style, $cellStyles[intval($cellStyle['xfId'])]);
+
+ // normal style, currently not using it for anything
+ }
+ }
+ }
+ }
+ }
+
+ $xmlWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+
+ // Set base date
+ if ($xmlWorkbook->workbookPr) {
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
+ if (isset($xmlWorkbook->workbookPr['date1904'])) {
+ $date1904 = (string)$xmlWorkbook->workbookPr['date1904'];
+ if ($date1904 == "true" || $date1904 == "1") {
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
+ }
+ }
+ }
+
+ $sheetId = 0; // keep track of new sheet id in final workbook
+ $oldSheetId = -1; // keep track of old sheet id in final workbook
+ $countSkippedSheets = 0; // keep track of number of skipped sheets
+ $mapSheetId = array(); // mapping of sheet ids from old to new
+
+ if ($xmlWorkbook->sheets)
+ {
+ foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+ ++$oldSheetId;
+
+ // Check if sheet should be skipped
+ if (isset($this->_loadSheetsOnly) && !in_array((string) $eleSheet["name"], $this->_loadSheetsOnly)) {
+ ++$countSkippedSheets;
+ $mapSheetId[$oldSheetId] = null;
+ continue;
+ }
+
+ // Map old sheet id in original workbook to new sheet id.
+ // They will differ if loadSheetsOnly() is being used
+ $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
+
+ // Load sheet
+ $docSheet = $excel->createSheet();
+ $docSheet->setTitle((string) $eleSheet["name"]);
+ $fileWorksheet = $worksheets[(string) self::array_item($eleSheet->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "id")];
+ $xmlSheet = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/$fileWorksheet")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+
+ $sharedFormulas = array();
+
+ if (isset($eleSheet["state"]) && (string) $eleSheet["state"] != '') {
+ $docSheet->setSheetState( (string) $eleSheet["state"] );
+ }
+
+ if (isset($xmlSheet->sheetViews) && isset($xmlSheet->sheetViews->sheetView)) {
+ if (isset($xmlSheet->sheetViews->sheetView['zoomScale'])) {
+ $docSheet->getSheetView()->setZoomScale( intval($xmlSheet->sheetViews->sheetView['zoomScale']) );
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['zoomScaleNormal'])) {
+ $docSheet->getSheetView()->setZoomScaleNormal( intval($xmlSheet->sheetViews->sheetView['zoomScaleNormal']) );
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['showGridLines'])) {
+ $docSheet->setShowGridLines((string)$xmlSheet->sheetViews->sheetView['showGridLines'] ? true : false);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['showRowColHeaders'])) {
+ $docSheet->setShowRowColHeaders((string)$xmlSheet->sheetViews->sheetView['showRowColHeaders'] ? true : false);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['rightToLeft'])) {
+ $docSheet->setRightToLeft((string)$xmlSheet->sheetViews->sheetView['rightToLeft'] ? true : false);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView->pane)) {
+ if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
+ $docSheet->freezePane( (string)$xmlSheet->sheetViews->sheetView->pane['topLeftCell'] );
+ } else {
+ $xSplit = 0;
+ $ySplit = 0;
+
+ if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
+ $xSplit = 1 + intval($xmlSheet->sheetViews->sheetView->pane['xSplit']);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
+ $ySplit = 1 + intval($xmlSheet->sheetViews->sheetView->pane['ySplit']);
+ }
+
+ $docSheet->freezePaneByColumnAndRow($xSplit, $ySplit);
+ }
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView->selection)) {
+ if (isset($xmlSheet->sheetViews->sheetView->selection['sqref'])) {
+ $sqref = (string)$xmlSheet->sheetViews->sheetView->selection['sqref'];
+ $sqref = explode(' ', $sqref);
+ $sqref = $sqref[0];
+ $docSheet->setSelectedCells($sqref);
+ }
+ }
+
+ }
+
+ if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->tabColor)) {
+ if (isset($xmlSheet->sheetPr->tabColor['rgb'])) {
+ $docSheet->getTabColor()->setARGB( (string)$xmlSheet->sheetPr->tabColor['rgb'] );
+ }
+ }
+
+ if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->outlinePr)) {
+ if (isset($xmlSheet->sheetPr->outlinePr['summaryRight']) && $xmlSheet->sheetPr->outlinePr['summaryRight'] == false) {
+ $docSheet->setShowSummaryRight(false);
+ } else {
+ $docSheet->setShowSummaryRight(true);
+ }
+
+ if (isset($xmlSheet->sheetPr->outlinePr['summaryBelow']) && $xmlSheet->sheetPr->outlinePr['summaryBelow'] == false) {
+ $docSheet->setShowSummaryBelow(false);
+ } else {
+ $docSheet->setShowSummaryBelow(true);
+ }
+ }
+
+ if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->pageSetUpPr)) {
+ if (isset($xmlSheet->sheetPr->pageSetUpPr['fitToPage']) && $xmlSheet->sheetPr->pageSetUpPr['fitToPage'] == false) {
+ $docSheet->getPageSetup()->setFitToPage(false);
+ } else {
+ $docSheet->getPageSetup()->setFitToPage(true);
+ }
+ }
+
+ if (isset($xmlSheet->sheetFormatPr)) {
+ if (isset($xmlSheet->sheetFormatPr['customHeight']) && ((string)$xmlSheet->sheetFormatPr['customHeight'] == '1' || strtolower((string)$xmlSheet->sheetFormatPr['customHeight']) == 'true') && isset($xmlSheet->sheetFormatPr['defaultRowHeight'])) {
+ $docSheet->getDefaultRowDimension()->setRowHeight( (float)$xmlSheet->sheetFormatPr['defaultRowHeight'] );
+ }
+ if (isset($xmlSheet->sheetFormatPr['defaultColWidth'])) {
+ $docSheet->getDefaultColumnDimension()->setWidth( (float)$xmlSheet->sheetFormatPr['defaultColWidth'] );
+ }
+ }
+
+ if (isset($xmlSheet->cols) && !$this->_readDataOnly) {
+ foreach ($xmlSheet->cols->col as $col) {
+ for ($i = intval($col["min"]) - 1; $i < intval($col["max"]); ++$i) {
+ if ($col["style"] && !$this->_readDataOnly) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setXfIndex(intval($col["style"]));
+ }
+ if ($col["bestFit"]) {
+ //$docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setAutoSize(true);
+ }
+ if ($col["hidden"]) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setVisible(false);
+ }
+ if ($col["collapsed"]) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setCollapsed(true);
+ }
+ if ($col["outlineLevel"] > 0) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setOutlineLevel(intval($col["outlineLevel"]));
+ }
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setWidth(floatval($col["width"]));
+
+ if (intval($col["max"]) == 16384) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (isset($xmlSheet->printOptions) && !$this->_readDataOnly) {
+ if ($xmlSheet->printOptions['gridLinesSet'] == 'true' && $xmlSheet->printOptions['gridLinesSet'] == '1') {
+ $docSheet->setShowGridlines(true);
+ }
+
+ if ($xmlSheet->printOptions['gridLines'] == 'true' || $xmlSheet->printOptions['gridLines'] == '1') {
+ $docSheet->setPrintGridlines(true);
+ }
+
+ if ($xmlSheet->printOptions['horizontalCentered']) {
+ $docSheet->getPageSetup()->setHorizontalCentered(true);
+ }
+ if ($xmlSheet->printOptions['verticalCentered']) {
+ $docSheet->getPageSetup()->setVerticalCentered(true);
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
+ foreach ($xmlSheet->sheetData->row as $row) {
+ if ($row["ht"] && !$this->_readDataOnly) {
+ $docSheet->getRowDimension(intval($row["r"]))->setRowHeight(floatval($row["ht"]));
+ }
+ if ($row["hidden"] && !$this->_readDataOnly) {
+ $docSheet->getRowDimension(intval($row["r"]))->setVisible(false);
+ }
+ if ($row["collapsed"]) {
+ $docSheet->getRowDimension(intval($row["r"]))->setCollapsed(true);
+ }
+ if ($row["outlineLevel"] > 0) {
+ $docSheet->getRowDimension(intval($row["r"]))->setOutlineLevel(intval($row["outlineLevel"]));
+ }
+ if ($row["s"] && !$this->_readDataOnly) {
+ $docSheet->getRowDimension(intval($row["r"]))->setXfIndex(intval($row["s"]));
+ }
+
+ foreach ($row->c as $c) {
+ $r = (string) $c["r"];
+ $cellDataType = (string) $c["t"];
+ $value = null;
+ $calculatedValue = null;
+
+ // Read cell?
+ if (!is_null($this->getReadFilter())) {
+ $coordinates = PHPExcel_Cell::coordinateFromString($r);
+
+ if (!$this->getReadFilter()->readCell($coordinates[0], $coordinates[1], $docSheet->getTitle())) {
+ continue;
+ }
+ }
+
+ // echo 'Reading cell '.$coordinates[0].$coordinates[1].'
';
+ // print_r($c);
+ // echo '
';
+ // echo 'Cell Data Type is '.$cellDataType.': ';
+ //
+ // Read cell!
+ switch ($cellDataType) {
+ case "s":
+ // echo 'String
';
+ if ((string)$c->v != '') {
+ $value = $sharedStrings[intval($c->v)];
+
+ if ($value instanceof PHPExcel_RichText) {
+ $value = clone $value;
+ }
+ } else {
+ $value = '';
+ }
+
+ break;
+ case "b":
+ // echo 'Boolean
';
+ if (!isset($c->f)) {
+ $value = $this->_castToBool($c);
+ } else {
+ // Formula
+ $this->_castToFormula($c,$r,$cellDataType,$value,$calculatedValue,$sharedFormulas,'_castToBool');
+ if (isset($c->f['t'])) {
+ $att = array();
+ $att = $c->f;
+ $docSheet->getCell($r)->setFormulaAttributes($att);
+ }
+ // echo '$calculatedValue = '.$calculatedValue.'
';
+ }
+ break;
+ case "inlineStr":
+ // echo 'Inline String
';
+ $value = $this->_parseRichText($c->is);
+
+ break;
+ case "e":
+ // echo 'Error
';
+ if (!isset($c->f)) {
+ $value = $this->_castToError($c);
+ } else {
+ // Formula
+ $this->_castToFormula($c,$r,$cellDataType,$value,$calculatedValue,$sharedFormulas,'_castToError');
+ // echo '$calculatedValue = '.$calculatedValue.'
';
+ }
+
+ break;
+
+ default:
+ // echo 'Default
';
+ if (!isset($c->f)) {
+ // echo 'Not a Formula
';
+ $value = $this->_castToString($c);
+ } else {
+ // echo 'Treat as Formula
';
+ // Formula
+ $this->_castToFormula($c,$r,$cellDataType,$value,$calculatedValue,$sharedFormulas,'_castToString');
+ // echo '$calculatedValue = '.$calculatedValue.'
';
+ }
+
+ break;
+ }
+ // echo 'Value is '.$value.'
';
+
+ // Check for numeric values
+ if (is_numeric($value) && $cellDataType != 's') {
+ if ($value == (int)$value) $value = (int)$value;
+ elseif ($value == (float)$value) $value = (float)$value;
+ elseif ($value == (double)$value) $value = (double)$value;
+ }
+
+ // Rich text?
+ if ($value instanceof PHPExcel_RichText && $this->_readDataOnly) {
+ $value = $value->getPlainText();
+ }
+
+ $cell = $docSheet->getCell($r);
+ // Assign value
+ if ($cellDataType != '') {
+ $cell->setValueExplicit($value, $cellDataType);
+ } else {
+ $cell->setValue($value);
+ }
+ if (!is_null($calculatedValue)) {
+ $cell->setCalculatedValue($calculatedValue);
+ }
+
+ // Style information?
+ if ($c["s"] && !$this->_readDataOnly) {
+ // no style index means 0, it seems
+ $cell->setXfIndex(isset($styles[intval($c["s"])]) ?
+ intval($c["s"]) : 0);
+ }
+ }
+ }
+ }
+
+ $conditionals = array();
+ if (!$this->_readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
+ foreach ($xmlSheet->conditionalFormatting as $conditional) {
+ foreach ($conditional->cfRule as $cfRule) {
+ if (
+ (
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_NONE ||
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_CELLIS ||
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT ||
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_EXPRESSION
+ ) && isset($dxfs[intval($cfRule["dxfId"])])
+ ) {
+ $conditionals[(string) $conditional["sqref"]][intval($cfRule["priority"])] = $cfRule;
+ }
+ }
+ }
+
+ foreach ($conditionals as $ref => $cfRules) {
+ ksort($cfRules);
+ $conditionalStyles = array();
+ foreach ($cfRules as $cfRule) {
+ $objConditional = new PHPExcel_Style_Conditional();
+ $objConditional->setConditionType((string)$cfRule["type"]);
+ $objConditional->setOperatorType((string)$cfRule["operator"]);
+
+ if ((string)$cfRule["text"] != '') {
+ $objConditional->setText((string)$cfRule["text"]);
+ }
+
+ if (count($cfRule->formula) > 1) {
+ foreach ($cfRule->formula as $formula) {
+ $objConditional->addCondition((string)$formula);
+ }
+ } else {
+ $objConditional->addCondition((string)$cfRule->formula);
+ }
+ $objConditional->setStyle(clone $dxfs[intval($cfRule["dxfId"])]);
+ $conditionalStyles[] = $objConditional;
+ }
+
+ // Extract all cell references in $ref
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($ref);
+ foreach ($aReferences as $reference) {
+ $docSheet->getStyle($reference)->setConditionalStyles($conditionalStyles);
+ }
+ }
+ }
+
+ $aKeys = array("sheet", "objects", "scenarios", "formatCells", "formatColumns", "formatRows", "insertColumns", "insertRows", "insertHyperlinks", "deleteColumns", "deleteRows", "selectLockedCells", "sort", "autoFilter", "pivotTables", "selectUnlockedCells");
+ if (!$this->_readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
+ foreach ($aKeys as $key) {
+ $method = "set" . ucfirst($key);
+ $docSheet->getProtection()->$method($xmlSheet->sheetProtection[$key] == "true");
+ }
+ }
+
+ if (!$this->_readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
+ $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection["password"], true);
+ if ($xmlSheet->protectedRanges->protectedRange) {
+ foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
+ $docSheet->protectCells((string) $protectedRange["sqref"], (string) $protectedRange["password"], true);
+ }
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->autoFilter && !$this->_readDataOnly) {
+ $docSheet->setAutoFilter((string) $xmlSheet->autoFilter["ref"]);
+ }
+
+ if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->_readDataOnly) {
+ foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
+ $docSheet->mergeCells((string) $mergeCell["ref"]);
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->pageMargins && !$this->_readDataOnly) {
+ $docPageMargins = $docSheet->getPageMargins();
+ $docPageMargins->setLeft(floatval($xmlSheet->pageMargins["left"]));
+ $docPageMargins->setRight(floatval($xmlSheet->pageMargins["right"]));
+ $docPageMargins->setTop(floatval($xmlSheet->pageMargins["top"]));
+ $docPageMargins->setBottom(floatval($xmlSheet->pageMargins["bottom"]));
+ $docPageMargins->setHeader(floatval($xmlSheet->pageMargins["header"]));
+ $docPageMargins->setFooter(floatval($xmlSheet->pageMargins["footer"]));
+ }
+
+ if ($xmlSheet && $xmlSheet->pageSetup && !$this->_readDataOnly) {
+ $docPageSetup = $docSheet->getPageSetup();
+
+ if (isset($xmlSheet->pageSetup["orientation"])) {
+ $docPageSetup->setOrientation((string) $xmlSheet->pageSetup["orientation"]);
+ }
+ if (isset($xmlSheet->pageSetup["paperSize"])) {
+ $docPageSetup->setPaperSize(intval($xmlSheet->pageSetup["paperSize"]));
+ }
+ if (isset($xmlSheet->pageSetup["scale"])) {
+ $docPageSetup->setScale(intval($xmlSheet->pageSetup["scale"]), false);
+ }
+ if (isset($xmlSheet->pageSetup["fitToHeight"]) && intval($xmlSheet->pageSetup["fitToHeight"]) >= 0) {
+ $docPageSetup->setFitToHeight(intval($xmlSheet->pageSetup["fitToHeight"]), false);
+ }
+ if (isset($xmlSheet->pageSetup["fitToWidth"]) && intval($xmlSheet->pageSetup["fitToWidth"]) >= 0) {
+ $docPageSetup->setFitToWidth(intval($xmlSheet->pageSetup["fitToWidth"]), false);
+ }
+ if (isset($xmlSheet->pageSetup["firstPageNumber"]) && isset($xmlSheet->pageSetup["useFirstPageNumber"]) &&
+ ((string)$xmlSheet->pageSetup["useFirstPageNumber"] == 'true' || (string)$xmlSheet->pageSetup["useFirstPageNumber"] == '1')) {
+ $docPageSetup->setFirstPageNumber(intval($xmlSheet->pageSetup["firstPageNumber"]));
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->headerFooter && !$this->_readDataOnly) {
+ $docHeaderFooter = $docSheet->getHeaderFooter();
+
+ if (isset($xmlSheet->headerFooter["differentOddEven"]) &&
+ ((string)$xmlSheet->headerFooter["differentOddEven"] == 'true' || (string)$xmlSheet->headerFooter["differentOddEven"] == '1')) {
+ $docHeaderFooter->setDifferentOddEven(true);
+ } else {
+ $docHeaderFooter->setDifferentOddEven(false);
+ }
+ if (isset($xmlSheet->headerFooter["differentFirst"]) &&
+ ((string)$xmlSheet->headerFooter["differentFirst"] == 'true' || (string)$xmlSheet->headerFooter["differentFirst"] == '1')) {
+ $docHeaderFooter->setDifferentFirst(true);
+ } else {
+ $docHeaderFooter->setDifferentFirst(false);
+ }
+ if (isset($xmlSheet->headerFooter["scaleWithDoc"]) &&
+ ((string)$xmlSheet->headerFooter["scaleWithDoc"] == 'false' || (string)$xmlSheet->headerFooter["scaleWithDoc"] == '0')) {
+ $docHeaderFooter->setScaleWithDocument(false);
+ } else {
+ $docHeaderFooter->setScaleWithDocument(true);
+ }
+ if (isset($xmlSheet->headerFooter["alignWithMargins"]) &&
+ ((string)$xmlSheet->headerFooter["alignWithMargins"] == 'false' || (string)$xmlSheet->headerFooter["alignWithMargins"] == '0')) {
+ $docHeaderFooter->setAlignWithMargins(false);
+ } else {
+ $docHeaderFooter->setAlignWithMargins(true);
+ }
+
+ $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
+ $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
+ $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
+ $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
+ $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
+ $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
+ }
+
+ if ($xmlSheet && $xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk && !$this->_readDataOnly) {
+ foreach ($xmlSheet->rowBreaks->brk as $brk) {
+ if ($brk["man"]) {
+ $docSheet->setBreak("A$brk[id]", PHPExcel_Worksheet::BREAK_ROW);
+ }
+ }
+ }
+ if ($xmlSheet && $xmlSheet->colBreaks && $xmlSheet->colBreaks->brk && !$this->_readDataOnly) {
+ foreach ($xmlSheet->colBreaks->brk as $brk) {
+ if ($brk["man"]) {
+ $docSheet->setBreak(PHPExcel_Cell::stringFromColumnIndex($brk["id"]) . "1", PHPExcel_Worksheet::BREAK_COLUMN);
+ }
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->dataValidations && !$this->_readDataOnly) {
+ foreach ($xmlSheet->dataValidations->dataValidation as $dataValidation) {
+ // Uppercase coordinate
+ $range = strtoupper($dataValidation["sqref"]);
+ $rangeSet = explode(' ',$range);
+ foreach($rangeSet as $range) {
+ $stRange = $docSheet->shrinkRangeToFit($range);
+
+ // Extract all cell references in $range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($stRange);
+ foreach ($aReferences as $reference) {
+ // Create validation
+ $docValidation = $docSheet->getCell($reference)->getDataValidation();
+ $docValidation->setType((string) $dataValidation["type"]);
+ $docValidation->setErrorStyle((string) $dataValidation["errorStyle"]);
+ $docValidation->setOperator((string) $dataValidation["operator"]);
+ $docValidation->setAllowBlank($dataValidation["allowBlank"] != 0);
+ $docValidation->setShowDropDown($dataValidation["showDropDown"] == 0);
+ $docValidation->setShowInputMessage($dataValidation["showInputMessage"] != 0);
+ $docValidation->setShowErrorMessage($dataValidation["showErrorMessage"] != 0);
+ $docValidation->setErrorTitle((string) $dataValidation["errorTitle"]);
+ $docValidation->setError((string) $dataValidation["error"]);
+ $docValidation->setPromptTitle((string) $dataValidation["promptTitle"]);
+ $docValidation->setPrompt((string) $dataValidation["prompt"]);
+ $docValidation->setFormula1((string) $dataValidation->formula1);
+ $docValidation->setFormula2((string) $dataValidation->formula2);
+ }
+ }
+ }
+ }
+
+ // Add hyperlinks
+ $hyperlinks = array();
+ if (!$this->_readDataOnly) {
+ // Locate hyperlink relations
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") {
+ $hyperlinks[(string)$ele["Id"]] = (string)$ele["Target"];
+ }
+ }
+ }
+
+ // Loop through hyperlinks
+ if ($xmlSheet && $xmlSheet->hyperlinks) {
+ foreach ($xmlSheet->hyperlinks->hyperlink as $hyperlink) {
+ // Link url
+ $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+ foreach (PHPExcel_Cell::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
+ $cell = $docSheet->getCell( $cellReference );
+ if (isset($linkRel['id'])) {
+ $cell->getHyperlink()->setUrl( $hyperlinks[ (string)$linkRel['id'] ] );
+ }
+ if (isset($hyperlink['location'])) {
+ $cell->getHyperlink()->setUrl( 'sheet://' . (string)$hyperlink['location'] );
+ }
+
+ // Tooltip
+ if (isset($hyperlink['tooltip'])) {
+ $cell->getHyperlink()->setTooltip( (string)$hyperlink['tooltip'] );
+ }
+ }
+ }
+ }
+ }
+
+ // Add comments
+ $comments = array();
+ $vmlComments = array();
+ if (!$this->_readDataOnly) {
+ // Locate comment relations
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments") {
+ $comments[(string)$ele["Id"]] = (string)$ele["Target"];
+ }
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing") {
+ $vmlComments[(string)$ele["Id"]] = (string)$ele["Target"];
+ }
+ }
+ }
+
+ // Loop through comments
+ foreach ($comments as $relName => $relPath) {
+ // Load comments file
+ $relPath = PHPExcel_Shared_File::realpath(dirname("$dir/$fileWorksheet") . "/" . $relPath);
+ $commentsFile = simplexml_load_string($this->_getFromZipArchive($zip, $relPath) );
+
+ // Utility variables
+ $authors = array();
+
+ // Loop through authors
+ foreach ($commentsFile->authors->author as $author) {
+ $authors[] = (string)$author;
+ }
+
+ // Loop through contents
+ foreach ($commentsFile->commentList->comment as $comment) {
+ $docSheet->getComment( (string)$comment['ref'] )->setAuthor( $authors[(string)$comment['authorId']] );
+ $docSheet->getComment( (string)$comment['ref'] )->setText( $this->_parseRichText($comment->text) );
+ }
+ }
+
+ // Loop through VML comments
+ foreach ($vmlComments as $relName => $relPath) {
+ // Load VML comments file
+ $relPath = PHPExcel_Shared_File::realpath(dirname("$dir/$fileWorksheet") . "/" . $relPath);
+ $vmlCommentsFile = simplexml_load_string( $this->_getFromZipArchive($zip, $relPath) );
+ $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+ $shapes = $vmlCommentsFile->xpath('//v:shape');
+ foreach ($shapes as $shape) {
+ $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+ if (isset($shape['style'])) {
+ $style = (string)$shape['style'];
+ $fillColor = strtoupper( substr( (string)$shape['fillcolor'], 1 ) );
+ $column = null;
+ $row = null;
+
+ $clientData = $shape->xpath('.//x:ClientData');
+ if (is_array($clientData) && count($clientData) > 0) {
+ $clientData = $clientData[0];
+
+ if ( isset($clientData['ObjectType']) && (string)$clientData['ObjectType'] == 'Note' ) {
+ $temp = $clientData->xpath('.//x:Row');
+ if (is_array($temp)) $row = $temp[0];
+
+ $temp = $clientData->xpath('.//x:Column');
+ if (is_array($temp)) $column = $temp[0];
+ }
+ }
+
+ if (!is_null($column) && !is_null($row)) {
+ // Set comment properties
+ $comment = $docSheet->getCommentByColumnAndRow($column, $row + 1);
+ $comment->getFillColor()->setRGB( $fillColor );
+
+ // Parse style
+ $styleArray = explode(';', str_replace(' ', '', $style));
+ foreach ($styleArray as $stylePair) {
+ $stylePair = explode(':', $stylePair);
+
+ if ($stylePair[0] == 'margin-left') $comment->setMarginLeft($stylePair[1]);
+ if ($stylePair[0] == 'margin-top') $comment->setMarginTop($stylePair[1]);
+ if ($stylePair[0] == 'width') $comment->setWidth($stylePair[1]);
+ if ($stylePair[0] == 'height') $comment->setHeight($stylePair[1]);
+ if ($stylePair[0] == 'visibility') $comment->setVisible( $stylePair[1] == 'visible' );
+
+ }
+ }
+ }
+ }
+ }
+
+ // Header/footer images
+ if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->_readDataOnly) {
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $vmlRelationship = '';
+
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing") {
+ $vmlRelationship = self::dir_add("$dir/$fileWorksheet", $ele["Target"]);
+ }
+ }
+
+ if ($vmlRelationship != '') {
+ // Fetch linked images
+ $relsVML = simplexml_load_string($this->_getFromZipArchive($zip, dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels' )); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $drawings = array();
+ foreach ($relsVML->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") {
+ $drawings[(string) $ele["Id"]] = self::dir_add($vmlRelationship, $ele["Target"]);
+ }
+ }
+
+ // Fetch VML document
+ $vmlDrawing = simplexml_load_string($this->_getFromZipArchive($zip, $vmlRelationship));
+ $vmlDrawing->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+ $hfImages = array();
+
+ $shapes = $vmlDrawing->xpath('//v:shape');
+ foreach ($shapes as $shape) {
+ $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+ $imageData = $shape->xpath('//v:imagedata');
+ $imageData = $imageData[0];
+
+ $imageData = $imageData->attributes('urn:schemas-microsoft-com:office:office');
+ $style = self::toCSSArray( (string)$shape['style'] );
+
+ $hfImages[ (string)$shape['id'] ] = new PHPExcel_Worksheet_HeaderFooterDrawing();
+ if (isset($imageData['title'])) {
+ $hfImages[ (string)$shape['id'] ]->setName( (string)$imageData['title'] );
+ }
+
+ $hfImages[ (string)$shape['id'] ]->setPath("zip://$pFilename#" . $drawings[(string)$imageData['relid']], false);
+ $hfImages[ (string)$shape['id'] ]->setResizeProportional(false);
+ $hfImages[ (string)$shape['id'] ]->setWidth($style['width']);
+ $hfImages[ (string)$shape['id'] ]->setHeight($style['height']);
+ $hfImages[ (string)$shape['id'] ]->setOffsetX($style['margin-left']);
+ $hfImages[ (string)$shape['id'] ]->setOffsetY($style['margin-top']);
+ $hfImages[ (string)$shape['id'] ]->setResizeProportional(true);
+ }
+
+ $docSheet->getHeaderFooter()->setImages($hfImages);
+ }
+ }
+ }
+
+ }
+
+ // TODO: Make sure drawings and graph are loaded differently!
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $drawings = array();
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing") {
+ $drawings[(string) $ele["Id"]] = self::dir_add("$dir/$fileWorksheet", $ele["Target"]);
+ }
+ }
+ if ($xmlSheet->drawing && !$this->_readDataOnly) {
+ foreach ($xmlSheet->drawing as $drawing) {
+ $fileDrawing = $drawings[(string) self::array_item($drawing->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "id")];
+ $relsDrawing = simplexml_load_string($this->_getFromZipArchive($zip, dirname($fileDrawing) . "/_rels/" . basename($fileDrawing) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $images = array();
+
+ if ($relsDrawing && $relsDrawing->Relationship) {
+ foreach ($relsDrawing->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") {
+ $images[(string) $ele["Id"]] = self::dir_add($fileDrawing, $ele["Target"]);
+ }
+ }
+ }
+ $xmlDrawing = simplexml_load_string($this->_getFromZipArchive($zip, $fileDrawing))->children("http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing");
+
+ if ($xmlDrawing->oneCellAnchor) {
+ foreach ($xmlDrawing->oneCellAnchor as $oneCellAnchor) {
+ if ($oneCellAnchor->pic->blipFill) {
+ $blip = $oneCellAnchor->pic->blipFill->children("http://schemas.openxmlformats.org/drawingml/2006/main")->blip;
+ $xfrm = $oneCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->xfrm;
+ $outerShdw = $oneCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->effectLst->outerShdw;
+ $objDrawing = new PHPExcel_Worksheet_Drawing;
+ $objDrawing->setName((string) self::array_item($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), "name"));
+ $objDrawing->setDescription((string) self::array_item($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), "descr"));
+ $objDrawing->setPath("zip://$pFilename#" . $images[(string) self::array_item($blip->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "embed")], false);
+ $objDrawing->setCoordinates(PHPExcel_Cell::stringFromColumnIndex($oneCellAnchor->from->col) . ($oneCellAnchor->from->row + 1));
+ $objDrawing->setOffsetX(PHPExcel_Shared_Drawing::EMUToPixels($oneCellAnchor->from->colOff));
+ $objDrawing->setOffsetY(PHPExcel_Shared_Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
+ $objDrawing->setResizeProportional(false);
+ $objDrawing->setWidth(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($oneCellAnchor->ext->attributes(), "cx")));
+ $objDrawing->setHeight(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($oneCellAnchor->ext->attributes(), "cy")));
+ if ($xfrm) {
+ $objDrawing->setRotation(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($xfrm->attributes(), "rot")));
+ }
+ if ($outerShdw) {
+ $shadow = $objDrawing->getShadow();
+ $shadow->setVisible(true);
+ $shadow->setBlurRadius(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "blurRad")));
+ $shadow->setDistance(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "dist")));
+ $shadow->setDirection(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($outerShdw->attributes(), "dir")));
+ $shadow->setAlignment((string) self::array_item($outerShdw->attributes(), "algn"));
+ $shadow->getColor()->setRGB(self::array_item($outerShdw->srgbClr->attributes(), "val"));
+ $shadow->setAlpha(self::array_item($outerShdw->srgbClr->alpha->attributes(), "val") / 1000);
+ }
+ $objDrawing->setWorksheet($docSheet);
+ }
+ }
+ }
+ if ($xmlDrawing->twoCellAnchor) {
+ foreach ($xmlDrawing->twoCellAnchor as $twoCellAnchor) {
+ if ($twoCellAnchor->pic->blipFill) {
+ $blip = $twoCellAnchor->pic->blipFill->children("http://schemas.openxmlformats.org/drawingml/2006/main")->blip;
+ $xfrm = $twoCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->xfrm;
+ $outerShdw = $twoCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->effectLst->outerShdw;
+ $objDrawing = new PHPExcel_Worksheet_Drawing;
+ $objDrawing->setName((string) self::array_item($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), "name"));
+ $objDrawing->setDescription((string) self::array_item($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), "descr"));
+ $objDrawing->setPath("zip://$pFilename#" . $images[(string) self::array_item($blip->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "embed")], false);
+ $objDrawing->setCoordinates(PHPExcel_Cell::stringFromColumnIndex($twoCellAnchor->from->col) . ($twoCellAnchor->from->row + 1));
+ $objDrawing->setOffsetX(PHPExcel_Shared_Drawing::EMUToPixels($twoCellAnchor->from->colOff));
+ $objDrawing->setOffsetY(PHPExcel_Shared_Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
+ $objDrawing->setResizeProportional(false);
+
+ $objDrawing->setWidth(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($xfrm->ext->attributes(), "cx")));
+ $objDrawing->setHeight(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($xfrm->ext->attributes(), "cy")));
+
+ if ($xfrm) {
+ $objDrawing->setRotation(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($xfrm->attributes(), "rot")));
+ }
+ if ($outerShdw) {
+ $shadow = $objDrawing->getShadow();
+ $shadow->setVisible(true);
+ $shadow->setBlurRadius(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "blurRad")));
+ $shadow->setDistance(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "dist")));
+ $shadow->setDirection(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($outerShdw->attributes(), "dir")));
+ $shadow->setAlignment((string) self::array_item($outerShdw->attributes(), "algn"));
+ $shadow->getColor()->setRGB(self::array_item($outerShdw->srgbClr->attributes(), "val"));
+ $shadow->setAlpha(self::array_item($outerShdw->srgbClr->alpha->attributes(), "val") / 1000);
+ }
+ $objDrawing->setWorksheet($docSheet);
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ // Loop through definedNames
+ if ($xmlWorkbook->definedNames) {
+ foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+ // Extract range
+ $extractedRange = (string)$definedName;
+ $extractedRange = preg_replace('/\'(\w+)\'\!/', '', $extractedRange);
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ // Valid range?
+ if (stripos((string)$definedName, '#REF!') !== false || $extractedRange == '') {
+ continue;
+ }
+
+ // Some definedNames are only applicable if we are on the same sheet...
+ if ((string)$definedName['localSheetId'] != '' && (string)$definedName['localSheetId'] == $sheetId) {
+ // Switch on type
+ switch ((string)$definedName['name']) {
+
+ case '_xlnm._FilterDatabase':
+ $docSheet->setAutoFilter($extractedRange);
+ break;
+
+ case '_xlnm.Print_Titles':
+ // Split $extractedRange
+ $extractedRange = explode(',', $extractedRange);
+
+ // Set print titles
+ foreach ($extractedRange as $range) {
+ $matches = array();
+
+ // check for repeating columns, e g. 'A:A' or 'A:D'
+ if (preg_match('/^([A-Z]+)\:([A-Z]+)$/', $range, $matches)) {
+ $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($matches[1], $matches[2]));
+ }
+ // check for repeating rows, e.g. '1:1' or '1:5'
+ elseif (preg_match('/^(\d+)\:(\d+)$/', $range, $matches)) {
+ $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($matches[1], $matches[2]));
+ }
+ }
+ break;
+
+ case '_xlnm.Print_Area':
+ $rangeSets = explode(',', $extractedRange); // FIXME: what if sheetname contains comma?
+ $newRangeSets = array();
+ foreach($rangeSets as $rangeSet) {
+ $range = explode('!', $rangeSet); // FIXME: what if sheetname contains exclamation mark?
+ $rangeSet = isset($range[1]) ? $range[1] : $range[0];
+ $newRangeSets[] = str_replace('$', '', $rangeSet);
+ }
+ $docSheet->getPageSetup()->setPrintArea(implode(',',$newRangeSets));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // Next sheet id
+ ++$sheetId;
+ }
+
+ // Loop through definedNames
+ if ($xmlWorkbook->definedNames) {
+ foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+ // Extract range
+ $extractedRange = (string)$definedName;
+ $extractedRange = preg_replace('/\'(\w+)\'\!/', '', $extractedRange);
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ // Valid range?
+ if (stripos((string)$definedName, '#REF!') !== false || $extractedRange == '') {
+ continue;
+ }
+
+ // Some definedNames are only applicable if we are on the same sheet...
+ if ((string)$definedName['localSheetId'] != '') {
+ // Local defined name
+ // Switch on type
+ switch ((string)$definedName['name']) {
+
+ case '_xlnm._FilterDatabase':
+ case '_xlnm.Print_Titles':
+ case '_xlnm.Print_Area':
+ break;
+
+ default:
+ $range = explode('!', (string)$definedName);
+ if (count($range) == 2) {
+ $range[0] = str_replace("''", "'", $range[0]);
+ $range[0] = str_replace("'", "", $range[0]);
+ if ($worksheet = $docSheet->getParent()->getSheetByName($range[0])) {
+ $extractedRange = str_replace('$', '', $range[1]);
+ $scope = $docSheet->getParent()->getSheet((string)$definedName['localSheetId']);
+
+ $excel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $worksheet, $extractedRange, true, $scope) );
+ }
+ }
+ break;
+ }
+ } else if (!isset($definedName['localSheetId'])) {
+ // "Global" definedNames
+ $locatedSheet = null;
+ $extractedSheetName = '';
+ if (strpos( (string)$definedName, '!' ) !== false) {
+ // Extract sheet name
+ $extractedSheetName = PHPExcel_Worksheet::extractSheetTitle( (string)$definedName, true );
+ $extractedSheetName = $extractedSheetName[0];
+
+ // Locate sheet
+ $locatedSheet = $excel->getSheetByName($extractedSheetName);
+
+ // Modify range
+ $range = explode('!', $extractedRange);
+ $extractedRange = isset($range[1]) ? $range[1] : $range[0];
+ }
+
+ if (!is_null($locatedSheet)) {
+ $excel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $locatedSheet, $extractedRange, false) );
+ }
+ }
+ }
+ }
+ }
+
+ if (!$this->_readDataOnly) {
+ // active sheet index
+ $activeTab = intval($xmlWorkbook->bookViews->workbookView["activeTab"]); // refers to old sheet index
+
+ // keep active sheet index if sheet is still loaded, else first sheet is set as the active
+ if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
+ $excel->setActiveSheetIndex($mapSheetId[$activeTab]);
+ } else {
+ if ($excel->getSheetCount() == 0)
+ {
+ $excel->createSheet();
+ }
+ $excel->setActiveSheetIndex(0);
+ }
+ }
+ break;
+ }
+
+ }
+
+ return $excel;
+ }
+
+ private function _readColor($color) {
+ if (isset($color["rgb"])) {
+ return (string)$color["rgb"];
+ } else if (isset($color["indexed"])) {
+ return PHPExcel_Style_Color::indexedColor($color["indexed"])->getARGB();
+ }
+ }
+
+ private function _readStyle($docStyle, $style) {
+ // format code
+ if (isset($style->numFmt)) {
+ $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+ }
+
+ // font
+ if (isset($style->font)) {
+ $docStyle->getFont()->setName((string) $style->font->name["val"]);
+ $docStyle->getFont()->setSize((string) $style->font->sz["val"]);
+ if (isset($style->font->b)) {
+ $docStyle->getFont()->setBold(!isset($style->font->b["val"]) || $style->font->b["val"] == 'true' || $style->font->b["val"] == '1');
+ }
+ if (isset($style->font->i)) {
+ $docStyle->getFont()->setItalic(!isset($style->font->i["val"]) || $style->font->i["val"] == 'true' || $style->font->i["val"] == '1');
+ }
+ if (isset($style->font->strike)) {
+ $docStyle->getFont()->setStrikethrough(!isset($style->font->strike["val"]) || $style->font->strike["val"] == 'true' || $style->font->strike["val"] == '1');
+ }
+ $docStyle->getFont()->getColor()->setARGB($this->_readColor($style->font->color));
+
+ if (isset($style->font->u) && !isset($style->font->u["val"])) {
+ $docStyle->getFont()->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
+ } else if (isset($style->font->u) && isset($style->font->u["val"])) {
+ $docStyle->getFont()->setUnderline((string)$style->font->u["val"]);
+ }
+
+ if (isset($style->font->vertAlign) && isset($style->font->vertAlign["val"])) {
+ $vertAlign = strtolower((string)$style->font->vertAlign["val"]);
+ if ($vertAlign == 'superscript') {
+ $docStyle->getFont()->setSuperScript(true);
+ }
+ if ($vertAlign == 'subscript') {
+ $docStyle->getFont()->setSubScript(true);
+ }
+ }
+ }
+
+ // fill
+ if (isset($style->fill)) {
+ if ($style->fill->gradientFill) {
+ $gradientFill = $style->fill->gradientFill[0];
+ $docStyle->getFill()->setFillType((string) $gradientFill["type"]);
+ $docStyle->getFill()->setRotation(floatval($gradientFill["degree"]));
+ $gradientFill->registerXPathNamespace("sml", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ $docStyle->getFill()->getStartColor()->setARGB($this->_readColor( self::array_item($gradientFill->xpath("sml:stop[@position=0]"))->color) );
+ $docStyle->getFill()->getEndColor()->setARGB($this->_readColor( self::array_item($gradientFill->xpath("sml:stop[@position=1]"))->color) );
+ } elseif ($style->fill->patternFill) {
+ $patternType = (string)$style->fill->patternFill["patternType"] != '' ? (string)$style->fill->patternFill["patternType"] : 'solid';
+ $docStyle->getFill()->setFillType($patternType);
+ if ($style->fill->patternFill->fgColor) {
+ $docStyle->getFill()->getStartColor()->setARGB($this->_readColor($style->fill->patternFill->fgColor));
+ } else {
+ $docStyle->getFill()->getStartColor()->setARGB('FF000000');
+ }
+ if ($style->fill->patternFill->bgColor) {
+ $docStyle->getFill()->getEndColor()->setARGB($this->_readColor($style->fill->patternFill->bgColor));
+ }
+ }
+ }
+
+ // border
+ if (isset($style->border)) {
+ $diagonalUp = false;
+ $diagonalDown = false;
+ if ($style->border["diagonalUp"] == 'true' || $style->border["diagonalUp"] == 1) {
+ $diagonalUp = true;
+ }
+ if ($style->border["diagonalDown"] == 'true' || $style->border["diagonalDown"] == 1) {
+ $diagonalDown = true;
+ }
+ if ($diagonalUp == false && $diagonalDown == false) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE);
+ } elseif ($diagonalUp == true && $diagonalDown == false) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP);
+ } elseif ($diagonalUp == false && $diagonalDown == true) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN);
+ } elseif ($diagonalUp == true && $diagonalDown == true) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH);
+ }
+ $this->_readBorder($docStyle->getBorders()->getLeft(), $style->border->left);
+ $this->_readBorder($docStyle->getBorders()->getRight(), $style->border->right);
+ $this->_readBorder($docStyle->getBorders()->getTop(), $style->border->top);
+ $this->_readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom);
+ $this->_readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal);
+ }
+
+ // alignment
+ if (isset($style->alignment)) {
+ $docStyle->getAlignment()->setHorizontal((string) $style->alignment["horizontal"]);
+ $docStyle->getAlignment()->setVertical((string) $style->alignment["vertical"]);
+
+ $textRotation = 0;
+ if ((int)$style->alignment["textRotation"] <= 90) {
+ $textRotation = (int)$style->alignment["textRotation"];
+ } else if ((int)$style->alignment["textRotation"] > 90) {
+ $textRotation = 90 - (int)$style->alignment["textRotation"];
+ }
+
+ $docStyle->getAlignment()->setTextRotation(intval($textRotation));
+ $docStyle->getAlignment()->setWrapText( (string)$style->alignment["wrapText"] == "true" || (string)$style->alignment["wrapText"] == "1" );
+ $docStyle->getAlignment()->setShrinkToFit( (string)$style->alignment["shrinkToFit"] == "true" || (string)$style->alignment["shrinkToFit"] == "1" );
+ $docStyle->getAlignment()->setIndent( intval((string)$style->alignment["indent"]) > 0 ? intval((string)$style->alignment["indent"]) : 0 );
+ }
+
+ // protection
+ if (isset($style->protection)) {
+ if (isset($style->protection['locked'])) {
+ if ((string)$style->protection['locked'] == 'true') {
+ $docStyle->getProtection()->setLocked(PHPExcel_Style_Protection::PROTECTION_PROTECTED);
+ } else {
+ $docStyle->getProtection()->setLocked(PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+ }
+ }
+
+ if (isset($style->protection['hidden'])) {
+ if ((string)$style->protection['hidden'] == 'true') {
+ $docStyle->getProtection()->setHidden(PHPExcel_Style_Protection::PROTECTION_PROTECTED);
+ } else {
+ $docStyle->getProtection()->setHidden(PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+ }
+ }
+ }
+ }
+
+ private function _readBorder($docBorder, $eleBorder) {
+ if (isset($eleBorder["style"])) {
+ $docBorder->setBorderStyle((string) $eleBorder["style"]);
+ }
+ if (isset($eleBorder->color)) {
+ $docBorder->getColor()->setARGB($this->_readColor($eleBorder->color));
+ }
+ }
+
+ private function _parseRichText($is = null) {
+ $value = new PHPExcel_RichText();
+
+ if (isset($is->t)) {
+ $value->createText( PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $is->t ) );
+ } else {
+ foreach ($is->r as $run) {
+ if (!isset($run->rPr)) {
+ $objText = $value->createText( PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $run->t ) );
+
+ } else {
+ $objText = $value->createTextRun( PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $run->t ) );
+
+ if (isset($run->rPr->rFont["val"])) {
+ $objText->getFont()->setName((string) $run->rPr->rFont["val"]);
+ }
+
+ if (isset($run->rPr->sz["val"])) {
+ $objText->getFont()->setSize((string) $run->rPr->sz["val"]);
+ }
+
+ if (isset($run->rPr->color)) {
+ $objText->getFont()->setColor( new PHPExcel_Style_Color( $this->_readColor($run->rPr->color) ) );
+ }
+
+ if ( (isset($run->rPr->b["val"]) && ((string) $run->rPr->b["val"] == 'true' || (string) $run->rPr->b["val"] == '1'))
+ || (isset($run->rPr->b) && !isset($run->rPr->b["val"])) ) {
+ $objText->getFont()->setBold(true);
+ }
+
+ if ( (isset($run->rPr->i["val"]) && ((string) $run->rPr->i["val"] == 'true' || (string) $run->rPr->i["val"] == '1'))
+ || (isset($run->rPr->i) && !isset($run->rPr->i["val"])) ) {
+ $objText->getFont()->setItalic(true);
+ }
+
+ if (isset($run->rPr->vertAlign) && isset($run->rPr->vertAlign["val"])) {
+ $vertAlign = strtolower((string)$run->rPr->vertAlign["val"]);
+ if ($vertAlign == 'superscript') {
+ $objText->getFont()->setSuperScript(true);
+ }
+ if ($vertAlign == 'subscript') {
+ $objText->getFont()->setSubScript(true);
+ }
+ }
+
+ if (isset($run->rPr->u) && !isset($run->rPr->u["val"])) {
+ $objText->getFont()->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
+ } else if (isset($run->rPr->u) && isset($run->rPr->u["val"])) {
+ $objText->getFont()->setUnderline((string)$run->rPr->u["val"]);
+ }
+
+ if ( (isset($run->rPr->strike["val"]) && ((string) $run->rPr->strike["val"] == 'true' || (string) $run->rPr->strike["val"] == '1'))
+ || (isset($run->rPr->strike) && !isset($run->rPr->strike["val"])) ) {
+ $objText->getFont()->setStrikethrough(true);
+ }
+ }
+ }
+ }
+
+ return $value;
+ }
+
+ private static function array_item($array, $key = 0) {
+ return (isset($array[$key]) ? $array[$key] : null);
+ }
+
+ private static function dir_add($base, $add) {
+ return preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
+ }
+
+ private static function toCSSArray($style) {
+ $style = str_replace("\r", "", $style);
+ $style = str_replace("\n", "", $style);
+
+ $temp = explode(';', $style);
+
+ $style = array();
+ foreach ($temp as $item) {
+ $item = explode(':', $item);
+
+ if (strpos($item[1], 'px') !== false) {
+ $item[1] = str_replace('px', '', $item[1]);
+ }
+ if (strpos($item[1], 'pt') !== false) {
+ $item[1] = str_replace('pt', '', $item[1]);
+ $item[1] = PHPExcel_Shared_Font::fontSizeToPixels($item[1]);
+ }
+ if (strpos($item[1], 'in') !== false) {
+ $item[1] = str_replace('in', '', $item[1]);
+ $item[1] = PHPExcel_Shared_Font::inchSizeToPixels($item[1]);
+ }
+ if (strpos($item[1], 'cm') !== false) {
+ $item[1] = str_replace('cm', '', $item[1]);
+ $item[1] = PHPExcel_Shared_Font::centimeterSizeToPixels($item[1]);
+ }
+
+ $style[$item[0]] = $item[1];
+ }
+
+ return $style;
+ }
+}
diff --git a/Classes/PHPExcel/Reader/Excel5.php b/Classes/PHPExcel/Reader/Excel5.php
new file mode 100644
index 0000000..96cca80
--- /dev/null
+++ b/Classes/PHPExcel/Reader/Excel5.php
@@ -0,0 +1,6184 @@
+_data
+ *
+ * @var int
+ */
+ private $_dataSize;
+
+ /**
+ * Current position in stream
+ *
+ * @var integer
+ */
+ private $_pos;
+
+ /**
+ * Workbook to be returned by the reader.
+ *
+ * @var PHPExcel
+ */
+ private $_phpExcel;
+
+ /**
+ * Worksheet that is currently being built by the reader.
+ *
+ * @var PHPExcel_Worksheet
+ */
+ private $_phpSheet;
+
+ /**
+ * BIFF version
+ *
+ * @var int
+ */
+ private $_version;
+
+ /**
+ * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
+ * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
+ *
+ * @var string
+ */
+ private $_codepage;
+
+ /**
+ * Shared formats
+ *
+ * @var array
+ */
+ private $_formats;
+
+ /**
+ * Shared fonts
+ *
+ * @var array
+ */
+ private $_objFonts;
+
+ /**
+ * Color palette
+ *
+ * @var array
+ */
+ private $_palette;
+
+ /**
+ * Worksheets
+ *
+ * @var array
+ */
+ private $_sheets;
+
+ /**
+ * External books
+ *
+ * @var array
+ */
+ private $_externalBooks;
+
+ /**
+ * REF structures. Only applies to BIFF8.
+ *
+ * @var array
+ */
+ private $_ref;
+
+ /**
+ * External names
+ *
+ * @var array
+ */
+ private $_externalNames;
+
+ /**
+ * Defined names
+ *
+ * @var array
+ */
+ private $_definedname;
+
+ /**
+ * Shared strings. Only applies to BIFF8.
+ *
+ * @var array
+ */
+ private $_sst;
+
+ /**
+ * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
+ *
+ * @var boolean
+ */
+ private $_frozen;
+
+ /**
+ * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
+ *
+ * @var boolean
+ */
+ private $_isFitToPages;
+
+ /**
+ * Objects. One OBJ record contributes with one entry.
+ *
+ * @var array
+ */
+ private $_objs;
+
+ /**
+ * The combined MSODRAWINGGROUP data
+ *
+ * @var string
+ */
+ private $_drawingGroupData;
+
+ /**
+ * The combined MSODRAWING data (per sheet)
+ *
+ * @var string
+ */
+ private $_drawingData;
+
+ /**
+ * Keep track of XF index
+ *
+ * @var int
+ */
+ private $_xfIndex;
+
+ /**
+ * Mapping of XF index (that is a cell XF) to final index in cellXf collection
+ *
+ * @var array
+ */
+ private $_mapCellXfIndex;
+
+ /**
+ * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
+ *
+ * @var array
+ */
+ private $_mapCellStyleXfIndex;
+
+ /**
+ * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
+ *
+ * @var array
+ */
+ private $_sharedFormulas;
+
+ /**
+ * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
+ * refers to a shared formula.
+ *
+ * @var array
+ */
+ private $_sharedFormulaParts;
+
+ /**
+ * Read data only?
+ *
+ * @return boolean
+ */
+ public function getReadDataOnly()
+ {
+ return $this->_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setReadDataOnly($pValue = false)
+ {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_Excel5 instance
+ */
+ public function __construct() {
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ try {
+ // Use ParseXL for the hard work.
+ $ole = new PHPExcel_Shared_OLERead();
+
+ // get excel data
+ $res = $ole->read($pFilename);
+ return true;
+
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Read the OLE file
+ $this->_loadOLE($pFilename);
+
+ // Initialisations
+ $this->_phpExcel = new PHPExcel;
+ $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
+ if (!$this->_readDataOnly) {
+ $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
+ $this->_phpExcel->removeCellXfByIndex(0); // remove the default style
+ }
+
+ // Read the summary information stream (containing meta data)
+ $this->_readSummaryInformation();
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->_dataSize = strlen($this->_data);
+
+ // initialize
+ $this->_pos = 0;
+ $this->_codepage = 'CP1252';
+ $this->_formats = array();
+ $this->_objFonts = array();
+ $this->_palette = array();
+ $this->_sheets = array();
+ $this->_externalBooks = array();
+ $this->_ref = array();
+ $this->_definedname = array();
+ $this->_sst = array();
+ $this->_drawingGroupData = '';
+ $this->_xfIndex = '';
+ $this->_mapCellXfIndex = array();
+ $this->_mapCellStyleXfIndex = array();
+
+ // Parse Workbook Global Substream
+ while ($this->_pos < $this->_dataSize) {
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+
+ switch ($code) {
+ case self::XLS_Type_BOF: $this->_readBof(); break;
+ case self::XLS_Type_FILEPASS: $this->_readFilepass(); break;
+ case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break;
+ case self::XLS_Type_DATEMODE: $this->_readDateMode(); break;
+ case self::XLS_Type_FONT: $this->_readFont(); break;
+ case self::XLS_Type_FORMAT: $this->_readFormat(); break;
+ case self::XLS_Type_XF: $this->_readXf(); break;
+ case self::XLS_Type_XFEXT: $this->_readXfExt(); break;
+ case self::XLS_Type_STYLE: $this->_readStyle(); break;
+ case self::XLS_Type_PALETTE: $this->_readPalette(); break;
+ case self::XLS_Type_SHEET: $this->_readSheet(); break;
+ case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break;
+ case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break;
+ case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break;
+ case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break;
+ case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break;
+ case self::XLS_Type_SST: $this->_readSst(); break;
+ case self::XLS_Type_EOF: $this->_readDefault(); break 2;
+ default: $this->_readDefault(); break;
+ }
+ }
+
+ // Resolve indexed colors for font, fill, and border colors
+ // Cannot be resolved already in XF record, because PALETTE record comes afterwards
+ if (!$this->_readDataOnly) {
+ foreach ($this->_objFonts as $objFont) {
+ if (isset($objFont->colorIndex)) {
+ $color = $this->_readColor($objFont->colorIndex);
+ $objFont->getColor()->setRGB($color['rgb']);
+ }
+ }
+
+ foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
+ // fill start and end color
+ $fill = $objStyle->getFill();
+
+ if (isset($fill->startcolorIndex)) {
+ $startColor = $this->_readColor($fill->startcolorIndex);
+ $fill->getStartColor()->setRGB($startColor['rgb']);
+ }
+
+ if (isset($fill->endcolorIndex)) {
+ $endColor = $this->_readColor($fill->endcolorIndex);
+ $fill->getEndColor()->setRGB($endColor['rgb']);
+ }
+
+ // border colors
+ $top = $objStyle->getBorders()->getTop();
+ $right = $objStyle->getBorders()->getRight();
+ $bottom = $objStyle->getBorders()->getBottom();
+ $left = $objStyle->getBorders()->getLeft();
+ $diagonal = $objStyle->getBorders()->getDiagonal();
+
+ if (isset($top->colorIndex)) {
+ $borderTopColor = $this->_readColor($top->colorIndex);
+ $top->getColor()->setRGB($borderTopColor['rgb']);
+ }
+
+ if (isset($right->colorIndex)) {
+ $borderRightColor = $this->_readColor($right->colorIndex);
+ $right->getColor()->setRGB($borderRightColor['rgb']);
+ }
+
+ if (isset($bottom->colorIndex)) {
+ $borderBottomColor = $this->_readColor($bottom->colorIndex);
+ $bottom->getColor()->setRGB($borderBottomColor['rgb']);
+ }
+
+ if (isset($left->colorIndex)) {
+ $borderLeftColor = $this->_readColor($left->colorIndex);
+ $left->getColor()->setRGB($borderLeftColor['rgb']);
+ }
+
+ if (isset($diagonal->colorIndex)) {
+ $borderDiagonalColor = $this->_readColor($diagonal->colorIndex);
+ $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
+ }
+ }
+ }
+
+ // treat MSODRAWINGGROUP records, workbook-level Escher
+ if (!$this->_readDataOnly && $this->_drawingGroupData) {
+ $escherWorkbook = new PHPExcel_Shared_Escher();
+ $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
+ $escherWorkbook = $reader->load($this->_drawingGroupData);
+
+ // debug Escher stream
+ //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
+ //$debug->load($this->_drawingGroupData);
+ }
+
+ // Parse the individual sheets
+ foreach ($this->_sheets as $sheet) {
+
+ if ($sheet['sheetType'] != 0x00) {
+ // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
+ continue;
+ }
+
+ // check if sheet should be skipped
+ if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
+ continue;
+ }
+
+ // add sheet to PHPExcel object
+ $this->_phpSheet = $this->_phpExcel->createSheet();
+ $this->_phpSheet->setTitle($sheet['name']);
+ $this->_phpSheet->setSheetState($sheet['sheetState']);
+
+ $this->_pos = $sheet['offset'];
+
+ // Initialize isFitToPages. May change after reading SHEETPR record.
+ $this->_isFitToPages = false;
+
+ // Initialize drawingData
+ $this->_drawingData = '';
+
+ // Initialize objs
+ $this->_objs = array();
+
+ // Initialize shared formula parts
+ $this->_sharedFormulaParts = array();
+
+ // Initialize shared formulas
+ $this->_sharedFormulas = array();
+
+ while ($this->_pos <= $this->_dataSize - 4) {
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+
+ switch ($code) {
+ case self::XLS_Type_BOF: $this->_readBof(); break;
+ case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break;
+ case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break;
+ case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break;
+ case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break;
+ case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break;
+ case self::XLS_Type_HEADER: $this->_readHeader(); break;
+ case self::XLS_Type_FOOTER: $this->_readFooter(); break;
+ case self::XLS_Type_HCENTER: $this->_readHcenter(); break;
+ case self::XLS_Type_VCENTER: $this->_readVcenter(); break;
+ case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break;
+ case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break;
+ case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break;
+ case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break;
+ case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break;
+ case self::XLS_Type_PROTECT: $this->_readProtect(); break;
+ case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break;
+ case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break;
+ case self::XLS_Type_PASSWORD: $this->_readPassword(); break;
+ case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break;
+ case self::XLS_Type_COLINFO: $this->_readColInfo(); break;
+ case self::XLS_Type_DIMENSION: $this->_readDefault(); break;
+ case self::XLS_Type_ROW: $this->_readRow(); break;
+ case self::XLS_Type_DBCELL: $this->_readDefault(); break;
+ case self::XLS_Type_RK: $this->_readRk(); break;
+ case self::XLS_Type_LABELSST: $this->_readLabelSst(); break;
+ case self::XLS_Type_MULRK: $this->_readMulRk(); break;
+ case self::XLS_Type_NUMBER: $this->_readNumber(); break;
+ case self::XLS_Type_FORMULA: $this->_readFormula(); break;
+ case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break;
+ case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break;
+ case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break;
+ case self::XLS_Type_LABEL: $this->_readLabel(); break;
+ case self::XLS_Type_BLANK: $this->_readBlank(); break;
+ case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break;
+ case self::XLS_Type_OBJ: $this->_readObj(); break;
+ case self::XLS_Type_WINDOW2: $this->_readWindow2(); break;
+ case self::XLS_Type_SCL: $this->_readScl(); break;
+ case self::XLS_Type_PANE: $this->_readPane(); break;
+ case self::XLS_Type_SELECTION: $this->_readSelection(); break;
+ case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break;
+ case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break;
+ case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break;
+ case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break;
+ case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break;
+ case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break;
+ case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break;
+ //case self::XLS_Type_IMDATA: $this->_readImData(); break;
+ case self::XLS_Type_CONTINUE: $this->_readContinue(); break;
+ case self::XLS_Type_EOF: $this->_readDefault(); break 2;
+ default: $this->_readDefault(); break;
+ }
+
+ }
+
+ // treat MSODRAWING records, sheet-level Escher
+ if (!$this->_readDataOnly && $this->_drawingData) {
+ $escherWorksheet = new PHPExcel_Shared_Escher();
+ $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
+ $escherWorksheet = $reader->load($this->_drawingData);
+
+ // debug Escher stream
+ //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
+ //$debug->load($this->_drawingData);
+
+ // get all spContainers in one long array, so they can be mapped to OBJ records
+ $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
+ }
+
+ // treat OBJ records
+ foreach ($this->_objs as $n => $obj) {
+
+ // the first shape container never has a corresponding OBJ record, hence $n + 1
+ $spContainer = $allSpContainers[$n + 1];
+
+ // we skip all spContainers that are a part of a group shape since we cannot yet handle those
+ if ($spContainer->getNestingLevel() > 1) {
+ continue;
+ }
+
+ // calculate the width and height of the shape
+ list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
+ list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
+
+ $startOffsetX = $spContainer->getStartOffsetX();
+ $startOffsetY = $spContainer->getStartOffsetY();
+ $endOffsetX = $spContainer->getEndOffsetX();
+ $endOffsetY = $spContainer->getEndOffsetY();
+
+ $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
+ $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
+
+ // calculate offsetX and offsetY of the shape
+ $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
+ $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
+
+ switch ($obj['type']) {
+
+ case 0x08:
+ // picture
+
+ // get index to BSE entry (1-based)
+ $BSEindex = $spContainer->getOPT(0x0104);
+ $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
+ $BSE = $BSECollection[$BSEindex - 1];
+ $blipType = $BSE->getBlipType();
+
+ // need check because some blip types are not supported by Escher reader such as EMF
+ if ($blip = $BSE->getBlip()) {
+ $ih = imagecreatefromstring($blip->getData());
+ $drawing = new PHPExcel_Worksheet_MemoryDrawing();
+ $drawing->setImageResource($ih);
+
+ // width, height, offsetX, offsetY
+ $drawing->setResizeProportional(false);
+ $drawing->setWidth($width);
+ $drawing->setHeight($height);
+ $drawing->setOffsetX($offsetX);
+ $drawing->setOffsetY($offsetY);
+
+ switch ($blipType) {
+
+ case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
+ $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
+ $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
+ break;
+
+ case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
+ $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
+ $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
+ break;
+ }
+
+ $drawing->setWorksheet($this->_phpSheet);
+ $drawing->setCoordinates($spContainer->getStartCoordinates());
+ }
+
+ break;
+
+ default:
+ // other object type
+ break;
+
+ }
+ }
+
+ // treat SHAREDFMLA records
+ if ($this->_version == self::XLS_BIFF8) {
+ foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
+ $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
+ $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
+ }
+ }
+ }
+
+ // add the named ranges (defined names)
+ foreach ($this->_definedname as $definedName) {
+ if ($definedName['isBuiltInName']) {
+ switch ($definedName['name']) {
+
+ case pack('C', 0x06):
+ // print area
+ // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
+
+ $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+
+ $extractedRanges = array();
+ foreach ($ranges as $range) {
+ // $range should look like one of these
+ // Foo!$C$7:$J$66
+ // Bar!$A$1:$IV$2
+
+ $explodes = explode('!', $range); // FIXME: what if sheetname contains exclamation mark?
+ $sheetName = $explodes[0];
+
+ if (count($explodes) == 2) {
+ $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
+ }
+ }
+ if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
+ $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
+ }
+ break;
+
+ case pack('C', 0x07):
+ // print titles (repeating rows)
+ // Assuming BIFF8, there are 3 cases
+ // 1. repeating rows
+ // formula looks like this: Sheet!$A$1:$IV$2
+ // rows 1-2 repeat
+ // 2. repeating columns
+ // formula looks like this: Sheet!$A$1:$B$65536
+ // columns A-B repeat
+ // 3. both repeating rows and repeating columns
+ // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
+
+ $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+
+ foreach ($ranges as $range) {
+ // $range should look like this one of these
+ // Sheet!$A$1:$B$65536
+ // Sheet!$A$1:$IV$2
+
+ $explodes = explode('!', $range);
+
+ if (count($explodes) == 2) {
+ if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
+
+ $extractedRange = $explodes[1];
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ $coordinateStrings = explode(':', $extractedRange);
+ if (count($coordinateStrings) == 2) {
+ list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
+ list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
+
+ if ($firstColumn == 'A' and $lastColumn == 'IV') {
+ // then we have repeating rows
+ $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
+ } elseif ($firstRow == 1 and $lastRow == 65536) {
+ // then we have repeating columns
+ $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ }
+ } else {
+ // Extract range
+ $explodes = explode('!', $definedName['formula']);
+
+ if (count($explodes) == 2) {
+ if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
+ $extractedRange = $explodes[1];
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ $localOnly = ($definedName['scope'] == 0) ? false : true;
+ $scope = ($definedName['scope'] == 0) ?
+ null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']);
+
+ $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) );
+ }
+ }
+ }
+ }
+
+ return $this->_phpExcel;
+ }
+
+ /**
+ * Use OLE reader to extract the relevant data streams from the OLE file
+ *
+ * @param string $pFilename
+ */
+ private function _loadOLE($pFilename)
+ {
+ // OLE reader
+ $ole = new PHPExcel_Shared_OLERead();
+
+ // get excel data
+ $res = $ole->read($pFilename);
+ $this->_data = $ole->getWorkBook();
+
+ // Get summary information data
+ $this->_summaryInformation = $ole->getSummaryInformation();
+ }
+
+ /**
+ * Read summary information
+ */
+ private function _readSummaryInformation()
+ {
+ if (!isset($this->_summaryInformation)) {
+ return;
+ }
+
+ // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+ // offset: 2; size: 2;
+ // offset: 4; size: 2; OS version
+ // offset: 6; size: 2; OS indicator
+ // offset: 8; size: 16
+ // offset: 24; size: 4; section count
+
+ // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
+ // offset: 44; size: 4
+
+ // section header
+ // offset: 48; size: 4; section length
+ $secLength = $this->_GetInt4d($this->_summaryInformation, 48);
+
+ // offset: 52; size: 4; property count
+ $countProperties = $this->_GetInt4d($this->_summaryInformation, 52);
+
+ // initialize code page (used to resolve string values)
+ $codePage = 'CP1252';
+
+ // offset: 56; size: var
+ // loop through property decarations and properties
+ for ($i = 0; $i < $countProperties; ++$i) {
+
+ // offset: 56 + 8 * $i; size: 4; property ID
+ $id = $this->_GetInt4d($this->_summaryInformation, 56 + 8 * $i);
+
+ // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
+ $offset = $this->_GetInt4d($this->_summaryInformation, 60 + 8 * $i);
+
+ $type = $this->_GetInt4d($this->_summaryInformation, 48 + $offset);
+
+ // initialize property value
+ $value = null;
+
+ // extract property value based on property type
+ switch ($type) {
+ case 0x02: // 2 byte signed integer
+ $value = $this->_GetInt2d($this->_summaryInformation, 52 + $offset);
+ break;
+
+ case 0x03: // 4 byte signed integer
+ $value = $this->_GetInt4d($this->_summaryInformation, 52 + $offset);
+ break;
+
+ case 0x13: // 4 byte unsigned integer
+ // not needed yet, fix later if necessary
+ break;
+
+ case 0x1E: // null-terminated string prepended by dword string length
+ $byteLength = $this->_GetInt4d($this->_summaryInformation, 52 + $offset);
+ $value = substr($this->_summaryInformation, 56 + $offset, $byteLength);
+ $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage);
+ $value = rtrim($value);
+ break;
+
+ case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ // PHP-time
+ $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, 52 + $offset, 8));
+ break;
+
+ case 0x47: // Clipboard format
+ // not needed yet, fix later if necessary
+ break;
+ }
+
+ // Use value of property id as appropriate
+ switch ($id) {
+ case 0x01: // Code Page
+ $codePage = PHPExcel_Shared_CodePage::NumberToName($value);
+ break;
+
+ case 0x02: // Title
+ $this->_phpExcel->getProperties()->setTitle($value);
+ break;
+
+ case 0x03: // Subject
+ $this->_phpExcel->getProperties()->setSubject($value);
+ break;
+
+ case 0x04: // Author (Creator)
+ $this->_phpExcel->getProperties()->setCreator($value);
+ break;
+
+ case 0x05: // Keywords
+ $this->_phpExcel->getProperties()->setKeywords($value);
+ break;
+
+ case 0x06: // Comments (Description)
+ $this->_phpExcel->getProperties()->setDescription($value);
+ break;
+
+ case 0x08: // Last Saved By (LastModifiedBy)
+ $this->_phpExcel->getProperties()->setLastModifiedBy($value);
+ break;
+
+ case 0x09: // Revision
+ // not supported by PHPExcel
+ break;
+
+ case 0x0C: // Created
+ $this->_phpExcel->getProperties()->setCreated($value);
+ break;
+
+ case 0x0D: // Modified
+ $this->_phpExcel->getProperties()->setModified($value);
+ break;
+
+ case 0x12: // Name of creating application
+ // not supported by PHPExcel
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
+ */
+ private function _readDefault()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+ }
+
+ /**
+ * Read BOF
+ */
+ private function _readBof()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 2; size: 2; type of the following data
+ $substreamType = $this->_GetInt2d($recordData, 2);
+
+ switch ($substreamType) {
+ case self::XLS_WorkbookGlobals:
+ $version = $this->_GetInt2d($recordData, 0);
+ if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
+ throw new Exception('Cannot read this Excel file. Version is too old.');
+ }
+ $this->_version = $version;
+ break;
+
+ case self::XLS_Worksheet:
+ // do not use this version information for anything
+ // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
+ break;
+
+ default:
+ // substream, e.g. chart
+ // just skip the entire substream
+ do {
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+ $this->_readDefault();
+ } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize);
+ break;
+ }
+ }
+
+ /**
+ * FILEPASS
+ *
+ * This record is part of the File Protection Block. It
+ * contains information about the read/write password of the
+ * file. All record contents following this record will be
+ * encrypted.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readFilepass()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ throw new Exception('Cannot read encrypted file');
+ }
+
+ /**
+ * CODEPAGE
+ *
+ * This record stores the text encoding used to write byte
+ * strings, stored as MS Windows code page identifier.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readCodepage()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; code page identifier
+ $codepage = $this->_GetInt2d($recordData, 0);
+
+ $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage);
+ }
+
+ /**
+ * DATEMODE
+ *
+ * This record specifies the base date for displaying date
+ * values. All dates are stored as count of days past this
+ * base date. In BIFF2-BIFF4 this record is part of the
+ * Calculation Settings Block. In BIFF5-BIFF8 it is
+ * stored in the Workbook Globals Substream.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readDateMode()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
+ if (ord($recordData{0}) == 1) {
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
+ }
+ }
+
+ /**
+ * Read a FONT record
+ */
+ private function _readFont()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ $objFont = new PHPExcel_Style_Font();
+
+ // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
+ $size = $this->_GetInt2d($recordData, 0);
+ $objFont->setSize($size / 20);
+
+ // offset: 2; size: 2; option flags
+ // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
+ // bit: 1; mask 0x0002; italic
+ $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1;
+ if ($isItalic) $objFont->setItalic(true);
+
+ // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
+ // bit: 3; mask 0x0008; strike
+ $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3;
+ if ($isStrike) $objFont->setStrikethrough(true);
+
+ // offset: 4; size: 2; colour index
+ $colorIndex = $this->_GetInt2d($recordData, 4);
+ $objFont->colorIndex = $colorIndex;
+
+ // offset: 6; size: 2; font weight
+ $weight = $this->_GetInt2d($recordData, 6);
+ switch ($weight) {
+ case 0x02BC:
+ $objFont->setBold(true);
+ break;
+ }
+
+ // offset: 8; size: 2; escapement type
+ $escapement = $this->_GetInt2d($recordData, 8);
+ switch ($escapement) {
+ case 0x0001:
+ $objFont->setSuperScript(true);
+ break;
+ case 0x0002:
+ $objFont->setSubScript(true);
+ break;
+ }
+
+ // offset: 10; size: 1; underline type
+ $underlineType = ord($recordData{10});
+ switch ($underlineType) {
+ case 0x00:
+ break; // no underline
+ case 0x01:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
+ break;
+ case 0x02:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE);
+ break;
+ case 0x21:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING);
+ break;
+ case 0x22:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING);
+ break;
+ }
+
+ // offset: 11; size: 1; font family
+ // offset: 12; size: 1; character set
+ // offset: 13; size: 1; not used
+ // offset: 14; size: var; font name
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringShort(substr($recordData, 14));
+ } else {
+ $string = $this->_readByteStringShort(substr($recordData, 14));
+ }
+ $objFont->setName($string['value']);
+
+ $this->_objFonts[] = $objFont;
+ }
+ }
+
+ /**
+ * FORMAT
+ *
+ * This record contains information about a number format.
+ * All FORMAT records occur together in a sequential list.
+ *
+ * In BIFF2-BIFF4 other records referencing a FORMAT record
+ * contain a zero-based index into this list. From BIFF5 on
+ * the FORMAT record contains the index itself that will be
+ * used by other records.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readFormat()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ $indexCode = $this->_GetInt2d($recordData, 0);
+
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong(substr($recordData, 2));
+ } else {
+ // BIFF7
+ $string = $this->_readByteStringShort(substr($recordData, 2));
+ }
+
+ $formatString = $string['value'];
+ $this->_formats[$indexCode] = $formatString;
+ }
+ }
+
+ /**
+ * XF - Extended Format
+ *
+ * This record contains formatting information for cells, rows, columns or styles.
+ * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
+ * and 1 cell XF.
+ * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
+ * and XF record 15 is a cell XF
+ * We only read the first cell style XF and skip the remaining cell style XF records
+ * We read all cell XF records.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readXf()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ $objStyle = new PHPExcel_Style();
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; Index to FONT record
+ if ($this->_GetInt2d($recordData, 0) < 4) {
+ $fontIndex = $this->_GetInt2d($recordData, 0);
+ } else {
+ // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+ // check the OpenOffice documentation of the FONT record
+ $fontIndex = $this->_GetInt2d($recordData, 0) - 1;
+ }
+ $objStyle->setFont($this->_objFonts[$fontIndex]);
+
+ // offset: 2; size: 2; Index to FORMAT record
+ $numberFormatIndex = $this->_GetInt2d($recordData, 2);
+ if (isset($this->_formats[$numberFormatIndex])) {
+ // then we have user-defined format code
+ $numberformat = array('code' => $this->_formats[$numberFormatIndex]);
+ } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
+ // then we have built-in format code
+ $numberformat = array('code' => $code);
+ } else {
+ // we set the general format code
+ $numberformat = array('code' => 'General');
+ }
+ $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
+
+ // offset: 4; size: 2; XF type, cell protection, and parent style XF
+ // bit 2-0; mask 0x0007; XF_TYPE_PROT
+ $xfTypeProt = $this->_GetInt2d($recordData, 4);
+ // bit 0; mask 0x01; 1 = cell is locked
+ $isLocked = (0x01 & $xfTypeProt) >> 0;
+ $objStyle->getProtection()->setLocked($isLocked ?
+ PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+
+ // bit 1; mask 0x02; 1 = Formula is hidden
+ $isHidden = (0x02 & $xfTypeProt) >> 1;
+ $objStyle->getProtection()->setHidden($isHidden ?
+ PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+
+ // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
+ $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
+
+ // offset: 6; size: 1; Alignment and text break
+ // bit 2-0, mask 0x07; horizontal alignment
+ $horAlign = (0x07 & ord($recordData{6})) >> 0;
+ switch ($horAlign) {
+ case 0:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
+ break;
+ case 2:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
+ break;
+ case 3:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
+ break;
+ case 5:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY);
+ break;
+ case 6:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS);
+ break;
+ }
+ // bit 3, mask 0x08; wrap text
+ $wrapText = (0x08 & ord($recordData{6})) >> 3;
+ switch ($wrapText) {
+ case 0:
+ $objStyle->getAlignment()->setWrapText(false);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setWrapText(true);
+ break;
+ }
+ // bit 6-4, mask 0x70; vertical alignment
+ $vertAlign = (0x70 & ord($recordData{6})) >> 4;
+ switch ($vertAlign) {
+ case 0:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
+ break;
+ case 2:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM);
+ break;
+ case 3:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY);
+ break;
+ }
+
+ if ($this->_version == self::XLS_BIFF8) {
+ // offset: 7; size: 1; XF_ROTATION: Text rotation angle
+ $angle = ord($recordData{7});
+ $rotation = 0;
+ if ($angle <= 90) {
+ $rotation = $angle;
+ } else if ($angle <= 180) {
+ $rotation = 90 - $angle;
+ } else if ($angle == 255) {
+ $rotation = -165;
+ }
+ $objStyle->getAlignment()->setTextRotation($rotation);
+
+ // offset: 8; size: 1; Indentation, shrink to cell size, and text direction
+ // bit: 3-0; mask: 0x0F; indent level
+ $indent = (0x0F & ord($recordData{8})) >> 0;
+ $objStyle->getAlignment()->setIndent($indent);
+
+ // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
+ $shrinkToFit = (0x10 & ord($recordData{8})) >> 4;
+ switch ($shrinkToFit) {
+ case 0:
+ $objStyle->getAlignment()->setShrinkToFit(false);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setShrinkToFit(true);
+ break;
+ }
+
+ // offset: 9; size: 1; Flags used for attribute groups
+
+ // offset: 10; size: 4; Cell border lines and background area
+ // bit: 3-0; mask: 0x0000000F; left style
+ if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) {
+ $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
+ }
+ // bit: 7-4; mask: 0x000000F0; right style
+ if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) {
+ $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
+ }
+ // bit: 11-8; mask: 0x00000F00; top style
+ if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) {
+ $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
+ }
+ // bit: 15-12; mask: 0x0000F000; bottom style
+ if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) {
+ $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
+ }
+ // bit: 22-16; mask: 0x007F0000; left color
+ $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16;
+
+ // bit: 29-23; mask: 0x3F800000; right color
+ $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23;
+
+ // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
+ $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ?
+ true : false;
+
+ // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
+ $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ?
+ true : false;
+
+ if ($diagonalUp == false && $diagonalDown == false) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE);
+ } elseif ($diagonalUp == true && $diagonalDown == false) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP);
+ } elseif ($diagonalUp == false && $diagonalDown == true) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN);
+ } elseif ($diagonalUp == true && $diagonalDown == true) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH);
+ }
+
+ // offset: 14; size: 4;
+ // bit: 6-0; mask: 0x0000007F; top color
+ $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0;
+
+ // bit: 13-7; mask: 0x00003F80; bottom color
+ $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7;
+
+ // bit: 20-14; mask: 0x001FC000; diagonal color
+ $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14;
+
+ // bit: 24-21; mask: 0x01E00000; diagonal style
+ if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14)) >> 21)) {
+ $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
+ }
+
+ // bit: 31-26; mask: 0xFC000000 fill pattern
+ if ($fillType = $this->_mapFillPattern((0xFC000000 & $this->_GetInt4d($recordData, 14)) >> 26)) {
+ $objStyle->getFill()->setFillType($fillType);
+ }
+ // offset: 18; size: 2; pattern and background colour
+ // bit: 6-0; mask: 0x007F; color index for pattern color
+ $objStyle->getFill()->startcolorIndex = (0x007F & $this->_GetInt2d($recordData, 18)) >> 0;
+
+ // bit: 13-7; mask: 0x3F80; color index for pattern background
+ $objStyle->getFill()->endcolorIndex = (0x3F80 & $this->_GetInt2d($recordData, 18)) >> 7;
+ } else {
+ // BIFF5
+
+ // offset: 7; size: 1; Text orientation and flags
+ $orientationAndFlags = ord($recordData{7});
+
+ // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
+ $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
+ switch ($xfOrientation) {
+ case 0:
+ $objStyle->getAlignment()->setTextRotation(0);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setTextRotation(-165);
+ break;
+ case 2:
+ $objStyle->getAlignment()->setTextRotation(90);
+ break;
+ case 3:
+ $objStyle->getAlignment()->setTextRotation(-90);
+ break;
+ }
+
+ // offset: 8; size: 4; cell border lines and background area
+ $borderAndBackground = $this->_GetInt4d($recordData, 8);
+
+ // bit: 6-0; mask: 0x0000007F; color index for pattern color
+ $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
+
+ // bit: 13-7; mask: 0x00003F80; color index for pattern background
+ $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
+
+ // bit: 21-16; mask: 0x003F0000; fill pattern
+ $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 & $borderAndBackground) >> 16));
+
+ // bit: 24-22; mask: 0x01C00000; bottom line style
+ $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 & $borderAndBackground) >> 22));
+
+ // bit: 31-25; mask: 0xFE000000; bottom line color
+ $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
+
+ // offset: 12; size: 4; cell border lines
+ $borderLines = $this->_GetInt4d($recordData, 12);
+
+ // bit: 2-0; mask: 0x00000007; top line style
+ $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 & $borderLines) >> 0));
+
+ // bit: 5-3; mask: 0x00000038; left line style
+ $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 & $borderLines) >> 3));
+
+ // bit: 8-6; mask: 0x000001C0; right line style
+ $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 & $borderLines) >> 6));
+
+ // bit: 15-9; mask: 0x0000FE00; top line color index
+ $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
+
+ // bit: 22-16; mask: 0x007F0000; left line color index
+ $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
+
+ // bit: 29-23; mask: 0x3F800000; right line color index
+ $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
+ }
+
+ // add cellStyleXf or cellXf and update mapping
+ if ($isCellStyleXf) {
+ // we only read one style XF record which is always the first
+ if ($this->_xfIndex == 0) {
+ $this->_phpExcel->addCellStyleXf($objStyle);
+ $this->_mapCellStyleXfIndex[$this->_xfIndex] = 0;
+ }
+ } else {
+ // we read all cell XF records
+ $this->_phpExcel->addCellXf($objStyle);
+ $this->_mapCellXfIndex[$this->_xfIndex] = count($this->_phpExcel->getCellXfCollection()) - 1;
+ }
+
+ // update XF index for when we read next record
+ ++$this->_xfIndex;
+ }
+ }
+
+ /**
+ *
+ */
+ private function _readXfExt()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 0x087D = repeated header
+
+ // offset: 2; size: 2
+
+ // offset: 4; size: 8; not used
+
+ // offset: 12; size: 2; record version
+
+ // offset: 14; size: 2; index to XF record which this record modifies
+ $ixfe = $this->_GetInt2d($recordData, 14);
+
+ // offset: 16; size: 2; not used
+
+ // offset: 18; size: 2; number of extension properties that follow
+ $cexts = $this->_GetInt2d($recordData, 18);
+
+ // start reading the actual extension data
+ $offset = 20;
+ while ($offset < $length) {
+ // extension type
+ $extType = $this->_GetInt2d($recordData, $offset);
+
+ // extension length
+ $cb = $this->_GetInt2d($recordData, $offset + 2);
+
+ // extension data
+ $extData = substr($recordData, $offset + 4, $cb);
+
+ switch ($extType) {
+ case 4: // fill start color
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill();
+ $fill->getStartColor()->setRGB($rgb);
+ unset($fill->startcolorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 5: // fill end color
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill();
+ $fill->getEndColor()->setRGB($rgb);
+ unset($fill->endcolorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 7: // border color top
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $top = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop();
+ $top->getColor()->setRGB($rgb);
+ unset($top->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 8: // border color bottom
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $bottom = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom();
+ $bottom->getColor()->setRGB($rgb);
+ unset($bottom->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 9: // border color left
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $left = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft();
+ $left->getColor()->setRGB($rgb);
+ unset($left->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 10: // border color right
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $right = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight();
+ $right->getColor()->setRGB($rgb);
+ unset($right->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 11: // border color diagonal
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $diagonal = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
+ $diagonal->getColor()->setRGB($rgb);
+ unset($diagonal->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 13: // font color
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $font = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont();
+ $font->getColor()->setRGB($rgb);
+ unset($font->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+ }
+
+ $offset += $cb;
+ }
+ }
+
+ }
+
+ /**
+ * Read STYLE record
+ */
+ private function _readStyle()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; index to XF record and flag for built-in style
+ $ixfe = $this->_GetInt2d($recordData, 0);
+
+ // bit: 11-0; mask 0x0FFF; index to XF record
+ $xfIndex = (0x0FFF & $ixfe) >> 0;
+
+ // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
+ $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
+
+ if ($isBuiltIn) {
+ // offset: 2; size: 1; identifier for built-in style
+ $builtInId = ord($recordData{2});
+
+ switch ($builtInId) {
+ case 0x00:
+ // currently, we are not using this for anything
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+ // user-defined; not supported by PHPExcel
+ }
+ }
+ }
+
+ /**
+ * Read PALETTE record
+ */
+ private function _readPalette()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; number of following colors
+ $nm = $this->_GetInt2d($recordData, 0);
+
+ // list of RGB colors
+ for ($i = 0; $i < $nm; ++$i) {
+ $rgb = substr($recordData, 2 + 4 * $i, 4);
+ $this->_palette[] = $this->_readRGB($rgb);
+ }
+ }
+ }
+
+ /**
+ * SHEET
+ *
+ * This record is located in the Workbook Globals
+ * Substream and represents a sheet inside the workbook.
+ * One SHEET record is written for each sheet. It stores the
+ * sheet name and a stream offset to the BOF record of the
+ * respective Sheet Substream within the Workbook Stream.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readSheet()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
+ $rec_offset = $this->_GetInt4d($recordData, 0);
+
+ // offset: 4; size: 1; sheet state
+ switch (ord($recordData{4})) {
+ case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break;
+ case 0x01: $sheetState = PHPExcel_Worksheet::SHEETSTATE_HIDDEN; break;
+ case 0x02: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN; break;
+ default: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break;
+ }
+
+ // offset: 5; size: 1; sheet type
+ $sheetType = ord($recordData{5});
+
+ // offset: 6; size: var; sheet name
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringShort(substr($recordData, 6));
+ $rec_name = $string['value'];
+ } elseif ($this->_version == self::XLS_BIFF7) {
+ $string = $this->_readByteStringShort(substr($recordData, 6));
+ $rec_name = $string['value'];
+ }
+
+ $this->_sheets[] = array(
+ 'name' => $rec_name,
+ 'offset' => $rec_offset,
+ 'sheetState' => $sheetState,
+ 'sheetType' => $sheetType,
+ );
+ }
+
+ /**
+ * Read EXTERNALBOOK record
+ */
+ private function _readExternalBook()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset within record data
+ $offset = 0;
+
+ // there are 4 types of records
+ if (strlen($recordData) > 4) {
+ // external reference
+ // offset: 0; size: 2; number of sheet names ($nm)
+ $nm = $this->_GetInt2d($recordData, 0);
+ $offset += 2;
+
+ // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
+ $encodedUrlString = $this->_readUnicodeStringLong(substr($recordData, 2));
+ $offset += $encodedUrlString['size'];
+
+ // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
+ $externalSheetNames = array();
+ for ($i = 0; $i < $nm; ++$i) {
+ $externalSheetNameString = $this->_readUnicodeStringLong(substr($recordData, $offset));
+ $externalSheetNames[] = $externalSheetNameString['value'];
+ $offset += $externalSheetNameString['size'];
+ }
+
+ // store the record data
+ $this->_externalBooks[] = array(
+ 'type' => 'external',
+ 'encodedUrl' => $encodedUrlString['value'],
+ 'externalSheetNames' => $externalSheetNames,
+ );
+
+ } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
+ // internal reference
+ // offset: 0; size: 2; number of sheet in this document
+ // offset: 2; size: 2; 0x01 0x04
+ $this->_externalBooks[] = array(
+ 'type' => 'internal',
+ );
+ } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
+ // add-in function
+ // offset: 0; size: 2; 0x0001
+ $this->_externalBooks[] = array(
+ 'type' => 'addInFunction',
+ );
+ } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
+ // DDE links, OLE links
+ // offset: 0; size: 2; 0x0000
+ // offset: 2; size: var; encoded source document name
+ $this->_externalBooks[] = array(
+ 'type' => 'DDEorOLE',
+ );
+ }
+ }
+
+ /**
+ * Read EXTERNNAME record.
+ */
+ private function _readExternName()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // external sheet references provided for named cells
+ if ($this->_version == self::XLS_BIFF8) {
+ // offset: 0; size: 2; options
+ $options = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2;
+
+ // offset: 4; size: 2; not used
+
+ // offset: 6; size: var
+ $nameString = $this->_readUnicodeStringShort(substr($recordData, 6));
+
+ // offset: var; size: var; formula data
+ $offset = 6 + $nameString['size'];
+ $formula = $this->_getFormulaFromStructure(substr($recordData, $offset));
+
+ $this->_externalNames[] = array(
+ 'name' => $nameString['value'],
+ 'formula' => $formula,
+ );
+ }
+ }
+
+ /**
+ * Read EXTERNSHEET record
+ */
+ private function _readExternSheet()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // external sheet references provided for named cells
+ if ($this->_version == self::XLS_BIFF8) {
+ // offset: 0; size: 2; number of following ref structures
+ $nm = $this->_GetInt2d($recordData, 0);
+ for ($i = 0; $i < $nm; ++$i) {
+ $this->_ref[] = array(
+ // offset: 2 + 6 * $i; index to EXTERNALBOOK record
+ 'externalBookIndex' => $this->_GetInt2d($recordData, 2 + 6 * $i),
+ // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
+ 'firstSheetIndex' => $this->_GetInt2d($recordData, 4 + 6 * $i),
+ // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
+ 'lastSheetIndex' => $this->_GetInt2d($recordData, 6 + 6 * $i),
+ );
+ }
+ }
+ }
+
+ /**
+ * DEFINEDNAME
+ *
+ * This record is part of a Link Table. It contains the name
+ * and the token array of an internal defined name. Token
+ * arrays of defined names contain tokens with aberrant
+ * token classes.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readDefinedName()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8) {
+ // retrieves named cells
+
+ // offset: 0; size: 2; option flags
+ $opts = $this->_GetInt2d($recordData, 0);
+
+ // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
+ $isBuiltInName = (0x0020 & $opts) >> 5;
+
+ // offset: 2; size: 1; keyboard shortcut
+
+ // offset: 3; size: 1; length of the name (character count)
+ $nlen = ord($recordData{3});
+
+ // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
+ // note: there can also be additional data, this is not included in $flen
+ $flen = $this->_GetInt2d($recordData, 4);
+
+ // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
+ $scope = $this->_GetInt2d($recordData, 8);
+
+ // offset: 14; size: var; Name (Unicode string without length field)
+ $string = $this->_readUnicodeString(substr($recordData, 14), $nlen);
+
+ // offset: var; size: $flen; formula data
+ $offset = 14 + $string['size'];
+ $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
+
+ try {
+ $formula = $this->_getFormulaFromStructure($formulaStructure);
+ } catch (Exception $e) {
+ $formula = '';
+ }
+
+ $this->_definedname[] = array(
+ 'isBuiltInName' => $isBuiltInName,
+ 'name' => $string['value'],
+ 'formula' => $formula,
+ 'scope' => $scope,
+ );
+ }
+ }
+
+ /**
+ * Read MSODRAWINGGROUP record
+ */
+ private function _readMsoDrawingGroup()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ $this->_drawingGroupData .= $recordData;
+ }
+
+ /**
+ * SST - Shared String Table
+ *
+ * This record contains a list of all strings used anywhere
+ * in the workbook. Each string occurs only once. The
+ * workbook uses indexes into the list to reference the
+ * strings.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ **/
+ private function _readSst()
+ {
+ // offset within (spliced) record data
+ $pos = 0;
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+
+ $recordData = $splicedRecordData['recordData'];
+ $spliceOffsets = $splicedRecordData['spliceOffsets'];
+
+ // offset: 0; size: 4; total number of strings in the workbook
+ $pos += 4;
+
+ // offset: 4; size: 4; number of following strings ($nm)
+ $nm = $this->_GetInt4d($recordData, 4);
+ $pos += 4;
+
+ // loop through the Unicode strings (16-bit length)
+ for ($i = 0; $i < $nm; ++$i) {
+
+ // number of characters in the Unicode string
+ $numChars = $this->_GetInt2d($recordData, $pos);
+ $pos += 2;
+
+ // option flags
+ $optionFlags = ord($recordData{$pos});
+ ++$pos;
+
+ // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
+ $isCompressed = (($optionFlags & 0x01) == 0) ;
+
+ // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
+ $hasAsian = (($optionFlags & 0x04) != 0);
+
+ // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
+ $hasRichText = (($optionFlags & 0x08) != 0);
+
+ if ($hasRichText) {
+ // number of Rich-Text formatting runs
+ $formattingRuns = $this->_GetInt2d($recordData, $pos);
+ $pos += 2;
+ }
+
+ if ($hasAsian) {
+ // size of Asian phonetic setting
+ $extendedRunLength = $this->_GetInt4d($recordData, $pos);
+ $pos += 4;
+ }
+
+ // expected byte length of character array if not split
+ $len = ($isCompressed) ? $numChars : $numChars * 2;
+
+ // look up limit position
+ foreach ($spliceOffsets as $spliceOffset) {
+ // it can happen that the string is empty, therefore we need
+ // <= and not just <
+ if ($pos <= $spliceOffset) {
+ $limitpos = $spliceOffset;
+ break;
+ }
+ }
+
+ if ($pos + $len <= $limitpos) {
+ // character array is not split between records
+
+ $retstr = substr($recordData, $pos, $len);
+ $pos += $len;
+
+ } else {
+ // character array is split between records
+
+ // first part of character array
+ $retstr = substr($recordData, $pos, $limitpos - $pos);
+
+ $bytesRead = $limitpos - $pos;
+
+ // remaining characters in Unicode string
+ $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
+
+ $pos = $limitpos;
+
+ // keep reading the characters
+ while ($charsLeft > 0) {
+
+ // look up next limit position, in case the string span more than one continue record
+ foreach ($spliceOffsets as $spliceOffset) {
+ if ($pos < $spliceOffset) {
+ $limitpos = $spliceOffset;
+ break;
+ }
+ }
+
+ // repeated option flags
+ // OpenOffice.org documentation 5.21
+ $option = ord($recordData{$pos});
+ ++$pos;
+
+ if ($isCompressed && ($option == 0)) {
+ // 1st fragment compressed
+ // this fragment compressed
+ $len = min($charsLeft, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len;
+ $isCompressed = true;
+
+ } elseif (!$isCompressed && ($option != 0)) {
+ // 1st fragment uncompressed
+ // this fragment uncompressed
+ $len = min($charsLeft * 2, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len / 2;
+ $isCompressed = false;
+
+ } elseif (!$isCompressed && ($option == 0)) {
+ // 1st fragment uncompressed
+ // this fragment compressed
+ $len = min($charsLeft, $limitpos - $pos);
+ for ($j = 0; $j < $len; ++$j) {
+ $retstr .= $recordData{$pos + $j} . chr(0);
+ }
+ $charsLeft -= $len;
+ $isCompressed = false;
+
+ } else {
+ // 1st fragment compressed
+ // this fragment uncompressed
+ $newstr = '';
+ for ($j = 0; $j < strlen($retstr); ++$j) {
+ $newstr .= $retstr[$j] . chr(0);
+ }
+ $retstr = $newstr;
+ $len = min($charsLeft * 2, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len / 2;
+ $isCompressed = false;
+ }
+
+ $pos += $len;
+ }
+ }
+
+ // convert to UTF-8
+ $retstr = $this->_encodeUTF16($retstr, $isCompressed);
+
+ // read additional Rich-Text information, if any
+ $fmtRuns = array();
+ if ($hasRichText) {
+ // list of formatting runs
+ for ($j = 0; $j < $formattingRuns; ++$j) {
+ // first formatted character; zero-based
+ $charPos = $this->_GetInt2d($recordData, $pos + $j * 4);
+
+ // index to font record
+ $fontIndex = $this->_GetInt2d($recordData, $pos + 2 + $j * 4);
+
+ $fmtRuns[] = array(
+ 'charPos' => $charPos,
+ 'fontIndex' => $fontIndex,
+ );
+ }
+ $pos += 4 * $formattingRuns;
+ }
+
+ // read additional Asian phonetics information, if any
+ if ($hasAsian) {
+ // For Asian phonetic settings, we skip the extended string data
+ $pos += $extendedRunLength;
+ }
+
+ // store the shared sting
+ $this->_sst[] = array(
+ 'value' => $retstr,
+ 'fmtRuns' => $fmtRuns,
+ );
+ }
+
+ // _getSplicedRecordData() takes care of moving current position in data stream
+ }
+
+ /**
+ * Read PRINTGRIDLINES record
+ */
+ private function _readPrintGridlines()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+ // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
+ $printGridlines = (bool) $this->_GetInt2d($recordData, 0);
+ $this->_phpSheet->setPrintGridlines($printGridlines);
+ }
+ }
+
+ /**
+ * Read DEFAULTROWHEIGHT record
+ */
+ private function _readDefaultRowHeight()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; option flags
+ // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
+ $height = $this->_GetInt2d($recordData, 2);
+ $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
+ }
+
+ /**
+ * Read SHEETPR record
+ */
+ private function _readSheetPr()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2
+
+ // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
+ $isSummaryBelow = (0x0040 & $this->_GetInt2d($recordData, 0)) >> 6;
+ $this->_phpSheet->setShowSummaryBelow($isSummaryBelow);
+
+ // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
+ $isSummaryRight = (0x0080 & $this->_GetInt2d($recordData, 0)) >> 7;
+ $this->_phpSheet->setShowSummaryRight($isSummaryRight);
+
+ // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
+ // this corresponds to radio button setting in page setup dialog in Excel
+ $this->_isFitToPages = (bool) ((0x0100 & $this->_GetInt2d($recordData, 0)) >> 8);
+ }
+
+ /**
+ * Read HORIZONTALPAGEBREAKS record
+ */
+ private function _readHorizontalPageBreaks()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+
+ // offset: 0; size: 2; number of the following row index structures
+ $nm = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 6 * $nm; list of $nm row index structures
+ for ($i = 0; $i < $nm; ++$i) {
+ $r = $this->_GetInt2d($recordData, 2 + 6 * $i);
+ $cf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2);
+ $cl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4);
+
+ // not sure why two column indexes are necessary?
+ $this->_phpSheet->setBreakByColumnAndRow($cf, $r, PHPExcel_Worksheet::BREAK_ROW);
+ }
+ }
+ }
+
+ /**
+ * Read VERTICALPAGEBREAKS record
+ */
+ private function _readVerticalPageBreaks()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+ // offset: 0; size: 2; number of the following column index structures
+ $nm = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 6 * $nm; list of $nm row index structures
+ for ($i = 0; $i < $nm; ++$i) {
+ $c = $this->_GetInt2d($recordData, 2 + 6 * $i);
+ $rf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2);
+ $rl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4);
+
+ // not sure why two row indexes are necessary?
+ $this->_phpSheet->setBreakByColumnAndRow($c, $rf, PHPExcel_Worksheet::BREAK_COLUMN);
+ }
+ }
+ }
+
+ /**
+ * Read HEADER record
+ */
+ private function _readHeader()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: var
+ // realized that $recordData can be empty even when record exists
+ if ($recordData) {
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong($recordData);
+ } else {
+ $string = $this->_readByteStringShort($recordData);
+ }
+
+ $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']);
+ $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
+ }
+ }
+ }
+
+ /**
+ * Read FOOTER record
+ */
+ private function _readFooter()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: var
+ // realized that $recordData can be empty even when record exists
+ if ($recordData) {
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong($recordData);
+ } else {
+ $string = $this->_readByteStringShort($recordData);
+ }
+ $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']);
+ $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
+ }
+ }
+ }
+
+ /**
+ * Read HCENTER record
+ */
+ private function _readHcenter()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
+ $isHorizontalCentered = (bool) $this->_GetInt2d($recordData, 0);
+
+ $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
+ }
+ }
+
+ /**
+ * Read VCENTER record
+ */
+ private function _readVcenter()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
+ $isVerticalCentered = (bool) $this->_GetInt2d($recordData, 0);
+
+ $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
+ }
+ }
+
+ /**
+ * Read LEFTMARGIN record
+ */
+ private function _readLeftMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read RIGHTMARGIN record
+ */
+ private function _readRightMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read TOPMARGIN record
+ */
+ private function _readTopMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read BOTTOMMARGIN record
+ */
+ private function _readBottomMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read PAGESETUP record
+ */
+ private function _readPageSetup()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; paper size
+ $paperSize = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; scaling factor
+ $scale = $this->_GetInt2d($recordData, 2);
+
+ // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
+ $fitToWidth = $this->_GetInt2d($recordData, 6);
+
+ // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
+ $fitToHeight = $this->_GetInt2d($recordData, 8);
+
+ // offset: 10; size: 2; option flags
+
+ // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
+ $isPortrait = (0x0002 & $this->_GetInt2d($recordData, 10)) >> 1;
+
+ // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
+ // when this bit is set, do not use flags for those properties
+ $isNotInit = (0x0004 & $this->_GetInt2d($recordData, 10)) >> 2;
+
+ if (!$isNotInit) {
+ $this->_phpSheet->getPageSetup()->setPaperSize($paperSize);
+ switch ($isPortrait) {
+ case 0: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); break;
+ case 1: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT); break;
+ }
+
+ $this->_phpSheet->getPageSetup()->setScale($scale, false);
+ $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages);
+ $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
+ $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
+ }
+
+ // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
+ $marginHeader = $this->_extractNumber(substr($recordData, 16, 8));
+ $this->_phpSheet->getPageMargins()->setHeader($marginHeader);
+
+ // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
+ $marginFooter = $this->_extractNumber(substr($recordData, 24, 8));
+ $this->_phpSheet->getPageMargins()->setFooter($marginFooter);
+ }
+ }
+
+ /**
+ * PROTECT - Sheet protection (BIFF2 through BIFF8)
+ * if this record is omitted, then it also means no sheet protection
+ */
+ private function _readProtect()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit 0, mask 0x01; 1 = sheet is protected
+ $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0;
+ $this->_phpSheet->getProtection()->setSheet((bool)$bool);
+ }
+
+ /**
+ * SCENPROTECT
+ */
+ private function _readScenProtect()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit: 0, mask 0x01; 1 = scenarios are protected
+ $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0;
+
+ $this->_phpSheet->getProtection()->setScenarios((bool)$bool);
+ }
+
+ /**
+ * OBJECTPROTECT
+ */
+ private function _readObjectProtect()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit: 0, mask 0x01; 1 = objects are protected
+ $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0;
+
+ $this->_phpSheet->getProtection()->setObjects((bool)$bool);
+ }
+
+ /**
+ * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
+ */
+ private function _readPassword()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 16-bit hash value of password
+ $password = strtoupper(dechex($this->_GetInt2d($recordData, 0))); // the hashed password
+ $this->_phpSheet->getProtection()->setPassword($password, true);
+ }
+ }
+
+ /**
+ * Read DEFCOLWIDTH record
+ */
+ private function _readDefColWidth()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; default column width
+ $width = $this->_GetInt2d($recordData, 0);
+ if ($width != 8) {
+ $this->_phpSheet->getDefaultColumnDimension()->setWidth($width);
+ }
+ }
+
+ /**
+ * Read COLINFO record
+ */
+ private function _readColInfo()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; index to first column in range
+ $fc = $this->_GetInt2d($recordData, 0); // first column index
+
+ // offset: 2; size: 2; index to last column in range
+ $lc = $this->_GetInt2d($recordData, 2); // first column index
+
+ // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
+ $width = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 2; index to XF record for default column formatting
+ $xfIndex = $this->_GetInt2d($recordData, 6);
+
+ // offset: 8; size: 2; option flags
+
+ // bit: 0; mask: 0x0001; 1= columns are hidden
+ $isHidden = (0x0001 & $this->_GetInt2d($recordData, 8)) >> 0;
+
+ // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
+ $level = (0x0700 & $this->_GetInt2d($recordData, 8)) >> 8;
+
+ // bit: 12; mask: 0x1000; 1 = collapsed
+ $isCollapsed = (0x1000 & $this->_GetInt2d($recordData, 8)) >> 12;
+
+ // offset: 10; size: 2; not used
+
+ for ($i = $fc; $i <= $lc; ++$i) {
+ if ($lc == 255 || $lc == 256) {
+ $this->_phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
+ break;
+ }
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * ROW
+ *
+ * This record contains the properties of a single row in a
+ * sheet. Rows and cells in a sheet are divided into blocks
+ * of 32 rows.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readRow()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; index of this row
+ $r = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column of the first cell which is described by a cell record
+
+ // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
+
+ // offset: 6; size: 2;
+
+ // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
+ $height = (0x7FFF & $this->_GetInt2d($recordData, 6)) >> 0;
+
+ // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
+ $useDefaultHeight = (0x8000 & $this->_GetInt2d($recordData, 6)) >> 15;
+
+ if (!$useDefaultHeight) {
+ $this->_phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
+ }
+
+ // offset: 8; size: 2; not used
+
+ // offset: 10; size: 2; not used in BIFF5-BIFF8
+
+ // offset: 12; size: 4; option flags and default row formatting
+
+ // bit: 2-0: mask: 0x00000007; outline level of the row
+ $level = (0x00000007 & $this->_GetInt4d($recordData, 12)) >> 0;
+ $this->_phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
+
+ // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
+ $isCollapsed = (0x00000010 & $this->_GetInt4d($recordData, 12)) >> 4;
+ $this->_phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
+
+ // bit: 5; mask: 0x00000020; 1 = row is hidden
+ $isHidden = (0x00000020 & $this->_GetInt4d($recordData, 12)) >> 5;
+ $this->_phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
+
+ // bit: 7; mask: 0x00000080; 1 = row has explicit format
+ $hasExplicitFormat = (0x00000080 & $this->_GetInt4d($recordData, 12)) >> 7;
+
+ // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
+ $xfIndex = (0x0FFF0000 & $this->_GetInt4d($recordData, 12)) >> 16;
+
+ if ($hasExplicitFormat) {
+ $this->_phpSheet->getRowDimension($r + 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read RK record
+ * This record represents a cell that contains an RK value
+ * (encoded integer or floating-point value). If a
+ * floating-point value cannot be encoded to an RK value,
+ * a NUMBER record will be written. This record replaces the
+ * record INTEGER written in BIFF2.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readRk()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 4; RK value
+ $rknum = $this->_GetInt4d($recordData, 6);
+ $numValue = $this->_GetIEEE754($rknum);
+
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->_readDataOnly) {
+ // add style information
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell
+ $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read LABELSST record
+ * This record represents a cell that contains a string. It
+ * replaces the LABEL record and RSTRING record used in
+ * BIFF2-BIFF5.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readLabelSst()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 4; index to SST record
+ $index = $this->_GetInt4d($recordData, 6);
+
+ // add cell
+ if (($fmtRuns = $this->_sst[$index]['fmtRuns']) && !$this->_readDataOnly) {
+ // then we should treat as rich text
+ $richText = new PHPExcel_RichText();
+ $charPos = 0;
+ for ($i = 0; $i <= count($this->_sst[$index]['fmtRuns']); ++$i) {
+ if (isset($fmtRuns[$i])) {
+ $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
+ $charPos = $fmtRuns[$i]['charPos'];
+ } else {
+ $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, PHPExcel_Shared_String::CountCharacters($this->_sst[$index]['value']));
+ }
+
+ if (PHPExcel_Shared_String::CountCharacters($text) > 0) {
+ if ($i == 0) { // first text run, no style
+ $richText->createText($text);
+ } else {
+ $textRun = $richText->createTextRun($text);
+ if (isset($fmtRuns[$i - 1])) {
+ if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
+ $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
+ } else {
+ // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+ // check the OpenOffice documentation of the FONT record
+ $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
+ }
+ $textRun->setFont(clone $this->_objFonts[$fontIndex]);
+ }
+ }
+ }
+ }
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ $cell->setValueExplicit($richText, PHPExcel_Cell_DataType::TYPE_STRING);
+ } else {
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ $cell->setValueExplicit($this->_sst[$index]['value'], PHPExcel_Cell_DataType::TYPE_STRING);
+ }
+
+ if (!$this->_readDataOnly) {
+ // add style information
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MULRK record
+ * This record represents a cell range containing RK value
+ * cells. All cells are located in the same row.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readMulRk()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first column
+ $colFirst = $this->_GetInt2d($recordData, 2);
+
+ // offset: var; size: 2; index to last column
+ $colLast = $this->_GetInt2d($recordData, $length - 2);
+ $columns = $colLast - $colFirst + 1;
+
+ // offset within record data
+ $offset = 4;
+
+ for ($i = 0; $i < $columns; ++$i) {
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($colFirst + $i);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+
+ // offset: var; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, $offset);
+
+ // offset: var; size: 4; RK value
+ $numValue = $this->_GetIEEE754($this->_GetInt4d($recordData, $offset + 2));
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->_readDataOnly) {
+ // add style
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell value
+ $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+ }
+
+ $offset += 6;
+ }
+ }
+
+ /**
+ * Read NUMBER record
+ * This record represents a cell that contains a
+ * floating-point value.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readNumber()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ $numValue = $this->_extractNumber(substr($recordData, 6, 8));
+
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->_readDataOnly) {
+ // add cell style
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell value
+ $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read FORMULA record + perhaps a following STRING record if formula result is a string
+ * This record contains the token array and the result of a
+ * formula cell.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readFormula()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; col index
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // offset: 20: size: variable; formula structure
+ $formulaStructure = substr($recordData, 20);
+
+ // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
+ $options = $this->_GetInt2d($recordData, 14);
+
+ // bit: 0; mask: 0x0001; 1 = recalculate always
+ // bit: 1; mask: 0x0002; 1 = calculate on open
+ // bit: 2; mask: 0x0008; 1 = part of a shared formula
+ $isPartOfSharedFormula = (bool) (0x0008 & $options);
+
+ // WARNING:
+ // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
+ // the formula data may be ordinary formula data, therefore we need to check
+ // explicitly for the tExp token (0x01)
+ $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01;
+
+ if ($isPartOfSharedFormula) {
+ // part of shared formula which means there will be a formula with a tExp token and nothing else
+ // get the base cell, grab tExp token
+ $baseRow = $this->_GetInt2d($formulaStructure, 3);
+ $baseCol = $this->_GetInt2d($formulaStructure, 5);
+ $this->_baseCell = PHPExcel_Cell::stringFromColumnIndex($baseCol). ($baseRow + 1);
+ }
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+
+ if ($isPartOfSharedFormula) {
+ // formula is added to this cell after the sheet has been read
+ $this->_sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
+ }
+
+ // offset: 16: size: 4; not used
+
+ // offset: 4; size: 2; XF index
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 8; result of the formula
+ if ( (ord($recordData{6}) == 0)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255) ) {
+
+ // String formula. Result follows in appended STRING record
+ $dataType = PHPExcel_Cell_DataType::TYPE_STRING;
+
+ // read possible SHAREDFMLA record
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+ if ($code == self::XLS_Type_SHAREDFMLA) {
+ $this->_readSharedFmla();
+ }
+
+ // read STRING record
+ $value = $this->_readString();
+
+ } elseif ((ord($recordData{6}) == 1)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255)) {
+
+ // Boolean formula. Result is in +2; 0=false, 1=true
+ $dataType = PHPExcel_Cell_DataType::TYPE_BOOL;
+ $value = (bool) ord($recordData{8});
+
+ } elseif ((ord($recordData{6}) == 2)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255)) {
+
+ // Error formula. Error code is in +2
+ $dataType = PHPExcel_Cell_DataType::TYPE_ERROR;
+ $value = $this->_mapErrorCode(ord($recordData{8}));
+
+ } elseif ((ord($recordData{6}) == 3)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255)) {
+
+ // Formula result is a null string
+ $dataType = PHPExcel_Cell_DataType::TYPE_NULL;
+ $value = '';
+
+ } else {
+
+ // forumla result is a number, first 14 bytes like _NUMBER record
+ $dataType = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $value = $this->_extractNumber(substr($recordData, 6, 8));
+
+ }
+
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->_readDataOnly) {
+ // add cell style
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // store the formula
+ if (!$isPartOfSharedFormula) {
+ // not part of shared formula
+ // add cell value. If we can read formula, populate with formula, otherwise just used cached value
+ try {
+ if ($this->_version != self::XLS_BIFF8) {
+ throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
+ }
+ $formula = $this->_getFormulaFromStructure($formulaStructure); // get formula in human language
+ $cell->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
+
+ } catch (Exception $e) {
+ $cell->setValueExplicit($value, $dataType);
+ }
+ } else {
+ if ($this->_version == self::XLS_BIFF8) {
+ // do nothing at this point, formula id added later in the code
+ } else {
+ $cell->setValueExplicit($value, $dataType);
+ }
+ }
+
+ // store the cached calculated value
+ $cell->setCalculatedValue($value);
+ }
+ }
+
+ /**
+ * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
+ * which usually contains relative references.
+ * These will be used to construct the formula in each shared formula part after the sheet is read.
+ */
+ private function _readSharedFmla()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
+ $cellRange = substr($recordData, 0, 6);
+ $cellRange = $this->_readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
+
+ // offset: 6, size: 1; not used
+
+ // offset: 7, size: 1; number of existing FORMULA records for this shared formula
+ $no = ord($recordData{7});
+
+ // offset: 8, size: var; Binary token array of the shared formula
+ $formula = substr($recordData, 8);
+
+ // at this point we only store the shared formula for later use
+ $this->_sharedFormulas[$this->_baseCell] = $formula;
+
+ }
+
+ /**
+ * Read a STRING record from current stream position and advance the stream pointer to next record
+ * This record is used for storing result from FORMULA record when it is a string, and
+ * it occurs directly after the FORMULA record
+ *
+ * @return string The string contents as UTF-8
+ */
+ private function _readString()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong($recordData);
+ $value = $string['value'];
+ } else {
+ $string = $this->_readByteStringLong($recordData);
+ $value = $string['value'];
+ }
+
+ return $value;
+ }
+
+ /**
+ * Read BOOLERR record
+ * This record represents a Boolean value or error value
+ * cell.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readBoolErr()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; column index
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 1; the boolean value or error value
+ $boolErr = ord($recordData{6});
+
+ // offset: 7; size: 1; 0=boolean; 1=error
+ $isError = ord($recordData{7});
+
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ switch ($isError) {
+ case 0: // boolean
+ $value = (bool) $boolErr;
+
+ // add cell value
+ $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_BOOL);
+ break;
+
+ case 1: // error type
+ $value = $this->_mapErrorCode($boolErr);
+
+ // add cell value
+ $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_ERROR);
+ break;
+ }
+
+ if (!$this->_readDataOnly) {
+ // add cell style
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MULBLANK record
+ * This record represents a cell range of empty cells. All
+ * cells are located in the same row
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readMulBlank()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first column
+ $fc = $this->_GetInt2d($recordData, 2);
+
+ // offset: 4; size: 2 x nc; list of indexes to XF records
+ // add style information
+ if (!$this->_readDataOnly) {
+ for ($i = 0; $i < $length / 2 - 3; ++$i) {
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($fc + $i);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ $xfIndex = $this->_GetInt2d($recordData, 4 + 2 * $i);
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ // offset: 6; size 2; index to last column (not needed)
+ }
+
+ /**
+ * Read LABEL record
+ * This record represents a cell that contains a string. In
+ * BIFF8 it is usually replaced by the LABELSST record.
+ * Excel still uses this record, if it copies unformatted
+ * text cells to the clipboard.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readLabel()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; XF index
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // add cell value
+ // todo: what if string is very long? continue record
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong(substr($recordData, 6));
+ $value = $string['value'];
+ } else {
+ $string = $this->_readByteStringLong(substr($recordData, 6));
+ $value = $string['value'];
+ }
+ $cell = $this->_phpSheet->getCell($columnString . ($row + 1));
+ $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING);
+
+ if (!$this->_readDataOnly) {
+ // add cell style
+ $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read BLANK record
+ */
+ private function _readBlank()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; col index
+ $col = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($col);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; XF index
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // add style information
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+
+ }
+
+ /**
+ * Read MSODRAWING record
+ */
+ private function _readMsoDrawing()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ $this->_drawingData .= $recordData;
+ }
+
+ /**
+ * Read OBJ record
+ */
+ private function _readObj()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8) {
+ return;
+ }
+
+ // recordData consists of an array of subrecords looking like this:
+ // ft: 2 bytes; id number
+ // cb: 2 bytes; size in bytes of following data
+ // data: var; subrecord data
+
+ // for now, we are just interested in the second subrecord containing the object type
+ $ot = $this->_GetInt2d($recordData, 4);
+
+ $this->_objs[] = array(
+ 'type' => $ot,
+ );
+ }
+
+ /**
+ * Read WINDOW2 record
+ */
+ private function _readWindow2()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; option flags
+ $options = $this->_GetInt2d($recordData, 0);
+
+ // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
+ $showGridlines = (bool) ((0x0002 & $options) >> 1);
+ $this->_phpSheet->setShowGridlines($showGridlines);
+
+ // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
+ $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
+ $this->_phpSheet->setShowRowColHeaders($showRowColHeaders);
+
+ // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
+ $this->_frozen = (bool) ((0x0008 & $options) >> 3);
+
+ // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
+ $this->_phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6));
+
+ // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
+ $isActive = (bool) ((0x0400 & $options) >> 10);
+ if ($isActive) {
+ $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet));
+ }
+ }
+
+ /**
+ * Read SCL record
+ */
+ private function _readScl()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; numerator of the view magnification
+ $numerator = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; numerator of the view magnification
+ $denumerator = $this->_GetInt2d($recordData, 2);
+
+ // set the zoom scale (in percent)
+ $this->_phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
+ }
+
+ /**
+ * Read PANE record
+ */
+ private function _readPane()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; position of vertical split
+ $px = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; position of horizontal split
+ $py = $this->_GetInt2d($recordData, 2);
+
+ if ($this->_frozen) {
+ // frozen panes
+ $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px) . ($py + 1));
+ } else {
+ // unfrozen panes; split windows; not supported by PHPExcel core
+ }
+ }
+ }
+
+ /**
+ * Read SELECTION record. There is one such record for each pane in the sheet.
+ */
+ private function _readSelection()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 1; pane identifier
+ $paneId = ord($recordData{0});
+
+ // offset: 1; size: 2; index to row of the active cell
+ $r = $this->_GetInt2d($recordData, 1);
+
+ // offset: 3; size: 2; index to column of the active cell
+ $c = $this->_GetInt2d($recordData, 3);
+
+ // offset: 5; size: 2; index into the following cell range list to the
+ // entry that contains the active cell
+ $index = $this->_GetInt2d($recordData, 5);
+
+ // offset: 7; size: var; cell range address list containing all selected cell ranges
+ $data = substr($recordData, 7);
+ $cellRangeAddressList = $this->_readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
+
+ $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
+
+ // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
+ if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
+ $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
+ }
+
+ // first row '1' + last row '65536' indicates that full column is selected
+ if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
+ $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
+ }
+
+ // first column 'A' + last column 'IV' indicates that full row is selected
+ if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
+ $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
+ }
+
+ $this->_phpSheet->setSelectedCells($selectedCells);
+ }
+ }
+
+ /**
+ * MERGEDCELLS
+ *
+ * This record contains the addresses of merged cell ranges
+ * in the current sheet.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readMergedCells()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+ $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($recordData);
+ foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
+ $this->_phpSheet->mergeCells($cellRangeAddress);
+ }
+ }
+ }
+
+ /**
+ * Read HYPERLINK record
+ */
+ private function _readHyperLink()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8; cell range address of all cells containing this hyperlink
+ try {
+ $cellRange = $this->_readBIFF8CellRangeAddressFixed($recordData, 0, 8);
+ } catch (Exception $e) {
+ return;
+ }
+
+ // offset: 8, size: 16; GUID of StdLink
+
+ // offset: 24, size: 4; unknown value
+
+ // offset: 28, size: 4; option flags
+
+ // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
+ $isFileLinkOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 0;
+
+ // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
+ $isAbsPathOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 1;
+
+ // bit: 2 (and 4); mask: 0x00000014; 0 = no description
+ $hasDesc = (0x00000014 & $this->_GetInt2d($recordData, 28)) >> 2;
+
+ // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
+ $hasText = (0x00000008 & $this->_GetInt2d($recordData, 28)) >> 3;
+
+ // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
+ $hasFrame = (0x00000080 & $this->_GetInt2d($recordData, 28)) >> 7;
+
+ // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
+ $isUNC = (0x00000100 & $this->_GetInt2d($recordData, 28)) >> 8;
+
+ // offset within record data
+ $offset = 32;
+
+ if ($hasDesc) {
+ // offset: 32; size: var; character count of description text
+ $dl = $this->_GetInt4d($recordData, 32);
+ // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
+ $desc = $this->_encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
+ $offset += 4 + 2 * $dl;
+ }
+ if ($hasFrame) {
+ $fl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4 + 2 * $fl;
+ }
+
+ // detect type of hyperlink (there are 4 types)
+ $hyperlinkType = null;
+
+ if ($isUNC) {
+ $hyperlinkType = 'UNC';
+ } else if (!$isFileLinkOrUrl) {
+ $hyperlinkType = 'workbook';
+ } else if (ord($recordData{$offset}) == 0x03) {
+ $hyperlinkType = 'local';
+ } else if (ord($recordData{$offset}) == 0xE0) {
+ $hyperlinkType = 'URL';
+ }
+
+ switch ($hyperlinkType) {
+ case 'URL':
+ // section 5.58.2: Hyperlink containing a URL
+ // e.g. http://example.org/index.php
+
+ // offset: var; size: 16; GUID of URL Moniker
+ $offset += 16;
+ // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
+ $us = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+ // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
+ $url = $this->_encodeUTF16(substr($recordData, $offset, $us - 2), false);
+ $url .= $hasText ? '#' : '';
+ $offset += $us;
+ break;
+
+ case 'local':
+ // section 5.58.3: Hyperlink to local file
+ // examples:
+ // mydoc.txt
+ // ../../somedoc.xls#Sheet!A1
+
+ // offset: var; size: 16; GUI of File Moniker
+ $offset += 16;
+
+ // offset: var; size: 2; directory up-level count.
+ $upLevelCount = $this->_GetInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
+ $sl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
+ $shortenedFilePath = substr($recordData, $offset, $sl);
+ $shortenedFilePath = $this->_encodeUTF16($shortenedFilePath, true);
+ $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
+
+ $offset += $sl;
+
+ // offset: var; size: 24; unknown sequence
+ $offset += 24;
+
+ // extended file path
+ // offset: var; size: 4; size of the following file link field including string lenth mark
+ $sz = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // only present if $sz > 0
+ if ($sz > 0) {
+ // offset: var; size: 4; size of the character array of the extended file path and name
+ $xl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size 2; unknown
+ $offset += 2;
+
+ // offset: var; size $xl; character array of the extended file path and name.
+ $extendedFilePath = substr($recordData, $offset, $xl);
+ $extendedFilePath = $this->_encodeUTF16($extendedFilePath, false);
+ $offset += $xl;
+ }
+
+ // construct the path
+ $url = str_repeat('..\\', $upLevelCount);
+ $url .= ($sz > 0) ?
+ $extendedFilePath : $shortenedFilePath; // use extended path if available
+ $url .= $hasText ? '#' : '';
+
+ break;
+
+
+ case 'UNC':
+ // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
+ // todo: implement
+ return;
+
+ case 'workbook':
+ // section 5.58.5: Hyperlink to the Current Workbook
+ // e.g. Sheet2!B1:C2, stored in text mark field
+ $url = 'sheet://';
+ break;
+
+ default:
+ return;
+
+ }
+
+ if ($hasText) {
+ // offset: var; size: 4; character count of text mark including trailing zero word
+ $tl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+ // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
+ $text = $this->_encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
+ $url .= $text;
+ }
+
+ // apply the hyperlink to all the relevant cells
+ foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
+ $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
+ }
+ }
+ }
+
+ /**
+ * Read DATAVALIDATIONS record
+ */
+ private function _readDataValidations()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->_pos += 4 + $length;
+ }
+
+ /**
+ * Read DATAVALIDATION record
+ */
+ private function _readDataValidation()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 4; Options
+ $options = $this->_GetInt4d($recordData, 0);
+
+ // bit: 0-3; mask: 0x0000000F; type
+ $type = (0x0000000F & $options) >> 0;
+ switch ($type) {
+ case 0x00: $type = PHPExcel_Cell_DataValidation::TYPE_NONE; break;
+ case 0x01: $type = PHPExcel_Cell_DataValidation::TYPE_WHOLE; break;
+ case 0x02: $type = PHPExcel_Cell_DataValidation::TYPE_DECIMAL; break;
+ case 0x03: $type = PHPExcel_Cell_DataValidation::TYPE_LIST; break;
+ case 0x04: $type = PHPExcel_Cell_DataValidation::TYPE_DATE; break;
+ case 0x05: $type = PHPExcel_Cell_DataValidation::TYPE_TIME; break;
+ case 0x06: $type = PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH; break;
+ case 0x07: $type = PHPExcel_Cell_DataValidation::TYPE_CUSTOM; break;
+ }
+
+ // bit: 4-6; mask: 0x00000070; error type
+ $errorStyle = (0x00000070 & $options) >> 4;
+ switch ($errorStyle) {
+ case 0x00: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP; break;
+ case 0x01: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_WARNING; break;
+ case 0x02: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_INFORMATION; break;
+ }
+
+ // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
+ // I have only seen cases where this is 1
+ $explicitFormula = (0x00000080 & $options) >> 7;
+
+ // bit: 8; mask: 0x00000100; 1= empty cells allowed
+ $allowBlank = (0x00000100 & $options) >> 8;
+
+ // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
+ $suppressDropDown = (0x00000200 & $options) >> 9;
+
+ // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
+ $showInputMessage = (0x00040000 & $options) >> 18;
+
+ // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
+ $showErrorMessage = (0x00080000 & $options) >> 19;
+
+ // bit: 20-23; mask: 0x00F00000; condition operator
+ $operator = (0x00F00000 & $options) >> 20;
+ switch ($operator) {
+ case 0x00: $operator = PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN ; break;
+ case 0x01: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN ; break;
+ case 0x02: $operator = PHPExcel_Cell_DataValidation::OPERATOR_EQUAL ; break;
+ case 0x03: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL ; break;
+ case 0x04: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN ; break;
+ case 0x05: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN ; break;
+ case 0x06: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL; break;
+ case 0x07: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL ; break;
+ }
+
+ // offset: 4; size: var; title of the prompt box
+ $offset = 4;
+ $string = $this->_readUnicodeStringLong(substr($recordData, $offset));
+ $promptTitle = $string['value'] !== chr(0) ?
+ $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: var; title of the error box
+ $string = $this->_readUnicodeStringLong(substr($recordData, $offset));
+ $errorTitle = $string['value'] !== chr(0) ?
+ $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: var; text of the prompt box
+ $string = $this->_readUnicodeStringLong(substr($recordData, $offset));
+ $prompt = $string['value'] !== chr(0) ?
+ $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: var; text of the error box
+ $string = $this->_readUnicodeStringLong(substr($recordData, $offset));
+ $error = $string['value'] !== chr(0) ?
+ $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: 2; size of the formula data for the first condition
+ $sz1 = $this->_GetInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 2; not used
+ $offset += 2;
+
+ // offset: var; size: $sz1; formula data for first condition (without size field)
+ $formula1 = substr($recordData, $offset, $sz1);
+ $formula1 = pack('v', $sz1) . $formula1; // prepend the length
+ try {
+ $formula1 = $this->_getFormulaFromStructure($formula1);
+
+ // in list type validity, null characters are used as item separators
+ if ($type == PHPExcel_Cell_DataValidation::TYPE_LIST) {
+ $formula1 = str_replace(chr(0), ',', $formula1);
+ }
+ } catch (Exception $e) {
+ return;
+ }
+ $offset += $sz1;
+
+ // offset: var; size: 2; size of the formula data for the first condition
+ $sz2 = $this->_GetInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 2; not used
+ $offset += 2;
+
+ // offset: var; size: $sz2; formula data for second condition (without size field)
+ $formula2 = substr($recordData, $offset, $sz2);
+ $formula2 = pack('v', $sz2) . $formula2; // prepend the length
+ try {
+ $formula2 = $this->_getFormulaFromStructure($formula2);
+ } catch (Exception $e) {
+ return;
+ }
+ $offset += $sz2;
+
+ // offset: var; size: var; cell range address list with
+ $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList(substr($recordData, $offset));
+ $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
+
+ foreach ($cellRangeAddresses as $cellRange) {
+ $stRange = $this->_phpSheet->shrinkRangeToFit($cellRange);
+ $stRange = PHPExcel_Cell::extractAllCellReferencesInRange($stRange);
+ foreach ($stRange as $coordinate) {
+ $objValidation = $this->_phpSheet->getCell($coordinate)->getDataValidation();
+ $objValidation->setType($type);
+ $objValidation->setErrorStyle($errorStyle);
+ $objValidation->setAllowBlank((bool)$allowBlank);
+ $objValidation->setShowInputMessage((bool)$showInputMessage);
+ $objValidation->setShowErrorMessage((bool)$showErrorMessage);
+ $objValidation->setShowDropDown(!$suppressDropDown);
+ $objValidation->setOperator($operator);
+ $objValidation->setErrorTitle($errorTitle);
+ $objValidation->setError($error);
+ $objValidation->setPromptTitle($promptTitle);
+ $objValidation->setPrompt($prompt);
+ $objValidation->setFormula1($formula1);
+ $objValidation->setFormula2($formula2);
+ }
+ }
+
+ }
+
+ /**
+ * Read SHEETLAYOUT record. Stores sheet tab color information.
+ */
+ private function _readSheetLayout()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // local pointer in record data
+ $offset = 0;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; repeated record identifier 0x0862
+
+ // offset: 2; size: 10; not used
+
+ // offset: 12; size: 4; size of record data
+ // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
+ $sz = $this->_GetInt4d($recordData, 12);
+
+ switch ($sz) {
+ case 0x14:
+ // offset: 16; size: 2; color index for sheet tab
+ $colorIndex = $this->_GetInt2d($recordData, 16);
+ $color = $this->_readColor($colorIndex);
+ $this->_phpSheet->getTabColor()->setRGB($color['rgb']);
+ break;
+
+ case 0x28:
+ // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
+ return;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Read SHEETPROTECTION record (FEATHEADR)
+ */
+ private function _readSheetProtection()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2; repeated record header
+
+ // offset: 2; size: 2; FRT cell reference flag (=0 currently)
+
+ // offset: 4; size: 8; Currently not used and set to 0
+
+ // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
+ $isf = $this->_GetInt2d($recordData, 12);
+ if ($isf != 2) {
+ return;
+ }
+
+ // offset: 14; size: 1; =1 since this is a feat header
+
+ // offset: 15; size: 4; size of rgbHdrSData
+
+ // rgbHdrSData, assume "Enhanced Protection"
+ // offset: 19; size: 2; option flags
+ $options = $this->_GetInt2d($recordData, 19);
+
+ // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
+ $bool = (0x0001 & $options) >> 0;
+ $this->_phpSheet->getProtection()->setObjects(!$bool);
+
+ // bit: 1; mask 0x0002; edit scenarios
+ $bool = (0x0002 & $options) >> 1;
+ $this->_phpSheet->getProtection()->setScenarios(!$bool);
+
+ // bit: 2; mask 0x0004; format cells
+ $bool = (0x0004 & $options) >> 2;
+ $this->_phpSheet->getProtection()->setFormatCells(!$bool);
+
+ // bit: 3; mask 0x0008; format columns
+ $bool = (0x0008 & $options) >> 3;
+ $this->_phpSheet->getProtection()->setFormatColumns(!$bool);
+
+ // bit: 4; mask 0x0010; format rows
+ $bool = (0x0010 & $options) >> 4;
+ $this->_phpSheet->getProtection()->setFormatRows(!$bool);
+
+ // bit: 5; mask 0x0020; insert columns
+ $bool = (0x0020 & $options) >> 5;
+ $this->_phpSheet->getProtection()->setInsertColumns(!$bool);
+
+ // bit: 6; mask 0x0040; insert rows
+ $bool = (0x0040 & $options) >> 6;
+ $this->_phpSheet->getProtection()->setInsertRows(!$bool);
+
+ // bit: 7; mask 0x0080; insert hyperlinks
+ $bool = (0x0080 & $options) >> 7;
+ $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool);
+
+ // bit: 8; mask 0x0100; delete columns
+ $bool = (0x0100 & $options) >> 8;
+ $this->_phpSheet->getProtection()->setDeleteColumns(!$bool);
+
+ // bit: 9; mask 0x0200; delete rows
+ $bool = (0x0200 & $options) >> 9;
+ $this->_phpSheet->getProtection()->setDeleteRows(!$bool);
+
+ // bit: 10; mask 0x0400; select locked cells
+ $bool = (0x0400 & $options) >> 10;
+ $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool);
+
+ // bit: 11; mask 0x0800; sort cell range
+ $bool = (0x0800 & $options) >> 11;
+ $this->_phpSheet->getProtection()->setSort(!$bool);
+
+ // bit: 12; mask 0x1000; auto filter
+ $bool = (0x1000 & $options) >> 12;
+ $this->_phpSheet->getProtection()->setAutoFilter(!$bool);
+
+ // bit: 13; mask 0x2000; pivot tables
+ $bool = (0x2000 & $options) >> 13;
+ $this->_phpSheet->getProtection()->setPivotTables(!$bool);
+
+ // bit: 14; mask 0x4000; select unlocked cells
+ $bool = (0x4000 & $options) >> 14;
+ $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
+
+ // offset: 21; size: 2; not used
+ }
+
+ /**
+ * Read RANGEPROTECTION record
+ * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
+ * where it is referred to as FEAT record
+ */
+ private function _readRangeProtection()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // local pointer in record data
+ $offset = 0;
+
+ if (!$this->_readDataOnly) {
+ $offset += 12;
+
+ // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
+ $isf = $this->_GetInt2d($recordData, 12);
+ if ($isf != 2) {
+ // we only read FEAT records of type 2
+ return;
+ }
+ $offset += 2;
+
+ $offset += 5;
+
+ // offset: 19; size: 2; count of ref ranges this feature is on
+ $cref = $this->_GetInt2d($recordData, 19);
+ $offset += 2;
+
+ $offset += 6;
+
+ // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
+ $cellRanges = array();
+ for ($i = 0; $i < $cref; ++$i) {
+ try {
+ $cellRange = $this->_readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
+ } catch (Exception $e) {
+ return;
+ }
+ $cellRanges[] = $cellRange;
+ $offset += 8;
+ }
+
+ // offset: var; size: var; variable length of feature specific data
+ $rgbFeat = substr($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
+ $wPassword = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // Apply range protection to sheet
+ if ($cellRanges) {
+ $this->_phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
+ }
+ }
+ }
+
+ /**
+ * Read IMDATA record
+ */
+ private function _readImData()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ // UNDER CONSTRUCTION
+
+ // offset: 0; size: 2; image format
+ $cf = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; environment from which the file was written
+ $env = $this->_GetInt2d($recordData, 2);
+
+ // offset: 4; size: 4; length of the image data
+ $lcb = $this->_GetInt4d($recordData, 4);
+
+ // offset: 8; size: var; image data
+ $iData = substr($recordData, 8);
+
+ switch ($cf) {
+ case 0x09: // Windows bitmap format
+ // BITMAPCOREINFO
+ // 1. BITMAPCOREHEADER
+ // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
+ $bcSize = $this->_GetInt4d($iData, 0);
+// var_dump($bcSize);
+
+ // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
+ $bcWidth = $this->_GetInt2d($iData, 4);
+// var_dump($bcWidth);
+
+ // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
+ $bcHeight = $this->_GetInt2d($iData, 6);
+// var_dump($bcHeight);
+ $ih = imagecreatetruecolor($bcWidth, $bcHeight);
+
+ // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
+
+ // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
+ $bcBitCount = $this->_GetInt2d($iData, 10);
+// var_dump($bcBitCount);
+
+ $rgbString = substr($iData, 12);
+ $rgbTriples = array();
+ while (strlen($rgbString) > 0) {
+ $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
+ $rgbString = substr($rgbString, 3);
+ }
+ $x = 0;
+ $y = 0;
+ foreach ($rgbTriples as $i => $rgbTriple) {
+ $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
+ imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
+ $x = ($x + 1) % $bcWidth;
+ $y = $y + floor(($x + 1) / $bcWidth);
+ }
+ //imagepng($ih, 'image.png');
+
+ $drawing = new PHPExcel_Worksheet_Drawing();
+ $drawing->setPath($filename);
+ $drawing->setWorksheet($this->_phpSheet);
+
+ break;
+
+ case 0x02: // Windows metafile or Macintosh PICT format
+ case 0x0e: // native format
+ default;
+ break;
+
+ }
+
+ // _getSplicedRecordData() takes care of moving current position in data stream
+ }
+
+ /**
+ * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
+ * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
+ * In this case, we must treat the CONTINUE record as a MSODRAWING record
+ */
+ private function _readContinue()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // check if we are reading drawing data
+ // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
+ if ($this->_drawingData == '') {
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ return;
+ }
+
+ // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
+ if ($length < 4) {
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ return;
+ }
+
+ // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
+ // look inside CONTINUE record to see if it looks like a part of an Escher stream
+ // we know that Escher stream may be split at least at
+ // 0xF003 MsofbtSpgrContainer
+ // 0xF004 MsofbtSpContainer
+ // 0xF00D MsofbtClientTextbox
+ $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more
+
+ $splitPoint = $this->_GetInt2d($recordData, 2);
+ if (in_array($splitPoint, $validSplitPoints)) {
+ // get spliced record data (and move pointer to next record)
+ $splicedRecordData = $this->_getSplicedRecordData();
+ $this->_drawingData .= $splicedRecordData['recordData'];
+
+ return;
+ }
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ }
+
+
+ /**
+ * Reads a record from current position in data stream and continues reading data as long as CONTINUE
+ * records are found. Splices the record data pieces and returns the combined string as if record data
+ * is in one piece.
+ * Moves to next current position in data stream to start of next record different from a CONtINUE record
+ *
+ * @return array
+ */
+ private function _getSplicedRecordData()
+ {
+ $data = '';
+ $spliceOffsets = array();
+
+ $i = 0;
+ $spliceOffsets[0] = 0;
+
+ do {
+ ++$i;
+
+ // offset: 0; size: 2; identifier
+ $identifier = $this->_GetInt2d($this->_data, $this->_pos);
+ // offset: 2; size: 2; length
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $data .= substr($this->_data, $this->_pos + 4, $length);
+
+ $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
+
+ $this->_pos += 4 + $length;
+ $nextIdentifier = $this->_GetInt2d($this->_data, $this->_pos);
+ }
+ while ($nextIdentifier == self::XLS_Type_CONTINUE);
+
+ $splicedData = array(
+ 'recordData' => $data,
+ 'spliceOffsets' => $spliceOffsets,
+ );
+
+ return $splicedData;
+
+ }
+
+ /**
+ * Convert formula structure into human readable Excel formula like 'A3+A5*5'
+ *
+ * @param string $formulaStructure The complete binary data for the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string Human readable formula
+ */
+ private function _getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
+ {
+ // offset: 0; size: 2; size of the following formula data
+ $sz = $this->_GetInt2d($formulaStructure, 0);
+
+ // offset: 2; size: sz
+ $formulaData = substr($formulaStructure, 2, $sz);
+
+ // for debug: dump the formula data
+ //echo '';
+ //echo 'size: ' . $sz . "\n";
+ //echo 'the entire formula data: ';
+ //Debug::dump($formulaData);
+ //echo "\n----\n";
+
+ // offset: 2 + sz; size: variable (optional)
+ if (strlen($formulaStructure) > 2 + $sz) {
+ $additionalData = substr($formulaStructure, 2 + $sz);
+
+ // for debug: dump the additional data
+ //echo 'the entire additional data: ';
+ //Debug::dump($additionalData);
+ //echo "\n----\n";
+
+ } else {
+ $additionalData = '';
+ }
+
+ return $this->_getFormulaFromData($formulaData, $additionalData, $baseCell);
+ }
+
+ /**
+ * Take formula data and additional data for formula and return human readable formula
+ *
+ * @param string $formulaData The binary data for the formula itself
+ * @param string $additionalData Additional binary data going with the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string Human readable formula
+ */
+ private function _getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
+ {
+ // start parsing the formula data
+ $tokens = array();
+
+ while (strlen($formulaData) > 0 and $token = $this->_getNextToken($formulaData, $baseCell)) {
+ $tokens[] = $token;
+ $formulaData = substr($formulaData, $token['size']);
+
+ // for debug: dump the token
+ //var_dump($token);
+ }
+
+ $formulaString = $this->_createFormulaFromTokens($tokens, $additionalData);
+
+ return $formulaString;
+ }
+
+ /**
+ * Take array of tokens together with additional data for formula and return human readable formula
+ *
+ * @param array $tokens
+ * @param array $additionalData Additional binary data going with the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string Human readable formula
+ */
+ private function _createFormulaFromTokens($tokens, $additionalData)
+ {
+ // empty formula?
+ if (count($tokens) == 0) {
+ return '';
+ }
+
+ $formulaStrings = array();
+ foreach ($tokens as $token) {
+ // initialize spaces
+ $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
+ $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
+ $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
+ $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
+ $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
+ $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
+
+ switch ($token['name']) {
+ case 'tAdd': // addition
+ case 'tConcat': // addition
+ case 'tDiv': // division
+ case 'tEQ': // equaltiy
+ case 'tGE': // greater than or equal
+ case 'tGT': // greater than
+ case 'tIsect': // intersection
+ case 'tLE': // less than or equal
+ case 'tList': // less than or equal
+ case 'tLT': // less than
+ case 'tMul': // multiplication
+ case 'tNE': // multiplication
+ case 'tPower': // power
+ case 'tRange': // range
+ case 'tSub': // subtraction
+ $op2 = array_pop($formulaStrings);
+ $op1 = array_pop($formulaStrings);
+ $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
+ unset($space0, $space1);
+ break;
+ case 'tUplus': // unary plus
+ case 'tUminus': // unary minus
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "$space1$space0{$token['data']}$op";
+ unset($space0, $space1);
+ break;
+ case 'tPercent': // percent sign
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "$op$space1$space0{$token['data']}";
+ unset($space0, $space1);
+ break;
+ case 'tAttrVolatile': // indicates volatile function
+ case 'tAttrIf':
+ case 'tAttrSkip':
+ case 'tAttrChoose':
+ // token is only important for Excel formula evaluator
+ // do nothing
+ break;
+ case 'tAttrSpace': // space / carriage return
+ // space will be used when next token arrives, do not alter formulaString stack
+ switch ($token['data']['spacetype']) {
+ case 'type0':
+ $space0 = str_repeat(' ', $token['data']['spacecount']);
+ break;
+ case 'type1':
+ $space1 = str_repeat("\n", $token['data']['spacecount']);
+ break;
+ case 'type2':
+ $space2 = str_repeat(' ', $token['data']['spacecount']);
+ break;
+ case 'type3':
+ $space3 = str_repeat("\n", $token['data']['spacecount']);
+ break;
+ case 'type4':
+ $space4 = str_repeat(' ', $token['data']['spacecount']);
+ break;
+ case 'type5':
+ $space5 = str_repeat("\n", $token['data']['spacecount']);
+ break;
+ }
+ break;
+ case 'tAttrSum': // SUM function with one parameter
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "{$space1}{$space0}SUM($op)";
+ unset($space0, $space1);
+ break;
+ case 'tFunc': // function with fixed number of arguments
+ case 'tFuncV': // function with variable number of arguments
+ if ($token['data']['function'] != '') {
+ // normal function
+ $ops = array(); // array of operators
+ for ($i = 0; $i < $token['data']['args']; ++$i) {
+ $ops[] = array_pop($formulaStrings);
+ }
+ $ops = array_reverse($ops);
+ $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")";
+ unset($space0, $space1);
+ } else {
+ // add-in function
+ $ops = array(); // array of operators
+ for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
+ $ops[] = array_pop($formulaStrings);
+ }
+ $ops = array_reverse($ops);
+ $function = array_pop($formulaStrings);
+ $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ")";
+ unset($space0, $space1);
+ }
+ break;
+ case 'tParen': // parenthesis
+ $expression = array_pop($formulaStrings);
+ $formulaStrings[] = "$space3$space2($expression$space5$space4)";
+ unset($space2, $space3, $space4, $space5);
+ break;
+ case 'tArray': // array constant
+ $constantArray = $this->_readBIFF8ConstantArray($additionalData);
+ $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
+ $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
+ unset($space0, $space1);
+ break;
+ case 'tMemArea':
+ // bite off chunk of additional data
+ $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($additionalData);
+ $additionalData = substr($additionalData, $cellRangeAddressList['size']);
+ $formulaStrings[] = "$space1$space0{$token['data']}";
+ unset($space0, $space1);
+ break;
+ case 'tArea': // cell range address
+ case 'tBool': // boolean
+ case 'tErr': // error code
+ case 'tInt': // integer
+ case 'tMemErr':
+ case 'tMemFunc':
+ case 'tMissArg':
+ case 'tName':
+ case 'tNameX':
+ case 'tNum': // number
+ case 'tRef': // single cell reference
+ case 'tRef3d': // 3d cell reference
+ case 'tArea3d': // 3d cell range reference
+ case 'tRefN':
+ case 'tAreaN':
+ case 'tStr': // string
+ $formulaStrings[] = "$space1$space0{$token['data']}";
+ unset($space0, $space1);
+ break;
+ }
+ }
+ $formulaString = $formulaStrings[0];
+
+ // for debug: dump the human readable formula
+ //echo '----' . "\n";
+ //echo 'Formula: ' . $formulaString;
+
+ return $formulaString;
+ }
+
+ /**
+ * Fetch next token from binary formula data
+ *
+ * @param string Formula data
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return array
+ * @throws Exception
+ */
+ private function _getNextToken($formulaData, $baseCell = 'A1')
+ {
+ // offset: 0; size: 1; token id
+ $id = ord($formulaData[0]); // token id
+ $name = false; // initialize token name
+
+ switch ($id) {
+ case 0x03: $name = 'tAdd'; $size = 1; $data = '+'; break;
+ case 0x04: $name = 'tSub'; $size = 1; $data = '-'; break;
+ case 0x05: $name = 'tMul'; $size = 1; $data = '*'; break;
+ case 0x06: $name = 'tDiv'; $size = 1; $data = '/'; break;
+ case 0x07: $name = 'tPower'; $size = 1; $data = '^'; break;
+ case 0x08: $name = 'tConcat'; $size = 1; $data = '&'; break;
+ case 0x09: $name = 'tLT'; $size = 1; $data = '<'; break;
+ case 0x0A: $name = 'tLE'; $size = 1; $data = '<='; break;
+ case 0x0B: $name = 'tEQ'; $size = 1; $data = '='; break;
+ case 0x0C: $name = 'tGE'; $size = 1; $data = '>='; break;
+ case 0x0D: $name = 'tGT'; $size = 1; $data = '>'; break;
+ case 0x0E: $name = 'tNE'; $size = 1; $data = '<>'; break;
+ case 0x0F: $name = 'tIsect'; $size = 1; $data = ' '; break;
+ case 0x10: $name = 'tList'; $size = 1; $data = ','; break;
+ case 0x11: $name = 'tRange'; $size = 1; $data = ':'; break;
+ case 0x12: $name = 'tUplus'; $size = 1; $data = '+'; break;
+ case 0x13: $name = 'tUminus'; $size = 1; $data = '-'; break;
+ case 0x14: $name = 'tPercent'; $size = 1; $data = '%'; break;
+ case 0x15: // parenthesis
+ $name = 'tParen';
+ $size = 1;
+ $data = null;
+ break;
+ case 0x16: // missing argument
+ $name = 'tMissArg';
+ $size = 1;
+ $data = '';
+ break;
+ case 0x17: // string
+ $name = 'tStr';
+ // offset: 1; size: var; Unicode string, 8-bit string length
+ $string = $this->_readUnicodeStringShort(substr($formulaData, 1));
+ $size = 1 + $string['size'];
+ $data = $this->_UTF8toExcelDoubleQuoted($string['value']);
+ break;
+ case 0x19: // Special attribute
+ // offset: 1; size: 1; attribute type flags:
+ switch (ord($formulaData[1])) {
+ case 0x01:
+ $name = 'tAttrVolatile';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x02:
+ $name = 'tAttrIf';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x04:
+ $name = 'tAttrChoose';
+ // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
+ $nc = $this->_GetInt2d($formulaData, 2);
+ // offset: 4; size: 2 * $nc
+ // offset: 4 + 2 * $nc; size: 2
+ $size = 2 * $nc + 6;
+ $data = null;
+ break;
+ case 0x08:
+ $name = 'tAttrSkip';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x10:
+ $name = 'tAttrSum';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x40:
+ case 0x41:
+ $name = 'tAttrSpace';
+ $size = 4;
+ // offset: 2; size: 2; space type and position
+ switch (ord($formulaData[2])) {
+ case 0x00:
+ $spacetype = 'type0';
+ break;
+ case 0x01:
+ $spacetype = 'type1';
+ break;
+ case 0x02:
+ $spacetype = 'type2';
+ break;
+ case 0x03:
+ $spacetype = 'type3';
+ break;
+ case 0x04:
+ $spacetype = 'type4';
+ break;
+ case 0x05:
+ $spacetype = 'type5';
+ break;
+ default:
+ throw new Exception('Unrecognized space type in tAttrSpace token');
+ break;
+ }
+ // offset: 3; size: 1; number of inserted spaces/carriage returns
+ $spacecount = ord($formulaData[3]);
+
+ $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount);
+ break;
+ default:
+ throw new Exception('Unrecognized attribute flag in tAttr token');
+ break;
+ }
+ break;
+ case 0x1C: // error code
+ // offset: 1; size: 1; error code
+ $name = 'tErr';
+ $size = 2;
+ $data = $this->_mapErrorCode(ord($formulaData[1]));
+ break;
+ case 0x1D: // boolean
+ // offset: 1; size: 1; 0 = false, 1 = true;
+ $name = 'tBool';
+ $size = 2;
+ $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
+ break;
+ case 0x1E: // integer
+ // offset: 1; size: 2; unsigned 16-bit integer
+ $name = 'tInt';
+ $size = 3;
+ $data = $this->_GetInt2d($formulaData, 1);
+ break;
+ case 0x1F: // number
+ // offset: 1; size: 8;
+ $name = 'tNum';
+ $size = 9;
+ $data = $this->_extractNumber(substr($formulaData, 1));
+ $data = str_replace(',', '.', (string)$data); // in case non-English locale
+ break;
+ case 0x20: // array constant
+ case 0x40:
+ case 0x60:
+ // offset: 1; size: 7; not used
+ $name = 'tArray';
+ $size = 8;
+ $data = null;
+ break;
+ case 0x21: // function with fixed number of arguments
+ case 0x41:
+ case 0x61:
+ $name = 'tFunc';
+ $size = 3;
+ // offset: 1; size: 2; index to built-in sheet function
+ switch ($this->_GetInt2d($formulaData, 1)) {
+ case 2: $function = 'ISNA'; $args = 1; break;
+ case 3: $function = 'ISERROR'; $args = 1; break;
+ case 10: $function = 'NA'; $args = 0; break;
+ case 15: $function = 'SIN'; $args = 1; break;
+ case 16: $function = 'COS'; $args = 1; break;
+ case 17: $function = 'TAN'; $args = 1; break;
+ case 18: $function = 'ATAN'; $args = 1; break;
+ case 19: $function = 'PI'; $args = 0; break;
+ case 20: $function = 'SQRT'; $args = 1; break;
+ case 21: $function = 'EXP'; $args = 1; break;
+ case 22: $function = 'LN'; $args = 1; break;
+ case 23: $function = 'LOG10'; $args = 1; break;
+ case 24: $function = 'ABS'; $args = 1; break;
+ case 25: $function = 'INT'; $args = 1; break;
+ case 26: $function = 'SIGN'; $args = 1; break;
+ case 27: $function = 'ROUND'; $args = 2; break;
+ case 30: $function = 'REPT'; $args = 2; break;
+ case 31: $function = 'MID'; $args = 3; break;
+ case 32: $function = 'LEN'; $args = 1; break;
+ case 33: $function = 'VALUE'; $args = 1; break;
+ case 34: $function = 'TRUE'; $args = 0; break;
+ case 35: $function = 'FALSE'; $args = 0; break;
+ case 38: $function = 'NOT'; $args = 1; break;
+ case 39: $function = 'MOD'; $args = 2; break;
+ case 40: $function = 'DCOUNT'; $args = 3; break;
+ case 41: $function = 'DSUM'; $args = 3; break;
+ case 42: $function = 'DAVERAGE'; $args = 3; break;
+ case 43: $function = 'DMIN'; $args = 3; break;
+ case 44: $function = 'DMAX'; $args = 3; break;
+ case 45: $function = 'DSTDEV'; $args = 3; break;
+ case 48: $function = 'TEXT'; $args = 2; break;
+ case 61: $function = 'MIRR'; $args = 3; break;
+ case 63: $function = 'RAND'; $args = 0; break;
+ case 65: $function = 'DATE'; $args = 3; break;
+ case 66: $function = 'TIME'; $args = 3; break;
+ case 67: $function = 'DAY'; $args = 1; break;
+ case 68: $function = 'MONTH'; $args = 1; break;
+ case 69: $function = 'YEAR'; $args = 1; break;
+ case 71: $function = 'HOUR'; $args = 1; break;
+ case 72: $function = 'MINUTE'; $args = 1; break;
+ case 73: $function = 'SECOND'; $args = 1; break;
+ case 74: $function = 'NOW'; $args = 0; break;
+ case 75: $function = 'AREAS'; $args = 1; break;
+ case 76: $function = 'ROWS'; $args = 1; break;
+ case 77: $function = 'COLUMNS'; $args = 1; break;
+ case 83: $function = 'TRANSPOSE'; $args = 1; break;
+ case 86: $function = 'TYPE'; $args = 1; break;
+ case 97: $function = 'ATAN2'; $args = 2; break;
+ case 98: $function = 'ASIN'; $args = 1; break;
+ case 99: $function = 'ACOS'; $args = 1; break;
+ case 105: $function = 'ISREF'; $args = 1; break;
+ case 111: $function = 'CHAR'; $args = 1; break;
+ case 112: $function = 'LOWER'; $args = 1; break;
+ case 113: $function = 'UPPER'; $args = 1; break;
+ case 114: $function = 'PROPER'; $args = 1; break;
+ case 117: $function = 'EXACT'; $args = 2; break;
+ case 118: $function = 'TRIM'; $args = 1; break;
+ case 119: $function = 'REPLACE'; $args = 4; break;
+ case 121: $function = 'CODE'; $args = 1; break;
+ case 126: $function = 'ISERR'; $args = 1; break;
+ case 127: $function = 'ISTEXT'; $args = 1; break;
+ case 128: $function = 'ISNUMBER'; $args = 1; break;
+ case 129: $function = 'ISBLANK'; $args = 1; break;
+ case 130: $function = 'T'; $args = 1; break;
+ case 131: $function = 'N'; $args = 1; break;
+ case 140: $function = 'DATEVALUE'; $args = 1; break;
+ case 141: $function = 'TIMEVALUE'; $args = 1; break;
+ case 142: $function = 'SLN'; $args = 3; break;
+ case 143: $function = 'SYD'; $args = 4; break;
+ case 162: $function = 'CLEAN'; $args = 1; break;
+ case 163: $function = 'MDETERM'; $args = 1; break;
+ case 164: $function = 'MINVERSE'; $args = 1; break;
+ case 165: $function = 'MMULT'; $args = 2; break;
+ case 184: $function = 'FACT'; $args = 1; break;
+ case 189: $function = 'DPRODUCT'; $args = 3; break;
+ case 190: $function = 'ISNONTEXT'; $args = 1; break;
+ case 195: $function = 'DSTDEVP'; $args = 3; break;
+ case 196: $function = 'DVARP'; $args = 3; break;
+ case 198: $function = 'ISLOGICAL'; $args = 1; break;
+ case 199: $function = 'DCOUNTA'; $args = 3; break;
+ case 207: $function = 'REPLACEB'; $args = 4; break;
+ case 210: $function = 'MIDB'; $args = 3; break;
+ case 211: $function = 'LENB'; $args = 1; break;
+ case 212: $function = 'ROUNDUP'; $args = 2; break;
+ case 213: $function = 'ROUNDDOWN'; $args = 2; break;
+ case 214: $function = 'ASC'; $args = 1; break;
+ case 215: $function = 'DBCS'; $args = 1; break;
+ case 221: $function = 'TODAY'; $args = 0; break;
+ case 229: $function = 'SINH'; $args = 1; break;
+ case 230: $function = 'COSH'; $args = 1; break;
+ case 231: $function = 'TANH'; $args = 1; break;
+ case 232: $function = 'ASINH'; $args = 1; break;
+ case 233: $function = 'ACOSH'; $args = 1; break;
+ case 234: $function = 'ATANH'; $args = 1; break;
+ case 235: $function = 'DGET'; $args = 3; break;
+ case 244: $function = 'INFO'; $args = 1; break;
+ case 252: $function = 'FREQUENCY'; $args = 2; break;
+ case 261: $function = 'ERROR.TYPE'; $args = 1; break;
+ case 271: $function = 'GAMMALN'; $args = 1; break;
+ case 273: $function = 'BINOMDIST'; $args = 4; break;
+ case 274: $function = 'CHIDIST'; $args = 2; break;
+ case 275: $function = 'CHIINV'; $args = 2; break;
+ case 276: $function = 'COMBIN'; $args = 2; break;
+ case 277: $function = 'CONFIDENCE'; $args = 3; break;
+ case 278: $function = 'CRITBINOM'; $args = 3; break;
+ case 279: $function = 'EVEN'; $args = 1; break;
+ case 280: $function = 'EXPONDIST'; $args = 3; break;
+ case 281: $function = 'FDIST'; $args = 3; break;
+ case 282: $function = 'FINV'; $args = 3; break;
+ case 283: $function = 'FISHER'; $args = 1; break;
+ case 284: $function = 'FISHERINV'; $args = 1; break;
+ case 285: $function = 'FLOOR'; $args = 2; break;
+ case 286: $function = 'GAMMADIST'; $args = 4; break;
+ case 287: $function = 'GAMMAINV'; $args = 3; break;
+ case 288: $function = 'CEILING'; $args = 2; break;
+ case 289: $function = 'HYPGEOMDIST'; $args = 4; break;
+ case 290: $function = 'LOGNORMDIST'; $args = 3; break;
+ case 291: $function = 'LOGINV'; $args = 3; break;
+ case 292: $function = 'NEGBINOMDIST'; $args = 3; break;
+ case 293: $function = 'NORMDIST'; $args = 4; break;
+ case 294: $function = 'NORMSDIST'; $args = 1; break;
+ case 295: $function = 'NORMINV'; $args = 3; break;
+ case 296: $function = 'NORMSINV'; $args = 1; break;
+ case 297: $function = 'STANDARDIZE'; $args = 3; break;
+ case 298: $function = 'ODD'; $args = 1; break;
+ case 299: $function = 'PERMUT'; $args = 2; break;
+ case 300: $function = 'POISSON'; $args = 3; break;
+ case 301: $function = 'TDIST'; $args = 3; break;
+ case 302: $function = 'WEIBULL'; $args = 4; break;
+ case 303: $function = 'SUMXMY2'; $args = 2; break;
+ case 304: $function = 'SUMX2MY2'; $args = 2; break;
+ case 305: $function = 'SUMX2PY2'; $args = 2; break;
+ case 306: $function = 'CHITEST'; $args = 2; break;
+ case 307: $function = 'CORREL'; $args = 2; break;
+ case 308: $function = 'COVAR'; $args = 2; break;
+ case 309: $function = 'FORECAST'; $args = 3; break;
+ case 310: $function = 'FTEST'; $args = 2; break;
+ case 311: $function = 'INTERCEPT'; $args = 2; break;
+ case 312: $function = 'PEARSON'; $args = 2; break;
+ case 313: $function = 'RSQ'; $args = 2; break;
+ case 314: $function = 'STEYX'; $args = 2; break;
+ case 315: $function = 'SLOPE'; $args = 2; break;
+ case 316: $function = 'TTEST'; $args = 4; break;
+ case 325: $function = 'LARGE'; $args = 2; break;
+ case 326: $function = 'SMALL'; $args = 2; break;
+ case 327: $function = 'QUARTILE'; $args = 2; break;
+ case 328: $function = 'PERCENTILE'; $args = 2; break;
+ case 331: $function = 'TRIMMEAN'; $args = 2; break;
+ case 332: $function = 'TINV'; $args = 2; break;
+ case 337: $function = 'POWER'; $args = 2; break;
+ case 342: $function = 'RADIANS'; $args = 1; break;
+ case 343: $function = 'DEGREES'; $args = 1; break;
+ case 346: $function = 'COUNTIF'; $args = 2; break;
+ case 347: $function = 'COUNTBLANK'; $args = 1; break;
+ case 350: $function = 'ISPMT'; $args = 4; break;
+ case 351: $function = 'DATEDIF'; $args = 3; break;
+ case 352: $function = 'DATESTRING'; $args = 1; break;
+ case 353: $function = 'NUMBERSTRING'; $args = 2; break;
+ case 360: $function = 'PHONETIC'; $args = 1; break;
+ default:
+ throw new Exception('Unrecognized function in formula');
+ break;
+ }
+ $data = array('function' => $function, 'args' => $args);
+ break;
+ case 0x22: // function with variable number of arguments
+ case 0x42:
+ case 0x62:
+ $name = 'tFuncV';
+ $size = 4;
+ // offset: 1; size: 1; number of arguments
+ $args = ord($formulaData[1]);
+ // offset: 2: size: 2; index to built-in sheet function
+ $index = $this->_GetInt2d($formulaData, 2);
+ switch ($index) {
+ case 0: $function = 'COUNT'; break;
+ case 1: $function = 'IF'; break;
+ case 4: $function = 'SUM'; break;
+ case 5: $function = 'AVERAGE'; break;
+ case 6: $function = 'MIN'; break;
+ case 7: $function = 'MAX'; break;
+ case 8: $function = 'ROW'; break;
+ case 9: $function = 'COLUMN'; break;
+ case 11: $function = 'NPV'; break;
+ case 12: $function = 'STDEV'; break;
+ case 13: $function = 'DOLLAR'; break;
+ case 14: $function = 'FIXED'; break;
+ case 28: $function = 'LOOKUP'; break;
+ case 29: $function = 'INDEX'; break;
+ case 36: $function = 'AND'; break;
+ case 37: $function = 'OR'; break;
+ case 46: $function = 'VAR'; break;
+ case 49: $function = 'LINEST'; break;
+ case 50: $function = 'TREND'; break;
+ case 51: $function = 'LOGEST'; break;
+ case 52: $function = 'GROWTH'; break;
+ case 56: $function = 'PV'; break;
+ case 57: $function = 'FV'; break;
+ case 58: $function = 'NPER'; break;
+ case 59: $function = 'PMT'; break;
+ case 60: $function = 'RATE'; break;
+ case 62: $function = 'IRR'; break;
+ case 64: $function = 'MATCH'; break;
+ case 70: $function = 'WEEKDAY'; break;
+ case 78: $function = 'OFFSET'; break;
+ case 82: $function = 'SEARCH'; break;
+ case 100: $function = 'CHOOSE'; break;
+ case 101: $function = 'HLOOKUP'; break;
+ case 102: $function = 'VLOOKUP'; break;
+ case 109: $function = 'LOG'; break;
+ case 115: $function = 'LEFT'; break;
+ case 116: $function = 'RIGHT'; break;
+ case 120: $function = 'SUBSTITUTE'; break;
+ case 124: $function = 'FIND'; break;
+ case 125: $function = 'CELL'; break;
+ case 144: $function = 'DDB'; break;
+ case 148: $function = 'INDIRECT'; break;
+ case 167: $function = 'IPMT'; break;
+ case 168: $function = 'PPMT'; break;
+ case 169: $function = 'COUNTA'; break;
+ case 183: $function = 'PRODUCT'; break;
+ case 193: $function = 'STDEVP'; break;
+ case 194: $function = 'VARP'; break;
+ case 197: $function = 'TRUNC'; break;
+ case 204: $function = 'USDOLLAR'; break;
+ case 205: $function = 'FINDB'; break;
+ case 206: $function = 'SEARCHB'; break;
+ case 208: $function = 'LEFTB'; break;
+ case 209: $function = 'RIGHTB'; break;
+ case 216: $function = 'RANK'; break;
+ case 219: $function = 'ADDRESS'; break;
+ case 220: $function = 'DAYS360'; break;
+ case 222: $function = 'VDB'; break;
+ case 227: $function = 'MEDIAN'; break;
+ case 228: $function = 'SUMPRODUCT'; break;
+ case 247: $function = 'DB'; break;
+ case 255: $function = ''; break;
+ case 269: $function = 'AVEDEV'; break;
+ case 270: $function = 'BETADIST'; break;
+ case 272: $function = 'BETAINV'; break;
+ case 317: $function = 'PROB'; break;
+ case 318: $function = 'DEVSQ'; break;
+ case 319: $function = 'GEOMEAN'; break;
+ case 320: $function = 'HARMEAN'; break;
+ case 321: $function = 'SUMSQ'; break;
+ case 322: $function = 'KURT'; break;
+ case 323: $function = 'SKEW'; break;
+ case 324: $function = 'ZTEST'; break;
+ case 329: $function = 'PERCENTRANK'; break;
+ case 330: $function = 'MODE'; break;
+ case 336: $function = 'CONCATENATE'; break;
+ case 344: $function = 'SUBTOTAL'; break;
+ case 345: $function = 'SUMIF'; break;
+ case 354: $function = 'ROMAN'; break;
+ case 358: $function = 'GETPIVOTDATA'; break;
+ case 359: $function = 'HYPERLINK'; break;
+ case 361: $function = 'AVERAGEA'; break;
+ case 362: $function = 'MAXA'; break;
+ case 363: $function = 'MINA'; break;
+ case 364: $function = 'STDEVPA'; break;
+ case 365: $function = 'VARPA'; break;
+ case 366: $function = 'STDEVA'; break;
+ case 367: $function = 'VARA'; break;
+ default:
+ throw new Exception('Unrecognized function in formula');
+ break;
+ }
+ $data = array('function' => $function, 'args' => $args);
+ break;
+ case 0x23: // index to defined name
+ case 0x43:
+ case 0x63:
+ $name = 'tName';
+ $size = 5;
+ // offset: 1; size: 2; one-based index to definedname record
+ $definedNameIndex = $this->_GetInt2d($formulaData, 1) - 1;
+ // offset: 2; size: 2; not used
+ $data = $this->_definedname[$definedNameIndex]['name'];
+ break;
+ case 0x24: // single cell reference e.g. A5
+ case 0x44:
+ case 0x64:
+ $name = 'tRef';
+ $size = 5;
+ $data = $this->_readBIFF8CellAddress(substr($formulaData, 1, 4));
+ break;
+ case 0x25: // cell range reference to cells in the same sheet (2d)
+ case 0x45:
+ case 0x65:
+ $name = 'tArea';
+ $size = 9;
+ $data = $this->_readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
+ break;
+ case 0x26: // Constant reference sub-expression
+ case 0x46:
+ case 0x66:
+ $name = 'tMemArea';
+ // offset: 1; size: 4; not used
+ // offset: 5; size: 2; size of the following subexpression
+ $subSize = $this->_GetInt2d($formulaData, 5);
+ $size = 7 + $subSize;
+ $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize));
+ break;
+ case 0x27: // Deleted constant reference sub-expression
+ case 0x47:
+ case 0x67:
+ $name = 'tMemErr';
+ // offset: 1; size: 4; not used
+ // offset: 5; size: 2; size of the following subexpression
+ $subSize = $this->_GetInt2d($formulaData, 5);
+ $size = 7 + $subSize;
+ $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize));
+ break;
+ case 0x29: // Variable reference sub-expression
+ case 0x49:
+ case 0x69:
+ $name = 'tMemFunc';
+ // offset: 1; size: 2; size of the following sub-expression
+ $subSize = $this->_GetInt2d($formulaData, 1);
+ $size = 3 + $subSize;
+ $data = $this->_getFormulaFromData(substr($formulaData, 3, $subSize));
+ break;
+
+ case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
+ case 0x4C:
+ case 0x6C:
+ $name = 'tRefN';
+ $size = 5;
+ $data = $this->_readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
+ break;
+
+ case 0x2D: // Relative 2d range reference
+ case 0x4D:
+ case 0x6D:
+ $name = 'tAreaN';
+ $size = 9;
+ $data = $this->_readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
+ break;
+
+ case 0x39: // External name
+ case 0x59:
+ case 0x79:
+ $name = 'tNameX';
+ $size = 7;
+ // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
+ // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
+ $index = $this->_GetInt2d($formulaData, 3);
+ // assume index is to EXTERNNAME record
+ $data = $this->_externalNames[$index - 1]['name'];
+ // offset: 5; size: 2; not used
+ break;
+
+ case 0x3A: // 3d reference to cell
+ case 0x5A:
+ case 0x7A:
+ $name = 'tRef3d';
+ $size = 7;
+
+ try {
+ // offset: 1; size: 2; index to REF entry
+ $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1));
+ // offset: 3; size: 4; cell address
+ $cellAddress = $this->_readBIFF8CellAddress(substr($formulaData, 3, 4));
+
+ $data = "$sheetRange!$cellAddress";
+ } catch (Exception $e) {
+ // deleted sheet reference
+ $data = '#REF!';
+ }
+
+ break;
+ case 0x3B: // 3d reference to cell range
+ case 0x5B:
+ case 0x7B:
+ $name = 'tArea3d';
+ $size = 11;
+
+ try {
+ // offset: 1; size: 2; index to REF entry
+ $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1));
+ // offset: 3; size: 8; cell address
+ $cellRangeAddress = $this->_readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
+
+ $data = "$sheetRange!$cellRangeAddress";
+ } catch (Exception $e) {
+ // deleted sheet reference
+ $data = '#REF!';
+ }
+
+ break;
+ // Unknown cases // don't know how to deal with
+ default:
+ throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
+ break;
+ }
+
+ return array(
+ 'id' => $id,
+ 'name' => $name,
+ 'size' => $size,
+ 'data' => $data,
+ );
+ }
+
+ /**
+ * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
+ * section 3.3.4
+ *
+ * @param string $cellAddressStructure
+ * @return string
+ */
+ private function _readBIFF8CellAddress($cellAddressStructure)
+ {
+ // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+ $row = $this->_GetInt2d($cellAddressStructure, 0) + 1;
+
+ // offset: 2; size: 2; index to column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $column = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($cellAddressStructure, 2));
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $column = '$' . $column;
+ }
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $row = '$' . $row;
+ }
+
+ return $column . $row;
+ }
+
+ /**
+ * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+ * to indicate offsets from a base cell
+ * section 3.3.4
+ *
+ * @param string $cellAddressStructure
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string
+ */
+ private function _readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
+ {
+ list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell);
+ $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1;
+
+ // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+ $rowIndex = $this->_GetInt2d($cellAddressStructure, 0);
+ $row = $this->_GetInt2d($cellAddressStructure, 0) + 1;
+
+ // offset: 2; size: 2; index to column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $colIndex = 0x00FF & $this->_GetInt2d($cellAddressStructure, 2);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $column = PHPExcel_Cell::stringFromColumnIndex($colIndex);
+ $column = '$' . $column;
+ } else {
+ $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
+ $column = PHPExcel_Cell::stringFromColumnIndex($baseCol + $colIndex);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $row = '$' . $row;
+ } else {
+ $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
+ $row = $baseRow + $rowIndex;
+ }
+
+ return $column . $row;
+ }
+
+ /**
+ * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
+ * always fixed range
+ * section 2.5.14
+ *
+ * @param string $subData
+ * @return string
+ * @throws Exception
+ */
+ private function _readBIFF5CellRangeAddressFixed($subData)
+ {
+ // offset: 0; size: 2; index to first row
+ $fr = $this->_GetInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row
+ $lr = $this->_GetInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 1; index to first column
+ $fc = ord($subData{4});
+
+ // offset: 5; size: 1; index to last column
+ $lc = ord($subData{5});
+
+ // check values
+ if ($fr > $lr || $fc > $lc) {
+ throw new Exception('Not a cell range address');
+ }
+
+ // column index to letter
+ $fc = PHPExcel_Cell::stringFromColumnIndex($fc);
+ $lc = PHPExcel_Cell::stringFromColumnIndex($lc);
+
+ if ($fr == $lr and $fc == $lc) {
+ return "$fc$fr";
+ }
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
+ * always fixed range
+ * section 2.5.14
+ *
+ * @param string $subData
+ * @return string
+ * @throws Exception
+ */
+ private function _readBIFF8CellRangeAddressFixed($subData)
+ {
+ // offset: 0; size: 2; index to first row
+ $fr = $this->_GetInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row
+ $lr = $this->_GetInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 2; index to first column
+ $fc = $this->_GetInt2d($subData, 4);
+
+ // offset: 6; size: 2; index to last column
+ $lc = $this->_GetInt2d($subData, 6);
+
+ // check values
+ if ($fr > $lr || $fc > $lc) {
+ throw new Exception('Not a cell range address');
+ }
+
+ // column index to letter
+ $fc = PHPExcel_Cell::stringFromColumnIndex($fc);
+ $lc = PHPExcel_Cell::stringFromColumnIndex($lc);
+
+ if ($fr == $lr and $fc == $lc) {
+ return "$fc$fr";
+ }
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
+ * there are flags indicating whether column/row index is relative
+ * section 3.3.4
+ *
+ * @param string $subData
+ * @return string
+ */
+ private function _readBIFF8CellRangeAddress($subData)
+ {
+ // todo: if cell range is just a single cell, should this funciton
+ // not just return e.g. 'A1' and not 'A1:A1' ?
+
+ // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
+ $fr = $this->_GetInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
+ $lr = $this->_GetInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 2; index to first column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $fc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 4));
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 4))) {
+ $fc = '$' . $fc;
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 4))) {
+ $fr = '$' . $fr;
+ }
+
+ // offset: 6; size: 2; index to last column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $lc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 6));
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 6))) {
+ $lc = '$' . $lc;
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 6))) {
+ $lr = '$' . $lr;
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+ * to indicate offsets from a base cell
+ * section 3.3.4
+ *
+ * @param string $subData
+ * @param string $baseCell Base cell
+ * @return string Cell range address
+ */
+ private function _readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
+ {
+ list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell);
+ $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1;
+
+ // TODO: if cell range is just a single cell, should this funciton
+ // not just return e.g. 'A1' and not 'A1:A1' ?
+
+ // offset: 0; size: 2; first row
+ $frIndex = $this->_GetInt2d($subData, 0); // adjust below
+
+ // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
+ $lrIndex = $this->_GetInt2d($subData, 2); // adjust below
+
+ // offset: 4; size: 2; first column with relative/absolute flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $fcIndex = 0x00FF & $this->_GetInt2d($subData, 4);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 4))) {
+ // absolute column index
+ $fc = PHPExcel_Cell::stringFromColumnIndex($fcIndex);
+ $fc = '$' . $fc;
+ } else {
+ // column offset
+ $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
+ $fc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $fcIndex);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 4))) {
+ // absolute row index
+ $fr = $frIndex + 1;
+ $fr = '$' . $fr;
+ } else {
+ // row offset
+ $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
+ $fr = $baseRow + $frIndex;
+ }
+
+ // offset: 6; size: 2; last column with relative/absolute flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $lcIndex = 0x00FF & $this->_GetInt2d($subData, 6);
+ $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
+ $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 6))) {
+ // absolute column index
+ $lc = PHPExcel_Cell::stringFromColumnIndex($lcIndex);
+ $lc = '$' . $lc;
+ } else {
+ // column offset
+ $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
+ $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 6))) {
+ // absolute row index
+ $lr = $lrIndex + 1;
+ $lr = '$' . $lr;
+ } else {
+ // row offset
+ $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
+ $lr = $baseRow + $lrIndex;
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Read BIFF8 cell range address list
+ * section 2.5.15
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readBIFF8CellRangeAddressList($subData)
+ {
+ $cellRangeAddresses = array();
+
+ // offset: 0; size: 2; number of the following cell range addresses
+ $nm = $this->_GetInt2d($subData, 0);
+
+ $offset = 2;
+ // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
+ for ($i = 0; $i < $nm; ++$i) {
+ $cellRangeAddresses[] = $this->_readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
+ $offset += 8;
+ }
+
+ return array(
+ 'size' => 2 + 8 * $nm,
+ 'cellRangeAddresses' => $cellRangeAddresses,
+ );
+ }
+
+ /**
+ * Read BIFF5 cell range address list
+ * section 2.5.15
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readBIFF5CellRangeAddressList($subData)
+ {
+ $cellRangeAddresses = array();
+
+ // offset: 0; size: 2; number of the following cell range addresses
+ $nm = $this->_GetInt2d($subData, 0);
+
+ $offset = 2;
+ // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
+ for ($i = 0; $i < $nm; ++$i) {
+ $cellRangeAddresses[] = $this->_readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
+ $offset += 6;
+ }
+
+ return array(
+ 'size' => 2 + 6 * $nm,
+ 'cellRangeAddresses' => $cellRangeAddresses,
+ );
+ }
+
+ /**
+ * Get a sheet range like Sheet1:Sheet3 from REF index
+ * Note: If there is only one sheet in the range, one gets e.g Sheet1
+ * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
+ * in which case an exception is thrown
+ *
+ * @param int $index
+ * @return string|false
+ * @throws Exception
+ */
+ private function _readSheetRangeByRefIndex($index)
+ {
+ if (isset($this->_ref[$index])) {
+
+ $type = $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type'];
+
+ switch ($type) {
+ case 'internal':
+ // check if we have a deleted 3d reference
+ if ($this->_ref[$index]['firstSheetIndex'] == 0xFFFF or $this->_ref[$index]['lastSheetIndex'] == 0xFFFF) {
+ throw new Exception('Deleted sheet reference');
+ }
+
+ // we have normal sheet range (collapsed or uncollapsed)
+ $firstSheetName = $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name'];
+ $lastSheetName = $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name'];
+
+ if ($firstSheetName == $lastSheetName) {
+ // collapsed sheet range
+ $sheetRange = $firstSheetName;
+ } else {
+ $sheetRange = "$firstSheetName:$lastSheetName";
+ }
+
+ // escape the single-quotes
+ $sheetRange = str_replace("'", "''", $sheetRange);
+
+ // if there are special characters, we need to enclose the range in single-quotes
+ // todo: check if we have identified the whole set of special characters
+ // it seems that the following characters are not accepted for sheet names
+ // and we may assume that they are not present: []*/:\?
+ if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
+ $sheetRange = "'$sheetRange'";
+ }
+
+ return $sheetRange;
+ break;
+
+ default:
+ // TODO: external sheet support
+ throw new Exception('Excel5 reader only supports internal sheets in fomulas');
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * read BIFF8 constant value array from array data
+ * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
+ * section 2.5.8
+ *
+ * @param string $arrayData
+ * @return array
+ */
+ private function _readBIFF8ConstantArray($arrayData)
+ {
+ // offset: 0; size: 1; number of columns decreased by 1
+ $nc = ord($arrayData[0]);
+
+ // offset: 1; size: 2; number of rows decreased by 1
+ $nr = $this->_GetInt2d($arrayData, 1);
+ $size = 3; // initialize
+ $arrayData = substr($arrayData, 3);
+
+ // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
+ $matrixChunks = array();
+ for ($r = 1; $r <= $nr + 1; ++$r) {
+ $items = array();
+ for ($c = 1; $c <= $nc + 1; ++$c) {
+ $constant = $this->_readBIFF8Constant($arrayData);
+ $items[] = $constant['value'];
+ $arrayData = substr($arrayData, $constant['size']);
+ $size += $constant['size'];
+ }
+ $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
+ }
+ $matrix = '{' . implode(';', $matrixChunks) . '}';
+
+ return array(
+ 'value' => $matrix,
+ 'size' => $size,
+ );
+ }
+
+ /**
+ * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
+ * section 2.5.7
+ * returns e.g. array('value' => '5', 'size' => 9)
+ *
+ * @param string $valueData
+ * @return array
+ */
+ private function _readBIFF8Constant($valueData)
+ {
+ // offset: 0; size: 1; identifier for type of constant
+ $identifier = ord($valueData[0]);
+
+ switch ($identifier) {
+ case 0x00: // empty constant (what is this?)
+ $value = '';
+ $size = 9;
+ break;
+ case 0x01: // number
+ // offset: 1; size: 8; IEEE 754 floating-point value
+ $value = $this->_extractNumber(substr($valueData, 1, 8));
+ $size = 9;
+ break;
+ case 0x02: // string value
+ // offset: 1; size: var; Unicode string, 16-bit string length
+ $string = $this->_readUnicodeStringLong(substr($valueData, 1));
+ $value = '"' . $string['value'] . '"';
+ $size = 1 + $string['size'];
+ break;
+ case 0x04: // boolean
+ // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
+ if (ord($valueData[1])) {
+ $value = 'TRUE';
+ } else {
+ $value = 'FALSE';
+ }
+ $size = 9;
+ break;
+ case 0x10: // error code
+ // offset: 1; size: 1; error code
+ $value = $this->_mapErrorCode(ord($valueData[1]));
+ $size = 9;
+ break;
+ }
+ return array(
+ 'value' => $value,
+ 'size' => $size,
+ );
+ }
+
+ /**
+ * Extract RGB color
+ * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
+ *
+ * @param string $rgb Encoded RGB value (4 bytes)
+ * @return array
+ */
+ private function _readRGB($rgb)
+ {
+ // offset: 0; size 1; Red component
+ $r = ord($rgb{0});
+
+ // offset: 1; size: 1; Green component
+ $g = ord($rgb{1});
+
+ // offset: 2; size: 1; Blue component
+ $b = ord($rgb{2});
+
+ // HEX notation, e.g. 'FF00FC'
+ $rgb = sprintf('%02X', $r) . sprintf('%02X', $g) . sprintf('%02X', $b);
+
+ return array('rgb' => $rgb);
+ }
+
+ /**
+ * Read byte string (8-bit string length)
+ * OpenOffice documentation: 2.5.2
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readByteStringShort($subData)
+ {
+ // offset: 0; size: 1; length of the string (character count)
+ $ln = ord($subData[0]);
+
+ // offset: 1: size: var; character array (8-bit characters)
+ $value = $this->_decodeCodepage(substr($subData, 1, $ln));
+
+ return array(
+ 'value' => $value,
+ 'size' => 1 + $ln, // size in bytes of data structure
+ );
+ }
+
+ /**
+ * Read byte string (16-bit string length)
+ * OpenOffice documentation: 2.5.2
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readByteStringLong($subData)
+ {
+ // offset: 0; size: 2; length of the string (character count)
+ $ln = $this->_GetInt2d($subData, 0);
+
+ // offset: 2: size: var; character array (8-bit characters)
+ $value = $this->_decodeCodepage(substr($subData, 2));
+
+ //return $string;
+ return array(
+ 'value' => $value,
+ 'size' => 2 + $ln, // size in bytes of data structure
+ );
+ }
+
+ /**
+ * Extracts an Excel Unicode short string (8-bit string length)
+ * OpenOffice documentation: 2.5.3
+ * function will automatically find out where the Unicode string ends.
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readUnicodeStringShort($subData)
+ {
+ $value = '';
+
+ // offset: 0: size: 1; length of the string (character count)
+ $characterCount = ord($subData[0]);
+
+ $string = $this->_readUnicodeString(substr($subData, 1), $characterCount);
+
+ // add 1 for the string length
+ $string['size'] += 1;
+
+ return $string;
+ }
+
+ /**
+ * Extracts an Excel Unicode long string (16-bit string length)
+ * OpenOffice documentation: 2.5.3
+ * this function is under construction, needs to support rich text, and Asian phonetic settings
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readUnicodeStringLong($subData)
+ {
+ $value = '';
+
+ // offset: 0: size: 2; length of the string (character count)
+ $characterCount = $this->_GetInt2d($subData, 0);
+
+ $string = $this->_readUnicodeString(substr($subData, 2), $characterCount);
+
+ // add 2 for the string length
+ $string['size'] += 2;
+
+ return $string;
+ }
+
+ /**
+ * Read Unicode string with no string length field, but with known character count
+ * this function is under construction, needs to support rich text, and Asian phonetic settings
+ * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
+ *
+ * @param string $subData
+ * @param int $characterCount
+ * @return array
+ */
+ private function _readUnicodeString($subData, $characterCount)
+ {
+ $value = '';
+
+ // offset: 0: size: 1; option flags
+
+ // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
+ $isCompressed = !((0x01 & ord($subData[0])) >> 0);
+
+ // bit: 2; mask: 0x04; Asian phonetic settings
+ $hasAsian = (0x04) & ord($subData[0]) >> 2;
+
+ // bit: 3; mask: 0x08; Rich-Text settings
+ $hasRichText = (0x08) & ord($subData[0]) >> 3;
+
+ // offset: 1: size: var; character array
+ // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
+ // needs to be fixed
+ $value = $this->_encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
+
+ return array(
+ 'value' => $value,
+ 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
+ );
+ }
+
+ /**
+ * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
+ * Example: hello"world --> "hello""world"
+ *
+ * @param string $value UTF-8 encoded string
+ * @return string
+ */
+ private function _UTF8toExcelDoubleQuoted($value)
+ {
+ return '"' . str_replace('"', '""', $value) . '"';
+ }
+
+ /**
+ * Reads first 8 bytes of a string and return IEEE 754 float
+ *
+ * @param string $data Binary string that is at least 8 bytes long
+ * @return float
+ */
+ private function _extractNumber($data)
+ {
+ $rknumhigh = $this->_GetInt4d($data, 4);
+ $rknumlow = $this->_GetInt4d($data, 0);
+ $sign = ($rknumhigh & 0x80000000) >> 31;
+ $exp = ($rknumhigh & 0x7ff00000) >> 20;
+ $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
+ $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
+ $mantissalow2 = ($rknumlow & 0x7fffffff);
+ $value = $mantissa / pow( 2 , (20 - ($exp - 1023)));
+
+ if ($mantissalow1 != 0) {
+ $value += 1 / pow (2 , (21 - ($exp - 1023)));
+ }
+
+ $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023)));
+ if ($sign) {
+ $value = -1 * $value;
+ }
+
+ return $value;
+ }
+
+ private function _GetIEEE754($rknum)
+ {
+ if (($rknum & 0x02) != 0) {
+ $value = $rknum >> 2;
+ }
+ else {
+ // changes by mmp, info on IEEE754 encoding from
+ // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
+ // The RK format calls for using only the most significant 30 bits
+ // of the 64 bit floating point value. The other 34 bits are assumed
+ // to be 0 so we use the upper 30 bits of $rknum as follows...
+ $sign = ($rknum & 0x80000000) >> 31;
+ $exp = ($rknum & 0x7ff00000) >> 20;
+ $mantissa = (0x100000 | ($rknum & 0x000ffffc));
+ $value = $mantissa / pow( 2 , (20- ($exp - 1023)));
+ if ($sign) {
+ $value = -1 * $value;
+ }
+ //end of changes by mmp
+ }
+ if (($rknum & 0x01) != 0) {
+ $value /= 100;
+ }
+ return $value;
+ }
+
+ /**
+ * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
+ *
+ * @param string $string
+ * @param bool $compressed
+ * @return string
+ */
+ private function _encodeUTF16($string, $compressed = '')
+ {
+ if ($compressed) {
+ $string = $this->_uncompressByteString($string);
+ }
+
+ $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', 'UTF-16LE');
+
+ return $result;
+ }
+
+ /**
+ * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
+ *
+ * @param string $string
+ * @return string
+ */
+ private function _uncompressByteString($string)
+ {
+ $uncompressedString = '';
+ for ($i = 0; $i < strlen($string); ++$i) {
+ $uncompressedString .= $string[$i] . "\0";
+ }
+
+ return $uncompressedString;
+ }
+
+ /**
+ * Convert string to UTF-8. Only used for BIFF5.
+ *
+ * @param string $string
+ * @return string
+ */
+ private function _decodeCodepage($string)
+ {
+ $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $this->_codepage);
+ return $result;
+ }
+
+ /**
+ * Read 16-bit unsigned integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt2d($data, $pos)
+ {
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
+ }
+
+ /**
+ * Read 32-bit signed integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt4d($data, $pos)
+ {
+ //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) |
+ // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
+
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+
+ /**
+ * Read color
+ *
+ * @param int $color Indexed color
+ * @return array RGB color value, example: array('rgb' => 'FF0000')
+ */
+ private function _readColor($color)
+ {
+ if ($color <= 0x07 || $color >= 0x40) {
+ // special built-in color
+ $color = $this->_mapBuiltInColor($color);
+ } else if (isset($this->_palette) && isset($this->_palette[$color - 8])) {
+ // palette color, color index 0x08 maps to pallete index 0
+ $color = $this->_palette[$color - 8];
+ } else {
+ // default color table
+ if ($this->_version == self::XLS_BIFF8) {
+ $color = $this->_mapColor($color);
+ } else {
+ // BIFF5
+ $color = $this->_mapColorBIFF5($color);
+ }
+ }
+
+ return $color;
+ }
+
+
+ /**
+ * Map border style
+ * OpenOffice documentation: 2.5.11
+ *
+ * @param int $index
+ * @return string
+ */
+ private function _mapBorderStyle($index)
+ {
+ switch ($index) {
+ case 0x00: return PHPExcel_Style_Border::BORDER_NONE;
+ case 0x01: return PHPExcel_Style_Border::BORDER_THIN;
+ case 0x02: return PHPExcel_Style_Border::BORDER_MEDIUM;
+ case 0x03: return PHPExcel_Style_Border::BORDER_DASHED;
+ case 0x04: return PHPExcel_Style_Border::BORDER_DOTTED;
+ case 0x05: return PHPExcel_Style_Border::BORDER_THICK;
+ case 0x06: return PHPExcel_Style_Border::BORDER_DOUBLE;
+ case 0x07: return PHPExcel_Style_Border::BORDER_HAIR;
+ case 0x08: return PHPExcel_Style_Border::BORDER_MEDIUMDASHED;
+ case 0x09: return PHPExcel_Style_Border::BORDER_DASHDOT;
+ case 0x0A: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT;
+ case 0x0B: return PHPExcel_Style_Border::BORDER_DASHDOTDOT;
+ case 0x0C: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT;
+ case 0x0D: return PHPExcel_Style_Border::BORDER_SLANTDASHDOT;
+ default: return PHPExcel_Style_Border::BORDER_NONE;
+ }
+ }
+
+ /**
+ * Get fill pattern from index
+ * OpenOffice documentation: 2.5.12
+ *
+ * @param int $index
+ * @return string
+ */
+ private function _mapFillPattern($index)
+ {
+ switch ($index) {
+ case 0x00: return PHPExcel_Style_Fill::FILL_NONE;
+ case 0x01: return PHPExcel_Style_Fill::FILL_SOLID;
+ case 0x02: return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY;
+ case 0x03: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY;
+ case 0x04: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY;
+ case 0x05: return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL;
+ case 0x06: return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL;
+ case 0x07: return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN;
+ case 0x08: return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP;
+ case 0x09: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID;
+ case 0x0A: return PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS;
+ case 0x0B: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL;
+ case 0x0C: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL;
+ case 0x0D: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN;
+ case 0x0E: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP;
+ case 0x0F: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID;
+ case 0x10: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS;
+ case 0x11: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125;
+ case 0x12: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625;
+ default: return PHPExcel_Style_Fill::FILL_NONE;
+ }
+ }
+
+ /**
+ * Map error code, e.g. '#N/A'
+ *
+ * @param int $subData
+ * @return string
+ */
+ private function _mapErrorCode($subData)
+ {
+ switch ($subData) {
+ case 0x00: return '#NULL!'; break;
+ case 0x07: return '#DIV/0!'; break;
+ case 0x0F: return '#VALUE!'; break;
+ case 0x17: return '#REF!'; break;
+ case 0x1D: return '#NAME?'; break;
+ case 0x24: return '#NUM!'; break;
+ case 0x2A: return '#N/A'; break;
+ default: return false;
+ }
+ }
+
+ /**
+ * Map built-in color to RGB value
+ *
+ * @param int $color Indexed color
+ * @return array
+ */
+ private function _mapBuiltInColor($color)
+ {
+ switch ($color) {
+ case 0x00: return array('rgb' => '000000');
+ case 0x01: return array('rgb' => 'FFFFFF');
+ case 0x02: return array('rgb' => 'FF0000');
+ case 0x03: return array('rgb' => '00FF00');
+ case 0x04: return array('rgb' => '0000FF');
+ case 0x05: return array('rgb' => 'FFFF00');
+ case 0x06: return array('rgb' => 'FF00FF');
+ case 0x07: return array('rgb' => '00FFFF');
+ case 0x40: return array('rgb' => '000000'); // system window text color
+ case 0x41: return array('rgb' => 'FFFFFF'); // system window background color
+ default: return array('rgb' => '000000');
+ }
+ }
+
+ /**
+ * Map color array from BIFF5 built-in color index
+ *
+ * @param int $subData
+ * @return array
+ */
+ private function _mapColorBIFF5($subData)
+ {
+ switch ($subData) {
+ case 0x08: return array('rgb' => '000000');
+ case 0x09: return array('rgb' => 'FFFFFF');
+ case 0x0A: return array('rgb' => 'FF0000');
+ case 0x0B: return array('rgb' => '00FF00');
+ case 0x0C: return array('rgb' => '0000FF');
+ case 0x0D: return array('rgb' => 'FFFF00');
+ case 0x0E: return array('rgb' => 'FF00FF');
+ case 0x0F: return array('rgb' => '00FFFF');
+ case 0x10: return array('rgb' => '800000');
+ case 0x11: return array('rgb' => '008000');
+ case 0x12: return array('rgb' => '000080');
+ case 0x13: return array('rgb' => '808000');
+ case 0x14: return array('rgb' => '800080');
+ case 0x15: return array('rgb' => '008080');
+ case 0x16: return array('rgb' => 'C0C0C0');
+ case 0x17: return array('rgb' => '808080');
+ case 0x18: return array('rgb' => '8080FF');
+ case 0x19: return array('rgb' => '802060');
+ case 0x1A: return array('rgb' => 'FFFFC0');
+ case 0x1B: return array('rgb' => 'A0E0F0');
+ case 0x1C: return array('rgb' => '600080');
+ case 0x1D: return array('rgb' => 'FF8080');
+ case 0x1E: return array('rgb' => '0080C0');
+ case 0x1F: return array('rgb' => 'C0C0FF');
+ case 0x20: return array('rgb' => '000080');
+ case 0x21: return array('rgb' => 'FF00FF');
+ case 0x22: return array('rgb' => 'FFFF00');
+ case 0x23: return array('rgb' => '00FFFF');
+ case 0x24: return array('rgb' => '800080');
+ case 0x25: return array('rgb' => '800000');
+ case 0x26: return array('rgb' => '008080');
+ case 0x27: return array('rgb' => '0000FF');
+ case 0x28: return array('rgb' => '00CFFF');
+ case 0x29: return array('rgb' => '69FFFF');
+ case 0x2A: return array('rgb' => 'E0FFE0');
+ case 0x2B: return array('rgb' => 'FFFF80');
+ case 0x2C: return array('rgb' => 'A6CAF0');
+ case 0x2D: return array('rgb' => 'DD9CB3');
+ case 0x2E: return array('rgb' => 'B38FEE');
+ case 0x2F: return array('rgb' => 'E3E3E3');
+ case 0x30: return array('rgb' => '2A6FF9');
+ case 0x31: return array('rgb' => '3FB8CD');
+ case 0x32: return array('rgb' => '488436');
+ case 0x33: return array('rgb' => '958C41');
+ case 0x34: return array('rgb' => '8E5E42');
+ case 0x35: return array('rgb' => 'A0627A');
+ case 0x36: return array('rgb' => '624FAC');
+ case 0x37: return array('rgb' => '969696');
+ case 0x38: return array('rgb' => '1D2FBE');
+ case 0x39: return array('rgb' => '286676');
+ case 0x3A: return array('rgb' => '004500');
+ case 0x3B: return array('rgb' => '453E01');
+ case 0x3C: return array('rgb' => '6A2813');
+ case 0x3D: return array('rgb' => '85396A');
+ case 0x3E: return array('rgb' => '4A3285');
+ case 0x3F: return array('rgb' => '424242');
+ default: return array('rgb' => '000000');
+ }
+ }
+
+ /**
+ * Map color array from BIFF8 built-in color index
+ *
+ * @param int $subData
+ * @return array
+ */
+ private function _mapColor($subData)
+ {
+ switch ($subData) {
+ case 0x08: return array('rgb' => '000000');
+ case 0x09: return array('rgb' => 'FFFFFF');
+ case 0x0A: return array('rgb' => 'FF0000');
+ case 0x0B: return array('rgb' => '00FF00');
+ case 0x0C: return array('rgb' => '0000FF');
+ case 0x0D: return array('rgb' => 'FFFF00');
+ case 0x0E: return array('rgb' => 'FF00FF');
+ case 0x0F: return array('rgb' => '00FFFF');
+ case 0x10: return array('rgb' => '800000');
+ case 0x11: return array('rgb' => '008000');
+ case 0x12: return array('rgb' => '000080');
+ case 0x13: return array('rgb' => '808000');
+ case 0x14: return array('rgb' => '800080');
+ case 0x15: return array('rgb' => '008080');
+ case 0x16: return array('rgb' => 'C0C0C0');
+ case 0x17: return array('rgb' => '808080');
+ case 0x18: return array('rgb' => '9999FF');
+ case 0x19: return array('rgb' => '993366');
+ case 0x1A: return array('rgb' => 'FFFFCC');
+ case 0x1B: return array('rgb' => 'CCFFFF');
+ case 0x1C: return array('rgb' => '660066');
+ case 0x1D: return array('rgb' => 'FF8080');
+ case 0x1E: return array('rgb' => '0066CC');
+ case 0x1F: return array('rgb' => 'CCCCFF');
+ case 0x20: return array('rgb' => '000080');
+ case 0x21: return array('rgb' => 'FF00FF');
+ case 0x22: return array('rgb' => 'FFFF00');
+ case 0x23: return array('rgb' => '00FFFF');
+ case 0x24: return array('rgb' => '800080');
+ case 0x25: return array('rgb' => '800000');
+ case 0x26: return array('rgb' => '008080');
+ case 0x27: return array('rgb' => '0000FF');
+ case 0x28: return array('rgb' => '00CCFF');
+ case 0x29: return array('rgb' => 'CCFFFF');
+ case 0x2A: return array('rgb' => 'CCFFCC');
+ case 0x2B: return array('rgb' => 'FFFF99');
+ case 0x2C: return array('rgb' => '99CCFF');
+ case 0x2D: return array('rgb' => 'FF99CC');
+ case 0x2E: return array('rgb' => 'CC99FF');
+ case 0x2F: return array('rgb' => 'FFCC99');
+ case 0x30: return array('rgb' => '3366FF');
+ case 0x31: return array('rgb' => '33CCCC');
+ case 0x32: return array('rgb' => '99CC00');
+ case 0x33: return array('rgb' => 'FFCC00');
+ case 0x34: return array('rgb' => 'FF9900');
+ case 0x35: return array('rgb' => 'FF6600');
+ case 0x36: return array('rgb' => '666699');
+ case 0x37: return array('rgb' => '969696');
+ case 0x38: return array('rgb' => '003366');
+ case 0x39: return array('rgb' => '339966');
+ case 0x3A: return array('rgb' => '003300');
+ case 0x3B: return array('rgb' => '333300');
+ case 0x3C: return array('rgb' => '993300');
+ case 0x3D: return array('rgb' => '993366');
+ case 0x3E: return array('rgb' => '333399');
+ case 0x3F: return array('rgb' => '333333');
+ default: return array('rgb' => '000000');
+ }
+ }
+
+}
diff --git a/Classes/PHPExcel/Reader/Excel5/Escher.php b/Classes/PHPExcel/Reader/Excel5/Escher.php
new file mode 100644
index 0000000..c25ae5b
--- /dev/null
+++ b/Classes/PHPExcel/Reader/Excel5/Escher.php
@@ -0,0 +1,677 @@
+_object = $object;
+ }
+
+ /**
+ * Load Escher stream data. May be a partial Escher stream.
+ *
+ * @param string $data
+ */
+ public function load($data)
+ {
+ $this->_data = $data;
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->_dataSize = strlen($this->_data);
+
+ $this->_pos = 0;
+
+ // Parse Escher stream
+ while ($this->_pos < $this->_dataSize) {
+
+
+ // offset: 2; size: 2: Record Type
+ $fbt = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ switch ($fbt) {
+ case self::DGGCONTAINER: $this->_readDggContainer(); break;
+ case self::DGG: $this->_readDgg(); break;
+ case self::BSTORECONTAINER: $this->_readBstoreContainer(); break;
+ case self::BSE: $this->_readBSE(); break;
+ case self::BLIPJPEG: $this->_readBlipJPEG(); break;
+ case self::BLIPPNG: $this->_readBlipPNG(); break;
+ case self::OPT: $this->_readOPT(); break;
+ case self::TERTIARYOPT: $this->_readTertiaryOPT(); break;
+ case self::SPLITMENUCOLORS: $this->_readSplitMenuColors(); break;
+ case self::DGCONTAINER: $this->_readDgContainer(); break;
+ case self::DG: $this->_readDg(); break;
+ case self::SPGRCONTAINER: $this->_readSpgrContainer(); break;
+ case self::SPCONTAINER: $this->_readSpContainer(); break;
+ case self::SPGR: $this->_readSpgr(); break;
+ case self::SP: $this->_readSp(); break;
+ case self::CLIENTTEXTBOX: $this->_readClientTextbox(); break;
+ case self::CLIENTANCHOR: $this->_readClientAnchor(); break;
+ case self::CLIENTDATA: $this->_readClientData(); break;
+ default: $this->_readDefault(); break;
+ }
+ }
+
+ return $this->_object;
+ }
+
+ /**
+ * Read a generic record
+ */
+ private function _readDefault()
+ {
+ // offset 0; size: 2; recVer and recInstance
+ $verInstance = $this->_GetInt2d($this->_data, $this->_pos);
+
+ // offset: 2; size: 2: Record Type
+ $fbt = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // bit: 0-3; mask: 0x000F; recVer
+ $recVer = (0x000F & $verInstance) >> 0;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read DggContainer record (Drawing Group Container)
+ */
+ private function _readDggContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $dggContainer = new PHPExcel_Shared_Escher_DggContainer();
+ $this->_object->setDggContainer($dggContainer);
+ $reader = new PHPExcel_Reader_Excel5_Escher($dggContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read Dgg record (Drawing Group)
+ */
+ private function _readDgg()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read BstoreContainer record (Blip Store Container)
+ */
+ private function _readBstoreContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $bstoreContainer = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
+ $this->_object->setBstoreContainer($bstoreContainer);
+ $reader = new PHPExcel_Reader_Excel5_Escher($bstoreContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read BSE record
+ */
+ private function _readBSE()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // add BSE to BstoreContainer
+ $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
+ $this->_object->addBSE($BSE);
+
+ $BSE->setBLIPType($recInstance);
+
+ // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
+ $btWin32 = ord($recordData[0]);
+
+ // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
+ $btMacOS = ord($recordData[1]);
+
+ // offset: 2; size: 16; MD4 digest
+ $rgbUid = substr($recordData, 2, 16);
+
+ // offset: 18; size: 2; tag
+ $tag = $this->_GetInt2d($recordData, 18);
+
+ // offset: 20; size: 4; size of BLIP in bytes
+ $size = $this->_GetInt4d($recordData, 20);
+
+ // offset: 24; size: 4; number of references to this BLIP
+ $cRef = $this->_GetInt4d($recordData, 24);
+
+ // offset: 28; size: 4; MSOFO file offset
+ $foDelay = $this->_GetInt4d($recordData, 28);
+
+ // offset: 32; size: 1; unused1
+ $unused1 = ord($recordData{32});
+
+ // offset: 33; size: 1; size of nameData in bytes (including null terminator)
+ $cbName = ord($recordData{33});
+
+ // offset: 34; size: 1; unused2
+ $unused2 = ord($recordData{34});
+
+ // offset: 35; size: 1; unused3
+ $unused3 = ord($recordData{35});
+
+ // offset: 36; size: $cbName; nameData
+ $nameData = substr($recordData, 36, $cbName);
+
+ // offset: 36 + $cbName, size: var; the BLIP data
+ $blipData = substr($recordData, 36 + $cbName);
+
+ // record is a container, read contents
+ $reader = new PHPExcel_Reader_Excel5_Escher($BSE);
+ $reader->load($blipData);
+ }
+
+ /**
+ * Read BlipJPEG record. Holds raw JPEG image data
+ */
+ private function _readBlipJPEG()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ $pos = 0;
+
+ // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+ $rgbUid1 = substr($recordData, 0, 16);
+ $pos += 16;
+
+ // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+ if (in_array($recInstance, array(0x046B, 0x06E3))) {
+ $rgbUid2 = substr($recordData, 16, 16);
+ $pos += 16;
+ }
+
+ // offset: var; size: 1; tag
+ $tag = ord($recordData{$pos});
+ $pos += 1;
+
+ // offset: var; size: var; the raw image data
+ $data = substr($recordData, $pos);
+
+ $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
+ $blip->setData($data);
+
+ $this->_object->setBlip($blip);
+ }
+
+ /**
+ * Read BlipPNG record. Holds raw PNG image data
+ */
+ private function _readBlipPNG()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ $pos = 0;
+
+ // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+ $rgbUid1 = substr($recordData, 0, 16);
+ $pos += 16;
+
+ // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+ if ($recInstance == 0x06E1) {
+ $rgbUid2 = substr($recordData, 16, 16);
+ $pos += 16;
+ }
+
+ // offset: var; size: 1; tag
+ $tag = ord($recordData{$pos});
+ $pos += 1;
+
+ // offset: var; size: var; the raw image data
+ $data = substr($recordData, $pos);
+
+ $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
+ $blip->setData($data);
+
+ $this->_object->setBlip($blip);
+ }
+
+ /**
+ * Read OPT record. This record may occur within DggContainer record or SpContainer
+ */
+ private function _readOPT()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ $this->_readOfficeArtRGFOPTE($recordData, $recInstance);
+ }
+
+ /**
+ * Read TertiaryOPT record
+ */
+ private function _readTertiaryOPT()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read SplitMenuColors record
+ */
+ private function _readSplitMenuColors()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read DgContainer record (Drawing Container)
+ */
+ private function _readDgContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $dgContainer = new PHPExcel_Shared_Escher_DgContainer();
+ $this->_object->setDgContainer($dgContainer);
+ $reader = new PHPExcel_Reader_Excel5_Escher($dgContainer);
+ $escher = $reader->load($recordData);
+ }
+
+ /**
+ * Read Dg record (Drawing)
+ */
+ private function _readDg()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read SpgrContainer record (Shape Group Container)
+ */
+ private function _readSpgrContainer()
+ {
+ // context is either context DgContainer or SpgrContainer
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $spgrContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer();
+
+ if ($this->_object instanceof PHPExcel_Shared_Escher_DgContainer) {
+ // DgContainer
+ $this->_object->setSpgrContainer($spgrContainer);
+ } else {
+ // SpgrContainer
+ $this->_object->addChild($spgrContainer);
+ }
+
+ $reader = new PHPExcel_Reader_Excel5_Escher($spgrContainer);
+ $escher = $reader->load($recordData);
+ }
+
+ /**
+ * Read SpContainer record (Shape Container)
+ */
+ private function _readSpContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // add spContainer to spgrContainer
+ $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
+ $this->_object->addChild($spContainer);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $reader = new PHPExcel_Reader_Excel5_Escher($spContainer);
+ $escher = $reader->load($recordData);
+ }
+
+ /**
+ * Read Spgr record (Shape Group)
+ */
+ private function _readSpgr()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read Sp record (Shape)
+ */
+ private function _readSp()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read ClientTextbox record
+ */
+ private function _readClientTextbox()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet
+ */
+ private function _readClientAnchor()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // offset: 2; size: 2; upper-left corner column index (0-based)
+ $c1 = $this->_GetInt2d($recordData, 2);
+
+ // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
+ $startOffsetX = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 2; upper-left corner row index (0-based)
+ $r1 = $this->_GetInt2d($recordData, 6);
+
+ // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
+ $startOffsetY = $this->_GetInt2d($recordData, 8);
+
+ // offset: 10; size: 2; bottom-right corner column index (0-based)
+ $c2 = $this->_GetInt2d($recordData, 10);
+
+ // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
+ $endOffsetX = $this->_GetInt2d($recordData, 12);
+
+ // offset: 14; size: 2; bottom-right corner row index (0-based)
+ $r2 = $this->_GetInt2d($recordData, 14);
+
+ // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
+ $endOffsetY = $this->_GetInt2d($recordData, 16);
+
+ // set the start coordinates
+ $this->_object->setStartCoordinates(PHPExcel_Cell::stringFromColumnIndex($c1) . ($r1 + 1));
+
+ // set the start offsetX
+ $this->_object->setStartOffsetX($startOffsetX);
+
+ // set the start offsetY
+ $this->_object->setStartOffsetY($startOffsetY);
+
+ // set the end coordinates
+ $this->_object->setEndCoordinates(PHPExcel_Cell::stringFromColumnIndex($c2) . ($r2 + 1));
+
+ // set the end offsetX
+ $this->_object->setEndOffsetX($endOffsetX);
+
+ // set the end offsetY
+ $this->_object->setEndOffsetY($endOffsetY);
+ }
+
+ /**
+ * Read ClientData record
+ */
+ private function _readClientData()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read OfficeArtRGFOPTE table of property-value pairs
+ *
+ * @param string $data Binary data
+ * @param int $n Number of properties
+ */
+ private function _readOfficeArtRGFOPTE($data, $n) {
+
+ $splicedComplexData = substr($data, 6 * $n);
+
+ // loop through property-value pairs
+ for ($i = 0; $i < $n; ++$i) {
+ // read 6 bytes at a time
+ $fopte = substr($data, 6 * $i, 6);
+
+ // offset: 0; size: 2; opid
+ $opid = $this->_GetInt2d($fopte, 0);
+
+ // bit: 0-13; mask: 0x3FFF; opid.opid
+ $opidOpid = (0x3FFF & $opid) >> 0;
+
+ // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
+ $opidFBid = (0x4000 & $opid) >> 14;
+
+ // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
+ $opidFComplex = (0x8000 & $opid) >> 15;
+
+ // offset: 2; size: 4; the value for this property
+ $op = $this->_GetInt4d($fopte, 2);
+
+ if ($opidFComplex) {
+ $complexData = substr($splicedComplexData, 0, $op);
+ $splicedComplexData = substr($splicedComplexData, $op);
+
+ // we store string value with complex data
+ $value = $complexData;
+ } else {
+ // we store integer value
+ $value = $op;
+ }
+
+ $this->_object->setOPT($opidOpid, $value);
+ }
+ }
+
+ /**
+ * Read 16-bit unsigned integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt2d($data, $pos)
+ {
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
+ }
+
+ /**
+ * Read 32-bit signed integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt4d($data, $pos)
+ {
+ //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) |
+ // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
+
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+
+}
diff --git a/Classes/PHPExcel/Reader/IReadFilter.php b/Classes/PHPExcel/Reader/IReadFilter.php
new file mode 100644
index 0000000..94568d5
--- /dev/null
+++ b/Classes/PHPExcel/Reader/IReadFilter.php
@@ -0,0 +1,47 @@
+_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadDataOnly($pValue = false) {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_OOCalc
+ */
+ public function __construct() {
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if zip class exists
+ if (!class_exists('ZipArchive')) {
+ return false;
+ }
+
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Load file
+ $zip = new ZipArchive;
+ if ($zip->open($pFilename) === true) {
+ // check if it is an OOXML archive
+ $mimeType = $zip->getFromName("mimetype");
+
+ $zip->close();
+
+ return ($mimeType === 'application/vnd.oasis.opendocument.spreadsheet');
+ }
+
+ return false;
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ }
+
+ private static function identifyFixedStyleValue($styleList,&$styleAttributeValue) {
+ $styleAttributeValue = strtolower($styleAttributeValue);
+ foreach($styleList as $style) {
+ if ($styleAttributeValue == strtolower($style)) {
+ $styleAttributeValue = $style;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ $timezoneObj = new DateTimeZone('Europe/London');
+ $GMT = new DateTimeZone('UTC');
+
+ $zip = new ZipArchive;
+ if ($zip->open($pFilename) === true) {
+// echo 'Meta Information
';
+ $xml = simplexml_load_string($zip->getFromName("meta.xml"));
+ $namespacesMeta = $xml->getNamespaces(true);
+// echo '';
+// print_r($namespacesMeta);
+// echo '
';
+
+ $docProps = $objPHPExcel->getProperties();
+ $officeProperty = $xml->children($namespacesMeta['office']);
+ foreach($officeProperty as $officePropertyData) {
+ $officePropertyDC = array();
+ if (isset($namespacesMeta['dc'])) {
+ $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']);
+ }
+ foreach($officePropertyDC as $propertyName => $propertyValue) {
+// echo $propertyName.' = '.$propertyValue.'
';
+
+ switch ($propertyName) {
+ case 'title' :
+ $docProps->setTitle($propertyValue);
+ break;
+ case 'subject' :
+ $docProps->setSubject($propertyValue);
+ break;
+ case 'creator' :
+ $docProps->setCreator($propertyValue);
+ break;
+ case 'date' :
+ $creationDate = strtotime($propertyValue);
+ $docProps->setCreated($creationDate);
+ break;
+ case 'description' :
+ $docProps->setDescription($propertyValue);
+ break;
+ }
+ }
+ $officePropertyMeta = array();
+ if (isset($namespacesMeta['dc'])) {
+ $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
+ }
+ foreach($officePropertyMeta as $propertyName => $propertyValue) {
+ $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
+
+// echo $propertyName.' = '.$propertyValue.'
';
+// foreach ($propertyValueAttributes as $key => $value) {
+// echo $key.' = '.$value.'
';
+// }
+// echo '
';
+//
+ switch ($propertyName) {
+ case 'keyword' :
+ $docProps->setKeywords($propertyValue);
+ break;
+ }
+ }
+ }
+
+
+// echo 'Workbook Content
';
+ $xml = simplexml_load_string($zip->getFromName("content.xml"));
+ $namespacesContent = $xml->getNamespaces(true);
+// echo '';
+// print_r($namespacesContent);
+// echo '
';
+
+ $workbook = $xml->children($namespacesContent['office']);
+ foreach($workbook->body->spreadsheet as $workbookData) {
+ $workbookData = $workbookData->children($namespacesContent['table']);
+ $worksheetID = 0;
+ foreach($workbookData->table as $worksheetDataSet) {
+ $worksheetData = $worksheetDataSet->children($namespacesContent['table']);
+// print_r($worksheetData);
+// echo '
';
+ $worksheetDataAttributes = $worksheetDataSet->attributes($namespacesContent['table']);
+// print_r($worksheetDataAttributes);
+// echo '
';
+ if ((isset($this->_loadSheetsOnly)) && (isset($worksheetDataAttributes['name'])) &&
+ (!in_array($worksheetDataAttributes['name'], $this->_loadSheetsOnly))) {
+ continue;
+ }
+
+// echo 'Worksheet '.$worksheetDataAttributes['name'].'
';
+ // Create new Worksheet
+ $objPHPExcel->createSheet();
+ $objPHPExcel->setActiveSheetIndex($worksheetID);
+ if (isset($worksheetDataAttributes['name'])) {
+ $worksheetName = (string) $worksheetDataAttributes['name'];
+ $objPHPExcel->getActiveSheet()->setTitle($worksheetName);
+ }
+
+ $rowID = 1;
+ foreach($worksheetData as $key => $rowData) {
+// echo ''.$key.'
';
+ switch ($key) {
+ case 'table-header-rows':
+ foreach ($rowData as $key=>$cellData) {
+ $rowData = $cellData;
+ break;
+ }
+ case 'table-row' :
+ $columnID = 'A';
+ foreach($rowData as $key => $cellData) {
+// echo ''.$columnID.$rowID.'
';
+ $cellDataText = $cellData->children($namespacesContent['text']);
+ $cellDataOfficeAttributes = $cellData->attributes($namespacesContent['office']);
+ $cellDataTableAttributes = $cellData->attributes($namespacesContent['table']);
+
+// echo 'Office Attributes: ';
+// print_r($cellDataOfficeAttributes);
+// echo '
Table Attributes: ';
+// print_r($cellDataTableAttributes);
+// echo '
Cell Data Text';
+// print_r($cellDataText);
+// echo '
';
+//
+ $type = $formatting = $hyperlink = null;
+ $hasCalculatedValue = false;
+ $cellDataFormula = '';
+ if (isset($cellDataTableAttributes['formula'])) {
+ $cellDataFormula = $cellDataTableAttributes['formula'];
+ $hasCalculatedValue = true;
+ }
+
+ if (isset($cellDataText->p)) {
+// echo 'Value Type is '.$cellDataOfficeAttributes['value-type'].'
';
+ switch ($cellDataOfficeAttributes['value-type']) {
+ case 'string' :
+ $type = PHPExcel_Cell_DataType::TYPE_STRING;
+ $dataValue = $cellDataText->p;
+ if (isset($dataValue->a)) {
+ $dataValue = $dataValue->a;
+ $cellXLinkAttributes = $dataValue->attributes($namespacesContent['xlink']);
+ $hyperlink = $cellXLinkAttributes['href'];
+ }
+ break;
+ case 'boolean' :
+ $type = PHPExcel_Cell_DataType::TYPE_BOOL;
+ $dataValue = ($cellDataText->p == 'TRUE') ? True : False;
+ break;
+ case 'float' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $dataValue = (float) $cellDataOfficeAttributes['value'];
+ if (floor($dataValue) == $dataValue) {
+ $dataValue = (integer) $dataValue;
+ }
+ break;
+ case 'date' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $dateObj = new DateTime($cellDataOfficeAttributes['date-value'], $GMT);
+ $dateObj->setTimeZone($timezoneObj);
+ list($year,$month,$day,$hour,$minute,$second) = explode(' ',$dateObj->format('Y m d H i s'));
+ $dataValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year,$month,$day,$hour,$minute,$second);
+ if ($dataValue != floor($dataValue)) {
+ $formatting = PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX15.' '.PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4;
+ } else {
+ $formatting = PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX15;
+ }
+ break;
+ case 'time' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $dataValue = PHPExcel_Shared_Date::PHPToExcel(strtotime('01-01-1970 '.implode(':',sscanf($cellDataOfficeAttributes['time-value'],'PT%dH%dM%dS'))));
+ $formatting = PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4;
+ break;
+ }
+// echo 'Data value is '.$dataValue.'
';
+// if (!is_null($hyperlink)) {
+// echo 'Hyperlink is '.$hyperlink.'
';
+// }
+ }
+
+ if ($hasCalculatedValue) {
+ $type = PHPExcel_Cell_DataType::TYPE_FORMULA;
+// echo 'Formula: '.$cellDataFormula.'
';
+ $cellDataFormula = substr($cellDataFormula,strpos($cellDataFormula,':=')+1);
+ $temp = explode('"',$cellDataFormula);
+ foreach($temp as $key => &$value) {
+ // Only replace in alternate array entries (i.e. non-quoted blocks)
+ if (($key % 2) == 0) {
+ $value = preg_replace('/\[\.(.*):\.(.*)\]/Ui','$1:$2',$value);
+ $value = preg_replace('/\[\.(.*)\]/Ui','$1',$value);
+ $value = PHPExcel_Calculation::_translateSeparator(';',',',$value,$inBraces);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $cellDataFormula = implode('"',$temp);
+// echo 'Adjusted Formula: '.$cellDataFormula.'
';
+ }
+
+ if (!is_null($type)) {
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $dataValue),$type);
+ if ($hasCalculatedValue) {
+// echo 'Forumla result is '.$dataValue.'
';
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setCalculatedValue($dataValue);
+ }
+ if (($cellDataOfficeAttributes['value-type'] == 'date') ||
+ ($cellDataOfficeAttributes['value-type'] == 'time')) {
+ $objPHPExcel->getActiveSheet()->getStyle($columnID.$rowID)->getNumberFormat()->setFormatCode($formatting);
+ }
+ if (!is_null($hyperlink)) {
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->getHyperlink()->setUrl($hyperlink);
+ }
+ }
+
+ // Merged cells
+ if ((isset($cellDataTableAttributes['number-columns-spanned'])) || (isset($cellDataTableAttributes['number-rows-spanned']))) {
+ $columnTo = $columnID;
+ if (isset($cellDataTableAttributes['number-columns-spanned'])) {
+ $columnTo = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cellDataTableAttributes['number-columns-spanned'] -2);
+ }
+ $rowTo = $rowID;
+ if (isset($cellDataTableAttributes['number-rows-spanned'])) {
+ $rowTo = $rowTo + $cellDataTableAttributes['number-rows-spanned'] - 1;
+ }
+ $cellRange = $columnID.$rowID.':'.$columnTo.$rowTo;
+ $objPHPExcel->getActiveSheet()->mergeCells($cellRange);
+ }
+
+ if (isset($cellDataTableAttributes['number-columns-repeated'])) {
+// echo 'Repeated '.$cellDataTableAttributes['number-columns-repeated'].' times
';
+ $columnID = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cellDataTableAttributes['number-columns-repeated'] - 2);
+ }
+ ++$columnID;
+ }
+ ++$rowID;
+ break;
+ }
+ }
+ ++$worksheetID;
+ }
+ }
+
+ }
+
+ // Return
+ return $objPHPExcel;
+ }
+
+ /**
+ * Get sheet index
+ *
+ * @return int
+ */
+ public function getSheetIndex() {
+ return $this->_sheetIndex;
+ }
+
+ /**
+ * Set sheet index
+ *
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Reader_OOCalc
+ */
+ public function setSheetIndex($pValue = 0) {
+ $this->_sheetIndex = $pValue;
+ return $this;
+ }
+}
diff --git a/Classes/PHPExcel/Reader/SYLK.php b/Classes/PHPExcel/Reader/SYLK.php
new file mode 100644
index 0000000..0678aec
--- /dev/null
+++ b/Classes/PHPExcel/Reader/SYLK.php
@@ -0,0 +1,509 @@
+_inputEncoding = 'ANSI';
+ $this->_delimiter = ';';
+ $this->_enclosure = '"';
+ $this->_lineEnding = PHP_EOL;
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Read sample data (first 2 KB will do)
+ $fh = fopen($pFilename, 'r');
+ $data = fread($fh, 2048);
+ fclose($fh);
+
+ // Count delimiters in file
+ $delimiterCount = substr_count($data, ';');
+ if ($delimiterCount < 1) {
+ return false;
+ }
+
+ // Analyze first line looking for ID; signature
+ $lines = explode("\n", $data);
+ if (substr($lines[0],0,4) != 'ID;P') {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Set input encoding
+ *
+ * @param string $pValue Input encoding
+ */
+ public function setInputEncoding($pValue = 'ANSI')
+ {
+ $this->_inputEncoding = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get input encoding
+ *
+ * @return string
+ */
+ public function getInputEncoding()
+ {
+ return $this->_inputEncoding;
+ }
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Create new PHPExcel
+ while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) {
+ $objPHPExcel->createSheet();
+ }
+ $objPHPExcel->setActiveSheetIndex( $this->_sheetIndex );
+
+ $fromFormats = array('\-', '\ ');
+ $toFormats = array('-', ' ');
+
+ // Open file
+ $fileHandle = fopen($pFilename, 'r');
+ if ($fileHandle === false) {
+ throw new Exception("Could not open file $pFilename for reading.");
+ }
+
+ // Loop through file
+ $rowData = array();
+ $column = $row = '';
+
+ // loop through one row (line) at a time in the file
+ while (($rowData = fgets($fileHandle)) !== FALSE) {
+
+ // convert SYLK encoded $rowData to UTF-8
+ $rowData = PHPExcel_Shared_String::SYLKtoUTF8($rowData);
+
+ // explode each row at semicolons while taking into account that literal semicolon (;)
+ // is escaped like this (;;)
+ $rowData = explode("\t",str_replace('¤',';',str_replace(';',"\t",str_replace(';;','¤',rtrim($rowData)))));
+
+ $dataType = array_shift($rowData);
+ // Read shared styles
+ if ($dataType == 'P') {
+ $formatArray = array();
+ foreach($rowData as $rowDatum) {
+ switch($rowDatum{0}) {
+ case 'P' : $formatArray['numberformat']['code'] = str_replace($fromFormats,$toFormats,substr($rowDatum,1));
+ break;
+ case 'E' :
+ case 'F' : $formatArray['font']['name'] = substr($rowDatum,1);
+ break;
+ case 'L' : $formatArray['font']['size'] = substr($rowDatum,1);
+ break;
+ case 'S' : $styleSettings = substr($rowDatum,1);
+ for ($i=0;$i_formats['P'.$this->_format++] = $formatArray;
+ // Read cell value data
+ } elseif ($dataType == 'C') {
+ $hasCalculatedValue = false;
+ $cellData = $cellDataFormula = '';
+ foreach($rowData as $rowDatum) {
+ switch($rowDatum{0}) {
+ case 'C' :
+ case 'X' : $column = substr($rowDatum,1);
+ break;
+ case 'R' :
+ case 'Y' : $row = substr($rowDatum,1);
+ break;
+ case 'K' : $cellData = substr($rowDatum,1);
+ break;
+ case 'E' : $cellDataFormula = '='.substr($rowDatum,1);
+ // Convert R1C1 style references to A1 style references (but only when not quoted)
+ $temp = explode('"',$cellDataFormula);
+ foreach($temp as $key => &$value) {
+ // Only count/replace in alternate array entries
+ if (($key % 2) == 0) {
+ preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/',$value, $cellReferences,PREG_SET_ORDER+PREG_OFFSET_CAPTURE);
+ // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
+ // through the formula from left to right. Reversing means that we work right to left.through
+ // the formula
+ $cellReferences = array_reverse($cellReferences);
+ // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
+ // then modify the formula to use that new reference
+ foreach($cellReferences as $cellReference) {
+ $rowReference = $cellReference[2][0];
+ // Empty R reference is the current row
+ if ($rowReference == '') $rowReference = $row;
+ // Bracketed R references are relative to the current row
+ if ($rowReference{0} == '[') $rowReference = $row + trim($rowReference,'[]');
+ $columnReference = $cellReference[4][0];
+ // Empty C reference is the current column
+ if ($columnReference == '') $columnReference = $column;
+ // Bracketed C references are relative to the current column
+ if ($columnReference{0} == '[') $columnReference = $column + trim($columnReference,'[]');
+ $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference;
+
+ $value = substr_replace($value,$A1CellReference,$cellReference[0][1],strlen($cellReference[0][0]));
+ }
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $cellDataFormula = implode('"',$temp);
+ $hasCalculatedValue = true;
+ break;
+ }
+ }
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1);
+ $cellData = PHPExcel_Calculation::_unwrapResult($cellData);
+
+ // Set cell value
+ $objPHPExcel->getActiveSheet()->getCell($columnLetter.$row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
+ if ($hasCalculatedValue) {
+ $cellData = PHPExcel_Calculation::_unwrapResult($cellData);
+ $objPHPExcel->getActiveSheet()->getCell($columnLetter.$row)->setCalculatedValue($cellData);
+ }
+ // Read cell formatting
+ } elseif ($dataType == 'F') {
+ $formatStyle = $columnWidth = $styleSettings = '';
+ $styleData = array();
+ foreach($rowData as $rowDatum) {
+ switch($rowDatum{0}) {
+ case 'C' :
+ case 'X' : $column = substr($rowDatum,1);
+ break;
+ case 'R' :
+ case 'Y' : $row = substr($rowDatum,1);
+ break;
+ case 'P' : $formatStyle = $rowDatum;
+ break;
+ case 'W' : list($startCol,$endCol,$columnWidth) = explode(' ',substr($rowDatum,1));
+ break;
+ case 'S' : $styleSettings = substr($rowDatum,1);
+ for ($i=0;$i '') && ($column > '') && ($row > '')) {
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1);
+ $objPHPExcel->getActiveSheet()->getStyle($columnLetter.$row)->applyFromArray($this->_formats[$formatStyle]);
+ }
+ if ((count($styleData) > 0) && ($column > '') && ($row > '')) {
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($column-1);
+ $objPHPExcel->getActiveSheet()->getStyle($columnLetter.$row)->applyFromArray($styleData);
+ }
+ if ($columnWidth > '') {
+ if ($startCol == $endCol) {
+ $startCol = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
+ $objPHPExcel->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
+ } else {
+ $startCol = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
+ $endCol = PHPExcel_Cell::stringFromColumnIndex($endCol-1);
+ $objPHPExcel->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
+ do {
+ $objPHPExcel->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth);
+ } while ($startCol != $endCol);
+ }
+ }
+ } else {
+ foreach($rowData as $rowDatum) {
+ switch($rowDatum{0}) {
+ case 'C' :
+ case 'X' : $column = substr($rowDatum,1);
+ break;
+ case 'R' :
+ case 'Y' : $row = substr($rowDatum,1);
+ break;
+ }
+ }
+ }
+ }
+
+ // Close file
+ fclose($fileHandle);
+
+ // Return
+ return $objPHPExcel;
+ }
+
+ /**
+ * Get delimiter
+ *
+ * @return string
+ */
+ public function getDelimiter() {
+ return $this->_delimiter;
+ }
+
+ /**
+ * Set delimiter
+ *
+ * @param string $pValue Delimiter, defaults to ,
+ * @return PHPExcel_Reader_SYLK
+ */
+ public function setDelimiter($pValue = ',') {
+ $this->_delimiter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get enclosure
+ *
+ * @return string
+ */
+ public function getEnclosure() {
+ return $this->_enclosure;
+ }
+
+ /**
+ * Set enclosure
+ *
+ * @param string $pValue Enclosure, defaults to "
+ * @return PHPExcel_Reader_SYLK
+ */
+ public function setEnclosure($pValue = '"') {
+ if ($pValue == '') {
+ $pValue = '"';
+ }
+ $this->_enclosure = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get line ending
+ *
+ * @return string
+ */
+ public function getLineEnding() {
+ return $this->_lineEnding;
+ }
+
+ /**
+ * Set line ending
+ *
+ * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
+ * @return PHPExcel_Reader_SYLK
+ */
+ public function setLineEnding($pValue = PHP_EOL) {
+ $this->_lineEnding = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get sheet index
+ *
+ * @return int
+ */
+ public function getSheetIndex() {
+ return $this->_sheetIndex;
+ }
+
+ /**
+ * Set sheet index
+ *
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Reader_SYLK
+ */
+ public function setSheetIndex($pValue = 0) {
+ $this->_sheetIndex = $pValue;
+ return $this;
+ }
+
+}
diff --git a/Classes/PHPExcel/Reader/Serialized.php b/Classes/PHPExcel/Reader/Serialized.php
new file mode 100644
index 0000000..1bc014a
--- /dev/null
+++ b/Classes/PHPExcel/Reader/Serialized.php
@@ -0,0 +1,130 @@
+fileSupportsUnserializePHPExcel($pFilename);
+ }
+
+ /**
+ * Loads PHPExcel Serialized file
+ *
+ * @param string $pFilename
+ * @return PHPExcel
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Unserialize... First make sure the file supports it!
+ if (!$this->fileSupportsUnserializePHPExcel($pFilename)) {
+ throw new Exception("Invalid file format for PHPExcel_Reader_Serialized: " . $pFilename . ".");
+ }
+
+ return $this->_loadSerialized($pFilename);
+ }
+
+ /**
+ * Load PHPExcel Serialized file
+ *
+ * @param string $pFilename
+ * @return PHPExcel
+ */
+ private function _loadSerialized($pFilename) {
+ $xmlData = simplexml_load_string(file_get_contents("zip://$pFilename#phpexcel.xml"));
+ $excel = unserialize(base64_decode((string)$xmlData->data));
+
+ // Update media links
+ for ($i = 0; $i < $excel->getSheetCount(); ++$i) {
+ for ($j = 0; $j < $excel->getSheet($i)->getDrawingCollection()->count(); ++$j) {
+ if ($excel->getSheet($i)->getDrawingCollection()->offsetGet($j) instanceof PHPExcl_Worksheet_BaseDrawing) {
+ $imgTemp =& $excel->getSheet($i)->getDrawingCollection()->offsetGet($j);
+ $imgTemp->setPath('zip://' . $pFilename . '#media/' . $imgTemp->getFilename(), false);
+ }
+ }
+ }
+
+ return $excel;
+ }
+
+ /**
+ * Does a file support UnserializePHPExcel ?
+ *
+ * @param string $pFilename
+ * @throws Exception
+ * @return boolean
+ */
+ public function fileSupportsUnserializePHPExcel($pFilename = '') {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // File exists, does it contain phpexcel.xml?
+ return PHPExcel_Shared_File::file_exists("zip://$pFilename#phpexcel.xml");
+ }
+}
diff --git a/Classes/PHPExcel/ReferenceHelper.php b/Classes/PHPExcel/ReferenceHelper.php
new file mode 100644
index 0000000..e52c743
--- /dev/null
+++ b/Classes/PHPExcel/ReferenceHelper.php
@@ -0,0 +1,614 @@
+getCellCollection();
+
+ // Get coordinates of $pBefore
+ $beforeColumn = 'A';
+ $beforeRow = 1;
+ list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
+
+
+ // Clear cells if we are removing columns or rows
+ $highestColumn = $pSheet->getHighestColumn();
+ $highestRow = $pSheet->getHighestRow();
+
+ // 1. Clear column strips if we are removing columns
+ if ($pNumCols < 0 && PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 + $pNumCols > 0) {
+ for ($i = 1; $i <= $highestRow - 1; ++$i) {
+ for ($j = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1 + $pNumCols; $j <= PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2; ++$j) {
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($j) . $i;
+ $pSheet->removeConditionalStyles($coordinate);
+ if ($pSheet->cellExists($coordinate)) {
+ $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
+ $pSheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
+ }
+ }
+
+ // 2. Clear row strips if we are removing rows
+ if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
+ for ($i = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
+ for ($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++$j) {
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . $j;
+ $pSheet->removeConditionalStyles($coordinate);
+ if ($pSheet->cellExists($coordinate)) {
+ $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
+ $pSheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
+ }
+ }
+
+
+ // Loop through cells, bottom-up, and change cell coordinates
+ while (($cellID = ($pNumCols < 0 || $pNumRows < 0) ? array_shift($aCellCollection) : array_pop($aCellCollection))) {
+ $cell = $pSheet->getCell($cellID);
+
+ // New coordinates
+ $newCoordinates = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1 + $pNumCols ) . ($cell->getRow() + $pNumRows);
+
+ // Should the cell be updated? Move value and cellXf index from one cell to another.
+ if ((PHPExcel_Cell::columnIndexFromString( $cell->getColumn() ) >= PHPExcel_Cell::columnIndexFromString($beforeColumn)) &&
+ ($cell->getRow() >= $beforeRow)) {
+
+ // Update cell styles
+ $pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex());
+ $cell->setXfIndex(0);
+
+ // Insert this cell at its new location
+ if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ // Formula should be adjusted
+ $pSheet->getCell($newCoordinates)
+ ->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
+ } else {
+ // Formula should not be adjusted
+ $pSheet->getCell($newCoordinates)->setValue($cell->getValue());
+ }
+
+ // Clear the original cell
+ $pSheet->getCell($cell->getCoordinate())->setValue('');
+ }
+ }
+
+
+ // Duplicate styles for the newly inserted cells
+ $highestColumn = $pSheet->getHighestColumn();
+ $highestRow = $pSheet->getHighestRow();
+
+ if ($pNumCols > 0 && PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 > 0) {
+ for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
+
+ // Style
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 ) . $i;
+ if ($pSheet->cellExists($coordinate)) {
+ $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
+ $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
+ $pSheet->getConditionalStyles($coordinate) : false;
+ for ($j = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1; $j <= PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 + $pNumCols; ++$j) {
+ $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
+ if ($conditionalStyles) {
+ $cloned = array();
+ foreach ($conditionalStyles as $conditionalStyle) {
+ $cloned[] = clone $conditionalStyle;
+ }
+ $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($j) . $i, $cloned);
+ }
+ }
+ }
+
+ }
+ }
+
+ if ($pNumRows > 0 && $beforeRow - 1 > 0) {
+ for ($i = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
+
+ // Style
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . ($beforeRow - 1);
+ if ($pSheet->cellExists($coordinate)) {
+ $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
+ $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
+ $pSheet->getConditionalStyles($coordinate) : false;
+ for ($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++$j) {
+ $pSheet->getCell(PHPExcel_Cell::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+ if ($conditionalStyles) {
+ $cloned = array();
+ foreach ($conditionalStyles as $conditionalStyle) {
+ $cloned[] = clone $conditionalStyle;
+ }
+ $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($i) . $j, $cloned);
+ }
+ }
+ }
+ }
+ }
+
+
+ // Update worksheet: column dimensions
+ $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
+ if (count($aColumnDimensions) > 0) {
+ foreach ($aColumnDimensions as $objColumnDimension) {
+ $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
+ list($newReference) = PHPExcel_Cell::coordinateFromString($newReference);
+ if ($objColumnDimension->getColumnIndex() != $newReference) {
+ $objColumnDimension->setColumnIndex($newReference);
+ }
+ }
+ $pSheet->refreshColumnDimensions();
+ }
+
+
+ // Update worksheet: row dimensions
+ $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
+ if (count($aRowDimensions) > 0) {
+ foreach ($aRowDimensions as $objRowDimension) {
+ $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
+ list(, $newReference) = PHPExcel_Cell::coordinateFromString($newReference);
+ if ($objRowDimension->getRowIndex() != $newReference) {
+ $objRowDimension->setRowIndex($newReference);
+ }
+ }
+ $pSheet->refreshRowDimensions();
+
+ $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
+ for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) {
+ $newDimension = $pSheet->getRowDimension($i);
+ $newDimension->setRowHeight($copyDimension->getRowHeight());
+ $newDimension->setVisible($copyDimension->getVisible());
+ $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
+ $newDimension->setCollapsed($copyDimension->getCollapsed());
+ }
+ }
+
+
+ // Update worksheet: breaks
+ $aBreaks = array_reverse($pSheet->getBreaks(), true);
+ foreach ($aBreaks as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->setBreak( $newReference, $value );
+ $pSheet->setBreak( $key, PHPExcel_Worksheet::BREAK_NONE );
+ }
+ }
+
+
+ // Update worksheet: hyperlinks
+ $aHyperlinkCollection = array_reverse($pSheet->getHyperlinkCollection(), true);
+ foreach ($aHyperlinkCollection as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->setHyperlink( $newReference, $value );
+ $pSheet->setHyperlink( $key, null );
+ }
+ }
+
+
+ // Update worksheet: data validations
+ $aDataValidationCollection = array_reverse($pSheet->getDataValidationCollection(), true);
+ foreach ($aDataValidationCollection as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->setDataValidation( $newReference, $value );
+ $pSheet->setDataValidation( $key, null );
+ }
+ }
+
+
+ // Update worksheet: merge cells
+ $aMergeCells = $pSheet->getMergeCells();
+ $aNewMergeCells = array(); // the new array of all merge cells
+ foreach ($aMergeCells as $key => &$value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ $aNewMergeCells[$newReference] = $newReference;
+ }
+ $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
+
+
+ // Update worksheet: protected cells
+ $aProtectedCells = array_reverse($pSheet->getProtectedCells(), true);
+ foreach ($aProtectedCells as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->protectCells( $newReference, $value, true );
+ $pSheet->unprotectCells( $key );
+ }
+ }
+
+
+ // Update worksheet: autofilter
+ if ($pSheet->getAutoFilter() != '') {
+ $pSheet->setAutoFilter( $this->updateCellReference($pSheet->getAutoFilter(), $pBefore, $pNumCols, $pNumRows) );
+ }
+
+
+ // Update worksheet: freeze pane
+ if ($pSheet->getFreezePane() != '') {
+ $pSheet->freezePane( $this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows) );
+ }
+
+
+ // Page setup
+ if ($pSheet->getPageSetup()->isPrintAreaSet()) {
+ $pSheet->getPageSetup()->setPrintArea( $this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows) );
+ }
+
+
+ // Update worksheet: drawings
+ $aDrawings = $pSheet->getDrawingCollection();
+ foreach ($aDrawings as $objDrawing) {
+ $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
+ if ($objDrawing->getCoordinates() != $newReference) {
+ $objDrawing->setCoordinates($newReference);
+ }
+ }
+
+
+ // Update workbook: named ranges
+ if (count($pSheet->getParent()->getNamedRanges()) > 0) {
+ foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
+ if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
+ $namedRange->setRange(
+ $this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows)
+ );
+ }
+ }
+ }
+
+ // Garbage collect
+ $pSheet->garbageCollect();
+ }
+
+ /**
+ * Update references within formulas
+ *
+ * @param string $pFormula Formula to update
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to insert
+ * @param int $pNumRows Number of rows to insert
+ * @return string Updated formula
+ * @throws Exception
+ */
+ public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '') {
+ // Update cell references in the formula
+ $formulaBlocks = explode('"',$pFormula);
+ foreach($formulaBlocks as $i => &$formulaBlock) {
+ // Ignore blocks that were enclosed in quotes (even entries in the $formulaBlocks array after the explode)
+ if (($i % 2) == 0) {
+ $adjustCount = 0;
+ $newCellTokens = $cellTokens = array();
+ // Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
+ $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_ROWRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
+ if ($matchCount > 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3].':'.$match[4];
+ $modified3 = substr($this->updateCellReference('$A'.$match[3],$pBefore,$pNumCols,$pNumRows),2);
+ $modified4 = substr($this->updateCellReference('$A'.$match[4],$pBefore,$pNumCols,$pNumRows),2);
+
+ if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3.':'.$modified4;
+ // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+ $column = 100000;
+ $row = 10000000+trim($match[3],'$');
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3].':'.$match[4];
+ $modified3 = substr($this->updateCellReference($match[3].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
+ $modified4 = substr($this->updateCellReference($match[4].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
+
+ if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3.':'.$modified4;
+ // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+ $column = PHPExcel_Cell::columnIndexFromString(trim($match[3],'$')) + 100000;
+ $row = 10000000;
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3].':'.$match[4];
+ $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
+ $modified4 = $this->updateCellReference($match[4],$pBefore,$pNumCols,$pNumRows);
+
+ if ($match[3].$match[4] !== $modified3.$modified4) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3.':'.$modified4;
+ list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
+ // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+ $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
+ $row = trim($row,'$') + 10000000;
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3];
+ $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
+
+ if ($match[3] !== $modified3) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3;
+ list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
+ // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
+ $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
+ $row = trim($row,'$') + 10000000;
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ krsort($cellTokens);
+ krsort($newCellTokens);
+ // Update cell references in the formula
+ $formulaBlock = str_replace('\\','',preg_replace($cellTokens,$newCellTokens,$formulaBlock));
+ }
+ }
+ }
+ unset($formulaBlock);
+
+ // Then rebuild the formula string
+ return implode('"',$formulaBlocks);
+ }
+
+ /**
+ * Update cell reference
+ *
+ * @param string $pCellRange Cell range
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to increment
+ * @param int $pNumRows Number of rows to increment
+ * @return string Updated cell range
+ * @throws Exception
+ */
+ public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
+ // Is it in another worksheet? Will not have to update anything.
+ if (strpos($pCellRange, "!") !== false) {
+ return $pCellRange;
+ // Is it a range or a single cell?
+ } elseif (strpos($pCellRange, ':') === false && strpos($pCellRange, ',') === false) {
+ // Single cell
+ return $this->_updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
+ } elseif (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false) {
+ // Range
+ return $this->_updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
+ } else {
+ // Return original
+ return $pCellRange;
+ }
+ }
+
+ /**
+ * Update named formulas (i.e. containing worksheet references / named ranges)
+ *
+ * @param PHPExcel $pPhpExcel Object to update
+ * @param string $oldName Old name (name to replace)
+ * @param string $newName New name
+ */
+ public function updateNamedFormulas(PHPExcel $pPhpExcel, $oldName = '', $newName = '') {
+ if ($oldName == '') {
+ return;
+ }
+
+ foreach ($pPhpExcel->getWorksheetIterator() as $sheet) {
+ foreach ($sheet->getCellCollection(false) as $cellID) {
+ $cell = $sheet->getCell($cellID);
+ if (!is_null($cell) && $cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ $formula = $cell->getValue();
+ if (strpos($formula, $oldName) !== false) {
+ $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
+ $formula = str_replace($oldName . "!", $newName . "!", $formula);
+ $cell->setValueExplicit($formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Update cell range
+ *
+ * @param string $pCellRange Cell range
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to increment
+ * @param int $pNumRows Number of rows to increment
+ * @return string Updated cell range
+ * @throws Exception
+ */
+ private function _updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
+ if (strpos($pCellRange,':') !== false || strpos($pCellRange, ',') !== false) {
+ // Update range
+ $range = PHPExcel_Cell::splitRange($pCellRange);
+ for ($i = 0; $i < count($range); ++$i) {
+ for ($j = 0; $j < count($range[$i]); ++$j) {
+ $range[$i][$j] = $this->_updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
+ }
+ }
+
+ // Recreate range string
+ return PHPExcel_Cell::buildRange($range);
+ } else {
+ throw new Exception("Only cell ranges may be passed to this method.");
+ }
+ }
+
+ /**
+ * Update single cell reference
+ *
+ * @param string $pCellReference Single cell reference
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to increment
+ * @param int $pNumRows Number of rows to increment
+ * @return string Updated cell reference
+ * @throws Exception
+ */
+ private function _updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
+ if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) {
+ // Get coordinates of $pBefore
+ $beforeColumn = 'A';
+ $beforeRow = 1;
+ list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
+
+ // Get coordinates
+ $newColumn = 'A';
+ $newRow = 1;
+ list($newColumn, $newRow) = PHPExcel_Cell::coordinateFromString( $pCellReference );
+
+ // Make sure the reference can be used
+ if ($newColumn == '' && $newRow == '')
+ {
+ return $pCellReference;
+ }
+
+ // Verify which parts should be updated
+ $updateColumn = (PHPExcel_Cell::columnIndexFromString($newColumn) >= PHPExcel_Cell::columnIndexFromString($beforeColumn))
+ && (strpos($newColumn, '$') === false)
+ && (strpos($beforeColumn, '$') === false);
+
+ $updateRow = ($newRow >= $beforeRow)
+ && (strpos($newRow, '$') === false)
+ && (strpos($beforeRow, '$') === false);
+
+ // Create new column reference
+ if ($updateColumn) {
+ $newColumn = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($newColumn) - 1 + $pNumCols );
+ }
+
+ // Create new row reference
+ if ($updateRow) {
+ $newRow = $newRow + $pNumRows;
+ }
+
+ // Return new reference
+ return $newColumn . $newRow;
+ } else {
+ throw new Exception("Only single cell references may be passed to this method.");
+ }
+ }
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ *
+ * @throws Exception
+ */
+ public final function __clone() {
+ throw new Exception("Cloning a Singleton is not allowed!");
+ }
+}
diff --git a/Classes/PHPExcel/RichText.php b/Classes/PHPExcel/RichText.php
new file mode 100644
index 0000000..9a2aaa8
--- /dev/null
+++ b/Classes/PHPExcel/RichText.php
@@ -0,0 +1,196 @@
+_richTextElements = array();
+
+ // Rich-Text string attached to cell?
+ if (!is_null($pCell)) {
+ // Add cell text and style
+ if ($pCell->getValue() != "") {
+ $objRun = new PHPExcel_RichText_Run($pCell->getValue());
+ $objRun->setFont(clone $pCell->getParent()->getStyle($pCell->getCoordinate())->getFont());
+ $this->addText($objRun);
+ }
+
+ // Set parent value
+ $pCell->setValueExplicit($this, PHPExcel_Cell_DataType::TYPE_STRING);
+ }
+ }
+
+ /**
+ * Add text
+ *
+ * @param PHPExcel_RichText_ITextElement $pText Rich text element
+ * @throws Exception
+ * @return PHPExcel_RichText
+ */
+ public function addText(PHPExcel_RichText_ITextElement $pText = null)
+ {
+ $this->_richTextElements[] = $pText;
+ return $this;
+ }
+
+ /**
+ * Create text
+ *
+ * @param string $pText Text
+ * @return PHPExcel_RichText_TextElement
+ * @throws Exception
+ */
+ public function createText($pText = '')
+ {
+ $objText = new PHPExcel_RichText_TextElement($pText);
+ $this->addText($objText);
+ return $objText;
+ }
+
+ /**
+ * Create text run
+ *
+ * @param string $pText Text
+ * @return PHPExcel_RichText_Run
+ * @throws Exception
+ */
+ public function createTextRun($pText = '')
+ {
+ $objText = new PHPExcel_RichText_Run($pText);
+ $this->addText($objText);
+ return $objText;
+ }
+
+ /**
+ * Get plain text
+ *
+ * @return string
+ */
+ public function getPlainText()
+ {
+ // Return value
+ $returnValue = '';
+
+ // Loop through all PHPExcel_RichText_ITextElement
+ foreach ($this->_richTextElements as $text) {
+ $returnValue .= $text->getText();
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getPlainText();
+ }
+
+ /**
+ * Get Rich Text elements
+ *
+ * @return PHPExcel_RichText_ITextElement[]
+ */
+ public function getRichTextElements()
+ {
+ return $this->_richTextElements;
+ }
+
+ /**
+ * Set Rich Text elements
+ *
+ * @param PHPExcel_RichText_ITextElement[] $pElements Array of elements
+ * @throws Exception
+ * @return PHPExcel_RichText
+ */
+ public function setRichTextElements($pElements = null)
+ {
+ if (is_array($pElements)) {
+ $this->_richTextElements = $pElements;
+ } else {
+ throw new Exception("Invalid PHPExcel_RichText_ITextElement[] array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ $hashElements = '';
+ foreach ($this->_richTextElements as $element) {
+ $hashElements .= $element->getHashCode();
+ }
+
+ return md5(
+ $hashElements
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/RichText/ITextElement.php b/Classes/PHPExcel/RichText/ITextElement.php
new file mode 100644
index 0000000..d98be1f
--- /dev/null
+++ b/Classes/PHPExcel/RichText/ITextElement.php
@@ -0,0 +1,64 @@
+setText($pText);
+ $this->_font = new PHPExcel_Style_Font();
+ }
+
+ /**
+ * Get font
+ *
+ * @return PHPExcel_Style_Font
+ */
+ public function getFont() {
+ return $this->_font;
+ }
+
+ /**
+ * Set font
+ *
+ * @param PHPExcel_Style_Font $pFont Font
+ * @throws Exception
+ * @return PHPExcel_RichText_ITextElement
+ */
+ public function setFont(PHPExcel_Style_Font $pFont = null) {
+ $this->_font = $pFont;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->getText()
+ . $this->_font->getHashCode()
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/RichText/TextElement.php b/Classes/PHPExcel/RichText/TextElement.php
new file mode 100644
index 0000000..a7fecc7
--- /dev/null
+++ b/Classes/PHPExcel/RichText/TextElement.php
@@ -0,0 +1,108 @@
+_text = $pText;
+ }
+
+ /**
+ * Get text
+ *
+ * @return string Text
+ */
+ public function getText() {
+ return $this->_text;
+ }
+
+ /**
+ * Set text
+ *
+ * @param $pText string Text
+ * @return PHPExcel_RichText_ITextElement
+ */
+ public function setText($pText = '') {
+ $this->_text = $pText;
+ return $this;
+ }
+
+ /**
+ * Get font
+ *
+ * @return PHPExcel_Style_Font
+ */
+ public function getFont() {
+ return null;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_text
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/Classes/PHPExcel/Settings.php b/Classes/PHPExcel/Settings.php
new file mode 100644
index 0000000..6659ed8
--- /dev/null
+++ b/Classes/PHPExcel/Settings.php
@@ -0,0 +1,65 @@
+setLocale($locale);
+ } // function setLocale()
+
+}
\ No newline at end of file
diff --git a/Classes/PHPExcel/Shared/CodePage.php b/Classes/PHPExcel/Shared/CodePage.php
new file mode 100644
index 0000000..b8ecf53
--- /dev/null
+++ b/Classes/PHPExcel/Shared/CodePage.php
@@ -0,0 +1,94 @@
+= 1) {
+ $utcDays = $dateValue - $myExcelBaseDate;
+ $returnValue = round($utcDays * 24 * 60 * 60);
+ if (($returnValue <= PHP_INT_MAX) && ($returnValue >= -PHP_INT_MAX)) {
+ $returnValue = (integer) $returnValue;
+ }
+ } else {
+ $hours = round($dateValue * 24);
+ $mins = round($dateValue * 24 * 60) - round($hours * 60);
+ $secs = round($dateValue * 24 * 60 * 60) - round($hours * 60 * 60) - round($mins * 60);
+ $returnValue = (integer) gmmktime($hours, $mins, $secs);
+ }
+
+ // Return
+ return $returnValue;
+ } // function ExcelToPHP()
+
+
+ /**
+ * Convert a date from Excel to a PHP Date/Time object
+ *
+ * @param long $dateValue Excel date/time value
+ * @return long PHP date/time object
+ */
+ public static function ExcelToPHPObject($dateValue = 0) {
+ $dateTime = self::ExcelToPHP($dateValue);
+ $days = floor($dateTime / 86400);
+ $time = round((($dateTime / 86400) - $days) * 86400);
+ $hours = round($time / 3600);
+ $minutes = round($time / 60) - ($hours * 60);
+ $seconds = round($time) - ($hours * 3600) - ($minutes * 60);
+
+ $dateObj = date_create('1-Jan-1970+'.$days.' days');
+ $dateObj->setTime($hours,$minutes,$seconds);
+
+ return $dateObj;
+ } // function ExcelToPHPObject()
+
+
+ /**
+ * Convert a date from PHP to Excel
+ *
+ * @param mixed $dateValue PHP serialized date/time or date object
+ * @return mixed Excel date/time value
+ * or boolean False on failure
+ */
+ public static function PHPToExcel($dateValue = 0) {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = False;
+ if ((is_object($dateValue)) && ($dateValue instanceof self::$dateTimeObjectType)) {
+ $retValue = self::FormattedPHPToExcel( $dateValue->format('Y'), $dateValue->format('m'), $dateValue->format('d'),
+ $dateValue->format('H'), $dateValue->format('i'), $dateValue->format('s')
+ );
+ } elseif (is_numeric($dateValue)) {
+ $retValue = self::FormattedPHPToExcel( date('Y',$dateValue), date('m',$dateValue), date('d',$dateValue),
+ date('H',$dateValue), date('i',$dateValue), date('s',$dateValue)
+ );
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ } // function PHPToExcel()
+
+
+ /**
+ * FormattedPHPToExcel
+ *
+ * @param long $year
+ * @param long $month
+ * @param long $day
+ * @param long $hours
+ * @param long $minutes
+ * @param long $seconds
+ * @return long Excel date/time value
+ */
+ public static function FormattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0) {
+ if (self::$ExcelBaseDate == self::CALENDAR_WINDOWS_1900) {
+ //
+ // Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
+ // This affects every date following 28th February 1900
+ //
+ $excel1900isLeapYear = True;
+ if (($year == 1900) && ($month <= 2)) { $excel1900isLeapYear = False; }
+ $myExcelBaseDate = 2415020;
+ } else {
+ $myExcelBaseDate = 2416481;
+ $excel1900isLeapYear = False;
+ }
+
+ // Julian base date Adjustment
+ if ($month > 2) {
+ $month = $month - 3;
+ } else {
+ $month = $month + 9;
+ --$year;
+ }
+
+ // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
+ $century = substr($year,0,2);
+ $decade = substr($year,2,2);
+ $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myExcelBaseDate + $excel1900isLeapYear;
+
+ $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
+
+ return (float) $excelDate + $excelTime;
+ } // function FormattedPHPToExcel()
+
+
+ /**
+ * Is a given cell a date/time?
+ *
+ * @param PHPExcel_Cell $pCell
+ * @return boolean
+ */
+ public static function isDateTime(PHPExcel_Cell $pCell) {
+ return self::isDateTimeFormat($pCell->getParent()->getStyle($pCell->getCoordinate())->getNumberFormat());
+ } // function isDateTime()
+
+
+ /**
+ * Is a given number format a date/time?
+ *
+ * @param PHPExcel_Style_NumberFormat $pFormat
+ * @return boolean
+ */
+ public static function isDateTimeFormat(PHPExcel_Style_NumberFormat $pFormat) {
+ return self::isDateTimeFormatCode($pFormat->getFormatCode());
+ } // function isDateTimeFormat()
+
+
+ private static $possibleDateFormatCharacters = 'ymdHis';
+
+ /**
+ * Is a given number format code a date/time?
+ *
+ * @param string $pFormatCode
+ * @return boolean
+ */
+ public static function isDateTimeFormatCode($pFormatCode = '') {
+ // Switch on formatcode
+ switch ($pFormatCode) {
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_DDMMYYYY:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_DMYSLASH:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_DMYMINUS:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_DMMINUS:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_MYMINUS:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_DATETIME:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME1:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME2:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME3:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME5:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME6:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME7:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME8:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDDSLASH:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX14:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX15:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX16:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX17:
+ case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX22:
+ return true;
+ }
+
+ // Try checking for any of the date formatting characters that don't appear within square braces
+ if (preg_match('/(^|\])[^\[]*['.self::$possibleDateFormatCharacters.']/i',$pFormatCode)) {
+ return true;
+ }
+
+ // No date...
+ return false;
+ } // function isDateTimeFormatCode()
+
+
+ /**
+ * Convert a date/time string to Excel time
+ *
+ * @param string $dateValue Examples: '2009-12-31', '2009-12-31 15:59', '2009-12-31 15:59:10'
+ * @return float|false Excel date/time serial value
+ */
+ public static function stringToExcel($dateValue = '') {
+ // restrict to dates and times like these because date_parse accepts too many strings
+ // '2009-12-31'
+ // '2009-12-31 15:59'
+ // '2009-12-31 15:59:10'
+ if (!preg_match('/^\d{4}\-\d{1,2}\-\d{1,2}( \d{1,2}:\d{1,2}(:\d{1,2})?)?$/', $dateValue)) {
+ return false;
+ }
+
+ // now try with date_parse
+ $PHPDateArray = date_parse($dateValue);
+
+ if ($PHPDateArray['error_count'] == 0) {
+ $year = $PHPDateArray['year'] !== false ? $PHPDateArray['year'] : self::getExcelCalendar();
+ $month = $PHPDateArray['month'] !== false ? $PHPDateArray['month'] : 1;
+ $day = $PHPDateArray['day'] !== false ? $PHPDateArray['day'] : 0;
+ $hour = $PHPDateArray['hour'] !== false ? $PHPDateArray['hour'] : 0;
+ $minute = $PHPDateArray['minute'] !== false ? $PHPDateArray['minute'] : 0;
+ $second = $PHPDateArray['second'] !== false ? $PHPDateArray['second'] : 0;
+
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day, $hour, $minute, $second);
+
+ return $excelDateValue;
+ }
+
+ return false;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Drawing.php b/Classes/PHPExcel/Shared/Drawing.php
new file mode 100644
index 0000000..3969ac4
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Drawing.php
@@ -0,0 +1,272 @@
+getName();
+ $size = $pDefaultFont->getSize();
+
+ if (isset(PHPExcel_Shared_Font::$defaultColumnWidths[$name][$size])) {
+ // Exact width can be determined
+ $colWidth = $pValue
+ * PHPExcel_Shared_Font::$defaultColumnWidths[$name][$size]['width']
+ / PHPExcel_Shared_Font::$defaultColumnWidths[$name][$size]['px'];
+ } else {
+ // We don't have data for this particular font and size, use approximation by
+ // extrapolating from Calibri 11
+ $colWidth = $pValue * 11
+ * PHPExcel_Shared_Font::$defaultColumnWidths['Calibri'][11]['width']
+ / PHPExcel_Shared_Font::$defaultColumnWidths['Calibri'][11]['px'] / $size;
+ }
+
+ return $colWidth;
+ }
+
+ /**
+ * Convert column width from (intrinsic) Excel units to pixels
+ *
+ * @param float $pValue Value in cell dimension
+ * @param PHPExcel_Style_Font $pDefaultFont Default font of the workbook
+ * @return int Value in pixels
+ */
+ public static function cellDimensionToPixels($pValue = 0, PHPExcel_Style_Font $pDefaultFont) {
+ // Font name and size
+ $name = $pDefaultFont->getName();
+ $size = $pDefaultFont->getSize();
+
+ if (isset(PHPExcel_Shared_Font::$defaultColumnWidths[$name][$size])) {
+ // Exact width can be determined
+ $colWidth = $pValue
+ * PHPExcel_Shared_Font::$defaultColumnWidths[$name][$size]['px']
+ / PHPExcel_Shared_Font::$defaultColumnWidths[$name][$size]['width'];
+
+ } else {
+ // We don't have data for this particular font and size, use approximation by
+ // extrapolating from Calibri 11
+ $colWidth = $pValue * $size
+ * PHPExcel_Shared_Font::$defaultColumnWidths['Calibri'][11]['px']
+ / PHPExcel_Shared_Font::$defaultColumnWidths['Calibri'][11]['width'] / 11;
+ }
+
+ // Round pixels to closest integer
+ $colWidth = (int) round($colWidth);
+
+ return $colWidth;
+ }
+
+ /**
+ * Convert pixels to points
+ *
+ * @param int $pValue Value in pixels
+ * @return int Value in points
+ */
+ public static function pixelsToPoints($pValue = 0) {
+ return $pValue * 0.67777777;
+ }
+
+ /**
+ * Convert points to pixels
+ *
+ * @param int $pValue Value in points
+ * @return int Value in pixels
+ */
+ public static function pointsToPixels($pValue = 0) {
+ if ($pValue != 0) {
+ return (int) ceil($pValue * 1.333333333);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Convert degrees to angle
+ *
+ * @param int $pValue Degrees
+ * @return int Angle
+ */
+ public static function degreesToAngle($pValue = 0) {
+ return (int)round($pValue * 60000);
+ }
+
+ /**
+ * Convert angle to degrees
+ *
+ * @param int $pValue Angle
+ * @return int Degrees
+ */
+ public static function angleToDegrees($pValue = 0) {
+ if ($pValue != 0) {
+ return round($pValue / 60000);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Create a new image from file. By alexander at alexauto dot nl
+ *
+ * @link http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
+ * @param string $filename Path to Windows DIB (BMP) image
+ * @return resource
+ */
+ public static function imagecreatefrombmp($p_sFile)
+ {
+ // Load the image into a string
+ $file = fopen($p_sFile,"rb");
+ $read = fread($file,10);
+ while(!feof($file)&&($read<>""))
+ $read .= fread($file,1024);
+
+ $temp = unpack("H*",$read);
+ $hex = $temp[1];
+ $header = substr($hex,0,108);
+
+ // Process the header
+ // Structure: http://www.fastgraph.com/help/bmp_header_format.html
+ if (substr($header,0,4)=="424d")
+ {
+ // Cut it in parts of 2 bytes
+ $header_parts = str_split($header,2);
+
+ // Get the width 4 bytes
+ $width = hexdec($header_parts[19].$header_parts[18]);
+
+ // Get the height 4 bytes
+ $height = hexdec($header_parts[23].$header_parts[22]);
+
+ // Unset the header params
+ unset($header_parts);
+ }
+
+ // Define starting X and Y
+ $x = 0;
+ $y = 1;
+
+ // Create newimage
+ $image = imagecreatetruecolor($width,$height);
+
+ // Grab the body from the image
+ $body = substr($hex,108);
+
+ // Calculate if padding at the end-line is needed
+ // Divided by two to keep overview.
+ // 1 byte = 2 HEX-chars
+ $body_size = (strlen($body)/2);
+ $header_size = ($width*$height);
+
+ // Use end-line padding? Only when needed
+ $usePadding = ($body_size>($header_size*3)+4);
+
+ // Using a for-loop with index-calculation instaid of str_split to avoid large memory consumption
+ // Calculate the next DWORD-position in the body
+ for ($i=0;$i<$body_size;$i+=3)
+ {
+ // Calculate line-ending and padding
+ if ($x>=$width)
+ {
+ // If padding needed, ignore image-padding
+ // Shift i to the ending of the current 32-bit-block
+ if ($usePadding)
+ $i += $width%4;
+
+ // Reset horizontal position
+ $x = 0;
+
+ // Raise the height-position (bottom-up)
+ $y++;
+
+ // Reached the image-height? Break the for-loop
+ if ($y>$height)
+ break;
+ }
+
+ // Calculation of the RGB-pixel (defined as BGR in image-data)
+ // Define $i_pos as absolute position in the body
+ $i_pos = $i*2;
+ $r = hexdec($body[$i_pos+4].$body[$i_pos+5]);
+ $g = hexdec($body[$i_pos+2].$body[$i_pos+3]);
+ $b = hexdec($body[$i_pos].$body[$i_pos+1]);
+
+ // Calculate and draw the pixel
+ $color = imagecolorallocate($image,$r,$g,$b);
+ imagesetpixel($image,$x,$height-$y,$color);
+
+ // Raise the horizontal position
+ $x++;
+ }
+
+ // Unset the body / free the memory
+ unset($body);
+
+ // Return image-object
+ return $image;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Escher.php b/Classes/PHPExcel/Shared/Escher.php
new file mode 100644
index 0000000..f8d535d
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher.php
@@ -0,0 +1,91 @@
+_dggContainer;
+ }
+
+ /**
+ * Set Drawing Group Container
+ *
+ * @param PHPExcel_Shared_Escher_DggContainer $dggContainer
+ */
+ public function setDggContainer($dggContainer)
+ {
+ return $this->_dggContainer = $dggContainer;
+ }
+
+ /**
+ * Get Drawing Container
+ *
+ * @return PHPExcel_Shared_Escher_DgContainer
+ */
+ public function getDgContainer()
+ {
+ return $this->_dgContainer;
+ }
+
+ /**
+ * Set Drawing Container
+ *
+ * @param PHPExcel_Shared_Escher_DgContainer $dgContainer
+ */
+ public function setDgContainer($dgContainer)
+ {
+ return $this->_dgContainer = $dgContainer;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DgContainer.php b/Classes/PHPExcel/Shared/Escher/DgContainer.php
new file mode 100644
index 0000000..77175ee
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DgContainer.php
@@ -0,0 +1,83 @@
+_dgId;
+ }
+
+ public function setDgId($value)
+ {
+ $this->_dgId = $value;
+ }
+
+ public function getLastSpId()
+ {
+ return $this->_lastSpId;
+ }
+
+ public function setLastSpId($value)
+ {
+ $this->_lastSpId = $value;
+ }
+
+ public function getSpgrContainer()
+ {
+ return $this->_spgrContainer;
+ }
+
+ public function setSpgrContainer($spgrContainer)
+ {
+ return $this->_spgrContainer = $spgrContainer;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer.php b/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer.php
new file mode 100644
index 0000000..9b420bd
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer.php
@@ -0,0 +1,109 @@
+_parent = $parent;
+ }
+
+ /**
+ * Get the parent Shape Group Container if any
+ *
+ * @return PHPExcel_Shared_Escher_DgContainer_SpgrContainer|null
+ */
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+
+ /**
+ * Add a child. This will be either spgrContainer or spContainer
+ *
+ * @param mixed $child
+ */
+ public function addChild($child)
+ {
+ $this->_children[] = $child;
+ $child->setParent($this);
+ }
+
+ /**
+ * Get collection of Shape Containers
+ */
+ public function getChildren()
+ {
+ return $this->_children;
+ }
+
+ /**
+ * Recursively get all spContainers within this spgrContainer
+ *
+ * @return PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer[]
+ */
+ public function getAllSpContainers()
+ {
+ $allSpContainers = array();
+
+ foreach ($this->_children as $child) {
+ if ($child instanceof PHPExcel_Shared_Escher_DgContainer_SpgrContainer) {
+ $allSpContainers = array_merge($allSpContainers, $child->getAllSpContainers());
+ } else {
+ $allSpContainers[] = $child;
+ }
+ }
+
+ return $allSpContainers;
+ }
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
new file mode 100644
index 0000000..7dea264
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
@@ -0,0 +1,368 @@
+_parent = $parent;
+ }
+
+ /**
+ * Get the parent Shape Group Container
+ *
+ * @return PHPExcel_Shared_Escher_DgContainer_SpgrContainer
+ */
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+
+ /**
+ * Set whether this is a group shape
+ *
+ * @param boolean $value
+ */
+ public function setSpgr($value = false)
+ {
+ $this->_spgr = $value;
+ }
+
+ /**
+ * Get whether this is a group shape
+ *
+ * @return boolean
+ */
+ public function getSpgr()
+ {
+ return $this->_spgr;
+ }
+
+ /**
+ * Set the shape type
+ *
+ * @param int $value
+ */
+ public function setSpType($value)
+ {
+ $this->_spType = $value;
+ }
+
+ /**
+ * Get the shape type
+ *
+ * @return int
+ */
+ public function getSpType()
+ {
+ return $this->_spType;
+ }
+
+ /**
+ * Set the shape index
+ *
+ * @param int $value
+ */
+ public function setSpId($value)
+ {
+ $this->_spId = $value;
+ }
+
+ /**
+ * Get the shape index
+ *
+ * @return int
+ */
+ public function getSpId()
+ {
+ return $this->_spId;
+ }
+
+ /**
+ * Set an option for the Shape Group Container
+ *
+ * @param int $property The number specifies the option
+ * @param mixed $value
+ */
+ public function setOPT($property, $value)
+ {
+ $this->_OPT[$property] = $value;
+ }
+
+ /**
+ * Get an option for the Shape Group Container
+ *
+ * @param int $property The number specifies the option
+ * @return mixed
+ */
+ public function getOPT($property)
+ {
+ if (isset($this->_OPT[$property])) {
+ return $this->_OPT[$property];
+ }
+ return null;
+ }
+
+ /**
+ * Get the collection of options
+ *
+ * @return array
+ */
+ public function getOPTCollection()
+ {
+ return $this->_OPT;
+ }
+
+ /**
+ * Set cell coordinates of upper-left corner of shape
+ *
+ * @param string $value
+ */
+ public function setStartCoordinates($value = 'A1')
+ {
+ $this->_startCoordinates = $value;
+ }
+
+ /**
+ * Get cell coordinates of upper-left corner of shape
+ *
+ * @return string
+ */
+ public function getStartCoordinates()
+ {
+ return $this->_startCoordinates;
+ }
+
+ /**
+ * Set offset in x-direction of upper-left corner of shape measured in 1/1024 of column width
+ *
+ * @param int $startOffsetX
+ */
+ public function setStartOffsetX($startOffsetX = 0)
+ {
+ $this->_startOffsetX = $startOffsetX;
+ }
+
+ /**
+ * Get offset in x-direction of upper-left corner of shape measured in 1/1024 of column width
+ *
+ * @return int
+ */
+ public function getStartOffsetX()
+ {
+ return $this->_startOffsetX;
+ }
+
+ /**
+ * Set offset in y-direction of upper-left corner of shape measured in 1/256 of row height
+ *
+ * @param int $startOffsetY
+ */
+ public function setStartOffsetY($startOffsetY = 0)
+ {
+ $this->_startOffsetY = $startOffsetY;
+ }
+
+ /**
+ * Get offset in y-direction of upper-left corner of shape measured in 1/256 of row height
+ *
+ * @return int
+ */
+ public function getStartOffsetY()
+ {
+ return $this->_startOffsetY;
+ }
+
+ /**
+ * Set cell coordinates of bottom-right corner of shape
+ *
+ * @param string $value
+ */
+ public function setEndCoordinates($value = 'A1')
+ {
+ $this->_endCoordinates = $value;
+ }
+
+ /**
+ * Get cell coordinates of bottom-right corner of shape
+ *
+ * @return string
+ */
+ public function getEndCoordinates()
+ {
+ return $this->_endCoordinates;
+ }
+
+ /**
+ * Set offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width
+ *
+ * @param int $startOffsetX
+ */
+ public function setEndOffsetX($endOffsetX = 0)
+ {
+ $this->_endOffsetX = $endOffsetX;
+ }
+
+ /**
+ * Get offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width
+ *
+ * @return int
+ */
+ public function getEndOffsetX()
+ {
+ return $this->_endOffsetX;
+ }
+
+ /**
+ * Set offset in y-direction of bottom-right corner of shape measured in 1/256 of row height
+ *
+ * @param int $endOffsetY
+ */
+ public function setEndOffsetY($endOffsetY = 0)
+ {
+ $this->_endOffsetY = $endOffsetY;
+ }
+
+ /**
+ * Get offset in y-direction of bottom-right corner of shape measured in 1/256 of row height
+ *
+ * @return int
+ */
+ public function getEndOffsetY()
+ {
+ return $this->_endOffsetY;
+ }
+
+ /**
+ * Get the nesting level of this spContainer. This is the number of spgrContainers between this spContainer and
+ * the dgContainer. A value of 1 = immediately within first spgrContainer
+ * Higher nesting level occurs if and only if spContainer is part of a shape group
+ *
+ * @return int Nesting level
+ */
+ public function getNestingLevel()
+ {
+ $nestingLevel = 0;
+
+ $parent = $this->getParent();
+ while ($parent instanceof PHPExcel_Shared_Escher_DgContainer_SpgrContainer) {
+ ++$nestingLevel;
+ $parent = $parent->getParent();
+ }
+
+ return $nestingLevel;
+ }
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DggContainer.php b/Classes/PHPExcel/Shared/Escher/DggContainer.php
new file mode 100644
index 0000000..79194bc
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DggContainer.php
@@ -0,0 +1,203 @@
+_spIdMax;
+ }
+
+ /**
+ * Set maximum shape index of all shapes in all drawings (plus one)
+ *
+ * @param int
+ */
+ public function setSpIdMax($value)
+ {
+ $this->_spIdMax = $value;
+ }
+
+ /**
+ * Get total number of drawings saved
+ *
+ * @return int
+ */
+ public function getCDgSaved()
+ {
+ return $this->_cDgSaved;
+ }
+
+ /**
+ * Set total number of drawings saved
+ *
+ * @param int
+ */
+ public function setCDgSaved($value)
+ {
+ $this->_cDgSaved = $value;
+ }
+
+ /**
+ * Get total number of shapes saved (including group shapes)
+ *
+ * @return int
+ */
+ public function getCSpSaved()
+ {
+ return $this->_cSpSaved;
+ }
+
+ /**
+ * Set total number of shapes saved (including group shapes)
+ *
+ * @param int
+ */
+ public function setCSpSaved($value)
+ {
+ $this->_cSpSaved = $value;
+ }
+
+ /**
+ * Get BLIP Store Container
+ *
+ * @return PHPExcel_Shared_Escher_DggContainer_BstoreContainer
+ */
+ public function getBstoreContainer()
+ {
+ return $this->_bstoreContainer;
+ }
+
+ /**
+ * Set BLIP Store Container
+ *
+ * @param PHPExcel_Shared_Escher_DggContainer_BstoreContainer $bstoreContainer
+ */
+ public function setBstoreContainer($bstoreContainer)
+ {
+ $this->_bstoreContainer = $bstoreContainer;
+ }
+
+ /**
+ * Set an option for the drawing group
+ *
+ * @param int $property The number specifies the option
+ * @param mixed $value
+ */
+ public function setOPT($property, $value)
+ {
+ $this->_OPT[$property] = $value;
+ }
+
+ /**
+ * Get an option for the drawing group
+ *
+ * @param int $property The number specifies the option
+ * @return mixed
+ */
+ public function getOPT($property)
+ {
+ if (isset($this->_OPT[$property])) {
+ return $this->_OPT[$property];
+ }
+ return null;
+ }
+
+ /**
+ * Get identifier clusters
+ *
+ * @return array
+ */
+ public function getIDCLs()
+ {
+ return $this->_IDCLs;
+ }
+
+ /**
+ * Set identifier clusters. array( => , ...)
+ *
+ * @param array $pValue
+ */
+ public function setIDCLs($pValue)
+ {
+ $this->_IDCLs = $pValue;
+ }
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer.php b/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer.php
new file mode 100644
index 0000000..9280e24
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer.php
@@ -0,0 +1,65 @@
+_BSECollection[] = $BSE;
+ $BSE->setParent($this);
+ }
+
+ /**
+ * Get the collection of BLIP Store Entries
+ *
+ * @return PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE[]
+ */
+ public function getBSECollection()
+ {
+ return $this->_BSECollection;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE.php b/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE.php
new file mode 100644
index 0000000..8ac5342
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE.php
@@ -0,0 +1,120 @@
+_parent = $parent;
+ }
+
+ /**
+ * Get the BLIP
+ *
+ * @return PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip
+ */
+ public function getBlip()
+ {
+ return $this->_blip;
+ }
+
+ /**
+ * Set the BLIP
+ *
+ * @param PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip $blip
+ */
+ public function setBlip($blip)
+ {
+ $this->_blip = $blip;
+ $blip->setParent($this);
+ }
+
+ /**
+ * Get the BLIP type
+ *
+ * @return int
+ */
+ public function getBlipType()
+ {
+ return $this->_blipType;
+ }
+
+ /**
+ * Set the BLIP type
+ *
+ * @param int
+ */
+ public function setBlipType($blipType)
+ {
+ $this->_blipType = $blipType;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php b/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
new file mode 100644
index 0000000..15edfe8
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
@@ -0,0 +1,91 @@
+_data;
+ }
+
+ /**
+ * Set the raw image data
+ *
+ * @param string
+ */
+ public function setData($data)
+ {
+ $this->_data = $data;
+ }
+
+ /**
+ * Set parent BSE
+ *
+ * @param PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE $parent
+ */
+ public function setParent($parent)
+ {
+ $this->_parent = $parent;
+ }
+
+ /**
+ * Get parent BSE
+ *
+ * @return PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE $parent
+ */
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Excel5.php b/Classes/PHPExcel/Shared/Excel5.php
new file mode 100644
index 0000000..22d8e71
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Excel5.php
@@ -0,0 +1,317 @@
+getParent()->getDefaultStyle()->getFont();
+
+ $columnDimensions = $sheet->getColumnDimensions();
+
+ // first find the true column width in pixels (uncollapsed and unhidden)
+ if ( isset($columnDimensions[$col]) and $columnDimensions[$col]->getWidth() != -1 ) {
+
+ // then we have column dimension with explicit width
+ $columnDimension = $columnDimensions[$col];
+ $width = $columnDimension->getWidth();
+ $pixelWidth = PHPExcel_Shared_Drawing::cellDimensionToPixels($width, $font);
+
+ } else if ($sheet->getDefaultColumnDimension()->getWidth() != -1) {
+
+ // then we have default column dimension with explicit width
+ $defaultColumnDimension = $sheet->getDefaultColumnDimension();
+ $width = $defaultColumnDimension->getWidth();
+ $pixelWidth = PHPExcel_Shared_Drawing::cellDimensionToPixels($width, $font);
+
+ } else {
+
+ // we don't even have any default column dimension. Width depends on default font
+ $pixelWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($font, true);
+ }
+
+ // now find the effective column width in pixels
+ if (isset($columnDimensions[$col]) and !$columnDimensions[$col]->getVisible()) {
+ $effectivePixelWidth = 0;
+ } else {
+ $effectivePixelWidth = $pixelWidth;
+ }
+
+ return $effectivePixelWidth;
+ }
+
+ /**
+ * Convert the height of a cell from user's units to pixels. By interpolation
+ * the relationship is: y = 4/3x. If the height hasn't been set by the user we
+ * use the default value. If the row is hidden we use a value of zero.
+ *
+ * @param PHPExcel_Worksheet $sheet The sheet
+ * @param integer $row The row index (1-based)
+ * @return integer The width in pixels
+ */
+ public static function sizeRow($sheet, $row = 1)
+ {
+ // default font of the workbook
+ $font = $sheet->getParent()->getDefaultStyle()->getFont();
+
+ $rowDimensions = $sheet->getRowDimensions();
+
+ // first find the true row height in pixels (uncollapsed and unhidden)
+ if ( isset($rowDimensions[$row]) and $rowDimensions[$row]->getRowHeight() != -1) {
+
+ // then we have a row dimension
+ $rowDimension = $rowDimensions[$row];
+ $rowHeight = $rowDimension->getRowHeight();
+ $pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10
+
+ } else if ($sheet->getDefaultRowDimension()->getRowHeight() != -1) {
+
+ // then we have a default row dimension with explicit height
+ $defaultRowDimension = $sheet->getDefaultRowDimension();
+ $rowHeight = $defaultRowDimension->getRowHeight();
+ $pixelRowHeight = PHPExcel_Shared_Drawing::pointsToPixels($rowHeight);
+
+ } else {
+
+ // we don't even have any default row dimension. Height depends on default font
+ $pointRowHeight = PHPExcel_Shared_Font::getDefaultRowHeightByFont($font);
+ $pixelRowHeight = PHPExcel_Shared_Font::fontSizeToPixels($pointRowHeight);
+
+ }
+
+ // now find the effective row height in pixels
+ if ( isset($rowDimensions[$row]) and !$rowDimensions[$row]->getVisible() ) {
+ $effectivePixelRowHeight = 0;
+ } else {
+ $effectivePixelRowHeight = $pixelRowHeight;
+ }
+
+ return $effectivePixelRowHeight;
+ }
+
+ /**
+ * Get the horizontal distance in pixels between two anchors
+ * The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets
+ *
+ * @param PHPExcel_Worksheet $sheet
+ * @param string $startColumn
+ * @param integer $startOffset Offset within start cell measured in 1/1024 of the cell width
+ * @param string $endColumn
+ * @param integer $endOffset Offset within end cell measured in 1/1024 of the cell width
+ * @return integer Horizontal measured in pixels
+ */
+ public static function getDistanceX(PHPExcel_Worksheet $sheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0)
+ {
+ $distanceX = 0;
+
+ // add the widths of the spanning columns
+ $startColumnIndex = PHPExcel_Cell::columnIndexFromString($startColumn) - 1; // 1-based
+ $endColumnIndex = PHPExcel_Cell::columnIndexFromString($endColumn) - 1; // 1-based
+ for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) {
+ $distanceX += self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($i));
+ }
+
+ // correct for offsetX in startcell
+ $distanceX -= (int) floor(self::sizeCol($sheet, $startColumn) * $startOffsetX / 1024);
+
+ // correct for offsetX in endcell
+ $distanceX -= (int) floor(self::sizeCol($sheet, $endColumn) * (1 - $endOffsetX / 1024));
+
+ return $distanceX;
+ }
+
+ /**
+ * Get the vertical distance in pixels between two anchors
+ * The distanceY is found as sum of all the spanning rows minus two offsets
+ *
+ * @param PHPExcel_Worksheet $sheet
+ * @param string $startRow (1-based)
+ * @param integer $startOffset Offset within start cell measured in 1/256 of the cell height
+ * @param string $endRow (1-based)
+ * @param integer $endOffset Offset within end cell measured in 1/256 of the cell height
+ * @return integer Vertical distance measured in pixels
+ */
+ public static function getDistanceY(PHPExcel_Worksheet $sheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0)
+ {
+ $distanceY = 0;
+
+ // add the widths of the spanning rows
+ for ($row = $startRow; $row <= $endRow; ++$row) {
+ $distanceY += self::sizeRow($sheet, $row);
+ }
+
+ // correct for offsetX in startcell
+ $distanceY -= (int) floor(self::sizeRow($sheet, $startRow) * $startOffsetY / 256);
+
+ // correct for offsetX in endcell
+ $distanceY -= (int) floor(self::sizeRow($sheet, $endRow) * (1 - $endOffsetY / 256));
+
+ return $distanceY;
+ }
+
+ /**
+ * Convert 1-cell anchor coordinates to 2-cell anchor coordinates
+ * This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications
+ *
+ * Calculate the vertices that define the position of the image as required by
+ * the OBJ record.
+ *
+ * +------------+------------+
+ * | A | B |
+ * +-----+------------+------------+
+ * | |(x1,y1) | |
+ * | 1 |(A1)._______|______ |
+ * | | | | |
+ * | | | | |
+ * +-----+----| BITMAP |-----+
+ * | | | | |
+ * | 2 | |______________. |
+ * | | | (B2)|
+ * | | | (x2,y2)|
+ * +---- +------------+------------+
+ *
+ * Example of a bitmap that covers some of the area from cell A1 to cell B2.
+ *
+ * Based on the width and height of the bitmap we need to calculate 8 vars:
+ * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
+ * The width and height of the cells are also variable and have to be taken into
+ * account.
+ * The values of $col_start and $row_start are passed in from the calling
+ * function. The values of $col_end and $row_end are calculated by subtracting
+ * the width and height of the bitmap from the width and height of the
+ * underlying cells.
+ * The vertices are expressed as a percentage of the underlying cell width as
+ * follows (rhs values are in pixels):
+ *
+ * x1 = X / W *1024
+ * y1 = Y / H *256
+ * x2 = (X-1) / W *1024
+ * y2 = (Y-1) / H *256
+ *
+ * Where: X is distance from the left side of the underlying cell
+ * Y is distance from the top of the underlying cell
+ * W is the width of the cell
+ * H is the height of the cell
+ *
+ * @param PHPExcel_Worksheet $sheet
+ * @param string $coordinates E.g. 'A1'
+ * @param integer $offsetX Horizontal offset in pixels
+ * @param integer $offsetY Vertical offset in pixels
+ * @param integer $width Width in pixels
+ * @param integer $height Height in pixels
+ * @return array
+ */
+ public static function oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height)
+ {
+ list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinates);
+ $col_start = PHPExcel_Cell::columnIndexFromString($column) - 1;
+ $row_start = $row - 1;
+
+ $x1 = $offsetX;
+ $y1 = $offsetY;
+
+ // Initialise end cell to the same as the start cell
+ $col_end = $col_start; // Col containing lower right corner of object
+ $row_end = $row_start; // Row containing bottom right corner of object
+
+ // Zero the specified offset if greater than the cell dimensions
+ if ($x1 >= self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_start))) {
+ $x1 = 0;
+ }
+ if ($y1 >= self::sizeRow($sheet, $row_start + 1)) {
+ $y1 = 0;
+ }
+
+ $width = $width + $x1 -1;
+ $height = $height + $y1 -1;
+
+ // Subtract the underlying cell widths to find the end cell of the image
+ while ($width >= self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end))) {
+ $width -= self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end));
+ ++$col_end;
+ }
+
+ // Subtract the underlying cell heights to find the end cell of the image
+ while ($height >= self::sizeRow($sheet, $row_end + 1)) {
+ $height -= self::sizeRow($sheet, $row_end + 1);
+ ++$row_end;
+ }
+
+ // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
+ // with zero height or width.
+ if (self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
+ return;
+ }
+ if (self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) == 0) {
+ return;
+ }
+ if (self::sizeRow($sheet, $row_start + 1) == 0) {
+ return;
+ }
+ if (self::sizeRow($sheet, $row_end + 1) == 0) {
+ return;
+ }
+
+ // Convert the pixel values to the percentage value expected by Excel
+ $x1 = $x1 / self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) * 1024;
+ $y1 = $y1 / self::sizeRow($sheet, $row_start + 1) * 256;
+ $x2 = ($width + 1) / self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
+ $y2 = ($height + 1) / self::sizeRow($sheet, $row_end + 1) * 256; // Distance to bottom of object
+
+ $startCoordinates = PHPExcel_Cell::stringFromColumnIndex($col_start) . ($row_start + 1);
+ $endCoordinates = PHPExcel_Cell::stringFromColumnIndex($col_end) . ($row_end + 1);
+
+ $twoAnchor = array(
+ 'startCoordinates' => $startCoordinates,
+ 'startOffsetX' => $x1,
+ 'startOffsetY' => $y1,
+ 'endCoordinates' => $endCoordinates,
+ 'endOffsetX' => $x2,
+ 'endOffsetY' => $y2,
+ );
+
+ return $twoAnchor;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/File.php b/Classes/PHPExcel/Shared/File.php
new file mode 100644
index 0000000..d3db8e9
--- /dev/null
+++ b/Classes/PHPExcel/Shared/File.php
@@ -0,0 +1,139 @@
+open($zipFile) === true) {
+ $returnValue = ($zip->getFromName($archiveFile) !== false);
+ $zip->close();
+ return $returnValue;
+ } else {
+ return false;
+ }
+ } else {
+ // Regular file_exists
+ return file_exists($pFilename);
+ }
+ }
+
+ /**
+ * Returns canonicalized absolute pathname, also for ZIP archives
+ *
+ * @param string $pFilename
+ * @return string
+ */
+ public static function realpath($pFilename) {
+ // Returnvalue
+ $returnValue = '';
+
+ // Try using realpath()
+ if (file_exists($pFilename)) {
+ $returnValue = realpath($pFilename);
+ }
+
+ // Found something?
+ if ($returnValue == '' || is_null($returnValue)) {
+ $pathArray = explode('/' , $pFilename);
+ while(in_array('..', $pathArray) && $pathArray[0] != '..') {
+ for ($i = 0; $i < count($pathArray); ++$i) {
+ if ($pathArray[$i] == '..' && $i > 0) {
+ unset($pathArray[$i]);
+ unset($pathArray[$i - 1]);
+ break;
+ }
+ }
+ }
+ $returnValue = implode('/', $pathArray);
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+ /**
+ * Get the systems temporary directory.
+ *
+ * @return string
+ */
+ public static function sys_get_temp_dir()
+ {
+ // sys_get_temp_dir is only available since PHP 5.2.1
+ // http://php.net/manual/en/function.sys-get-temp-dir.php#94119
+
+ if ( !function_exists('sys_get_temp_dir')) {
+ if ($temp = getenv('TMP') ) {
+ if (file_exists($temp)) { return realpath($temp); }
+ }
+ if ($temp = getenv('TEMP') ) {
+ if (file_exists($temp)) { return realpath($temp); }
+ }
+ if ($temp = getenv('TMPDIR') ) {
+ if (file_exists($temp)) { return realpath($temp); }
+ }
+
+ // trick for creating a file in system's temporary dir
+ // without knowing the path of the system's temporary dir
+ $temp = tempnam(__FILE__, '');
+ if (file_exists($temp)) {
+ unlink($temp);
+ return realpath(dirname($temp));
+ }
+
+ return null;
+ }
+
+ // use ordinary built-in PHP function
+ // There should be no problem with the 5.2.4 Suhosin realpath() bug, because this line should only
+ // be called if we're running 5.2.1 or earlier
+ return realpath(sys_get_temp_dir());
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/Font.php b/Classes/PHPExcel/Shared/Font.php
new file mode 100644
index 0000000..2dc9435
--- /dev/null
+++ b/Classes/PHPExcel/Shared/Font.php
@@ -0,0 +1,763 @@
+ array(
+ 1 => array('px' => 24, 'width' => 12.00000000),
+ 2 => array('px' => 24, 'width' => 12.00000000),
+ 3 => array('px' => 32, 'width' => 10.66406250),
+ 4 => array('px' => 32, 'width' => 10.66406250),
+ 5 => array('px' => 40, 'width' => 10.00000000),
+ 6 => array('px' => 48, 'width' => 9.59765625),
+ 7 => array('px' => 48, 'width' => 9.59765625),
+ 8 => array('px' => 56, 'width' => 9.33203125),
+ 9 => array('px' => 64, 'width' => 9.14062500),
+ 10 => array('px' => 64, 'width' => 9.14062500),
+ ),
+ 'Calibri' => array(
+ 1 => array('px' => 24, 'width' => 12.00000000),
+ 2 => array('px' => 24, 'width' => 12.00000000),
+ 3 => array('px' => 32, 'width' => 10.66406250),
+ 4 => array('px' => 32, 'width' => 10.66406250),
+ 5 => array('px' => 40, 'width' => 10.00000000),
+ 6 => array('px' => 48, 'width' => 9.59765625),
+ 7 => array('px' => 48, 'width' => 9.59765625),
+ 8 => array('px' => 56, 'width' => 9.33203125),
+ 9 => array('px' => 56, 'width' => 9.33203125),
+ 10 => array('px' => 64, 'width' => 9.14062500),
+ 11 => array('px' => 64, 'width' => 9.14062500),
+ ),
+ 'Verdana' => array(
+ 1 => array('px' => 24, 'width' => 12.00000000),
+ 2 => array('px' => 24, 'width' => 12.00000000),
+ 3 => array('px' => 32, 'width' => 10.66406250),
+ 4 => array('px' => 32, 'width' => 10.66406250),
+ 5 => array('px' => 40, 'width' => 10.00000000),
+ 6 => array('px' => 48, 'width' => 9.59765625),
+ 7 => array('px' => 48, 'width' => 9.59765625),
+ 8 => array('px' => 64, 'width' => 9.14062500),
+ 9 => array('px' => 72, 'width' => 9.00000000),
+ 10 => array('px' => 72, 'width' => 9.00000000),
+ ),
+ );
+
+ /**
+ * Set autoSize method
+ *
+ * @param string $pValue
+ */
+ public static function setAutoSizeMethod($pValue = 'approx')
+ {
+ self::$autoSizeMethod = $pValue;
+ }
+
+ /**
+ * Get autoSize method
+ *
+ * @return string
+ */
+ public static function getAutoSizeMethod()
+ {
+ return self::$autoSizeMethod;
+ }
+
+ /**
+ * Set the path to the folder containing .ttf files. There should be a trailing slash.
+ * Typical locations on variout some platforms:
+ *
+ * - C:/Windows/Fonts/
+ * - /usr/share/fonts/truetype/
+ * - ~/.fonts/
+ *
+ *
+ * @param string $pValue
+ */
+ public static function setTrueTypeFontPath($pValue = '')
+ {
+ self::$trueTypeFontPath = $pValue;
+ }
+
+ /**
+ * Get the path to the folder containing .ttf files.
+ *
+ * @return string
+ */
+ public static function getTrueTypeFontPath()
+ {
+ return self::$trueTypeFontPath;
+ }
+
+ /**
+ * Calculate an (approximate) OpenXML column width, based on font size and text contained
+ *
+ * @param int $fontSize Font size (in pixels or points)
+ * @param bool $fontSizeInPixels Is the font size specified in pixels (true) or in points (false) ?
+ * @param string $cellText Text to calculate width
+ * @param int $rotation Rotation angle
+ * @return int Column width
+ */
+ public static function calculateColumnWidth(PHPExcel_Style_Font $font, $cellText = '', $rotation = 0, PHPExcel_Style_Font $defaultFont = null) {
+
+ // If it is rich text, use plain text
+ if ($cellText instanceof PHPExcel_RichText) {
+ $cellText = $cellText->getPlainText();
+ }
+
+ // Special case if there are one or more newline characters ("\n")
+ if (strpos($cellText, "\n") !== false) {
+ $lineTexts = explode("\n", $cellText);
+ $lineWitdhs = array();
+ foreach ($lineTexts as $lineText) {
+ $lineWidths[] = self::calculateColumnWidth($font, $lineText, $rotation = 0, $defaultFont);
+ }
+ return max($lineWidths); // width of longest line in cell
+ }
+
+ // Try to get the exact text width in pixels
+ try {
+ // If autosize method is set to 'approx', use approximation
+ if (self::$autoSizeMethod == self::AUTOSIZE_METHOD_APPROX) {
+ throw new Exception('AutoSize method is set to approx');
+ }
+
+ // Width of text in pixels excl. padding
+ $columnWidth = self::getTextWidthPixelsExact($cellText, $font, $rotation);
+
+ // Excel adds some padding, use 1.07 of the width of an 'n' glyph
+ $columnWidth += ceil(self::getTextWidthPixelsExact('0', $font, 0) * 1.07); // pixels incl. padding
+
+ } catch (Exception $e) {
+ // Width of text in pixels excl. padding, approximation
+ $columnWidth = self::getTextWidthPixelsApprox($cellText, $font, $rotation);
+
+ // Excel adds some padding, just use approx width of 'n' glyph
+ $columnWidth += self::getTextWidthPixelsApprox('n', $font, 0);
+ }
+
+ // Convert from pixel width to column width
+ $columnWidth = PHPExcel_Shared_Drawing::pixelsToCellDimension($columnWidth, $defaultFont);
+
+ // Return
+ return round($columnWidth, 6);
+ }
+
+ /**
+ * Get GD text width in pixels for a string of text in a certain font at a certain rotation angle
+ *
+ * @param string $text
+ * @param PHPExcel_Style_Font
+ * @param int $rotation
+ * @return int
+ * @throws Exception
+ */
+ public static function getTextWidthPixelsExact($text, PHPExcel_Style_Font $font, $rotation = 0) {
+ if (!function_exists('imagettfbbox')) {
+ throw new Exception('GD library needs to be enabled');
+ }
+
+ // font size should really be supplied in pixels in GD2,
+ // but since GD2 seems to assume 72dpi, pixels and points are the same
+ $fontFile = self::getTrueTypeFontFileFromFont($font);
+ $textBox = imagettfbbox($font->getSize(), $rotation, $fontFile, $text);
+
+ // Get corners positions
+ $lowerLeftCornerX = $textBox[0];
+ $lowerLeftCornerY = $textBox[1];
+ $lowerRightCornerX = $textBox[2];
+ $lowerRightCornerY = $textBox[3];
+ $upperRightCornerX = $textBox[4];
+ $upperRightCornerY = $textBox[5];
+ $upperLeftCornerX = $textBox[6];
+ $upperLeftCornerY = $textBox[7];
+
+ // Consider the rotation when calculating the width
+ $textWidth = max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
+
+ return $textWidth;
+ }
+
+ /**
+ * Get approximate width in pixels for a string of text in a certain font at a certain rotation angle
+ *
+ * @param string $columnText
+ * @param PHPExcel_Style_Font $font
+ * @param int $rotation
+ * @return int Text width in pixels (no padding added)
+ */
+ public static function getTextWidthPixelsApprox($columnText, PHPExcel_Style_Font $font = null, $rotation = 0)
+ {
+ $fontName = $font->getName();
+ $fontSize = $font->getSize();
+
+ // Calculate column width in pixels. We assume fixed glyph width. Result varies with font name and size.
+ switch ($fontName) {
+ case 'Calibri':
+ // value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font.
+ $columnWidth = (int) (8.26 * PHPExcel_Shared_String::CountCharacters($columnText));
+ $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
+ break;
+
+ case 'Arial':
+ // value 7 was found via interpolation by inspecting real Excel files with Arial 10 font.
+ $columnWidth = (int) (7 * PHPExcel_Shared_String::CountCharacters($columnText));
+ $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
+ break;
+
+ case 'Verdana':
+ // value 8 was found via interpolation by inspecting real Excel files with Verdana 10 font.
+ $columnWidth = (int) (8 * PHPExcel_Shared_String::CountCharacters($columnText));
+ $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
+ break;
+
+ default:
+ // just assume Calibri
+ $columnWidth = (int) (8.26 * PHPExcel_Shared_String::CountCharacters($columnText));
+ $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
+ break;
+ }
+
+ // Calculate approximate rotated column width
+ if ($rotation !== 0) {
+ if ($rotation == -165) {
+ // stacked text
+ $columnWidth = 4; // approximation
+ } else {
+ // rotated text
+ $columnWidth = $columnWidth * cos(deg2rad($rotation))
+ + $fontSize * abs(sin(deg2rad($rotation))) / 5; // approximation
+ }
+ }
+
+ // pixel width is an integer
+ $columnWidth = (int) $columnWidth;
+ return $columnWidth;
+ }
+
+ /**
+ * Calculate an (approximate) pixel size, based on a font points size
+ *
+ * @param int $fontSizeInPoints Font size (in points)
+ * @return int Font size (in pixels)
+ */
+ public static function fontSizeToPixels($fontSizeInPoints = 11) {
+ return (int) ((4 / 3) * $fontSizeInPoints);
+ }
+
+ /**
+ * Calculate an (approximate) pixel size, based on inch size
+ *
+ * @param int $sizeInInch Font size (in inch)
+ * @return int Size (in pixels)
+ */
+ public static function inchSizeToPixels($sizeInInch = 1) {
+ return ($sizeInInch * 96);
+ }
+
+ /**
+ * Calculate an (approximate) pixel size, based on centimeter size
+ *
+ * @param int $sizeInCm Font size (in centimeters)
+ * @return int Size (in pixels)
+ */
+ public static function centimeterSizeToPixels($sizeInCm = 1) {
+ return ($sizeInCm * 37.795275591);
+ }
+
+ /**
+ * Returns the font path given the font
+ *
+ * @param PHPExcel_Style_Font
+ * @return string Path to TrueType font file
+ */
+ public static function getTrueTypeFontFileFromFont($font) {
+ if (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath)) {
+ throw new Exception('Valid directory to TrueType Font files not specified');
+ }
+
+ $name = $font->getName();
+ $bold = $font->getBold();
+ $italic = $font->getItalic();
+
+ // Check if we can map font to true type font file
+ switch ($name) {
+ case 'Arial':
+ $fontFile = (
+ $bold ? ($italic ? self::ARIAL_BOLD_ITALIC : self::ARIAL_BOLD)
+ : ($italic ? self::ARIAL_ITALIC : self::ARIAL)
+ );
+ break;
+
+ case 'Calibri':
+ $fontFile = (
+ $bold ? ($italic ? self::CALIBRI_BOLD_ITALIC : self::CALIBRI_BOLD)
+ : ($italic ? self::CALIBRI_ITALIC : self::CALIBRI)
+ );
+ break;
+
+ case 'Courier New':
+ $fontFile = (
+ $bold ? ($italic ? self::COURIER_NEW_BOLD_ITALIC : self::COURIER_NEW_BOLD)
+ : ($italic ? self::COURIER_NEW_ITALIC : self::COURIER_NEW)
+ );
+ break;
+
+ case 'Comic Sans MS':
+ $fontFile = (
+ $bold ? self::COMIC_SANS_MS_BOLD : self::COMIC_SANS_MS
+ );
+ break;
+
+ case 'Georgia':
+ $fontFile = (
+ $bold ? ($italic ? self::GEORGIA_BOLD_ITALIC : self::GEORGIA_BOLD)
+ : ($italic ? self::GEORGIA_ITALIC : self::GEORGIA)
+ );
+ break;
+
+ case 'Impact':
+ $fontFile = self::IMPACT;
+ break;
+
+ case 'Liberation Sans':
+ $fontFile = (
+ $bold ? ($italic ? self::LIBERATION_SANS_BOLD_ITALIC : self::LIBERATION_SANS_BOLD)
+ : ($italic ? self::LIBERATION_SANS_ITALIC : self::LIBERATION_SANS)
+ );
+ break;
+
+ case 'Lucida Console':
+ $fontFile = self::LUCIDA_CONSOLE;
+ break;
+
+ case 'Lucida Sans Unicode':
+ $fontFile = self::LUCIDA_SANS_UNICODE;
+ break;
+
+ case 'Microsoft Sans Serif':
+ $fontFile = self::MICROSOFT_SANS_SERIF;
+ break;
+
+ case 'Palatino Linotype':
+ $fontFile = (
+ $bold ? ($italic ? self::PALATINO_LINOTYPE_BOLD_ITALIC : self::PALATINO_LINOTYPE_BOLD)
+ : ($italic ? self::PALATINO_LINOTYPE_ITALIC : self::PALATINO_LINOTYPE)
+ );
+ break;
+
+ case 'Symbol':
+ $fontFile = self::SYMBOL;
+ break;
+
+ case 'Tahoma':
+ $fontFile = (
+ $bold ? self::TAHOMA_BOLD : self::TAHOMA
+ );
+ break;
+
+ case 'Times New Roman':
+ $fontFile = (
+ $bold ? ($italic ? self::TIMES_NEW_ROMAN_BOLD_ITALIC : self::TIMES_NEW_ROMAN_BOLD)
+ : ($italic ? self::TIMES_NEW_ROMAN_ITALIC : self::TIMES_NEW_ROMAN)
+ );
+ break;
+
+ case 'Trebuchet MS':
+ $fontFile = (
+ $bold ? ($italic ? self::TREBUCHET_MS_BOLD_ITALIC : self::TREBUCHET_MS_BOLD)
+ : ($italic ? self::TREBUCHET_MS_ITALIC : self::TREBUCHET_MS)
+ );
+ break;
+
+ case 'Verdana':
+ $fontFile = (
+ $bold ? ($italic ? self::VERDANA_BOLD_ITALIC : self::VERDANA_BOLD)
+ : ($italic ? self::VERDANA_ITALIC : self::VERDANA)
+ );
+ break;
+
+ default:
+ throw new Exception('Unknown font name "'. $name .'". Cannot map to TrueType font file');
+ break;
+ }
+
+ $fontFile = self::$trueTypeFontPath . $fontFile;
+
+ // Check if file actually exists
+ if (!file_exists($fontFile)) {
+ throw New Exception('TrueType Font file not found');
+ }
+
+ return $fontFile;
+ }
+
+ /**
+ * Returns the associated charset for the font name.
+ *
+ * @param string $name Font name
+ * @return int Character set code
+ */
+ public static function getCharsetFromFontName($name)
+ {
+ switch ($name) {
+ // Add more cases. Check FONT records in real Excel files.
+ case 'EucrosiaUPC': return self::CHARSET_ANSI_THAI;
+ case 'Wingdings': return self::CHARSET_SYMBOL;
+ case 'Wingdings 2': return self::CHARSET_SYMBOL;
+ case 'Wingdings 3': return self::CHARSET_SYMBOL;
+ default: return self::CHARSET_ANSI_LATIN;
+ }
+ }
+
+ /**
+ * Get the effective column width for columns without a column dimension or column with width -1
+ * For example, for Calibri 11 this is 9.140625 (64 px)
+ *
+ * @param PHPExcel_Style_Font $font The workbooks default font
+ * @param boolean $pPixels true = return column width in pixels, false = return in OOXML units
+ * @return mixed Column width
+ */
+ public static function getDefaultColumnWidthByFont(PHPExcel_Style_Font $font, $pPixels = false)
+ {
+ if (isset(self::$defaultColumnWidths[$font->getName()][$font->getSize()])) {
+ // Exact width can be determined
+ $columnWidth = $pPixels ?
+ self::$defaultColumnWidths[$font->getName()][$font->getSize()]['px']
+ : self::$defaultColumnWidths[$font->getName()][$font->getSize()]['width'];
+
+ } else {
+ // We don't have data for this particular font and size, use approximation by
+ // extrapolating from Calibri 11
+ $columnWidth = $pPixels ?
+ self::$defaultColumnWidths['Calibri'][11]['px']
+ : self::$defaultColumnWidths['Calibri'][11]['width'];
+ $columnWidth = $columnWidth * $font->getSize() / 11;
+
+ // Round pixels to closest integer
+ if ($pPixels) {
+ $columnWidth = (int) round($columnWidth);
+ }
+ }
+
+ return $columnWidth;
+ }
+
+ /**
+ * Get the effective row height for rows without a row dimension or rows with height -1
+ * For example, for Calibri 11 this is 15 points
+ *
+ * @param PHPExcel_Style_Font $font The workbooks default font
+ * @return float Row height in points
+ */
+ public static function getDefaultRowHeightByFont(PHPExcel_Style_Font $font)
+ {
+ switch ($font->getName()) {
+ case 'Arial':
+ switch ($font->getSize()) {
+ case 10:
+ // inspection of Arial 10 workbook says 12.75pt ~17px
+ $rowHeight = 12.75;
+ break;
+
+ case 9:
+ // inspection of Arial 9 workbook says 12.00pt ~16px
+ $rowHeight = 12;
+ break;
+
+ case 8:
+ // inspection of Arial 8 workbook says 11.25pt ~15px
+ $rowHeight = 11.25;
+ break;
+
+ case 7:
+ // inspection of Arial 7 workbook says 9.00pt ~12px
+ $rowHeight = 9;
+ break;
+
+ case 6:
+ case 5:
+ // inspection of Arial 5,6 workbook says 8.25pt ~11px
+ $rowHeight = 8.25;
+ break;
+
+ case 4:
+ // inspection of Arial 4 workbook says 6.75pt ~9px
+ $rowHeight = 6.75;
+ break;
+
+ case 3:
+ // inspection of Arial 3 workbook says 6.00pt ~8px
+ $rowHeight = 6;
+ break;
+
+ case 2:
+ case 1:
+ // inspection of Arial 1,2 workbook says 5.25pt ~7px
+ $rowHeight = 5.25;
+ break;
+
+ default:
+ // use Arial 10 workbook as an approximation, extrapolation
+ $rowHeight = 12.75 * $font->getSize() / 10;
+ break;
+ }
+ break;
+
+ case 'Calibri':
+ switch ($font->getSize()) {
+ case 11:
+ // inspection of Calibri 11 workbook says 15.00pt ~20px
+ $rowHeight = 15;
+ break;
+
+ case 10:
+ // inspection of Calibri 10 workbook says 12.75pt ~17px
+ $rowHeight = 12.75;
+ break;
+
+ case 9:
+ // inspection of Calibri 9 workbook says 12.00pt ~16px
+ $rowHeight = 12;
+ break;
+
+ case 8:
+ // inspection of Calibri 8 workbook says 11.25pt ~15px
+ $rowHeight = 11.25;
+ break;
+
+ case 7:
+ // inspection of Calibri 7 workbook says 9.00pt ~12px
+ $rowHeight = 9;
+ break;
+
+ case 6:
+ case 5:
+ // inspection of Calibri 5,6 workbook says 8.25pt ~11px
+ $rowHeight = 8.25;
+ break;
+
+ case 4:
+ // inspection of Calibri 4 workbook says 6.75pt ~9px
+ $rowHeight = 6.75;
+ break;
+
+ case 3:
+ // inspection of Calibri 3 workbook says 6.00pt ~8px
+ $rowHeight = 6.00;
+ break;
+
+ case 2:
+ case 1:
+ // inspection of Calibri 1,2 workbook says 5.25pt ~7px
+ $rowHeight = 5.25;
+ break;
+
+ default:
+ // use Calibri 11 workbook as an approximation, extrapolation
+ $rowHeight = 15 * $font->getSize() / 11;
+ break;
+ }
+ break;
+
+ case 'Verdana':
+ switch ($font->getSize()) {
+ case 10:
+ // inspection of Verdana 10 workbook says 12.75pt ~17px
+ $rowHeight = 12.75;
+ break;
+
+ case 9:
+ // inspection of Verdana 9 workbook says 11.25pt ~15px
+ $rowHeight = 11.25;
+ break;
+
+ case 8:
+ // inspection of Verdana 8 workbook says 10.50pt ~14px
+ $rowHeight = 10.50;
+ break;
+
+ case 7:
+ // inspection of Verdana 7 workbook says 9.00pt ~12px
+ $rowHeight = 9.00;
+ break;
+
+ case 6:
+ case 5:
+ // inspection of Verdana 5,6 workbook says 8.25pt ~11px
+ $rowHeight = 8.25;
+ break;
+
+ case 4:
+ // inspection of Verdana 4 workbook says 6.75pt ~9px
+ $rowHeight = 6.75;
+ break;
+
+ case 3:
+ // inspection of Verdana 3 workbook says 6.00pt ~8px
+ $rowHeight = 6;
+ break;
+
+ case 2:
+ case 1:
+ // inspection of Verdana 1,2 workbook says 5.25pt ~7px
+ $rowHeight = 5.25;
+ break;
+
+ default:
+ // use Verdana 10 workbook as an approximation, extrapolation
+ $rowHeight = 12.75 * $font->getSize() / 10;
+ break;
+ }
+ break;
+
+ default:
+ // just use Calibri as an approximation
+ $rowHeight = 15 * $font->getSize() / 11;
+ break;
+ }
+
+ return $rowHeight;
+ }
+
+}
diff --git a/Classes/PHPExcel/Shared/JAMA/CHANGELOG.TXT b/Classes/PHPExcel/Shared/JAMA/CHANGELOG.TXT
new file mode 100644
index 0000000..1c18a5d
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/CHANGELOG.TXT
@@ -0,0 +1,16 @@
+Mar 1, 2005 11:15 AST by PM
+
++ For consistency, renamed Math.php to Maths.java, utils to util,
+ tests to test, docs to doc -
+
++ Removed conditional logic from top of Matrix class.
+
++ Switched to using hypo function in Maths.php for all php-hypot calls.
+ NOTE TO SELF: Need to make sure that all decompositions have been
+ switched over to using the bundled hypo.
+
+Feb 25, 2005 at 10:00 AST by PM
+
++ Recommend using simpler Error.php instead of JAMA_Error.php but
+ can be persuaded otherwise.
+
diff --git a/Classes/PHPExcel/Shared/JAMA/CholeskyDecomposition.php b/Classes/PHPExcel/Shared/JAMA/CholeskyDecomposition.php
new file mode 100644
index 0000000..9d064f9
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/CholeskyDecomposition.php
@@ -0,0 +1,149 @@
+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 ($k = $i+1; $k < $this->m; ++$k) {
+ $this->L[$i][$k] = 0.0;
+ }
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function __construct()
+
+
+ /**
+ * Is the matrix symmetric and positive definite?
+ *
+ * @return boolean
+ */
+ public function isSPD() {
+ return $this->isspd;
+ } // function isSPD()
+
+
+ /**
+ * getL
+ *
+ * Return triangular factor.
+ * @return Matrix Lower triangular matrix
+ */
+ public function getL() {
+ return new Matrix($this->L);
+ } // function getL()
+
+
+ /**
+ * Solve A*X = B
+ *
+ * @param $B Row-equal matrix
+ * @return Matrix L * L' * X = B
+ */
+ public function solve($B = null) {
+ if ($B instanceof Matrix) {
+ 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 ($j = 0; $j < $nx; ++$j) {
+ $X[$k][$j] /= $this->L[$k][$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];
+ }
+ }
+ }
+
+ return new Matrix($X, $this->m, $nx);
+ } else {
+ throw new Exception(JAMAError(MatrixSPDException));
+ }
+ } else {
+ throw new Exception(JAMAError(MatrixDimensionException));
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function solve()
+
+} // class CholeskyDecomposition
diff --git a/Classes/PHPExcel/Shared/JAMA/EigenvalueDecomposition.php b/Classes/PHPExcel/Shared/JAMA/EigenvalueDecomposition.php
new file mode 100644
index 0000000..2a696d0
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/EigenvalueDecomposition.php
@@ -0,0 +1,862 @@
+d = $this->V[$this->n-1];
+ // Householder reduction to tridiagonal form.
+ for ($i = $this->n-1; $i > 0; --$i) {
+ $i_ = $i -1;
+ // Scale to avoid under/overflow.
+ $h = $scale = 0.0;
+ $scale += array_sum(array_map(abs, $this->d));
+ if ($scale == 0.0) {
+ $this->e[$i] = $this->d[$i_];
+ $this->d = array_slice($this->V[$i_], 0, $i_);
+ for ($j = 0; $j < $i; ++$j) {
+ $this->V[$j][$i] = $this->V[$i][$j] = 0.0;
+ }
+ } else {
+ // Generate Householder vector.
+ for ($k = 0; $k < $i; ++$k) {
+ $this->d[$k] /= $scale;
+ $h += pow($this->d[$k], 2);
+ }
+ $f = $this->d[$i_];
+ $g = sqrt($h);
+ if ($f > 0) {
+ $g = -$g;
+ }
+ $this->e[$i] = $scale * $g;
+ $h = $h - $f * $g;
+ $this->d[$i_] = $f - $g;
+ for ($j = 0; $j < $i; ++$j) {
+ $this->e[$j] = 0.0;
+ }
+ // Apply similarity transformation to remaining columns.
+ for ($j = 0; $j < $i; ++$j) {
+ $f = $this->d[$j];
+ $this->V[$j][$i] = $f;
+ $g = $this->e[$j] + $this->V[$j][$j] * $f;
+ for ($k = $j+1; $k <= $i_; ++$k) {
+ $g += $this->V[$k][$j] * $this->d[$k];
+ $this->e[$k] += $this->V[$k][$j] * $f;
+ }
+ $this->e[$j] = $g;
+ }
+ $f = 0.0;
+ for ($j = 0; $j < $i; ++$j) {
+ $this->e[$j] /= $h;
+ $f += $this->e[$j] * $this->d[$j];
+ }
+ $hh = $f / (2 * $h);
+ for ($j=0; $j < $i; ++$j) {
+ $this->e[$j] -= $hh * $this->d[$j];
+ }
+ for ($j = 0; $j < $i; ++$j) {
+ $f = $this->d[$j];
+ $g = $this->e[$j];
+ for ($k = $j; $k <= $i_; ++$k) {
+ $this->V[$k][$j] -= ($f * $this->e[$k] + $g * $this->d[$k]);
+ }
+ $this->d[$j] = $this->V[$i-1][$j];
+ $this->V[$i][$j] = 0.0;
+ }
+ }
+ $this->d[$i] = $h;
+ }
+
+ // Accumulate transformations.
+ for ($i = 0; $i < $this->n-1; ++$i) {
+ $this->V[$this->n-1][$i] = $this->V[$i][$i];
+ $this->V[$i][$i] = 1.0;
+ $h = $this->d[$i+1];
+ if ($h != 0.0) {
+ for ($k = 0; $k <= $i; ++$k) {
+ $this->d[$k] = $this->V[$k][$i+1] / $h;
+ }
+ for ($j = 0; $j <= $i; ++$j) {
+ $g = 0.0;
+ for ($k = 0; $k <= $i; ++$k) {
+ $g += $this->V[$k][$i+1] * $this->V[$k][$j];
+ }
+ for ($k = 0; $k <= $i; ++$k) {
+ $this->V[$k][$j] -= $g * $this->d[$k];
+ }
+ }
+ }
+ for ($k = 0; $k <= $i; ++$k) {
+ $this->V[$k][$i+1] = 0.0;
+ }
+ }
+
+ $this->d = $this->V[$this->n-1];
+ $this->V[$this->n-1] = array_fill(0, $j, 0.0);
+ $this->V[$this->n-1][$this->n-1] = 1.0;
+ $this->e[0] = 0.0;
+ }
+
+
+ /**
+ * Symmetric tridiagonal QL algorithm.
+ *
+ * This is derived from the Algol procedures tql2, by
+ * Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ * Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ * Fortran subroutine in EISPACK.
+ *
+ * @access private
+ */
+ private function tql2() {
+ for ($i = 1; $i < $this->n; ++$i) {
+ $this->e[$i-1] = $this->e[$i];
+ }
+ $this->e[$this->n-1] = 0.0;
+ $f = 0.0;
+ $tst1 = 0.0;
+ $eps = pow(2.0,-52.0);
+
+ for ($l = 0; $l < $this->n; ++$l) {
+ // Find small subdiagonal element
+ $tst1 = max($tst1, abs($this->d[$l]) + abs($this->e[$l]));
+ $m = $l;
+ while ($m < $this->n) {
+ if (abs($this->e[$m]) <= $eps * $tst1)
+ break;
+ ++$m;
+ }
+ // If m == l, $this->d[l] is an eigenvalue,
+ // otherwise, iterate.
+ if ($m > $l) {
+ $iter = 0;
+ do {
+ // Could check iteration count here.
+ $iter += 1;
+ // Compute implicit shift
+ $g = $this->d[$l];
+ $p = ($this->d[$l+1] - $g) / (2.0 * $this->e[$l]);
+ $r = hypo($p, 1.0);
+ if ($p < 0)
+ $r *= -1;
+ $this->d[$l] = $this->e[$l] / ($p + $r);
+ $this->d[$l+1] = $this->e[$l] * ($p + $r);
+ $dl1 = $this->d[$l+1];
+ $h = $g - $this->d[$l];
+ for ($i = $l + 2; $i < $this->n; ++$i)
+ $this->d[$i] -= $h;
+ $f += $h;
+ // Implicit QL transformation.
+ $p = $this->d[$m];
+ $c = 1.0;
+ $c2 = $c3 = $c;
+ $el1 = $this->e[$l + 1];
+ $s = $s2 = 0.0;
+ for ($i = $m-1; $i >= $l; --$i) {
+ $c3 = $c2;
+ $c2 = $c;
+ $s2 = $s;
+ $g = $c * $this->e[$i];
+ $h = $c * $p;
+ $r = hypo($p, $this->e[$i]);
+ $this->e[$i+1] = $s * $r;
+ $s = $this->e[$i] / $r;
+ $c = $p / $r;
+ $p = $c * $this->d[$i] - $s * $g;
+ $this->d[$i+1] = $h + $s * ($c * $g + $s * $this->d[$i]);
+ // Accumulate transformation.
+ for ($k = 0; $k < $this->n; ++$k) {
+ $h = $this->V[$k][$i+1];
+ $this->V[$k][$i+1] = $s * $this->V[$k][$i] + $c * $h;
+ $this->V[$k][$i] = $c * $this->V[$k][$i] - $s * $h;
+ }
+ }
+ $p = -$s * $s2 * $c3 * $el1 * $this->e[$l] / $dl1;
+ $this->e[$l] = $s * $p;
+ $this->d[$l] = $c * $p;
+ // Check for convergence.
+ } while (abs($this->e[$l]) > $eps * $tst1);
+ }
+ $this->d[$l] = $this->d[$l] + $f;
+ $this->e[$l] = 0.0;
+ }
+
+ // Sort eigenvalues and corresponding vectors.
+ for ($i = 0; $i < $this->n - 1; ++$i) {
+ $k = $i;
+ $p = $this->d[$i];
+ for ($j = $i+1; $j < $this->n; ++$j) {
+ if ($this->d[$j] < $p) {
+ $k = $j;
+ $p = $this->d[$j];
+ }
+ }
+ if ($k != $i) {
+ $this->d[$k] = $this->d[$i];
+ $this->d[$i] = $p;
+ for ($j = 0; $j < $this->n; ++$j) {
+ $p = $this->V[$j][$i];
+ $this->V[$j][$i] = $this->V[$j][$k];
+ $this->V[$j][$k] = $p;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Nonsymmetric reduction to Hessenberg form.
+ *
+ * This is derived from the Algol procedures orthes and ortran,
+ * by Martin and Wilkinson, Handbook for Auto. Comp.,
+ * Vol.ii-Linear Algebra, and the corresponding
+ * Fortran subroutines in EISPACK.
+ *
+ * @access private
+ */
+ private function orthes () {
+ $low = 0;
+ $high = $this->n-1;
+
+ for ($m = $low+1; $m <= $high-1; ++$m) {
+ // Scale column.
+ $scale = 0.0;
+ for ($i = $m; $i <= $high; ++$i) {
+ $scale = $scale + abs($this->H[$i][$m-1]);
+ }
+ if ($scale != 0.0) {
+ // Compute Householder transformation.
+ $h = 0.0;
+ for ($i = $high; $i >= $m; --$i) {
+ $this->ort[$i] = $this->H[$i][$m-1] / $scale;
+ $h += $this->ort[$i] * $this->ort[$i];
+ }
+ $g = sqrt($h);
+ if ($this->ort[$m] > 0) {
+ $g *= -1;
+ }
+ $h -= $this->ort[$m] * $g;
+ $this->ort[$m] -= $g;
+ // Apply Householder similarity transformation
+ // H = (I -u * u' / h) * H * (I -u * u') / h)
+ for ($j = $m; $j < $this->n; ++$j) {
+ $f = 0.0;
+ for ($i = $high; $i >= $m; --$i) {
+ $f += $this->ort[$i] * $this->H[$i][$j];
+ }
+ $f /= $h;
+ for ($i = $m; $i <= $high; ++$i) {
+ $this->H[$i][$j] -= $f * $this->ort[$i];
+ }
+ }
+ for ($i = 0; $i <= $high; ++$i) {
+ $f = 0.0;
+ for ($j = $high; $j >= $m; --$j) {
+ $f += $this->ort[$j] * $this->H[$i][$j];
+ }
+ $f = $f / $h;
+ for ($j = $m; $j <= $high; ++$j) {
+ $this->H[$i][$j] -= $f * $this->ort[$j];
+ }
+ }
+ $this->ort[$m] = $scale * $this->ort[$m];
+ $this->H[$m][$m-1] = $scale * $g;
+ }
+ }
+
+ // Accumulate transformations (Algol's ortran).
+ for ($i = 0; $i < $this->n; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ $this->V[$i][$j] = ($i == $j ? 1.0 : 0.0);
+ }
+ }
+ for ($m = $high-1; $m >= $low+1; --$m) {
+ if ($this->H[$m][$m-1] != 0.0) {
+ for ($i = $m+1; $i <= $high; ++$i) {
+ $this->ort[$i] = $this->H[$i][$m-1];
+ }
+ for ($j = $m; $j <= $high; ++$j) {
+ $g = 0.0;
+ for ($i = $m; $i <= $high; ++$i) {
+ $g += $this->ort[$i] * $this->V[$i][$j];
+ }
+ // Double division avoids possible underflow
+ $g = ($g / $this->ort[$m]) / $this->H[$m][$m-1];
+ for ($i = $m; $i <= $high; ++$i) {
+ $this->V[$i][$j] += $g * $this->ort[$i];
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Performs complex division.
+ *
+ * @access private
+ */
+ private function cdiv($xr, $xi, $yr, $yi) {
+ if (abs($yr) > abs($yi)) {
+ $r = $yi / $yr;
+ $d = $yr + $r * $yi;
+ $this->cdivr = ($xr + $r * $xi) / $d;
+ $this->cdivi = ($xi - $r * $xr) / $d;
+ } else {
+ $r = $yr / $yi;
+ $d = $yi + $r * $yr;
+ $this->cdivr = ($r * $xr + $xi) / $d;
+ $this->cdivi = ($r * $xi - $xr) / $d;
+ }
+ }
+
+
+ /**
+ * Nonsymmetric reduction from Hessenberg to real Schur form.
+ *
+ * Code is derived from the Algol procedure hqr2,
+ * by Martin and Wilkinson, Handbook for Auto. Comp.,
+ * Vol.ii-Linear Algebra, and the corresponding
+ * Fortran subroutine in EISPACK.
+ *
+ * @access private
+ */
+ private function hqr2 () {
+ // Initialize
+ $nn = $this->n;
+ $n = $nn - 1;
+ $low = 0;
+ $high = $nn - 1;
+ $eps = pow(2.0, -52.0);
+ $exshift = 0.0;
+ $p = $q = $r = $s = $z = 0;
+ // Store roots isolated by balanc and compute matrix norm
+ $norm = 0.0;
+
+ for ($i = 0; $i < $nn; ++$i) {
+ if (($i < $low) OR ($i > $high)) {
+ $this->d[$i] = $this->H[$i][$i];
+ $this->e[$i] = 0.0;
+ }
+ for ($j = max($i-1, 0); $j < $nn; ++$j) {
+ $norm = $norm + abs($this->H[$i][$j]);
+ }
+ }
+
+ // Outer loop over eigenvalue index
+ $iter = 0;
+ while ($n >= $low) {
+ // Look for single small sub-diagonal element
+ $l = $n;
+ while ($l > $low) {
+ $s = abs($this->H[$l-1][$l-1]) + abs($this->H[$l][$l]);
+ if ($s == 0.0) {
+ $s = $norm;
+ }
+ if (abs($this->H[$l][$l-1]) < $eps * $s) {
+ break;
+ }
+ --$l;
+ }
+ // Check for convergence
+ // One root found
+ if ($l == $n) {
+ $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
+ $this->d[$n] = $this->H[$n][$n];
+ $this->e[$n] = 0.0;
+ --$n;
+ $iter = 0;
+ // Two roots found
+ } else if ($l == $n-1) {
+ $w = $this->H[$n][$n-1] * $this->H[$n-1][$n];
+ $p = ($this->H[$n-1][$n-1] - $this->H[$n][$n]) / 2.0;
+ $q = $p * $p + $w;
+ $z = sqrt(abs($q));
+ $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
+ $this->H[$n-1][$n-1] = $this->H[$n-1][$n-1] + $exshift;
+ $x = $this->H[$n][$n];
+ // Real pair
+ if ($q >= 0) {
+ if ($p >= 0) {
+ $z = $p + $z;
+ } else {
+ $z = $p - $z;
+ }
+ $this->d[$n-1] = $x + $z;
+ $this->d[$n] = $this->d[$n-1];
+ if ($z != 0.0) {
+ $this->d[$n] = $x - $w / $z;
+ }
+ $this->e[$n-1] = 0.0;
+ $this->e[$n] = 0.0;
+ $x = $this->H[$n][$n-1];
+ $s = abs($x) + abs($z);
+ $p = $x / $s;
+ $q = $z / $s;
+ $r = sqrt($p * $p + $q * $q);
+ $p = $p / $r;
+ $q = $q / $r;
+ // Row modification
+ for ($j = $n-1; $j < $nn; ++$j) {
+ $z = $this->H[$n-1][$j];
+ $this->H[$n-1][$j] = $q * $z + $p * $this->H[$n][$j];
+ $this->H[$n][$j] = $q * $this->H[$n][$j] - $p * $z;
+ }
+ // Column modification
+ for ($i = 0; $i <= n; ++$i) {
+ $z = $this->H[$i][$n-1];
+ $this->H[$i][$n-1] = $q * $z + $p * $this->H[$i][$n];
+ $this->H[$i][$n] = $q * $this->H[$i][$n] - $p * $z;
+ }
+ // Accumulate transformations
+ for ($i = $low; $i <= $high; ++$i) {
+ $z = $this->V[$i][$n-1];
+ $this->V[$i][$n-1] = $q * $z + $p * $this->V[$i][$n];
+ $this->V[$i][$n] = $q * $this->V[$i][$n] - $p * $z;
+ }
+ // Complex pair
+ } else {
+ $this->d[$n-1] = $x + $p;
+ $this->d[$n] = $x + $p;
+ $this->e[$n-1] = $z;
+ $this->e[$n] = -$z;
+ }
+ $n = $n - 2;
+ $iter = 0;
+ // No convergence yet
+ } else {
+ // Form shift
+ $x = $this->H[$n][$n];
+ $y = 0.0;
+ $w = 0.0;
+ if ($l < $n) {
+ $y = $this->H[$n-1][$n-1];
+ $w = $this->H[$n][$n-1] * $this->H[$n-1][$n];
+ }
+ // Wilkinson's original ad hoc shift
+ if ($iter == 10) {
+ $exshift += $x;
+ for ($i = $low; $i <= $n; ++$i) {
+ $this->H[$i][$i] -= $x;
+ }
+ $s = abs($this->H[$n][$n-1]) + abs($this->H[$n-1][$n-2]);
+ $x = $y = 0.75 * $s;
+ $w = -0.4375 * $s * $s;
+ }
+ // MATLAB's new ad hoc shift
+ if ($iter == 30) {
+ $s = ($y - $x) / 2.0;
+ $s = $s * $s + $w;
+ if ($s > 0) {
+ $s = sqrt($s);
+ if ($y < $x) {
+ $s = -$s;
+ }
+ $s = $x - $w / (($y - $x) / 2.0 + $s);
+ for ($i = $low; $i <= $n; ++$i) {
+ $this->H[$i][$i] -= $s;
+ }
+ $exshift += $s;
+ $x = $y = $w = 0.964;
+ }
+ }
+ // Could check iteration count here.
+ $iter = $iter + 1;
+ // Look for two consecutive small sub-diagonal elements
+ $m = $n - 2;
+ while ($m >= $l) {
+ $z = $this->H[$m][$m];
+ $r = $x - $z;
+ $s = $y - $z;
+ $p = ($r * $s - $w) / $this->H[$m+1][$m] + $this->H[$m][$m+1];
+ $q = $this->H[$m+1][$m+1] - $z - $r - $s;
+ $r = $this->H[$m+2][$m+1];
+ $s = abs($p) + abs($q) + abs($r);
+ $p = $p / $s;
+ $q = $q / $s;
+ $r = $r / $s;
+ if ($m == $l) {
+ break;
+ }
+ if (abs($this->H[$m][$m-1]) * (abs($q) + abs($r)) <
+ $eps * (abs($p) * (abs($this->H[$m-1][$m-1]) + abs($z) + abs($this->H[$m+1][$m+1])))) {
+ break;
+ }
+ --$m;
+ }
+ for ($i = $m + 2; $i <= $n; ++$i) {
+ $this->H[$i][$i-2] = 0.0;
+ if ($i > $m+2) {
+ $this->H[$i][$i-3] = 0.0;
+ }
+ }
+ // Double QR step involving rows l:n and columns m:n
+ for ($k = $m; $k <= $n-1; ++$k) {
+ $notlast = ($k != $n-1);
+ if ($k != $m) {
+ $p = $this->H[$k][$k-1];
+ $q = $this->H[$k+1][$k-1];
+ $r = ($notlast ? $this->H[$k+2][$k-1] : 0.0);
+ $x = abs($p) + abs($q) + abs($r);
+ if ($x != 0.0) {
+ $p = $p / $x;
+ $q = $q / $x;
+ $r = $r / $x;
+ }
+ }
+ if ($x == 0.0) {
+ break;
+ }
+ $s = sqrt($p * $p + $q * $q + $r * $r);
+ if ($p < 0) {
+ $s = -$s;
+ }
+ if ($s != 0) {
+ if ($k != $m) {
+ $this->H[$k][$k-1] = -$s * $x;
+ } elseif ($l != $m) {
+ $this->H[$k][$k-1] = -$this->H[$k][$k-1];
+ }
+ $p = $p + $s;
+ $x = $p / $s;
+ $y = $q / $s;
+ $z = $r / $s;
+ $q = $q / $p;
+ $r = $r / $p;
+ // Row modification
+ for ($j = $k; $j < $nn; ++$j) {
+ $p = $this->H[$k][$j] + $q * $this->H[$k+1][$j];
+ if ($notlast) {
+ $p = $p + $r * $this->H[$k+2][$j];
+ $this->H[$k+2][$j] = $this->H[$k+2][$j] - $p * $z;
+ }
+ $this->H[$k][$j] = $this->H[$k][$j] - $p * $x;
+ $this->H[$k+1][$j] = $this->H[$k+1][$j] - $p * $y;
+ }
+ // Column modification
+ for ($i = 0; $i <= min($n, $k+3); ++$i) {
+ $p = $x * $this->H[$i][$k] + $y * $this->H[$i][$k+1];
+ if ($notlast) {
+ $p = $p + $z * $this->H[$i][$k+2];
+ $this->H[$i][$k+2] = $this->H[$i][$k+2] - $p * $r;
+ }
+ $this->H[$i][$k] = $this->H[$i][$k] - $p;
+ $this->H[$i][$k+1] = $this->H[$i][$k+1] - $p * $q;
+ }
+ // Accumulate transformations
+ for ($i = $low; $i <= $high; ++$i) {
+ $p = $x * $this->V[$i][$k] + $y * $this->V[$i][$k+1];
+ if ($notlast) {
+ $p = $p + $z * $this->V[$i][$k+2];
+ $this->V[$i][$k+2] = $this->V[$i][$k+2] - $p * $r;
+ }
+ $this->V[$i][$k] = $this->V[$i][$k] - $p;
+ $this->V[$i][$k+1] = $this->V[$i][$k+1] - $p * $q;
+ }
+ } // ($s != 0)
+ } // k loop
+ } // check convergence
+ } // while ($n >= $low)
+
+ // Backsubstitute to find vectors of upper triangular form
+ if ($norm == 0.0) {
+ return;
+ }
+
+ for ($n = $nn-1; $n >= 0; --$n) {
+ $p = $this->d[$n];
+ $q = $this->e[$n];
+ // Real vector
+ if ($q == 0) {
+ $l = $n;
+ $this->H[$n][$n] = 1.0;
+ for ($i = $n-1; $i >= 0; --$i) {
+ $w = $this->H[$i][$i] - $p;
+ $r = 0.0;
+ for ($j = $l; $j <= $n; ++$j) {
+ $r = $r + $this->H[$i][$j] * $this->H[$j][$n];
+ }
+ if ($this->e[$i] < 0.0) {
+ $z = $w;
+ $s = $r;
+ } else {
+ $l = $i;
+ if ($this->e[$i] == 0.0) {
+ if ($w != 0.0) {
+ $this->H[$i][$n] = -$r / $w;
+ } else {
+ $this->H[$i][$n] = -$r / ($eps * $norm);
+ }
+ // Solve real equations
+ } else {
+ $x = $this->H[$i][$i+1];
+ $y = $this->H[$i+1][$i];
+ $q = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i];
+ $t = ($x * $s - $z * $r) / $q;
+ $this->H[$i][$n] = $t;
+ if (abs($x) > abs($z)) {
+ $this->H[$i+1][$n] = (-$r - $w * $t) / $x;
+ } else {
+ $this->H[$i+1][$n] = (-$s - $y * $t) / $z;
+ }
+ }
+ // Overflow control
+ $t = abs($this->H[$i][$n]);
+ if (($eps * $t) * $t > 1) {
+ for ($j = $i; $j <= $n; ++$j) {
+ $this->H[$j][$n] = $this->H[$j][$n] / $t;
+ }
+ }
+ }
+ }
+ // Complex vector
+ } else if ($q < 0) {
+ $l = $n-1;
+ // Last vector component imaginary so matrix is triangular
+ if (abs($this->H[$n][$n-1]) > abs($this->H[$n-1][$n])) {
+ $this->H[$n-1][$n-1] = $q / $this->H[$n][$n-1];
+ $this->H[$n-1][$n] = -($this->H[$n][$n] - $p) / $this->H[$n][$n-1];
+ } else {
+ $this->cdiv(0.0, -$this->H[$n-1][$n], $this->H[$n-1][$n-1] - $p, $q);
+ $this->H[$n-1][$n-1] = $this->cdivr;
+ $this->H[$n-1][$n] = $this->cdivi;
+ }
+ $this->H[$n][$n-1] = 0.0;
+ $this->H[$n][$n] = 1.0;
+ for ($i = $n-2; $i >= 0; --$i) {
+ // double ra,sa,vr,vi;
+ $ra = 0.0;
+ $sa = 0.0;
+ for ($j = $l; $j <= $n; ++$j) {
+ $ra = $ra + $this->H[$i][$j] * $this->H[$j][$n-1];
+ $sa = $sa + $this->H[$i][$j] * $this->H[$j][$n];
+ }
+ $w = $this->H[$i][$i] - $p;
+ if ($this->e[$i] < 0.0) {
+ $z = $w;
+ $r = $ra;
+ $s = $sa;
+ } else {
+ $l = $i;
+ if ($this->e[$i] == 0) {
+ $this->cdiv(-$ra, -$sa, $w, $q);
+ $this->H[$i][$n-1] = $this->cdivr;
+ $this->H[$i][$n] = $this->cdivi;
+ } else {
+ // Solve complex equations
+ $x = $this->H[$i][$i+1];
+ $y = $this->H[$i+1][$i];
+ $vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
+ $vi = ($this->d[$i] - $p) * 2.0 * $q;
+ if ($vr == 0.0 & $vi == 0.0) {
+ $vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
+ }
+ $this->cdiv($x * $r - $z * $ra + $q * $sa, $x * $s - $z * $sa - $q * $ra, $vr, $vi);
+ $this->H[$i][$n-1] = $this->cdivr;
+ $this->H[$i][$n] = $this->cdivi;
+ if (abs($x) > (abs($z) + abs($q))) {
+ $this->H[$i+1][$n-1] = (-$ra - $w * $this->H[$i][$n-1] + $q * $this->H[$i][$n]) / $x;
+ $this->H[$i+1][$n] = (-$sa - $w * $this->H[$i][$n] - $q * $this->H[$i][$n-1]) / $x;
+ } else {
+ $this->cdiv(-$r - $y * $this->H[$i][$n-1], -$s - $y * $this->H[$i][$n], $z, $q);
+ $this->H[$i+1][$n-1] = $this->cdivr;
+ $this->H[$i+1][$n] = $this->cdivi;
+ }
+ }
+ // Overflow control
+ $t = max(abs($this->H[$i][$n-1]),abs($this->H[$i][$n]));
+ if (($eps * $t) * $t > 1) {
+ for ($j = $i; $j <= $n; ++$j) {
+ $this->H[$j][$n-1] = $this->H[$j][$n-1] / $t;
+ $this->H[$j][$n] = $this->H[$j][$n] / $t;
+ }
+ }
+ } // end else
+ } // end for
+ } // end else for complex case
+ } // end for
+
+ // Vectors of isolated roots
+ for ($i = 0; $i < $nn; ++$i) {
+ if ($i < $low | $i > $high) {
+ for ($j = $i; $j < $nn; ++$j) {
+ $this->V[$i][$j] = $this->H[$i][$j];
+ }
+ }
+ }
+
+ // Back transformation to get eigenvectors of original matrix
+ for ($j = $nn-1; $j >= $low; --$j) {
+ for ($i = $low; $i <= $high; ++$i) {
+ $z = 0.0;
+ for ($k = $low; $k <= min($j,$high); ++$k) {
+ $z = $z + $this->V[$i][$k] * $this->H[$k][$j];
+ }
+ $this->V[$i][$j] = $z;
+ }
+ }
+ } // end hqr2
+
+
+ /**
+ * Constructor: Check for symmetry, then construct the eigenvalue decomposition
+ *
+ * @access public
+ * @param A Square matrix
+ * @return Structure to access D and V.
+ */
+ public function __construct($Arg) {
+ $this->A = $Arg->getArray();
+ $this->n = $Arg->getColumnDimension();
+
+ $issymmetric = true;
+ for ($j = 0; ($j < $this->n) & $issymmetric; ++$j) {
+ for ($i = 0; ($i < $this->n) & $issymmetric; ++$i) {
+ $issymmetric = ($this->A[$i][$j] == $this->A[$j][$i]);
+ }
+ }
+
+ if ($issymmetric) {
+ $this->V = $this->A;
+ // Tridiagonalize.
+ $this->tred2();
+ // Diagonalize.
+ $this->tql2();
+ } else {
+ $this->H = $this->A;
+ $this->ort = array();
+ // Reduce to Hessenberg form.
+ $this->orthes();
+ // Reduce Hessenberg to real Schur form.
+ $this->hqr2();
+ }
+ }
+
+
+ /**
+ * Return the eigenvector matrix
+ *
+ * @access public
+ * @return V
+ */
+ public function getV() {
+ return new Matrix($this->V, $this->n, $this->n);
+ }
+
+
+ /**
+ * Return the real parts of the eigenvalues
+ *
+ * @access public
+ * @return real(diag(D))
+ */
+ public function getRealEigenvalues() {
+ return $this->d;
+ }
+
+
+ /**
+ * Return the imaginary parts of the eigenvalues
+ *
+ * @access public
+ * @return imag(diag(D))
+ */
+ public function getImagEigenvalues() {
+ return $this->e;
+ }
+
+
+ /**
+ * Return the block diagonal eigenvalue matrix
+ *
+ * @access public
+ * @return D
+ */
+ public function getD() {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $D[$i] = array_fill(0, $this->n, 0.0);
+ $D[$i][$i] = $this->d[$i];
+ if ($this->e[$i] == 0) {
+ continue;
+ }
+ $o = ($this->e[$i] > 0) ? $i + 1 : $i - 1;
+ $D[$i][$o] = $this->e[$i];
+ }
+ return new Matrix($D);
+ }
+
+} // class EigenvalueDecomposition
diff --git a/Classes/PHPExcel/Shared/JAMA/LUDecomposition.php b/Classes/PHPExcel/Shared/JAMA/LUDecomposition.php
new file mode 100644
index 0000000..4fd43f9
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/LUDecomposition.php
@@ -0,0 +1,255 @@
+= n, the LU decomposition is an m-by-n
+ * unit lower triangular matrix L, an n-by-n upper triangular matrix U,
+ * and a permutation vector piv of length m so that A(piv,:) = L*U.
+ * If m < n, then L is m-by-m and U is m-by-n.
+ *
+ * The LU decompostion with pivoting always exists, even if the matrix is
+ * singular, so the constructor will never fail. The primary use of the
+ * LU decomposition is in the solution of square systems of simultaneous
+ * linear equations. This will fail if isNonsingular() returns false.
+ *
+ * @author Paul Meagher
+ * @author Bartosz Matosiuk
+ * @author Michael Bommarito
+ * @version 1.1
+ * @license PHP v3.0
+ */
+class LUDecomposition {
+
+ /**
+ * Decomposition storage
+ * @var array
+ */
+ private $LU = array();
+
+ /**
+ * Row dimension.
+ * @var int
+ */
+ private $m;
+
+ /**
+ * Column dimension.
+ * @var int
+ */
+ private $n;
+
+ /**
+ * Pivot sign.
+ * @var int
+ */
+ private $pivsign;
+
+ /**
+ * Internal storage of pivot vector.
+ * @var array
+ */
+ private $piv = array();
+
+
+ /**
+ * LU Decomposition constructor.
+ *
+ * @param $A Rectangular matrix
+ * @return Structure to access L, U and piv.
+ */
+ public function __construct($A) {
+ if ($A instanceof Matrix) {
+ // Use a "left-looking", dot-product, Crout/Doolittle algorithm.
+ $this->LU = $A->getArrayCopy();
+ $this->m = $A->getRowDimension();
+ $this->n = $A->getColumnDimension();
+ for ($i = 0; $i < $this->m; ++$i) {
+ $this->piv[$i] = $i;
+ }
+ $this->pivsign = 1;
+ $LUrowi = $LUcolj = array();
+
+ // Outer loop.
+ for ($j = 0; $j < $this->n; ++$j) {
+ // Make a copy of the j-th column to localize references.
+ for ($i = 0; $i < $this->m; ++$i) {
+ $LUcolj[$i] = &$this->LU[$i][$j];
+ }
+ // Apply previous transformations.
+ for ($i = 0; $i < $this->m; ++$i) {
+ $LUrowi = $this->LU[$i];
+ // Most of the time is spent in the following dot product.
+ $kmax = min($i,$j);
+ $s = 0.0;
+ for ($k = 0; $k < $kmax; ++$k) {
+ $s += $LUrowi[$k] * $LUcolj[$k];
+ }
+ $LUrowi[$j] = $LUcolj[$i] -= $s;
+ }
+ // Find pivot and exchange if necessary.
+ $p = $j;
+ for ($i = $j+1; $i < $this->m; ++$i) {
+ if (abs($LUcolj[$i]) > abs($LUcolj[$p])) {
+ $p = $i;
+ }
+ }
+ if ($p != $j) {
+ for ($k = 0; $k < $this->n; ++$k) {
+ $t = $this->LU[$p][$k];
+ $this->LU[$p][$k] = $this->LU[$j][$k];
+ $this->LU[$j][$k] = $t;
+ }
+ $k = $this->piv[$p];
+ $this->piv[$p] = $this->piv[$j];
+ $this->piv[$j] = $k;
+ $this->pivsign = $this->pivsign * -1;
+ }
+ // Compute multipliers.
+ if (($j < $this->m) && ($this->LU[$j][$j] != 0.0)) {
+ for ($i = $j+1; $i < $this->m; ++$i) {
+ $this->LU[$i][$j] /= $this->LU[$j][$j];
+ }
+ }
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function __construct()
+
+
+ /**
+ * Get lower triangular factor.
+ *
+ * @return array Lower triangular factor
+ */
+ public function getL() {
+ for ($i = 0; $i < $this->m; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ if ($i > $j) {
+ $L[$i][$j] = $this->LU[$i][$j];
+ } elseif ($i == $j) {
+ $L[$i][$j] = 1.0;
+ } else {
+ $L[$i][$j] = 0.0;
+ }
+ }
+ }
+ return new Matrix($L);
+ } // function getL()
+
+
+ /**
+ * Get upper triangular factor.
+ *
+ * @return array Upper triangular factor
+ */
+ public function getU() {
+ for ($i = 0; $i < $this->n; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ if ($i <= $j) {
+ $U[$i][$j] = $this->LU[$i][$j];
+ } else {
+ $U[$i][$j] = 0.0;
+ }
+ }
+ }
+ return new Matrix($U);
+ } // function getU()
+
+
+ /**
+ * Return pivot permutation vector.
+ *
+ * @return array Pivot vector
+ */
+ public function getPivot() {
+ return $this->piv;
+ } // function getPivot()
+
+
+ /**
+ * Alias for getPivot
+ *
+ * @see getPivot
+ */
+ public function getDoublePivot() {
+ return $this->getPivot();
+ } // function getDoublePivot()
+
+
+ /**
+ * Is the matrix nonsingular?
+ *
+ * @return true if U, and hence A, is nonsingular.
+ */
+ public function isNonsingular() {
+ for ($j = 0; $j < $this->n; ++$j) {
+ if ($this->LU[$j][$j] == 0) {
+ return false;
+ }
+ }
+ return true;
+ } // function isNonsingular()
+
+
+ /**
+ * Count determinants
+ *
+ * @return array d matrix deterninat
+ */
+ public function det() {
+ if ($this->m == $this->n) {
+ $d = $this->pivsign;
+ for ($j = 0; $j < $this->n; ++$j) {
+ $d *= $this->LU[$j][$j];
+ }
+ return $d;
+ } else {
+ throw new Exception(JAMAError(MatrixDimensionException));
+ }
+ } // function det()
+
+
+ /**
+ * Solve A*X = B
+ *
+ * @param $B A Matrix with as many rows as A and any number of columns.
+ * @return X so that L*U*X = B(piv,:)
+ * @exception IllegalArgumentException Matrix row dimensions must agree.
+ * @exception RuntimeException Matrix is singular.
+ */
+ public function solve($B) {
+ if ($B->getRowDimension() == $this->m) {
+ if ($this->isNonsingular()) {
+ // Copy right hand side with pivoting
+ $nx = $B->getColumnDimension();
+ $X = $B->getMatrix($this->piv, 0, $nx-1);
+ // Solve L*Y = B(piv,:)
+ for ($k = 0; $k < $this->n; ++$k) {
+ for ($i = $k+1; $i < $this->n; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
+ }
+ }
+ }
+ // Solve U*X = Y;
+ for ($k = $this->n-1; $k >= 0; --$k) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X->A[$k][$j] /= $this->LU[$k][$k];
+ }
+ for ($i = 0; $i < $k; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
+ }
+ }
+ }
+ return $X;
+ } else {
+ throw new Exception(JAMAError(MatrixSingularException));
+ }
+ } else {
+ throw new Exception(JAMAError(MatrixSquareException));
+ }
+ } // function solve()
+
+} // class LUDecomposition
diff --git a/Classes/PHPExcel/Shared/JAMA/Matrix.php b/Classes/PHPExcel/Shared/JAMA/Matrix.php
new file mode 100644
index 0000000..c168567
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/Matrix.php
@@ -0,0 +1,1445 @@
+ 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ //Square matrix - n x n
+ case 'integer':
+ $this->m = $args[0];
+ $this->n = $args[0];
+ $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
+ break;
+ //Rectangular matrix - m x n
+ case 'integer,integer':
+ $this->m = $args[0];
+ $this->n = $args[1];
+ $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
+ break;
+ //Rectangular matrix constant-filled - m x n filled with c
+ case 'integer,integer,integer':
+ $this->m = $args[0];
+ $this->n = $args[1];
+ $this->A = array_fill(0, $this->m, array_fill(0, $this->n, $args[2]));
+ break;
+ //Rectangular matrix constant-filled - m x n filled with c
+ case 'integer,integer,double':
+ $this->m = $args[0];
+ $this->n = $args[1];
+ $this->A = array_fill(0, $this->m, array_fill(0, $this->n, $args[2]));
+ break;
+ //Rectangular matrix - m x n initialized from 2D array
+ case 'array':
+ $this->m = count($args[0]);
+ $this->n = count($args[0][0]);
+ $this->A = $args[0];
+ break;
+ //Rectangular matrix - m x n initialized from 2D array
+ case 'array,integer,integer':
+ $this->m = $args[1];
+ $this->n = $args[2];
+ $this->A = $args[0];
+ break;
+ //Rectangular matrix - m x n initialized from packed array
+ case 'array,integer':
+ $this->m = $args[1];
+ if ($this->m != 0) {
+ $this->n = count($args[0]) / $this->m;
+ } else {
+ $this->n = 0;
+ }
+ if (($this->m * $this->n) == count($args[0])) {
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $this->A[$i][$j] = $args[0][$i + $j * $this->m];
+ }
+ }
+ } else {
+ throw new Exception(JAMAError(ArrayLengthException));
+ }
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function __construct()
+
+
+ /**
+ * getArray
+ *
+ * @return array Matrix array
+ */
+ public function getArray() {
+ return $this->A;
+ } // function getArray()
+
+
+ /**
+ * getArrayCopy
+ *
+ * @return array Matrix array copy
+ */
+ public function getArrayCopy() {
+ return $this->A;
+ } // function getArrayCopy()
+
+
+ /**
+ * constructWithCopy
+ * Construct a matrix from a copy of a 2-D array.
+ *
+ * @param double A[][] Two-dimensional array of doubles.
+ * @exception IllegalArgumentException All rows must have the same length
+ */
+ public function constructWithCopy($A) {
+ $this->m = count($A);
+ $this->n = count($A[0]);
+ $newCopyMatrix = new Matrix($this->m, $this->n);
+ for ($i = 0; $i < $this->m; ++$i) {
+ if (count($A[$i]) != $this->n) {
+ throw new Exception(JAMAError(RowLengthException));
+ }
+ for ($j = 0; $j < $this->n; ++$j) {
+ $newCopyMatrix->A[$i][$j] = $A[$i][$j];
+ }
+ }
+ return $newCopyMatrix;
+ } // function constructWithCopy()
+
+
+ /**
+ * getColumnPackedCopy
+ *
+ * Get a column-packed array
+ * @return array Column-packed matrix array
+ */
+ public function getColumnPackedCopy() {
+ $P = array();
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ array_push($P, $this->A[$j][$i]);
+ }
+ }
+ return $P;
+ } // function getColumnPackedCopy()
+
+
+ /**
+ * getRowPackedCopy
+ *
+ * Get a row-packed array
+ * @return array Row-packed matrix array
+ */
+ public function getRowPackedCopy() {
+ $P = array();
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ array_push($P, $this->A[$i][$j]);
+ }
+ }
+ return $P;
+ } // function getRowPackedCopy()
+
+
+ /**
+ * getRowDimension
+ *
+ * @return int Row dimension
+ */
+ public function getRowDimension() {
+ return $this->m;
+ } // function getRowDimension()
+
+
+ /**
+ * getColumnDimension
+ *
+ * @return int Column dimension
+ */
+ public function getColumnDimension() {
+ return $this->n;
+ } // function getColumnDimension()
+
+
+ /**
+ * get
+ *
+ * Get the i,j-th element of the matrix.
+ * @param int $i Row position
+ * @param int $j Column position
+ * @return mixed Element (int/float/double)
+ */
+ public function get($i = null, $j = null) {
+ return $this->A[$i][$j];
+ } // function get()
+
+
+ /**
+ * getMatrix
+ *
+ * Get a submatrix
+ * @param int $i0 Initial row index
+ * @param int $iF Final row index
+ * @param int $j0 Initial column index
+ * @param int $jF Final column index
+ * @return Matrix Submatrix
+ */
+ public function getMatrix() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ //A($i0...; $j0...)
+ case 'integer,integer':
+ list($i0, $j0) = $args;
+ if ($i0 >= 0) { $m = $this->m - $i0; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if ($j0 >= 0) { $n = $this->n - $j0; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ $R = new Matrix($m, $n);
+ for($i = $i0; $i < $this->m; ++$i) {
+ for($j = $j0; $j < $this->n; ++$j) {
+ $R->set($i, $j, $this->A[$i][$j]);
+ }
+ }
+ return $R;
+ break;
+ //A($i0...$iF; $j0...$jF)
+ case 'integer,integer,integer,integer':
+ list($i0, $iF, $j0, $jF) = $args;
+ if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) { $m = $iF - $i0; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (($jF > $j0) && ($this->n >= $jF) && ($j0 >= 0)) { $n = $jF - $j0; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ $R = new Matrix($m+1, $n+1);
+ for($i = $i0; $i <= $iF; ++$i) {
+ for($j = $j0; $j <= $jF; ++$j) {
+ $R->set($i - $i0, $j - $j0, $this->A[$i][$j]);
+ }
+ }
+ return $R;
+ break;
+ //$R = array of row indices; $C = array of column indices
+ case 'array,array':
+ list($RL, $CL) = $args;
+ if (count($RL) > 0) { $m = count($RL); } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (count($CL) > 0) { $n = count($CL); } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ $R = new Matrix($m, $n);
+ for($i = 0; $i < $m; ++$i) {
+ for($j = 0; $j < $n; ++$j) {
+ $R->set($i - $i0, $j - $j0, $this->A[$RL[$i]][$CL[$j]]);
+ }
+ }
+ return $R;
+ break;
+ //$RL = array of row indices; $CL = array of column indices
+ case 'array,array':
+ list($RL, $CL) = $args;
+ if (count($RL) > 0) { $m = count($RL); } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (count($CL) > 0) { $n = count($CL); } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ $R = new Matrix($m, $n);
+ for($i = 0; $i < $m; ++$i) {
+ for($j = 0; $j < $n; ++$j) {
+ $R->set($i, $j, $this->A[$RL[$i]][$CL[$j]]);
+ }
+ }
+ return $R;
+ break;
+ //A($i0...$iF); $CL = array of column indices
+ case 'integer,integer,array':
+ list($i0, $iF, $CL) = $args;
+ if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) { $m = $iF - $i0; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (count($CL) > 0) { $n = count($CL); } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ $R = new Matrix($m, $n);
+ for($i = $i0; $i < $iF; ++$i) {
+ for($j = 0; $j < $n; ++$j) {
+ $R->set($i - $i0, $j, $this->A[$RL[$i]][$j]);
+ }
+ }
+ return $R;
+ break;
+ //$RL = array of row indices
+ case 'array,integer,integer':
+ list($RL, $j0, $jF) = $args;
+ if (count($RL) > 0) { $m = count($RL); } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (($jF >= $j0) && ($this->n >= $jF) && ($j0 >= 0)) { $n = $jF - $j0; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ $R = new Matrix($m, $n+1);
+ for($i = 0; $i < $m; ++$i) {
+ for($j = $j0; $j <= $jF; ++$j) {
+ $R->set($i, $j - $j0, $this->A[$RL[$i]][$j]);
+ }
+ }
+ return $R;
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function getMatrix()
+
+
+ /**
+ * setMatrix
+ *
+ * Set a submatrix
+ * @param int $i0 Initial row index
+ * @param int $j0 Initial column index
+ * @param mixed $S Matrix/Array submatrix
+ * ($i0, $j0, $S) $S = Matrix
+ * ($i0, $j0, $S) $S = Array
+ */
+ public function setMatrix() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'integer,integer,object':
+ if ($args[2] instanceof Matrix) { $M = $args[2]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ if (($args[0] + $M->m) <= $this->m) { $i0 = $args[0]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (($args[1] + $M->n) <= $this->n) { $j0 = $args[1]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ for($i = $i0; $i < $i0 + $M->m; ++$i) {
+ for($j = $j0; $j < $j0 + $M->n; ++$j) {
+ $this->A[$i][$j] = $M->get($i - $i0, $j - $j0);
+ }
+ }
+ break;
+ case 'integer,integer,array':
+ $M = new Matrix($args[2]);
+ if (($args[0] + $M->m) <= $this->m) { $i0 = $args[0]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ if (($args[1] + $M->n) <= $this->n) { $j0 = $args[1]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); }
+ for($i = $i0; $i < $i0 + $M->m; ++$i) {
+ for($j = $j0; $j < $j0 + $M->n; ++$j) {
+ $this->A[$i][$j] = $M->get($i - $i0, $j - $j0);
+ }
+ }
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function setMatrix()
+
+
+ /**
+ * checkMatrixDimensions
+ *
+ * Is matrix B the same size?
+ * @param Matrix $B Matrix B
+ * @return boolean
+ */
+ public function checkMatrixDimensions($B = null) {
+ if ($B instanceof Matrix) {
+ if (($this->m == $B->getRowDimension()) && ($this->n == $B->getColumnDimension())) {
+ return true;
+ } else {
+ throw new Exception(JAMAError(MatrixDimensionException));
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function checkMatrixDimensions()
+
+
+
+ /**
+ * set
+ *
+ * Set the i,j-th element of the matrix.
+ * @param int $i Row position
+ * @param int $j Column position
+ * @param mixed $c Int/float/double value
+ * @return mixed Element (int/float/double)
+ */
+ public function set($i = null, $j = null, $c = null) {
+ // Optimized set version just has this
+ $this->A[$i][$j] = $c;
+ /*
+ if (is_int($i) && is_int($j) && is_numeric($c)) {
+ if (($i < $this->m) && ($j < $this->n)) {
+ $this->A[$i][$j] = $c;
+ } else {
+ echo "A[$i][$j] = $c
";
+ throw new Exception(JAMAError(ArgumentBoundsException));
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ */
+ } // function set()
+
+
+ /**
+ * identity
+ *
+ * Generate an identity matrix.
+ * @param int $m Row dimension
+ * @param int $n Column dimension
+ * @return Matrix Identity matrix
+ */
+ public function identity($m = null, $n = null) {
+ return $this->diagonal($m, $n, 1);
+ } // function identity()
+
+
+ /**
+ * diagonal
+ *
+ * Generate a diagonal matrix
+ * @param int $m Row dimension
+ * @param int $n Column dimension
+ * @param mixed $c Diagonal value
+ * @return Matrix Diagonal matrix
+ */
+ public function diagonal($m = null, $n = null, $c = 1) {
+ $R = new Matrix($m, $n);
+ for($i = 0; $i < $m; ++$i) {
+ $R->set($i, $i, $c);
+ }
+ return $R;
+ } // function diagonal()
+
+
+ /**
+ * filled
+ *
+ * Generate a filled matrix
+ * @param int $m Row dimension
+ * @param int $n Column dimension
+ * @param int $c Fill constant
+ * @return Matrix Filled matrix
+ */
+ public function filled($m = null, $n = null, $c = 0) {
+ if (is_int($m) && is_int($n) && is_numeric($c)) {
+ $R = new Matrix($m, $n, $c);
+ return $R;
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function filled()
+
+ /**
+ * random
+ *
+ * Generate a random matrix
+ * @param int $m Row dimension
+ * @param int $n Column dimension
+ * @return Matrix Random matrix
+ */
+ public function random($m = null, $n = null, $a = RAND_MIN, $b = RAND_MAX) {
+ if (is_int($m) && is_int($n) && is_numeric($a) && is_numeric($b)) {
+ $R = new Matrix($m, $n);
+ for($i = 0; $i < $m; ++$i) {
+ for($j = 0; $j < $n; ++$j) {
+ $R->set($i, $j, mt_rand($a, $b));
+ }
+ }
+ return $R;
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function random()
+
+
+ /**
+ * packed
+ *
+ * Alias for getRowPacked
+ * @return array Packed array
+ */
+ public function packed() {
+ return $this->getRowPacked();
+ } // function packed()
+
+
+ /**
+ * getMatrixByRow
+ *
+ * Get a submatrix by row index/range
+ * @param int $i0 Initial row index
+ * @param int $iF Final row index
+ * @return Matrix Submatrix
+ */
+ public function getMatrixByRow($i0 = null, $iF = null) {
+ if (is_int($i0)) {
+ if (is_int($iF)) {
+ return $this->getMatrix($i0, 0, $iF + 1, $this->n);
+ } else {
+ return $this->getMatrix($i0, 0, $i0 + 1, $this->n);
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function getMatrixByRow()
+
+
+ /**
+ * getMatrixByCol
+ *
+ * Get a submatrix by column index/range
+ * @param int $i0 Initial column index
+ * @param int $iF Final column index
+ * @return Matrix Submatrix
+ */
+ public function getMatrixByCol($j0 = null, $jF = null) {
+ if (is_int($j0)) {
+ if (is_int($jF)) {
+ return $this->getMatrix(0, $j0, $this->m, $jF + 1);
+ } else {
+ return $this->getMatrix(0, $j0, $this->m, $j0 + 1);
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function getMatrixByCol()
+
+
+ /**
+ * transpose
+ *
+ * Tranpose matrix
+ * @return Matrix Transposed matrix
+ */
+ public function transpose() {
+ $R = new Matrix($this->n, $this->m);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $R->set($j, $i, $this->A[$i][$j]);
+ }
+ }
+ return $R;
+ } // function transpose()
+
+
+ /**
+ * norm1
+ *
+ * One norm
+ * @return float Maximum column sum
+ */
+ public function norm1() {
+ $r = 0;
+ for($j = 0; $j < $this->n; ++$j) {
+ $s = 0;
+ for($i = 0; $i < $this->m; ++$i) {
+ $s += abs($this->A[$i][$j]);
+ }
+ $r = ($r > $s) ? $r : $s;
+ }
+ return $r;
+ } // function norm1()
+
+
+ /**
+ * norm2
+ *
+ * Maximum singular value
+ * @return float Maximum singular value
+ */
+ public function norm2() {
+ } // function norm2()
+
+
+ /**
+ * normInf
+ *
+ * Infinite norm
+ * @return float Maximum row sum
+ */
+ public function normInf() {
+ $r = 0;
+ for($i = 0; $i < $this->m; ++$i) {
+ $s = 0;
+ for($j = 0; $j < $this->n; ++$j) {
+ $s += abs($this->A[$i][$j]);
+ }
+ $r = ($r > $s) ? $r : $s;
+ }
+ return $r;
+ } // function normInf()
+
+
+ /**
+ * normF
+ *
+ * Frobenius norm
+ * @return float Square root of the sum of all elements squared
+ */
+ public function normF() {
+ $f = 0;
+ for ($i = 0; $i < $this->m; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ $f = hypo($f,$this->A[$i][$j]);
+ }
+ }
+ return $f;
+ } // function normF()
+
+
+ /**
+ * Matrix rank
+ *
+ * @return effective numerical rank, obtained from SVD.
+ */
+ public function rank () {
+ $svd = new SingularValueDecomposition($this);
+ return $svd->rank();
+ } // function rank ()
+
+
+ /**
+ * Matrix condition (2 norm)
+ *
+ * @return ratio of largest to smallest singular value.
+ */
+ public function cond () {
+ $svd = new SingularValueDecomposition($this);
+ return $svd->cond();
+ } // function cond ()
+
+
+ /**
+ * trace
+ *
+ * Sum of diagonal elements
+ * @return float Sum of diagonal elements
+ */
+ public function trace() {
+ $s = 0;
+ $n = min($this->m, $this->n);
+ for($i = 0; $i < $n; ++$i) {
+ $s += $this->A[$i][$i];
+ }
+ return $s;
+ } // function trace()
+
+
+ /**
+ * uminus
+ *
+ * Unary minus matrix -A
+ * @return Matrix Unary minus matrix
+ */
+ public function uminus() {
+ } // function uminus()
+
+
+ /**
+ * plus
+ *
+ * A + B
+ * @param mixed $B Matrix/Array
+ * @return Matrix Sum
+ */
+ public function plus() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]);
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function plus()
+
+
+ /**
+ * plusEquals
+ *
+ * A = A + B
+ * @param mixed $B Matrix/Array
+ * @return Matrix Sum
+ */
+ public function plusEquals() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $validValues = True;
+ $value = $M->get($i, $j);
+ if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+ $this->A[$i][$j] = trim($this->A[$i][$j],'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($this->A[$i][$j]);
+ }
+ if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+ $value = trim($value,'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($value);
+ }
+ if ($validValues) {
+ $this->A[$i][$j] += $value;
+ } else {
+ $this->A[$i][$j] = PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+ }
+ return $this;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function plusEquals()
+
+
+ /**
+ * minus
+ *
+ * A - B
+ * @param mixed $B Matrix/Array
+ * @return Matrix Sum
+ */
+ public function minus() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $M->set($i, $j, $M->get($i, $j) - $this->A[$i][$j]);
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function minus()
+
+
+ /**
+ * minusEquals
+ *
+ * A = A - B
+ * @param mixed $B Matrix/Array
+ * @return Matrix Sum
+ */
+ public function minusEquals() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $validValues = True;
+ $value = $M->get($i, $j);
+ if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+ $this->A[$i][$j] = trim($this->A[$i][$j],'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($this->A[$i][$j]);
+ }
+ if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+ $value = trim($value,'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($value);
+ }
+ if ($validValues) {
+ $this->A[$i][$j] -= $value;
+ } else {
+ $this->A[$i][$j] = PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+ }
+ return $this;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function minusEquals()
+
+
+ /**
+ * arrayTimes
+ *
+ * Element-by-element multiplication
+ * Cij = Aij * Bij
+ * @param mixed $B Matrix/Array
+ * @return Matrix Matrix Cij
+ */
+ public function arrayTimes() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $M->set($i, $j, $M->get($i, $j) * $this->A[$i][$j]);
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function arrayTimes()
+
+
+ /**
+ * arrayTimesEquals
+ *
+ * Element-by-element multiplication
+ * Aij = Aij * Bij
+ * @param mixed $B Matrix/Array
+ * @return Matrix Matrix Aij
+ */
+ public function arrayTimesEquals() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $validValues = True;
+ $value = $M->get($i, $j);
+ if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+ $this->A[$i][$j] = trim($this->A[$i][$j],'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($this->A[$i][$j]);
+ }
+ if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+ $value = trim($value,'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($value);
+ }
+ if ($validValues) {
+ $this->A[$i][$j] *= $value;
+ } else {
+ $this->A[$i][$j] = PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+ }
+ return $this;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function arrayTimesEquals()
+
+
+ /**
+ * arrayRightDivide
+ *
+ * Element-by-element right division
+ * A / B
+ * @param Matrix $B Matrix B
+ * @return Matrix Division result
+ */
+ public function arrayRightDivide() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $validValues = True;
+ $value = $M->get($i, $j);
+ if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+ $this->A[$i][$j] = trim($this->A[$i][$j],'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($this->A[$i][$j]);
+ }
+ if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+ $value = trim($value,'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($value);
+ }
+ if ($validValues) {
+ if ($value == 0) {
+ // Trap for Divide by Zero error
+ $M->set($i, $j, '#DIV/0!');
+ } else {
+ $M->set($i, $j, $this->A[$i][$j] / $value);
+ }
+ } else {
+ $this->A[$i][$j] = PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function arrayRightDivide()
+
+
+ /**
+ * arrayRightDivideEquals
+ *
+ * Element-by-element right division
+ * Aij = Aij / Bij
+ * @param mixed $B Matrix/Array
+ * @return Matrix Matrix Aij
+ */
+ public function arrayRightDivideEquals() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $this->A[$i][$j] = $this->A[$i][$j] / $M->get($i, $j);
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function arrayRightDivideEquals()
+
+
+ /**
+ * arrayLeftDivide
+ *
+ * Element-by-element Left division
+ * A / B
+ * @param Matrix $B Matrix B
+ * @return Matrix Division result
+ */
+ public function arrayLeftDivide() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $M->set($i, $j, $M->get($i, $j) / $this->A[$i][$j]);
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function arrayLeftDivide()
+
+
+ /**
+ * arrayLeftDivideEquals
+ *
+ * Element-by-element Left division
+ * Aij = Aij / Bij
+ * @param mixed $B Matrix/Array
+ * @return Matrix Matrix Aij
+ */
+ public function arrayLeftDivideEquals() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $this->A[$i][$j] = $M->get($i, $j) / $this->A[$i][$j];
+ }
+ }
+ return $M;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function arrayLeftDivideEquals()
+
+
+ /**
+ * times
+ *
+ * Matrix multiplication
+ * @param mixed $n Matrix/Array/Scalar
+ * @return Matrix Product
+ */
+ public function times() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $B = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ if ($this->n == $B->m) {
+ $C = new Matrix($this->m, $B->n);
+ for($j = 0; $j < $B->n; ++$j) {
+ for ($k = 0; $k < $this->n; ++$k) {
+ $Bcolj[$k] = $B->A[$k][$j];
+ }
+ for($i = 0; $i < $this->m; ++$i) {
+ $Arowi = $this->A[$i];
+ $s = 0;
+ for($k = 0; $k < $this->n; ++$k) {
+ $s += $Arowi[$k] * $Bcolj[$k];
+ }
+ $C->A[$i][$j] = $s;
+ }
+ }
+ return $C;
+ } else {
+ throw new Exception(JAMAError(MatrixDimensionMismatch));
+ }
+ break;
+ case 'array':
+ $B = new Matrix($args[0]);
+ if ($this->n == $B->m) {
+ $C = new Matrix($this->m, $B->n);
+ for($i = 0; $i < $C->m; ++$i) {
+ for($j = 0; $j < $C->n; ++$j) {
+ $s = "0";
+ for($k = 0; $k < $C->n; ++$k) {
+ $s += $this->A[$i][$k] * $B->A[$k][$j];
+ }
+ $C->A[$i][$j] = $s;
+ }
+ }
+ return $C;
+ } else {
+ throw new Exception(JAMAError(MatrixDimensionMismatch));
+ }
+ return $M;
+ break;
+ case 'integer':
+ $C = new Matrix($this->A);
+ for($i = 0; $i < $C->m; ++$i) {
+ for($j = 0; $j < $C->n; ++$j) {
+ $C->A[$i][$j] *= $args[0];
+ }
+ }
+ return $C;
+ break;
+ case 'double':
+ $C = new Matrix($this->m, $this->n);
+ for($i = 0; $i < $C->m; ++$i) {
+ for($j = 0; $j < $C->n; ++$j) {
+ $C->A[$i][$j] = $args[0] * $this->A[$i][$j];
+ }
+ }
+ return $C;
+ break;
+ case 'float':
+ $C = new Matrix($this->A);
+ for($i = 0; $i < $C->m; ++$i) {
+ for($j = 0; $j < $C->n; ++$j) {
+ $C->A[$i][$j] *= $args[0];
+ }
+ }
+ return $C;
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ } else {
+ throw new Exception(PolymorphicArgumentException);
+ }
+ } // function times()
+
+
+ /**
+ * power
+ *
+ * A = A ^ B
+ * @param mixed $B Matrix/Array
+ * @return Matrix Sum
+ */
+ public function power() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ break;
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+ $validValues = True;
+ $value = $M->get($i, $j);
+ if ((is_string($this->A[$i][$j])) && (strlen($this->A[$i][$j]) > 0) && (!is_numeric($this->A[$i][$j]))) {
+ $this->A[$i][$j] = trim($this->A[$i][$j],'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($this->A[$i][$j]);
+ }
+ if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
+ $value = trim($value,'"');
+ $validValues &= PHPExcel_Shared_String::convertToNumberIfFraction($value);
+ }
+ if ($validValues) {
+ $this->A[$i][$j] = pow($this->A[$i][$j],$value);
+ } else {
+ $this->A[$i][$j] = PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+ }
+ return $this;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function power()
+
+
+ /**
+ * concat
+ *
+ * A = A & B
+ * @param mixed $B Matrix/Array
+ * @return Matrix Sum
+ */
+ public function concat() {
+ if (func_num_args() > 0) {
+ $args = func_get_args();
+ $match = implode(",", array_map('gettype', $args));
+
+ switch($match) {
+ case 'object':
+ if ($args[0] instanceof Matrix) { $M = $args[0]; } else { throw new Exception(JAMAError(ArgumentTypeException)); }
+ case 'array':
+ $M = new Matrix($args[0]);
+ break;
+ default:
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ break;
+ }
+ $this->checkMatrixDimensions($M);
+ for($i = 0; $i < $this->m; ++$i) {
+ for($j = 0; $j < $this->n; ++$j) {
+// $this->A[$i][$j] = '"'.trim($this->A[$i][$j],'"').trim($M->get($i, $j),'"').'"';
+ $this->A[$i][$j] = trim($this->A[$i][$j],'"').trim($M->get($i, $j),'"');
+ }
+ }
+ return $this;
+ } else {
+ throw new Exception(JAMAError(PolymorphicArgumentException));
+ }
+ } // function concat()
+
+
+ /**
+ * chol
+ *
+ * Cholesky decomposition
+ * @return Matrix Cholesky decomposition
+ */
+ public function chol() {
+ return new CholeskyDecomposition($this);
+ } // function chol()
+
+
+ /**
+ * lu
+ *
+ * LU decomposition
+ * @return Matrix LU decomposition
+ */
+ public function lu() {
+ return new LUDecomposition($this);
+ } // function lu()
+
+
+ /**
+ * qr
+ *
+ * QR decomposition
+ * @return Matrix QR decomposition
+ */
+ public function qr() {
+ return new QRDecomposition($this);
+ } // function qr()
+
+
+ /**
+ * eig
+ *
+ * Eigenvalue decomposition
+ * @return Matrix Eigenvalue decomposition
+ */
+ public function eig() {
+ return new EigenvalueDecomposition($this);
+ } // function eig()
+
+
+ /**
+ * svd
+ *
+ * Singular value decomposition
+ * @return Singular value decomposition
+ */
+ public function svd() {
+ return new SingularValueDecomposition($this);
+ } // function svd()
+
+
+ /**
+ * Solve A*X = B.
+ *
+ * @param Matrix $B Right hand side
+ * @return Matrix ... Solution if A is square, least squares solution otherwise
+ */
+ public function solve($B) {
+ if ($this->m == $this->n) {
+ $LU = new LUDecomposition($this);
+ return $LU->solve($B);
+ } else {
+ $QR = new QRDecomposition($this);
+ return $QR->solve($B);
+ }
+ } // function solve()
+
+
+ /**
+ * Matrix inverse or pseudoinverse.
+ *
+ * @return Matrix ... Inverse(A) if A is square, pseudoinverse otherwise.
+ */
+ public function inverse() {
+ return $this->solve($this->identity($this->m, $this->m));
+ } // function inverse()
+
+
+ /**
+ * det
+ *
+ * Calculate determinant
+ * @return float Determinant
+ */
+ public function det() {
+ $L = new LUDecomposition($this);
+ return $L->det();
+ } // function det()
+
+
+ /**
+ * Older debugging utility for backwards compatability.
+ *
+ * @return html version of matrix
+ */
+ public function mprint($A, $format="%01.2f", $width=2) {
+ $m = count($A);
+ $n = count($A[0]);
+ $spacing = str_repeat(' ',$width);
+
+ for ($i = 0; $i < $m; ++$i) {
+ for ($j = 0; $j < $n; ++$j) {
+ $formatted = sprintf($format, $A[$i][$j]);
+ echo $formatted.$spacing;
+ }
+ echo "
";
+ }
+ } // function mprint()
+
+
+ /**
+ * Debugging utility.
+ *
+ * @return Output HTML representation of matrix
+ */
+ public function toHTML($width=2) {
+ print('');
+ for($i = 0; $i < $this->m; ++$i) {
+ print('');
+ for($j = 0; $j < $this->n; ++$j) {
+ print('' . $this->A[$i][$j] . ' | ');
+ }
+ print('
');
+ }
+ print('
');
+ } // function toHTML()
+
+} // class Matrix
diff --git a/Classes/PHPExcel/Shared/JAMA/QRDecomposition.php b/Classes/PHPExcel/Shared/JAMA/QRDecomposition.php
new file mode 100644
index 0000000..8068059
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/QRDecomposition.php
@@ -0,0 +1,232 @@
+= n, the QR decomposition is an m-by-n
+ * orthogonal matrix Q and an n-by-n upper triangular matrix R so that
+ * A = Q*R.
+ *
+ * The QR decompostion always exists, even if the matrix does not have
+ * full rank, so the constructor will never fail. The primary use of the
+ * QR decomposition is in the least squares solution of nonsquare systems
+ * of simultaneous linear equations. This will fail if isFullRank()
+ * returns false.
+ *
+ * @author Paul Meagher
+ * @license PHP v3.0
+ * @version 1.1
+ */
+class QRDecomposition {
+
+ /**
+ * Array for internal storage of decomposition.
+ * @var array
+ */
+ private $QR = array();
+
+ /**
+ * Row dimension.
+ * @var integer
+ */
+ private $m;
+
+ /**
+ * Column dimension.
+ * @var integer
+ */
+ private $n;
+
+ /**
+ * Array for internal storage of diagonal of R.
+ * @var array
+ */
+ private $Rdiag = array();
+
+
+ /**
+ * QR Decomposition computed by Householder reflections.
+ *
+ * @param matrix $A Rectangular matrix
+ * @return Structure to access R and the Householder vectors and compute Q.
+ */
+ public function __construct($A) {
+ if($A instanceof Matrix) {
+ // Initialize.
+ $this->QR = $A->getArrayCopy();
+ $this->m = $A->getRowDimension();
+ $this->n = $A->getColumnDimension();
+ // Main loop.
+ for ($k = 0; $k < $this->n; ++$k) {
+ // Compute 2-norm of k-th column without under/overflow.
+ $nrm = 0.0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $nrm = hypo($nrm, $this->QR[$i][$k]);
+ }
+ if ($nrm != 0.0) {
+ // Form k-th Householder vector.
+ if ($this->QR[$k][$k] < 0) {
+ $nrm = -$nrm;
+ }
+ for ($i = $k; $i < $this->m; ++$i) {
+ $this->QR[$i][$k] /= $nrm;
+ }
+ $this->QR[$k][$k] += 1.0;
+ // Apply transformation to remaining columns.
+ for ($j = $k+1; $j < $this->n; ++$j) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $s += $this->QR[$i][$k] * $this->QR[$i][$j];
+ }
+ $s = -$s/$this->QR[$k][$k];
+ for ($i = $k; $i < $this->m; ++$i) {
+ $this->QR[$i][$j] += $s * $this->QR[$i][$k];
+ }
+ }
+ }
+ $this->Rdiag[$k] = -$nrm;
+ }
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ } // function __construct()
+
+
+ /**
+ * Is the matrix full rank?
+ *
+ * @return boolean true if R, and hence A, has full rank, else false.
+ */
+ public function isFullRank() {
+ for ($j = 0; $j < $this->n; ++$j) {
+ if ($this->Rdiag[$j] == 0) {
+ return false;
+ }
+ }
+ return true;
+ } // function isFullRank()
+
+
+ /**
+ * Return the Householder vectors
+ *
+ * @return Matrix Lower trapezoidal matrix whose columns define the reflections
+ */
+ public function getH() {
+ for ($i = 0; $i < $this->m; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ if ($i >= $j) {
+ $H[$i][$j] = $this->QR[$i][$j];
+ } else {
+ $H[$i][$j] = 0.0;
+ }
+ }
+ }
+ return new Matrix($H);
+ } // function getH()
+
+
+ /**
+ * Return the upper triangular factor
+ *
+ * @return Matrix upper triangular factor
+ */
+ public function getR() {
+ for ($i = 0; $i < $this->n; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ if ($i < $j) {
+ $R[$i][$j] = $this->QR[$i][$j];
+ } elseif ($i == $j) {
+ $R[$i][$j] = $this->Rdiag[$i];
+ } else {
+ $R[$i][$j] = 0.0;
+ }
+ }
+ }
+ return new Matrix($R);
+ } // function getR()
+
+
+ /**
+ * Generate and return the (economy-sized) orthogonal factor
+ *
+ * @return Matrix orthogonal factor
+ */
+ public function getQ() {
+ for ($k = $this->n-1; $k >= 0; --$k) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $Q[$i][$k] = 0.0;
+ }
+ $Q[$k][$k] = 1.0;
+ for ($j = $k; $j < $this->n; ++$j) {
+ if ($this->QR[$k][$k] != 0) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $s += $this->QR[$i][$k] * $Q[$i][$j];
+ }
+ $s = -$s/$this->QR[$k][$k];
+ for ($i = $k; $i < $this->m; ++$i) {
+ $Q[$i][$j] += $s * $this->QR[$i][$k];
+ }
+ }
+ }
+ }
+ /*
+ for($i = 0; $i < count($Q); ++$i) {
+ for($j = 0; $j < count($Q); ++$j) {
+ if(! isset($Q[$i][$j]) ) {
+ $Q[$i][$j] = 0;
+ }
+ }
+ }
+ */
+ return new Matrix($Q);
+ } // function getQ()
+
+
+ /**
+ * Least squares solution of A*X = B
+ *
+ * @param Matrix $B A Matrix with as many rows as A and any number of columns.
+ * @return Matrix Matrix that minimizes the two norm of Q*R*X-B.
+ */
+ public function solve($B) {
+ if ($B->getRowDimension() == $this->m) {
+ if ($this->isFullRank()) {
+ // Copy right hand side
+ $nx = $B->getColumnDimension();
+ $X = $B->getArrayCopy();
+ // Compute Y = transpose(Q)*B
+ for ($k = 0; $k < $this->n; ++$k) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $s += $this->QR[$i][$k] * $X[$i][$j];
+ }
+ $s = -$s/$this->QR[$k][$k];
+ for ($i = $k; $i < $this->m; ++$i) {
+ $X[$i][$j] += $s * $this->QR[$i][$k];
+ }
+ }
+ }
+ // Solve R*X = Y;
+ for ($k = $this->n-1; $k >= 0; --$k) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$k][$j] /= $this->Rdiag[$k];
+ }
+ for ($i = 0; $i < $k; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$i][$j] -= $X[$k][$j]* $this->QR[$i][$k];
+ }
+ }
+ }
+ $X = new Matrix($X);
+ return ($X->getMatrix(0, $this->n-1, 0, $nx));
+ } else {
+ throw new Exception(JAMAError(MatrixRankException));
+ }
+ } else {
+ throw new Exception(JAMAError(MatrixDimensionException));
+ }
+ } // function solve()
+
+} // class QRDecomposition
diff --git a/Classes/PHPExcel/Shared/JAMA/SingularValueDecomposition.php b/Classes/PHPExcel/Shared/JAMA/SingularValueDecomposition.php
new file mode 100644
index 0000000..a4b096c
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/SingularValueDecomposition.php
@@ -0,0 +1,526 @@
+= n, the singular value decomposition is
+ * an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
+ * an n-by-n orthogonal matrix V so that A = U*S*V'.
+ *
+ * The singular values, sigma[$k] = S[$k][$k], are ordered so that
+ * sigma[0] >= sigma[1] >= ... >= sigma[n-1].
+ *
+ * The singular value decompostion always exists, so the constructor will
+ * never fail. The matrix condition number and the effective numerical
+ * rank can be computed from this decomposition.
+ *
+ * @author Paul Meagher
+ * @license PHP v3.0
+ * @version 1.1
+ */
+class SingularValueDecomposition {
+
+ /**
+ * Internal storage of U.
+ * @var array
+ */
+ private $U = array();
+
+ /**
+ * Internal storage of V.
+ * @var array
+ */
+ private $V = array();
+
+ /**
+ * Internal storage of singular values.
+ * @var array
+ */
+ private $s = array();
+
+ /**
+ * Row dimension.
+ * @var int
+ */
+ private $m;
+
+ /**
+ * Column dimension.
+ * @var int
+ */
+ private $n;
+
+
+ /**
+ * Construct the singular value decomposition
+ *
+ * Derived from LINPACK code.
+ *
+ * @param $A Rectangular matrix
+ * @return Structure to access U, S and V.
+ */
+ public function __construct($Arg) {
+
+ // Initialize.
+ $A = $Arg->getArrayCopy();
+ $this->m = $Arg->getRowDimension();
+ $this->n = $Arg->getColumnDimension();
+ $nu = min($this->m, $this->n);
+ $e = array();
+ $work = array();
+ $wantu = true;
+ $wantv = true;
+ $nct = min($this->m - 1, $this->n);
+ $nrt = max(0, min($this->n - 2, $this->m));
+
+ // Reduce A to bidiagonal form, storing the diagonal elements
+ // in s and the super-diagonal elements in e.
+ for ($k = 0; $k < max($nct,$nrt); ++$k) {
+
+ if ($k < $nct) {
+ // Compute the transformation for the k-th column and
+ // place the k-th diagonal in s[$k].
+ // Compute 2-norm of k-th column without under/overflow.
+ $this->s[$k] = 0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
+ }
+ if ($this->s[$k] != 0.0) {
+ if ($A[$k][$k] < 0.0) {
+ $this->s[$k] = -$this->s[$k];
+ }
+ for ($i = $k; $i < $this->m; ++$i) {
+ $A[$i][$k] /= $this->s[$k];
+ }
+ $A[$k][$k] += 1.0;
+ }
+ $this->s[$k] = -$this->s[$k];
+ }
+
+ for ($j = $k + 1; $j < $this->n; ++$j) {
+ if (($k < $nct) & ($this->s[$k] != 0.0)) {
+ // Apply the transformation.
+ $t = 0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $t += $A[$i][$k] * $A[$i][$j];
+ }
+ $t = -$t / $A[$k][$k];
+ for ($i = $k; $i < $this->m; ++$i) {
+ $A[$i][$j] += $t * $A[$i][$k];
+ }
+ // Place the k-th row of A into e for the
+ // subsequent calculation of the row transformation.
+ $e[$j] = $A[$k][$j];
+ }
+ }
+
+ if ($wantu AND ($k < $nct)) {
+ // Place the transformation in U for subsequent back
+ // multiplication.
+ for ($i = $k; $i < $this->m; ++$i) {
+ $this->U[$i][$k] = $A[$i][$k];
+ }
+ }
+
+ if ($k < $nrt) {
+ // Compute the k-th row transformation and place the
+ // k-th super-diagonal in e[$k].
+ // Compute 2-norm without under/overflow.
+ $e[$k] = 0;
+ for ($i = $k + 1; $i < $this->n; ++$i) {
+ $e[$k] = hypo($e[$k], $e[$i]);
+ }
+ if ($e[$k] != 0.0) {
+ if ($e[$k+1] < 0.0) {
+ $e[$k] = -$e[$k];
+ }
+ for ($i = $k + 1; $i < $this->n; ++$i) {
+ $e[$i] /= $e[$k];
+ }
+ $e[$k+1] += 1.0;
+ }
+ $e[$k] = -$e[$k];
+ if (($k+1 < $this->m) AND ($e[$k] != 0.0)) {
+ // Apply the transformation.
+ for ($i = $k+1; $i < $this->m; ++$i) {
+ $work[$i] = 0.0;
+ }
+ for ($j = $k+1; $j < $this->n; ++$j) {
+ for ($i = $k+1; $i < $this->m; ++$i) {
+ $work[$i] += $e[$j] * $A[$i][$j];
+ }
+ }
+ for ($j = $k + 1; $j < $this->n; ++$j) {
+ $t = -$e[$j] / $e[$k+1];
+ for ($i = $k + 1; $i < $this->m; ++$i) {
+ $A[$i][$j] += $t * $work[$i];
+ }
+ }
+ }
+ if ($wantv) {
+ // Place the transformation in V for subsequent
+ // back multiplication.
+ for ($i = $k + 1; $i < $this->n; ++$i) {
+ $this->V[$i][$k] = $e[$i];
+ }
+ }
+ }
+ }
+
+ // Set up the final bidiagonal matrix or order p.
+ $p = min($this->n, $this->m + 1);
+ if ($nct < $this->n) {
+ $this->s[$nct] = $A[$nct][$nct];
+ }
+ if ($this->m < $p) {
+ $this->s[$p-1] = 0.0;
+ }
+ if ($nrt + 1 < $p) {
+ $e[$nrt] = $A[$nrt][$p-1];
+ }
+ $e[$p-1] = 0.0;
+ // If required, generate U.
+ if ($wantu) {
+ for ($j = $nct; $j < $nu; ++$j) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $this->U[$i][$j] = 0.0;
+ }
+ $this->U[$j][$j] = 1.0;
+ }
+ for ($k = $nct - 1; $k >= 0; --$k) {
+ if ($this->s[$k] != 0.0) {
+ for ($j = $k + 1; $j < $nu; ++$j) {
+ $t = 0;
+ for ($i = $k; $i < $this->m; ++$i) {
+ $t += $this->U[$i][$k] * $this->U[$i][$j];
+ }
+ $t = -$t / $this->U[$k][$k];
+ for ($i = $k; $i < $this->m; ++$i) {
+ $this->U[$i][$j] += $t * $this->U[$i][$k];
+ }
+ }
+ for ($i = $k; $i < $this->m; ++$i ) {
+ $this->U[$i][$k] = -$this->U[$i][$k];
+ }
+ $this->U[$k][$k] = 1.0 + $this->U[$k][$k];
+ for ($i = 0; $i < $k - 1; ++$i) {
+ $this->U[$i][$k] = 0.0;
+ }
+ } else {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $this->U[$i][$k] = 0.0;
+ }
+ $this->U[$k][$k] = 1.0;
+ }
+ }
+ }
+
+ // If required, generate V.
+ if ($wantv) {
+ for ($k = $this->n - 1; $k >= 0; --$k) {
+ if (($k < $nrt) AND ($e[$k] != 0.0)) {
+ for ($j = $k + 1; $j < $nu; ++$j) {
+ $t = 0;
+ for ($i = $k + 1; $i < $this->n; ++$i) {
+ $t += $this->V[$i][$k]* $this->V[$i][$j];
+ }
+ $t = -$t / $this->V[$k+1][$k];
+ for ($i = $k + 1; $i < $this->n; ++$i) {
+ $this->V[$i][$j] += $t * $this->V[$i][$k];
+ }
+ }
+ }
+ for ($i = 0; $i < $this->n; ++$i) {
+ $this->V[$i][$k] = 0.0;
+ }
+ $this->V[$k][$k] = 1.0;
+ }
+ }
+
+ // Main iteration loop for the singular values.
+ $pp = $p - 1;
+ $iter = 0;
+ $eps = pow(2.0, -52.0);
+
+ while ($p > 0) {
+ // Here is where a test for too many iterations would go.
+ // This section of the program inspects for negligible
+ // elements in the s and e arrays. On completion the
+ // variables kase and k are set as follows:
+ // kase = 1 if s(p) and e[k-1] are negligible and k= -1; --$k) {
+ if ($k == -1) {
+ break;
+ }
+ if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k+1]))) {
+ $e[$k] = 0.0;
+ break;
+ }
+ }
+ if ($k == $p - 2) {
+ $kase = 4;
+ } else {
+ for ($ks = $p - 1; $ks >= $k; --$ks) {
+ if ($ks == $k) {
+ break;
+ }
+ $t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks-1]) : 0.);
+ if (abs($this->s[$ks]) <= $eps * $t) {
+ $this->s[$ks] = 0.0;
+ break;
+ }
+ }
+ if ($ks == $k) {
+ $kase = 3;
+ } else if ($ks == $p-1) {
+ $kase = 1;
+ } else {
+ $kase = 2;
+ $k = $ks;
+ }
+ }
+ ++$k;
+
+ // Perform the task indicated by kase.
+ switch ($kase) {
+ // Deflate negligible s(p).
+ case 1:
+ $f = $e[$p-2];
+ $e[$p-2] = 0.0;
+ for ($j = $p - 2; $j >= $k; --$j) {
+ $t = hypo($this->s[$j],$f);
+ $cs = $this->s[$j] / $t;
+ $sn = $f / $t;
+ $this->s[$j] = $t;
+ if ($j != $k) {
+ $f = -$sn * $e[$j-1];
+ $e[$j-1] = $cs * $e[$j-1];
+ }
+ if ($wantv) {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p-1];
+ $this->V[$i][$p-1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p-1];
+ $this->V[$i][$j] = $t;
+ }
+ }
+ }
+ break;
+ // Split at negligible s(k).
+ case 2:
+ $f = $e[$k-1];
+ $e[$k-1] = 0.0;
+ for ($j = $k; $j < $p; ++$j) {
+ $t = hypo($this->s[$j], $f);
+ $cs = $this->s[$j] / $t;
+ $sn = $f / $t;
+ $this->s[$j] = $t;
+ $f = -$sn * $e[$j];
+ $e[$j] = $cs * $e[$j];
+ if ($wantu) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k-1];
+ $this->U[$i][$k-1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k-1];
+ $this->U[$i][$j] = $t;
+ }
+ }
+ }
+ break;
+ // Perform one qr step.
+ case 3:
+ // Calculate the shift.
+ $scale = max(max(max(max(
+ abs($this->s[$p-1]),abs($this->s[$p-2])),abs($e[$p-2])),
+ abs($this->s[$k])), abs($e[$k]));
+ $sp = $this->s[$p-1] / $scale;
+ $spm1 = $this->s[$p-2] / $scale;
+ $epm1 = $e[$p-2] / $scale;
+ $sk = $this->s[$k] / $scale;
+ $ek = $e[$k] / $scale;
+ $b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
+ $c = ($sp * $epm1) * ($sp * $epm1);
+ $shift = 0.0;
+ if (($b != 0.0) || ($c != 0.0)) {
+ $shift = sqrt($b * $b + $c);
+ if ($b < 0.0) {
+ $shift = -$shift;
+ }
+ $shift = $c / ($b + $shift);
+ }
+ $f = ($sk + $sp) * ($sk - $sp) + $shift;
+ $g = $sk * $ek;
+ // Chase zeros.
+ for ($j = $k; $j < $p-1; ++$j) {
+ $t = hypo($f,$g);
+ $cs = $f/$t;
+ $sn = $g/$t;
+ if ($j != $k) {
+ $e[$j-1] = $t;
+ }
+ $f = $cs * $this->s[$j] + $sn * $e[$j];
+ $e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
+ $g = $sn * $this->s[$j+1];
+ $this->s[$j+1] = $cs * $this->s[$j+1];
+ if ($wantv) {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j+1];
+ $this->V[$i][$j+1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j+1];
+ $this->V[$i][$j] = $t;
+ }
+ }
+ $t = hypo($f,$g);
+ $cs = $f/$t;
+ $sn = $g/$t;
+ $this->s[$j] = $t;
+ $f = $cs * $e[$j] + $sn * $this->s[$j+1];
+ $this->s[$j+1] = -$sn * $e[$j] + $cs * $this->s[$j+1];
+ $g = $sn * $e[$j+1];
+ $e[$j+1] = $cs * $e[$j+1];
+ if ($wantu && ($j < $this->m - 1)) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j+1];
+ $this->U[$i][$j+1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j+1];
+ $this->U[$i][$j] = $t;
+ }
+ }
+ }
+ $e[$p-2] = $f;
+ $iter = $iter + 1;
+ break;
+ // Convergence.
+ case 4:
+ // Make the singular values positive.
+ if ($this->s[$k] <= 0.0) {
+ $this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
+ if ($wantv) {
+ for ($i = 0; $i <= $pp; ++$i) {
+ $this->V[$i][$k] = -$this->V[$i][$k];
+ }
+ }
+ }
+ // Order the singular values.
+ while ($k < $pp) {
+ if ($this->s[$k] >= $this->s[$k+1]) {
+ break;
+ }
+ $t = $this->s[$k];
+ $this->s[$k] = $this->s[$k+1];
+ $this->s[$k+1] = $t;
+ if ($wantv AND ($k < $this->n - 1)) {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $t = $this->V[$i][$k+1];
+ $this->V[$i][$k+1] = $this->V[$i][$k];
+ $this->V[$i][$k] = $t;
+ }
+ }
+ if ($wantu AND ($k < $this->m-1)) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $t = $this->U[$i][$k+1];
+ $this->U[$i][$k+1] = $this->U[$i][$k];
+ $this->U[$i][$k] = $t;
+ }
+ }
+ ++$k;
+ }
+ $iter = 0;
+ --$p;
+ break;
+ } // end switch
+ } // end while
+
+ } // end constructor
+
+
+ /**
+ * Return the left singular vectors
+ *
+ * @access public
+ * @return U
+ */
+ public function getU() {
+ return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
+ }
+
+
+ /**
+ * Return the right singular vectors
+ *
+ * @access public
+ * @return V
+ */
+ public function getV() {
+ return new Matrix($this->V);
+ }
+
+
+ /**
+ * Return the one-dimensional array of singular values
+ *
+ * @access public
+ * @return diagonal of S.
+ */
+ public function getSingularValues() {
+ return $this->s;
+ }
+
+
+ /**
+ * Return the diagonal matrix of singular values
+ *
+ * @access public
+ * @return S
+ */
+ public function getS() {
+ for ($i = 0; $i < $this->n; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ $S[$i][$j] = 0.0;
+ }
+ $S[$i][$i] = $this->s[$i];
+ }
+ return new Matrix($S);
+ }
+
+
+ /**
+ * Two norm
+ *
+ * @access public
+ * @return max(S)
+ */
+ public function norm2() {
+ return $this->s[0];
+ }
+
+
+ /**
+ * Two norm condition number
+ *
+ * @access public
+ * @return max(S)/min(S)
+ */
+ public function cond() {
+ return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
+ }
+
+
+ /**
+ * Effective numerical matrix rank
+ *
+ * @access public
+ * @return Number of nonnegligible singular values.
+ */
+ public function rank() {
+ $eps = pow(2.0, -52.0);
+ $tol = max($this->m, $this->n) * $this->s[0] * $eps;
+ $r = 0;
+ for ($i = 0; $i < count($this->s); ++$i) {
+ if ($this->s[$i] > $tol) {
+ ++$r;
+ }
+ }
+ return $r;
+ }
+
+} // class SingularValueDecomposition
diff --git a/Classes/PHPExcel/Shared/JAMA/docs/docs.php b/Classes/PHPExcel/Shared/JAMA/docs/docs.php
new file mode 100644
index 0000000..d27a42f
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/docs/docs.php
@@ -0,0 +1,6 @@
+
diff --git a/Classes/PHPExcel/Shared/JAMA/docs/download.php b/Classes/PHPExcel/Shared/JAMA/docs/download.php
new file mode 100644
index 0000000..2df6e0c
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/docs/download.php
@@ -0,0 +1,65 @@
+create($files);
+
+ // create the download url
+ $webDir = substr($_SERVER['PHP_SELF'], 0, -18);
+ $urlPath = "http://".$_SERVER['HTTP_HOST'].$webDir."/downloads";
+
+ // redirect to download url
+ header("Location: $urlPath/$tarName");
+
+}
+
+include_once "includes/header.php";
+include_once "includes/navbar.php";
+?>
+
+Download current version:
+
+
+
diff --git a/Classes/PHPExcel/Shared/JAMA/docs/example.php b/Classes/PHPExcel/Shared/JAMA/docs/example.php
new file mode 100644
index 0000000..d76c0f5
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/docs/example.php
@@ -0,0 +1,166 @@
+
+Magic Square Example
+
+The Jama distribution comes with a magic square example that is used to
+test and benchmark the LU, QR, SVD and symmetric Eig decompositions.
+The example outputs a multi-column table with these column headings:
+
+
+
+
+ n |
+ Order of magic square. |
+
+
+ trace |
+ Diagonal sum, should be the magic sum, (n^3 + n)/2. |
+
+
+ max_eig |
+ Maximum eigenvalue of (A + A')/2, should equal trace. |
+
+
+ rank |
+ Linear algebraic rank, should equal n if n is odd, be less than n if n is even. |
+
+
+ cond |
+ L_2 condition number, ratio of singular values. |
+
+
+ lu_res |
+ test of LU factorization, norm1(L*U-A(p,:))/(n*eps). |
+
+
+ qr_res |
+ test of QR factorization, norm1(Q*R-A)/(n*eps). |
+
+
+
+Running the Java-based version of the matix square example produces these results:
+
+
+
+
+ n |
+ trace |
+ max_eig |
+ rank |
+ cond |
+ lu_res |
+ qr_res |
+
+
+ 3 | 15 | 15.000 | 3 | 4.330 | 0.000 | 11.333 |
+
+
+ 4 | 34 | 34.000 | 3 | Inf | 0.000 | 13.500 |
+
+ 5 | 65 | 65.000 | 5 | 5.462 | 0.000 | 14.400 |
+
+
+ 6 | 111 | 111.000 | 5 | Inf | 5.333 | 16.000 |
+
+
+ 7 | 175 | 175.000 | 7 | 7.111 | 2.286 | 37.714 |
+
+
+ 8 | 260 | 260.000 | 3 | Inf | 0.000 | 59.000 |
+
+
+ 9 | 369 | 369.000 | 9 | 9.102 | 7.111 | 53.333 |
+
+
+ 10 | 505 | 505.000 | 7 | Inf | 3.200 | 159.200 |
+
+
+ 11 | 671 | 671.000 | 11 | 11.102 | 2.909 | 215.273 |
+
+
+ 12 | 870 | 870.000 | 3 | Inf | 0.000 | 185.333 |
+
+
+ 13 | 1105 | 1105.000 | 13 | 13.060 | 4.923 | 313.846 |
+
+
+ 14 | 1379 | 1379.000 | 9 | Inf | 4.571 | 540.571 |
+
+
+ 15 | 1695 | 1695.000 | 15 | 15.062 | 4.267 | 242.133 |
+
+
+ 16 | 2056 | 2056.000 | 3 | Inf | 0.000 | 488.500 |
+
+
+ 17 | 2465 | 2465.000 | 17 | 17.042 | 7.529 | 267.294 |
+
+
+ 18 | 2925 | 2925.000 | 11 | Inf | 7.111 | 520.889 |
+
+
+ 19 | 3439 | 3439.000 | 19 | 19.048 | 16.842 | 387.368 |
+
+
+ 20 | 4010 | 4010.000 | 3 | Inf | 14.400 | 584.800 |
+
+
+ 21 | 4641 | 4641.000 | 21 | 21.035 | 6.095 | 1158.095 |
+
+
+ 22 | 5335 | 5335.000 | 13 | Inf | 6.545 | 1132.364 |
+
+
+ 23 | 6095 | 6095.000 | 23 | 23.037 | 11.130 | 1268.870 |
+
+
+ 24 | 6924 | 6924.000 | 3 | Inf | 10.667 | 827.500 |
+
+
+ 25 | 7825 | 7825.000 | 25 | 25.029 | 35.840 | 1190.400 |
+
+
+ 26 | 8801 | 8801.000 | 15 | Inf | 4.923 | 1859.077 |
+
+
+ 27 | 9855 | 9855.000 | 27 | 27.032 | 37.926 | 1365.333 |
+
+
+ 28 | 10990 | 10990.000 | 3 | Inf | 34.286 | 1365.714 |
+
+
+ 29 | 12209 | 12209.000 | 29 | 29.025 | 30.897 | 1647.448 |
+
+
+ 30 | 13515 | 13515.000 | 17 | Inf | 8.533 | 2571.733 |
+
+
+ 31 | 14911 | 14911.000 | 31 | 31.027 | 33.032 | 1426.581 |
+
+
+ 32 | 16400 | 16400.000 | 3 | Inf | 0.000 | 1600.125 |
+
+
+Elapsed Time = 0.710 seconds
+
+
+The magic square example does not fare well when run as a PHP script. For a 32x32 matrix array
+it takes around a second to complete just the last row of computations in the above table.
+Hopefully this result will spur PHP developers to find optimizations and better attuned algorithms
+to speed things up. Matrix algebra is a great testing ground for ideas about time and memory
+performance optimation. Keep in perspective that PHP JAMA scripts are still plenty fast for use as
+a tool for learning about matrix algebra and quickly extending your knowledge with new scripts
+to apply knowledge.
+
+
+
+To learn more about the subject of magic squares you can visit the Drexel Math Forum on Magic Squares.
+You can also learn more by carefully examining the MagicSquareExample.php
source code below.
+
+
+
diff --git a/Classes/PHPExcel/Shared/JAMA/docs/includes/credits.php b/Classes/PHPExcel/Shared/JAMA/docs/includes/credits.php
new file mode 100644
index 0000000..efc91c2
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/docs/includes/credits.php
@@ -0,0 +1,14 @@
+
+
+ Brought to you by:
+
+
+
diff --git a/Classes/PHPExcel/Shared/JAMA/docs/includes/footer.php b/Classes/PHPExcel/Shared/JAMA/docs/includes/footer.php
new file mode 100644
index 0000000..7fb2bd6
--- /dev/null
+++ b/Classes/PHPExcel/Shared/JAMA/docs/includes/footer.php
@@ -0,0 +1,2 @@
+