From 47bfb47761d6c72d54eab6727de5e86cbf1da617 Mon Sep 17 00:00:00 2001
From: Dennis Eichhorn
Date: Sat, 7 Jan 2023 18:57:49 +0100
Subject: [PATCH] update office and its dependencies
---
Laminas/Escaper/Escaper.php | 412 ++
.../Escaper/Exception/ExceptionInterface.php | 11 +
.../Exception/InvalidArgumentException.php | 13 +
.../Escaper/Exception/RuntimeException.php | 13 +
Laminas/Escaper/LICENSE.md | 26 +
MyCLabs/Enum/Enum.php | 319 ++
MyCLabs/LICENSE | 18 +
.../Common/Adapter/Zip/PclZipAdapter.php | 11 +-
.../Common/Adapter/Zip/ZipArchiveAdapter.php | 3 +-
PhpOffice/Common/Adapter/Zip/ZipInterface.php | 10 +-
PhpOffice/Common/Autoloader.php | 11 +-
PhpOffice/Common/COPYING | 674 ----
PhpOffice/Common/COPYING.LESSER | 165 -
PhpOffice/Common/Drawing.php | 157 +-
PhpOffice/Common/File.php | 42 +-
PhpOffice/Common/Font.php | 50 +-
PhpOffice/Common/LICENSE | 15 -
PhpOffice/Common/Microsoft/OLERead.php | 160 +-
.../Common/Microsoft/PasswordEncoder.php | 130 +-
PhpOffice/Common/Text.php | 92 +-
PhpOffice/Common/XMLReader.php | 90 +-
PhpOffice/Common/XMLWriter.php | 38 +-
PhpOffice/PhpPresentation/AbstractShape.php | 220 +-
PhpOffice/PhpPresentation/Autoloader.php | 26 +-
PhpOffice/PhpPresentation/COPYING | 674 ----
PhpOffice/PhpPresentation/COPYING.LESSER | 165 -
.../PhpPresentation/ComparableInterface.php | 25 +-
PhpOffice/PhpPresentation/DocumentLayout.php | 141 +-
.../PhpPresentation/DocumentProperties.php | 222 +-
.../Exception/DirectoryNotFoundException.php | 32 +
.../FeatureNotImplementedException.php | 29 +
.../Exception/FileCopyException.php | 33 +
.../Exception/FileNotFoundException.php | 32 +
.../Exception/FileRemoveException.php | 32 +
.../Exception/InvalidClassException.php | 33 +
.../Exception/InvalidFileFormatException.php | 42 +
.../Exception/InvalidParameterException.php | 33 +
.../Exception/OutOfBoundsException.php | 34 +
.../PhpPresentationException.php} | 15 +-
...ShapeContainerAlreadyAssignedException.php | 32 +
.../UnauthorizedMimetypeException.php | 37 +
.../UndefinedChartTypeException.php} | 17 +-
.../PhpPresentation/GeometryCalculator.php | 58 +-
PhpOffice/PhpPresentation/HashTable.php | 115 +-
PhpOffice/PhpPresentation/IOFactory.php | 90 +-
PhpOffice/PhpPresentation/LICENSE | 15 -
PhpOffice/PhpPresentation/PhpPresentation.php | 280 +-
.../PresentationProperties.php | 186 +-
.../PhpPresentation/Reader/ODPresentation.php | 594 ++-
.../PhpPresentation/Reader/PowerPoint2007.php | 834 ++--
.../PhpPresentation/Reader/PowerPoint97.php | 2136 +++++-----
.../Reader/ReaderInterface.php | 22 +-
.../PhpPresentation/Reader/Serialized.php | 87 +-
.../PhpPresentation/Shape/AbstractGraphic.php | 105 +-
PhpOffice/PhpPresentation/Shape/AutoShape.php | 300 ++
PhpOffice/PhpPresentation/Shape/Chart.php | 132 +-
.../PhpPresentation/Shape/Chart/Axis.php | 380 +-
.../PhpPresentation/Shape/Chart/Gridlines.php | 20 +-
.../PhpPresentation/Shape/Chart/Legend.php | 186 +-
.../PhpPresentation/Shape/Chart/Marker.php | 116 +-
.../PhpPresentation/Shape/Chart/PlotArea.php | 145 +-
.../PhpPresentation/Shape/Chart/Series.php | 366 +-
.../PhpPresentation/Shape/Chart/Title.php | 144 +-
.../Shape/Chart/Type/AbstractType.php | 99 +-
.../Shape/Chart/Type/AbstractTypeBar.php | 89 +-
.../Shape/Chart/Type/AbstractTypeLine.php | 65 +
.../Shape/Chart/Type/AbstractTypePie.php | 40 +-
.../PhpPresentation/Shape/Chart/Type/Area.php | 12 +-
.../PhpPresentation/Shape/Chart/Type/Bar.php | 11 +-
.../Shape/Chart/Type/Bar3D.php | 11 +-
.../Shape/Chart/Type/Doughnut.php | 19 +-
.../PhpPresentation/Shape/Chart/Type/Line.php | 17 +-
.../PhpPresentation/Shape/Chart/Type/Pie.php | 11 +-
.../Shape/Chart/Type/Pie3D.php | 11 +-
.../Shape/Chart/Type/Radar.php | 41 +
.../Shape/Chart/Type/Scatter.php | 17 +-
.../PhpPresentation/Shape/Chart/View3D.php | 97 +-
PhpOffice/PhpPresentation/Shape/Comment.php | 47 +-
.../PhpPresentation/Shape/Comment/Author.php | 28 +-
.../Shape/Drawing/AbstractDrawingAdapter.php | 45 +-
.../PhpPresentation/Shape/Drawing/Base64.php | 97 +-
.../PhpPresentation/Shape/Drawing/File.php | 78 +-
.../PhpPresentation/Shape/Drawing/Gd.php | 112 +-
.../PhpPresentation/Shape/Drawing/ZipFile.php | 69 +-
PhpOffice/PhpPresentation/Shape/Group.php | 210 +-
PhpOffice/PhpPresentation/Shape/Hyperlink.php | 133 +-
PhpOffice/PhpPresentation/Shape/Line.php | 13 +-
PhpOffice/PhpPresentation/Shape/Media.php | 14 +-
.../PhpPresentation/Shape/Placeholder.php | 61 +-
PhpOffice/PhpPresentation/Shape/RichText.php | 463 ++-
.../Shape/RichText/BreakElement.php | 43 +-
.../Shape/RichText/Paragraph.php | 260 +-
.../PhpPresentation/Shape/RichText/Run.php | 27 +-
.../Shape/RichText/TextElement.php | 60 +-
.../Shape/RichText/TextElementInterface.php | 21 +-
PhpOffice/PhpPresentation/Shape/Table.php | 76 +-
.../PhpPresentation/Shape/Table/Cell.php | 196 +-
PhpOffice/PhpPresentation/Shape/Table/Row.php | 143 +-
.../ShapeContainerInterface.php | 60 +-
PhpOffice/PhpPresentation/Slide.php | 109 +-
.../Slide/AbstractBackground.php | 19 +-
.../PhpPresentation/Slide/AbstractSlide.php | 223 +-
PhpOffice/PhpPresentation/Slide/Animation.php | 35 +-
.../Slide/Background/Color.php | 32 +-
.../Slide/Background/Image.php | 61 +-
.../Slide/Background/SchemeColor.php | 29 +-
PhpOffice/PhpPresentation/Slide/Iterator.php | 43 +-
PhpOffice/PhpPresentation/Slide/Layout.php | 29 +-
PhpOffice/PhpPresentation/Slide/Note.php | 125 +-
.../PhpPresentation/Slide/SlideLayout.php | 42 +-
.../PhpPresentation/Slide/SlideMaster.php | 77 +-
.../PhpPresentation/Slide/Transition.php | 156 +-
PhpOffice/PhpPresentation/Style/Alignment.php | 275 +-
PhpOffice/PhpPresentation/Style/Border.php | 150 +-
PhpOffice/PhpPresentation/Style/Borders.php | 69 +-
PhpOffice/PhpPresentation/Style/Bullet.php | 198 +-
PhpOffice/PhpPresentation/Style/Color.php | 100 +-
PhpOffice/PhpPresentation/Style/ColorMap.php | 71 +-
PhpOffice/PhpPresentation/Style/Fill.php | 163 +-
PhpOffice/PhpPresentation/Style/Font.php | 377 +-
PhpOffice/PhpPresentation/Style/Outline.php | 42 +-
.../PhpPresentation/Style/SchemeColor.php | 20 +-
PhpOffice/PhpPresentation/Style/Shadow.php | 182 +-
PhpOffice/PhpPresentation/Style/TextStyle.php | 90 +-
.../Writer/AbstractDecoratorWriter.php | 26 +-
.../PhpPresentation/Writer/AbstractWriter.php | 90 +-
.../PhpPresentation/Writer/ODPresentation.php | 89 +-
.../AbstractDecoratorWriter.php | 20 +
.../Writer/ODPresentation/Content.php | 461 +--
.../Writer/ODPresentation/Meta.php | 60 +-
.../Writer/ODPresentation/MetaInfManifest.php | 35 +-
.../Writer/ODPresentation/Mimetype.php | 24 +-
.../Writer/ODPresentation/ObjectsChart.php | 518 ++-
.../Writer/ODPresentation/Pictures.php | 28 +-
.../Writer/ODPresentation/Styles.php | 107 +-
.../ODPresentation/ThumbnailsThumbnail.php | 27 +-
.../PhpPresentation/Writer/PowerPoint2007.php | 116 +-
.../AbstractDecoratorWriter.php | 160 +-
.../Writer/PowerPoint2007/AbstractSlide.php | 463 ++-
.../Writer/PowerPoint2007/CommentAuthors.php | 29 +-
.../Writer/PowerPoint2007/ContentTypes.php | 69 +-
.../Writer/PowerPoint2007/DocPropsApp.php | 27 +-
.../Writer/PowerPoint2007/DocPropsCore.php | 25 +-
.../Writer/PowerPoint2007/DocPropsCustom.php | 62 +-
.../PowerPoint2007/DocPropsThumbnail.php | 26 +-
.../LayoutPack/AbstractLayoutPack.php | 225 --
.../PowerPoint2007/LayoutPack/PackDefault.php | 3281 ---------------
.../LayoutPack/TemplateBased.php | 203 -
.../Writer/PowerPoint2007/PptCharts.php | 761 ++--
.../Writer/PowerPoint2007/PptComments.php | 29 +-
.../Writer/PowerPoint2007/PptMedia.php | 24 +-
.../Writer/PowerPoint2007/PptPresProps.php | 36 +-
.../Writer/PowerPoint2007/PptPresentation.php | 32 +-
.../Writer/PowerPoint2007/PptSlideLayouts.php | 44 +-
.../Writer/PowerPoint2007/PptSlideMasters.php | 69 +-
.../Writer/PowerPoint2007/PptSlides.php | 208 +-
.../Writer/PowerPoint2007/PptTableProps.php | 25 +-
.../Writer/PowerPoint2007/PptTheme.php | 48 +-
.../Writer/PowerPoint2007/PptViewProps.php | 29 +-
.../Writer/PowerPoint2007/Relationships.php | 44 +-
.../PhpPresentation/Writer/Serialized.php | 52 +-
.../Writer/WriterInterface.php | 12 +-
.../Calculation/ArrayEnabled.php | 133 +
.../Calculation/BinaryComparison.php | 181 +
.../Calculation/Calculation.php | 3539 +++++++++++------
.../PhpSpreadsheet/Calculation/Category.php | 2 +
.../PhpSpreadsheet/Calculation/Database.php | 326 +-
.../Calculation/Database/DAverage.php | 46 +
.../Calculation/Database/DCount.php | 47 +
.../Calculation/Database/DCountA.php | 46 +
.../Calculation/Database/DGet.php | 51 +
.../Calculation/Database/DMax.php | 47 +
.../Calculation/Database/DMin.php | 47 +
.../Calculation/Database/DProduct.php | 46 +
.../Calculation/Database/DStDev.php | 47 +
.../Calculation/Database/DStDevP.php | 47 +
.../Calculation/Database/DSum.php | 46 +
.../Calculation/Database/DVar.php | 47 +
.../Calculation/Database/DVarP.php | 47 +
.../Calculation/Database/DatabaseAbstract.php | 192 +
.../PhpSpreadsheet/Calculation/DateTime.php | 1427 ++-----
.../Calculation/DateTimeExcel/Constants.php | 38 +
.../Calculation/DateTimeExcel/Current.php | 59 +
.../Calculation/DateTimeExcel/Date.php | 172 +
.../Calculation/DateTimeExcel/DateParts.php | 151 +
.../Calculation/DateTimeExcel/DateValue.php | 157 +
.../Calculation/DateTimeExcel/Days.php | 62 +
.../Calculation/DateTimeExcel/Days360.php | 118 +
.../Calculation/DateTimeExcel/Difference.php | 158 +
.../Calculation/DateTimeExcel/Helpers.php | 307 ++
.../Calculation/DateTimeExcel/Month.php | 101 +
.../Calculation/DateTimeExcel/NetworkDays.php | 119 +
.../Calculation/DateTimeExcel/Time.php | 130 +
.../Calculation/DateTimeExcel/TimeParts.php | 132 +
.../Calculation/DateTimeExcel/TimeValue.php | 78 +
.../Calculation/DateTimeExcel/Week.php | 278 ++
.../Calculation/DateTimeExcel/WorkDay.php | 201 +
.../Calculation/DateTimeExcel/YearFrac.php | 133 +
.../Engine/ArrayArgumentHelper.php | 209 +
.../Engine/ArrayArgumentProcessor.php | 175 +
.../Calculation/Engine/BranchPruner.php | 223 ++
.../Engine/CyclicReferenceStack.php | 4 +-
.../Calculation/Engine/FormattedNumber.php | 126 +
.../Calculation/Engine/Logger.php | 42 +-
.../Calculation/Engine/Operands/Operand.php | 10 +
.../Engine/Operands/StructuredReference.php | 332 ++
.../Calculation/Engineering.php | 2128 ++--------
.../Calculation/Engineering/BesselI.php | 152 +
.../Calculation/Engineering/BesselJ.php | 180 +
.../Calculation/Engineering/BesselK.php | 135 +
.../Calculation/Engineering/BesselY.php | 141 +
.../Calculation/Engineering/BitWise.php | 277 ++
.../Calculation/Engineering/Compare.php | 82 +
.../Calculation/Engineering/Complex.php | 121 +
.../Engineering/ComplexFunctions.php | 611 +++
.../Engineering/ComplexOperations.php | 134 +
.../Calculation/Engineering/Constants.php | 11 +
.../Calculation/Engineering/ConvertBase.php | 69 +
.../Calculation/Engineering/ConvertBinary.php | 163 +
.../Engineering/ConvertDecimal.php | 213 +
.../Calculation/Engineering/ConvertHex.php | 175 +
.../Calculation/Engineering/ConvertOctal.php | 174 +
.../Calculation/Engineering/ConvertUOM.php | 694 ++++
.../Engineering/EngineeringValidations.php | 33 +
.../Calculation/Engineering/Erf.php | 105 +
.../Calculation/Engineering/ErfC.php | 77 +
.../PhpSpreadsheet/Calculation/Exception.php | 4 +-
.../Calculation/ExceptionHandler.php | 4 +-
.../PhpSpreadsheet/Calculation/Financial.php | 1926 +++------
.../Calculation/Financial/Amortization.php | 214 +
.../CashFlow/CashFlowValidations.php | 53 +
.../Financial/CashFlow/Constant/Periodic.php | 200 +
.../CashFlow/Constant/Periodic/Cumulative.php | 142 +
.../CashFlow/Constant/Periodic/Interest.php | 219 +
.../Periodic/InterestAndPrincipal.php | 44 +
.../CashFlow/Constant/Periodic/Payments.php | 116 +
.../Calculation/Financial/CashFlow/Single.php | 109 +
.../CashFlow/Variable/NonPeriodic.php | 325 ++
.../Financial/CashFlow/Variable/Periodic.php | 168 +
.../Calculation/Financial/Constants.php | 19 +
.../Calculation/Financial/Coupons.php | 426 ++
.../Calculation/Financial/Depreciation.php | 270 ++
.../Calculation/Financial/Dollar.php | 132 +
.../Financial/FinancialValidations.php | 158 +
.../Calculation/Financial/Helpers.php | 58 +
.../Calculation/Financial/InterestRate.php | 73 +
.../Financial/Securities/AccruedInterest.php | 159 +
.../Financial/Securities/Price.php | 284 ++
.../Financial/Securities/Rates.php | 138 +
.../Securities/SecurityValidations.php | 42 +
.../Financial/Securities/Yields.php | 153 +
.../Calculation/Financial/TreasuryBill.php | 148 +
.../Calculation/FormulaParser.php | 79 +-
.../Calculation/FormulaToken.php | 20 +-
.../PhpSpreadsheet/Calculation/Functions.php | 602 ++-
.../Calculation/Information/ErrorValue.php | 71 +
.../Calculation/Information/ExcelError.php | 171 +
.../Calculation/Information/Value.php | 328 ++
.../Calculation/Internal/MakeMatrix.php | 11 +
.../Calculation/Internal/WildcardMatch.php | 39 +
.../PhpSpreadsheet/Calculation/Logical.php | 254 +-
.../Calculation/Logical/Boolean.php | 36 +
.../Calculation/Logical/Conditional.php | 211 +
.../Calculation/Logical/Operations.php | 207 +
.../PhpSpreadsheet/Calculation/LookupRef.php | 896 +----
.../Calculation/LookupRef/Address.php | 124 +
.../Calculation/LookupRef/ExcelMatch.php | 282 ++
.../Calculation/LookupRef/Filter.php | 81 +
.../Calculation/LookupRef/Formula.php | 43 +
.../Calculation/LookupRef/HLookup.php | 121 +
.../Calculation/LookupRef/Helpers.php | 74 +
.../Calculation/LookupRef/Hyperlink.php | 41 +
.../Calculation/LookupRef/Indirect.php | 130 +
.../Calculation/LookupRef/Lookup.php | 110 +
.../Calculation/LookupRef/LookupBase.php | 66 +
.../LookupRef/LookupRefValidations.php | 40 +
.../Calculation/LookupRef/Matrix.php | 142 +
.../Calculation/LookupRef/Offset.php | 148 +
.../LookupRef/RowColumnInformation.php | 209 +
.../Calculation/LookupRef/Selection.php | 51 +
.../Calculation/LookupRef/Sort.php | 342 ++
.../Calculation/LookupRef/Unique.php | 141 +
.../Calculation/LookupRef/VLookup.php | 117 +
.../PhpSpreadsheet/Calculation/MathTrig.php | 1866 ++++-----
.../Calculation/MathTrig/Absolute.php | 37 +
.../Calculation/MathTrig/Angle.php | 63 +
.../Calculation/MathTrig/Arabic.php | 112 +
.../Calculation/MathTrig/Base.php | 68 +
.../Calculation/MathTrig/Ceiling.php | 167 +
.../Calculation/MathTrig/Combinations.php | 91 +
.../Calculation/MathTrig/Exp.php | 37 +
.../Calculation/MathTrig/Factorial.php | 125 +
.../Calculation/MathTrig/Floor.php | 195 +
.../Calculation/MathTrig/Gcd.php | 70 +
.../Calculation/MathTrig/Helpers.php | 130 +
.../Calculation/MathTrig/IntClass.php | 40 +
.../Calculation/MathTrig/Lcm.php | 111 +
.../Calculation/MathTrig/Logarithms.php | 102 +
.../Calculation/MathTrig/MatrixFunctions.php | 179 +
.../Calculation/MathTrig/Operations.php | 162 +
.../Calculation/MathTrig/Random.php | 99 +
.../Calculation/MathTrig/Roman.php | 846 ++++
.../Calculation/MathTrig/Round.php | 218 +
.../Calculation/MathTrig/SeriesSum.php | 53 +
.../Calculation/MathTrig/Sign.php | 38 +
.../Calculation/MathTrig/Sqrt.php | 64 +
.../Calculation/MathTrig/Subtotal.php | 135 +
.../Calculation/MathTrig/Sum.php | 118 +
.../Calculation/MathTrig/SumSquares.php | 143 +
.../Calculation/MathTrig/Trig/Cosecant.php | 64 +
.../Calculation/MathTrig/Trig/Cosine.php | 116 +
.../Calculation/MathTrig/Trig/Cotangent.php | 118 +
.../Calculation/MathTrig/Trig/Secant.php | 64 +
.../Calculation/MathTrig/Trig/Sine.php | 116 +
.../Calculation/MathTrig/Trig/Tangent.php | 161 +
.../Calculation/MathTrig/Trunc.php | 50 +
.../Calculation/Statistical.php | 3172 +++------------
.../Calculation/Statistical/AggregateBase.php | 70 +
.../Calculation/Statistical/Averages.php | 259 ++
.../Calculation/Statistical/Averages/Mean.php | 132 +
.../Calculation/Statistical/Conditional.php | 310 ++
.../Calculation/Statistical/Confidence.php | 52 +
.../Calculation/Statistical/Counts.php | 102 +
.../Calculation/Statistical/Deviations.php | 142 +
.../Statistical/Distributions/Beta.php | 283 ++
.../Statistical/Distributions/Binomial.php | 237 ++
.../Statistical/Distributions/ChiSquared.php | 337 ++
.../Distributions/DistributionValidations.php | 24 +
.../Statistical/Distributions/Exponential.php | 55 +
.../Statistical/Distributions/F.php | 64 +
.../Statistical/Distributions/Fisher.php | 74 +
.../Statistical/Distributions/Gamma.php | 151 +
.../Statistical/Distributions/GammaBase.php | 380 ++
.../Distributions/HyperGeometric.php | 76 +
.../Statistical/Distributions/LogNormal.php | 139 +
.../Distributions/NewtonRaphson.php | 63 +
.../Statistical/Distributions/Normal.php | 180 +
.../Statistical/Distributions/Poisson.php | 66 +
.../Distributions/StandardNormal.php | 151 +
.../Statistical/Distributions/StudentT.php | 139 +
.../Statistical/Distributions/Weibull.php | 57 +
.../Calculation/Statistical/MaxMinBase.php | 17 +
.../Calculation/Statistical/Maximum.php | 89 +
.../Calculation/Statistical/Minimum.php | 89 +
.../Calculation/Statistical/Percentiles.php | 206 +
.../Calculation/Statistical/Permutations.php | 90 +
.../Calculation/Statistical/Size.php | 97 +
.../Statistical/StandardDeviations.php | 95 +
.../Calculation/Statistical/Standardize.php | 49 +
.../Statistical/StatisticalValidations.php | 45 +
.../Calculation/Statistical/Trends.php | 430 ++
.../Calculation/Statistical/VarianceBase.php | 28 +
.../Calculation/Statistical/Variances.php | 186 +
.../PhpSpreadsheet/Calculation/TextData.php | 598 +--
.../Calculation/TextData/CaseConvert.php | 80 +
.../Calculation/TextData/CharacterConvert.php | 81 +
.../Calculation/TextData/Concatenate.php | 137 +
.../Calculation/TextData/Extract.php | 280 ++
.../Calculation/TextData/Format.php | 314 ++
.../Calculation/TextData/Helpers.php | 91 +
.../Calculation/TextData/Replace.php | 118 +
.../Calculation/TextData/Search.php | 97 +
.../Calculation/TextData/Text.php | 255 ++
.../Calculation/TextData/Trim.php | 52 +
.../Calculation/Token/Stack.php | 96 +-
PhpOffice/PhpSpreadsheet/Calculation/Web.php | 30 +
.../Calculation/Web/Service.php | 75 +
.../Calculation/functionlist.txt | 388 --
.../Calculation/locale/Translations.xlsx | Bin 0 -> 108747 bytes
.../Calculation/locale/bg/config | 0
.../Calculation/locale/bg/functions | 0
.../Calculation/locale/cs/config | 31 +-
.../Calculation/locale/cs/functions | 856 ++--
.../Calculation/locale/da/config | 33 +-
.../Calculation/locale/da/functions | 874 ++--
.../Calculation/locale/de/config | 32 +-
.../Calculation/locale/de/functions | 870 ++--
.../Calculation/locale/en/uk/config | 0
.../Calculation/locale/es/config | 32 +-
.../Calculation/locale/es/functions | 874 ++--
.../Calculation/locale/fi/config | 32 +-
.../Calculation/locale/fi/functions | 874 ++--
.../Calculation/locale/fr/config | 32 +-
.../Calculation/locale/fr/functions | 861 ++--
.../Calculation/locale/hu/config | 31 +-
.../Calculation/locale/hu/functions | 874 ++--
.../Calculation/locale/it/config | 32 +-
.../Calculation/locale/it/functions | 873 ++--
.../Calculation/locale/nb/config | 20 +
.../Calculation/locale/nb/functions | 539 +++
.../Calculation/locale/nl/config | 32 +-
.../Calculation/locale/nl/functions | 873 ++--
.../Calculation/locale/no/config | 24 -
.../Calculation/locale/no/functions | 416 --
.../Calculation/locale/pl/config | 32 +-
.../Calculation/locale/pl/functions | 872 ++--
.../Calculation/locale/pt/br/config | 32 +-
.../Calculation/locale/pt/br/functions | 864 ++--
.../Calculation/locale/pt/config | 32 +-
.../Calculation/locale/pt/functions | 874 ++--
.../Calculation/locale/ru/config | 32 +-
.../Calculation/locale/ru/functions | 891 +++--
.../Calculation/locale/sv/config | 32 +-
.../Calculation/locale/sv/functions | 869 ++--
.../Calculation/locale/tr/config | 32 +-
.../Calculation/locale/tr/functions | 873 ++--
.../PhpSpreadsheet/Cell/AddressHelper.php | 177 +
.../PhpSpreadsheet/Cell/AddressRange.php | 24 +
.../Cell/AdvancedValueBinder.php | 158 +-
PhpOffice/PhpSpreadsheet/Cell/Cell.php | 435 +-
PhpOffice/PhpSpreadsheet/Cell/CellAddress.php | 166 +
PhpOffice/PhpSpreadsheet/Cell/CellRange.php | 136 +
PhpOffice/PhpSpreadsheet/Cell/ColumnRange.php | 125 +
PhpOffice/PhpSpreadsheet/Cell/Coordinate.php | 327 +-
PhpOffice/PhpSpreadsheet/Cell/DataType.php | 36 +-
.../PhpSpreadsheet/Cell/DataValidation.php | 120 +-
.../PhpSpreadsheet/Cell/DataValidator.php | 7 +-
.../Cell/DefaultValueBinder.php | 43 +-
PhpOffice/PhpSpreadsheet/Cell/Hyperlink.php | 26 +-
.../PhpSpreadsheet/Cell/IValueBinder.php | 0
PhpOffice/PhpSpreadsheet/Cell/RowRange.php | 93 +
.../PhpSpreadsheet/Cell/StringValueBinder.php | 105 +-
.../PhpSpreadsheet/CellReferenceHelper.php | 131 +
PhpOffice/PhpSpreadsheet/Chart/Axis.php | 577 +--
PhpOffice/PhpSpreadsheet/Chart/Chart.php | 417 +-
PhpOffice/PhpSpreadsheet/Chart/ChartColor.php | 177 +
PhpOffice/PhpSpreadsheet/Chart/DataSeries.php | 75 +-
.../PhpSpreadsheet/Chart/DataSeriesValues.php | 280 +-
PhpOffice/PhpSpreadsheet/Chart/Exception.php | 0
PhpOffice/PhpSpreadsheet/Chart/GridLines.php | 442 --
PhpOffice/PhpSpreadsheet/Chart/Layout.php | 338 +-
PhpOffice/PhpSpreadsheet/Chart/Legend.php | 30 +-
PhpOffice/PhpSpreadsheet/Chart/PlotArea.php | 84 +-
PhpOffice/PhpSpreadsheet/Chart/Properties.php | 1072 +++--
.../Chart/Renderer/IRenderer.php | 2 -
.../PhpSpreadsheet/Chart/Renderer/JpGraph.php | 846 +---
.../Chart/Renderer/JpGraphRendererBase.php | 852 ++++
.../Chart/Renderer/MtJpGraphRenderer.php | 36 +
.../Chart/Renderer/PHP Charting Libraries.txt | 7 +-
.../Chart/Renderer/Polyfill.php | 9 -
PhpOffice/PhpSpreadsheet/Chart/Title.php | 49 +-
PhpOffice/PhpSpreadsheet/Chart/TrendLine.php | 226 ++
PhpOffice/PhpSpreadsheet/Collection/Cells.php | 348 +-
.../Collection/CellsFactory.php | 9 +-
.../{Memory.php => Memory/SimpleCache1.php} | 53 +-
.../Collection/Memory/SimpleCache3.php | 109 +
PhpOffice/PhpSpreadsheet/Comment.php | 203 +-
PhpOffice/PhpSpreadsheet/DefinedName.php | 273 ++
.../PhpSpreadsheet/Document/Properties.php | 456 +--
.../PhpSpreadsheet/Document/Security.php | 125 +-
PhpOffice/PhpSpreadsheet/Exception.php | 0
PhpOffice/PhpSpreadsheet/HashTable.php | 87 +-
PhpOffice/PhpSpreadsheet/Helper/Dimension.php | 115 +
PhpOffice/PhpSpreadsheet/Helper/Html.php | 189 +-
PhpOffice/PhpSpreadsheet/Helper/Migrator.php | 333 --
PhpOffice/PhpSpreadsheet/Helper/Sample.php | 83 +-
PhpOffice/PhpSpreadsheet/Helper/Size.php | 52 +
PhpOffice/PhpSpreadsheet/Helper/TextGrid.php | 139 +
PhpOffice/PhpSpreadsheet/IComparable.php | 0
PhpOffice/PhpSpreadsheet/IOFactory.php | 163 +-
PhpOffice/PhpSpreadsheet/NamedFormula.php | 45 +
PhpOffice/PhpSpreadsheet/NamedRange.php | 235 +-
.../PhpSpreadsheet/Reader/BaseReader.php | 88 +-
PhpOffice/PhpSpreadsheet/Reader/Csv.php | 636 +--
.../PhpSpreadsheet/Reader/Csv/Delimiter.php | 151 +
.../Reader/DefaultReadFilter.php | 4 +-
PhpOffice/PhpSpreadsheet/Reader/Exception.php | 0
PhpOffice/PhpSpreadsheet/Reader/Gnumeric.php | 1071 ++---
.../Reader/Gnumeric/PageSetup.php | 139 +
.../Reader/Gnumeric/Properties.php | 164 +
.../PhpSpreadsheet/Reader/Gnumeric/Styles.php | 281 ++
PhpOffice/PhpSpreadsheet/Reader/Html.php | 867 ++--
.../PhpSpreadsheet/Reader/IReadFilter.php | 4 +-
PhpOffice/PhpSpreadsheet/Reader/IReader.php | 39 +-
PhpOffice/PhpSpreadsheet/Reader/Ods.php | 452 ++-
.../PhpSpreadsheet/Reader/Ods/AutoFilter.php | 45 +
.../PhpSpreadsheet/Reader/Ods/BaseLoader.php | 27 +
.../Reader/Ods/DefinedNames.php | 66 +
.../Reader/Ods/FormulaTranslator.php | 97 +
.../Reader/Ods/PageSettings.php | 186 +
.../PhpSpreadsheet/Reader/Ods/Properties.php | 33 +-
.../Reader/Security/XmlScanner.php | 45 +-
PhpOffice/PhpSpreadsheet/Reader/Slk.php | 666 ++--
PhpOffice/PhpSpreadsheet/Reader/Xls.php | 979 +++--
PhpOffice/PhpSpreadsheet/Reader/Xls/Color.php | 2 +-
.../PhpSpreadsheet/Reader/Xls/Color/BIFF5.php | 8 +-
.../PhpSpreadsheet/Reader/Xls/Color/BIFF8.php | 8 +-
.../Reader/Xls/Color/BuiltIn.php | 8 +-
.../Reader/Xls/ConditionalFormatting.php | 49 +
.../Reader/Xls/DataValidationHelper.php | 72 +
.../PhpSpreadsheet/Reader/Xls/ErrorCode.php | 8 +-
.../PhpSpreadsheet/Reader/Xls/Escher.php | 269 +-
PhpOffice/PhpSpreadsheet/Reader/Xls/MD5.php | 70 +-
PhpOffice/PhpSpreadsheet/Reader/Xls/RC4.php | 6 +-
.../Reader/Xls/Style/Border.php | 19 +-
.../Reader/Xls/Style/CellAlignment.php | 50 +
.../Reader/Xls/Style/CellFont.php | 39 +
.../Reader/Xls/Style/FillPattern.php | 9 +-
PhpOffice/PhpSpreadsheet/Reader/Xlsx.php | 1961 ++++-----
.../PhpSpreadsheet/Reader/Xlsx/AutoFilter.php | 59 +-
.../Reader/Xlsx/BaseParserClass.php | 9 +-
.../PhpSpreadsheet/Reader/Xlsx/Chart.php | 1133 +++++-
.../Reader/Xlsx/ColumnAndRowAttributes.php | 125 +-
.../Reader/Xlsx/ConditionalStyles.php | 238 +-
.../Reader/Xlsx/DataValidations.php | 19 +-
.../PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php | 42 +-
.../PhpSpreadsheet/Reader/Xlsx/Namespaces.php | 118 +
.../PhpSpreadsheet/Reader/Xlsx/PageSetup.php | 71 +-
.../PhpSpreadsheet/Reader/Xlsx/Properties.php | 44 +-
.../Reader/Xlsx/SheetViewOptions.php | 78 +-
.../PhpSpreadsheet/Reader/Xlsx/SheetViews.php | 95 +-
.../PhpSpreadsheet/Reader/Xlsx/Styles.php | 373 +-
.../Reader/Xlsx/TableReader.php | 113 +
.../PhpSpreadsheet/Reader/Xlsx/Theme.php | 41 +-
.../Reader/Xlsx/WorkbookView.php | 153 +
PhpOffice/PhpSpreadsheet/Reader/Xml.php | 675 +---
.../Reader/Xml/PageSettings.php | 135 +
.../PhpSpreadsheet/Reader/Xml/Properties.php | 157 +
PhpOffice/PhpSpreadsheet/Reader/Xml/Style.php | 83 +
.../Reader/Xml/Style/Alignment.php | 58 +
.../Reader/Xml/Style/Border.php | 98 +
.../PhpSpreadsheet/Reader/Xml/Style/Fill.php | 63 +
.../PhpSpreadsheet/Reader/Xml/Style/Font.php | 79 +
.../Reader/Xml/Style/NumberFormat.php | 33 +
.../Reader/Xml/Style/StyleBase.php | 32 +
PhpOffice/PhpSpreadsheet/ReferenceHelper.php | 1144 ++++--
.../PhpSpreadsheet/RichText/ITextElement.php | 2 +-
.../PhpSpreadsheet/RichText/RichText.php | 54 +-
PhpOffice/PhpSpreadsheet/RichText/Run.php | 14 +-
.../PhpSpreadsheet/RichText/TextElement.php | 25 +-
PhpOffice/PhpSpreadsheet/Settings.php | 167 +-
PhpOffice/PhpSpreadsheet/Shared/CodePage.php | 208 +-
PhpOffice/PhpSpreadsheet/Shared/Date.php | 272 +-
PhpOffice/PhpSpreadsheet/Shared/Drawing.php | 188 +-
PhpOffice/PhpSpreadsheet/Shared/Escher.php | 0
.../Shared/Escher/DgContainer.php | 4 +-
.../Escher/DgContainer/SpgrContainer.php | 12 +-
.../DgContainer/SpgrContainer/SpContainer.php | 24 +-
.../Shared/Escher/DggContainer.php | 16 +-
.../Escher/DggContainer/BstoreContainer.php | 6 +-
.../DggContainer/BstoreContainer/BSE.php | 14 +-
.../DggContainer/BstoreContainer/BSE/Blip.php | 14 +-
PhpOffice/PhpSpreadsheet/Shared/File.php | 125 +-
PhpOffice/PhpSpreadsheet/Shared/Font.php | 631 ++-
.../PhpSpreadsheet/Shared/IntOrFloat.php | 21 +
.../PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT | 16 -
.../Shared/JAMA/CholeskyDecomposition.php | 147 -
.../Shared/JAMA/EigenvalueDecomposition.php | 861 ----
.../Shared/JAMA/LUDecomposition.php | 285 --
.../PhpSpreadsheet/Shared/JAMA/Matrix.php | 1233 ------
.../Shared/JAMA/QRDecomposition.php | 249 --
.../JAMA/SingularValueDecomposition.php | 528 ---
.../Shared/JAMA/utils/Maths.php | 30 -
PhpOffice/PhpSpreadsheet/Shared/OLE.php | 146 +-
.../Shared/OLE/ChainedBlockStream.php | 17 +-
PhpOffice/PhpSpreadsheet/Shared/OLE/PPS.php | 33 +-
.../PhpSpreadsheet/Shared/OLE/PPS/File.php | 4 +-
.../PhpSpreadsheet/Shared/OLE/PPS/Root.php | 113 +-
PhpOffice/PhpSpreadsheet/Shared/OLERead.php | 30 +-
.../PhpSpreadsheet/Shared/PasswordHasher.php | 104 +-
.../PhpSpreadsheet/Shared/StringHelper.php | 258 +-
PhpOffice/PhpSpreadsheet/Shared/TimeZone.php | 44 +-
.../PhpSpreadsheet/Shared/Trend/BestFit.php | 109 +-
.../Shared/Trend/ExponentialBestFit.php | 23 +-
.../Shared/Trend/LinearBestFit.php | 5 +-
.../Shared/Trend/LogarithmicBestFit.php | 23 +-
.../Shared/Trend/PolynomialBestFit.php | 31 +-
.../Shared/Trend/PowerBestFit.php | 39 +-
.../PhpSpreadsheet/Shared/Trend/Trend.php | 18 +-
PhpOffice/PhpSpreadsheet/Shared/XMLWriter.php | 31 +-
PhpOffice/PhpSpreadsheet/Shared/Xls.php | 106 +-
PhpOffice/PhpSpreadsheet/Spreadsheet.php | 671 ++--
PhpOffice/PhpSpreadsheet/Style/Alignment.php | 243 +-
PhpOffice/PhpSpreadsheet/Style/Border.php | 95 +-
PhpOffice/PhpSpreadsheet/Style/Borders.php | 101 +-
PhpOffice/PhpSpreadsheet/Style/Color.php | 375 +-
.../PhpSpreadsheet/Style/Conditional.php | 144 +-
.../ConditionalFormatting/CellMatcher.php | 313 ++
.../CellStyleAssessor.php | 45 +
.../ConditionalDataBar.php | 93 +
.../ConditionalDataBarExtension.php | 290 ++
.../ConditionalFormatValueObject.php | 78 +
.../ConditionalFormattingRuleExtension.php | 209 +
.../ConditionalFormatting/StyleMerger.php | 118 +
.../Style/ConditionalFormatting/Wizard.php | 95 +
.../ConditionalFormatting/Wizard/Blanks.php | 99 +
.../Wizard/CellValue.php | 200 +
.../Wizard/DateValue.php | 111 +
.../Wizard/Duplicates.php | 78 +
.../ConditionalFormatting/Wizard/Errors.php | 95 +
.../Wizard/Expression.php | 75 +
.../Wizard/TextValue.php | 164 +
.../Wizard/WizardAbstract.php | 199 +
.../Wizard/WizardInterface.php | 25 +
PhpOffice/PhpSpreadsheet/Style/Fill.php | 121 +-
PhpOffice/PhpSpreadsheet/Style/Font.php | 487 ++-
.../PhpSpreadsheet/Style/NumberFormat.php | 540 +--
.../Style/NumberFormat/BaseFormatter.php | 12 +
.../Style/NumberFormat/DateFormatter.php | 182 +
.../Style/NumberFormat/Formatter.php | 165 +
.../Style/NumberFormat/FractionFormatter.php | 67 +
.../Style/NumberFormat/NumberFormatter.php | 278 ++
.../NumberFormat/PercentageFormatter.php | 47 +
PhpOffice/PhpSpreadsheet/Style/Protection.php | 54 +-
PhpOffice/PhpSpreadsheet/Style/Style.php | 354 +-
PhpOffice/PhpSpreadsheet/Style/Supervisor.php | 64 +-
.../PhpSpreadsheet/Worksheet/AutoFilter.php | 809 ++--
.../Worksheet/AutoFilter/Column.php | 160 +-
.../Worksheet/AutoFilter/Column/Rule.php | 197 +-
.../PhpSpreadsheet/Worksheet/BaseDrawing.php | 434 +-
.../PhpSpreadsheet/Worksheet/CellIterator.php | 37 +-
PhpOffice/PhpSpreadsheet/Worksheet/Column.php | 66 +-
.../Worksheet/ColumnCellIterator.php | 104 +-
.../Worksheet/ColumnDimension.php | 80 +-
.../Worksheet/ColumnIterator.php | 51 +-
.../PhpSpreadsheet/Worksheet/Dimension.php | 65 +-
.../PhpSpreadsheet/Worksheet/Drawing.php | 142 +-
.../Worksheet/Drawing/Shadow.php | 60 +-
.../PhpSpreadsheet/Worksheet/HeaderFooter.php | 87 +-
.../Worksheet/HeaderFooterDrawing.php | 0
.../PhpSpreadsheet/Worksheet/Iterator.php | 29 +-
.../Worksheet/MemoryDrawing.php | 213 +-
.../PhpSpreadsheet/Worksheet/PageMargins.php | 78 +-
.../PhpSpreadsheet/Worksheet/PageSetup.php | 246 +-
.../PhpSpreadsheet/Worksheet/Protection.php | 569 ++-
PhpOffice/PhpSpreadsheet/Worksheet/Row.php | 53 +-
.../Worksheet/RowCellIterator.php | 84 +-
.../PhpSpreadsheet/Worksheet/RowDimension.php | 53 +-
.../PhpSpreadsheet/Worksheet/RowIterator.php | 52 +-
.../PhpSpreadsheet/Worksheet/SheetView.php | 78 +-
PhpOffice/PhpSpreadsheet/Worksheet/Table.php | 585 +++
.../PhpSpreadsheet/Worksheet/Table/Column.php | 254 ++
.../Worksheet/Table/TableStyle.php | 230 ++
.../PhpSpreadsheet/Worksheet/Validations.php | 115 +
.../PhpSpreadsheet/Worksheet/Worksheet.php | 1941 +++++----
.../PhpSpreadsheet/Writer/BaseWriter.php | 84 +-
PhpOffice/PhpSpreadsheet/Writer/Csv.php | 228 +-
PhpOffice/PhpSpreadsheet/Writer/Exception.php | 0
PhpOffice/PhpSpreadsheet/Writer/Html.php | 1677 ++++----
PhpOffice/PhpSpreadsheet/Writer/IWriter.php | 31 +-
PhpOffice/PhpSpreadsheet/Writer/Ods.php | 191 +-
.../PhpSpreadsheet/Writer/Ods/AutoFilters.php | 66 +
.../Writer/Ods/Cell/Comment.php | 5 +-
.../PhpSpreadsheet/Writer/Ods/Cell/Style.php | 262 ++
.../PhpSpreadsheet/Writer/Ods/Content.php | 240 +-
.../PhpSpreadsheet/Writer/Ods/Formula.php | 119 +
PhpOffice/PhpSpreadsheet/Writer/Ods/Meta.php | 73 +-
.../PhpSpreadsheet/Writer/Ods/MetaInf.php | 4 +-
.../PhpSpreadsheet/Writer/Ods/Mimetype.php | 6 +-
.../Writer/Ods/NamedExpressions.php | 140 +
.../PhpSpreadsheet/Writer/Ods/Settings.php | 120 +-
.../PhpSpreadsheet/Writer/Ods/Styles.php | 7 +-
.../PhpSpreadsheet/Writer/Ods/Thumbnails.php | 6 +-
.../PhpSpreadsheet/Writer/Ods/WriterPart.php | 4 +-
PhpOffice/PhpSpreadsheet/Writer/Pdf.php | 82 +-
.../PhpSpreadsheet/Writer/Pdf/Dompdf.php | 60 +-
PhpOffice/PhpSpreadsheet/Writer/Pdf/Mpdf.php | 67 +-
PhpOffice/PhpSpreadsheet/Writer/Pdf/Tcpdf.php | 64 +-
PhpOffice/PhpSpreadsheet/Writer/Xls.php | 396 +-
.../PhpSpreadsheet/Writer/Xls/BIFFwriter.php | 8 +-
.../Writer/Xls/CellDataValidation.php | 78 +
.../Writer/Xls/ConditionalHelper.php | 76 +
.../PhpSpreadsheet/Writer/Xls/ErrorCode.php | 28 +
.../PhpSpreadsheet/Writer/Xls/Escher.php | 10 +-
PhpOffice/PhpSpreadsheet/Writer/Xls/Font.php | 27 +-
.../PhpSpreadsheet/Writer/Xls/Parser.php | 181 +-
.../Writer/Xls/Style/CellAlignment.php | 59 +
.../Writer/Xls/Style/CellBorder.php | 39 +
.../Writer/Xls/Style/CellFill.php | 46 +
.../Writer/Xls/Style/ColorMap.php | 90 +
.../PhpSpreadsheet/Writer/Xls/Workbook.php | 155 +-
.../PhpSpreadsheet/Writer/Xls/Worksheet.php | 2081 ++--------
PhpOffice/PhpSpreadsheet/Writer/Xls/Xf.php | 304 +-
PhpOffice/PhpSpreadsheet/Writer/Xlsx.php | 852 ++--
.../PhpSpreadsheet/Writer/Xlsx/AutoFilter.php | 125 +
.../PhpSpreadsheet/Writer/Xlsx/Chart.php | 1564 ++++----
.../PhpSpreadsheet/Writer/Xlsx/Comments.php | 70 +-
.../Writer/Xlsx/ContentTypes.php | 79 +-
.../Writer/Xlsx/DefinedNames.php | 244 ++
.../PhpSpreadsheet/Writer/Xlsx/DocProps.php | 64 +-
.../PhpSpreadsheet/Writer/Xlsx/Drawing.php | 260 +-
.../Writer/Xlsx/FunctionPrefix.php | 194 +
PhpOffice/PhpSpreadsheet/Writer/Xlsx/Rels.php | 246 +-
.../PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php | 9 +-
.../PhpSpreadsheet/Writer/Xlsx/RelsVBA.php | 12 +-
.../Writer/Xlsx/StringTable.php | 216 +-
.../PhpSpreadsheet/Writer/Xlsx/Style.php | 368 +-
.../PhpSpreadsheet/Writer/Xlsx/Table.php | 115 +
.../PhpSpreadsheet/Writer/Xlsx/Theme.php | 35 +-
.../PhpSpreadsheet/Writer/Xlsx/Workbook.php | 262 +-
.../PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 1233 +++---
.../PhpSpreadsheet/Writer/Xlsx/WriterPart.php | 6 +-
PhpOffice/PhpWord/COPYING | 674 ----
PhpOffice/PhpWord/COPYING.LESSER | 165 -
.../PhpWord/Collection/AbstractCollection.php | 20 +-
PhpOffice/PhpWord/Collection/Bookmarks.php | 4 +-
PhpOffice/PhpWord/Collection/Charts.php | 4 +-
PhpOffice/PhpWord/Collection/Comments.php | 4 +-
PhpOffice/PhpWord/Collection/Endnotes.php | 4 +-
PhpOffice/PhpWord/Collection/Footnotes.php | 4 +-
PhpOffice/PhpWord/Collection/Titles.php | 4 +-
.../ComplexType/FootnoteProperties.php | 47 +-
PhpOffice/PhpWord/ComplexType/ProofState.php | 30 +-
PhpOffice/PhpWord/ComplexType/TblWidth.php | 2 +-
.../PhpWord/ComplexType/TrackChangesView.php | 46 +-
.../PhpWord/Element/AbstractContainer.php | 157 +-
PhpOffice/PhpWord/Element/AbstractElement.php | 142 +-
PhpOffice/PhpWord/Element/Bookmark.php | 16 +-
PhpOffice/PhpWord/Element/Cell.php | 16 +-
PhpOffice/PhpWord/Element/Chart.php | 38 +-
PhpOffice/PhpWord/Element/CheckBox.php | 17 +-
PhpOffice/PhpWord/Element/Comment.php | 33 +-
PhpOffice/PhpWord/Element/Endnote.php | 8 +-
PhpOffice/PhpWord/Element/Field.php | 158 +-
PhpOffice/PhpWord/Element/Footer.php | 24 +-
PhpOffice/PhpWord/Element/Footnote.php | 42 +-
PhpOffice/PhpWord/Element/FormField.php | 61 +-
PhpOffice/PhpWord/Element/Header.php | 7 +-
PhpOffice/PhpWord/Element/Image.php | 161 +-
PhpOffice/PhpWord/Element/Line.php | 10 +-
PhpOffice/PhpWord/Element/Link.php | 84 +-
PhpOffice/PhpWord/Element/ListItem.php | 29 +-
PhpOffice/PhpWord/Element/ListItemRun.php | 38 +-
PhpOffice/PhpWord/Element/OLEObject.php | 58 +-
PhpOffice/PhpWord/Element/PageBreak.php | 6 +-
PhpOffice/PhpWord/Element/PreserveText.php | 36 +-
PhpOffice/PhpWord/Element/Row.php | 25 +-
PhpOffice/PhpWord/Element/SDT.php | 51 +-
PhpOffice/PhpWord/Element/Section.php | 134 +-
PhpOffice/PhpWord/Element/Shape.php | 19 +-
PhpOffice/PhpWord/Element/TOC.php | 42 +-
PhpOffice/PhpWord/Element/Table.php | 40 +-
PhpOffice/PhpWord/Element/Text.php | 49 +-
PhpOffice/PhpWord/Element/TextBox.php | 10 +-
PhpOffice/PhpWord/Element/TextBreak.php | 46 +-
PhpOffice/PhpWord/Element/TextRun.php | 23 +-
PhpOffice/PhpWord/Element/Title.php | 27 +-
PhpOffice/PhpWord/Element/TrackChange.php | 29 +-
PhpOffice/PhpWord/Escaper/AbstractEscaper.php | 2 +-
.../PhpWord/Escaper/EscaperInterface.php | 2 +-
PhpOffice/PhpWord/Escaper/RegExp.php | 2 +-
PhpOffice/PhpWord/Escaper/Rtf.php | 7 +-
PhpOffice/PhpWord/Escaper/Xml.php | 5 +-
.../PhpWord/Exception/CopyFileException.php | 4 +-
.../CreateTemporaryFileException.php | 4 +-
PhpOffice/PhpWord/Exception/Exception.php | 4 +-
.../Exception/InvalidImageException.php | 4 +-
.../Exception/InvalidObjectException.php | 4 +-
.../Exception/InvalidStyleException.php | 4 +-
.../UnsupportedImageTypeException.php | 4 +-
PhpOffice/PhpWord/IOFactory.php | 29 +-
PhpOffice/PhpWord/LICENSE | 15 -
PhpOffice/PhpWord/Media.php | 221 +-
PhpOffice/PhpWord/Metadata/Compatibility.php | 12 +-
PhpOffice/PhpWord/Metadata/DocInfo.php | 159 +-
PhpOffice/PhpWord/Metadata/Protection.php | 46 +-
PhpOffice/PhpWord/Metadata/Settings.php | 112 +-
PhpOffice/PhpWord/PhpWord.php | 201 +-
PhpOffice/PhpWord/Reader/AbstractReader.php | 28 +-
PhpOffice/PhpWord/Reader/HTML.php | 11 +-
PhpOffice/PhpWord/Reader/MsDoc.php | 410 +-
PhpOffice/PhpWord/Reader/ODText.php | 25 +-
.../PhpWord/Reader/ODText/AbstractPart.php | 5 +-
PhpOffice/PhpWord/Reader/ODText/Content.php | 24 +-
PhpOffice/PhpWord/Reader/ODText/Meta.php | 25 +-
PhpOffice/PhpWord/Reader/RTF.php | 11 +-
PhpOffice/PhpWord/Reader/RTF/Document.php | 130 +-
PhpOffice/PhpWord/Reader/ReaderInterface.php | 7 +-
PhpOffice/PhpWord/Reader/Word2007.php | 55 +-
.../PhpWord/Reader/Word2007/AbstractPart.php | 336 +-
.../PhpWord/Reader/Word2007/DocPropsApp.php | 12 +-
.../PhpWord/Reader/Word2007/DocPropsCore.php | 36 +-
.../Reader/Word2007/DocPropsCustom.php | 10 +-
.../PhpWord/Reader/Word2007/Document.php | 80 +-
.../PhpWord/Reader/Word2007/Endnotes.php | 8 +-
.../PhpWord/Reader/Word2007/Footnotes.php | 22 +-
.../PhpWord/Reader/Word2007/Numbering.php | 30 +-
.../PhpWord/Reader/Word2007/Settings.php | 69 +-
PhpOffice/PhpWord/Reader/Word2007/Styles.php | 17 +-
PhpOffice/PhpWord/Settings.php | 135 +-
PhpOffice/PhpWord/Shared/AbstractEnum.php | 28 +-
PhpOffice/PhpWord/Shared/Converter.php | 105 +-
PhpOffice/PhpWord/Shared/Css.php | 80 +
PhpOffice/PhpWord/Shared/Drawing.php | 263 ++
PhpOffice/PhpWord/Shared/Html.php | 742 +++-
.../Shared/Microsoft/PasswordEncoder.php | 243 ++
PhpOffice/PhpWord/Shared/OLERead.php | 82 +-
.../PhpWord/Shared/PCLZip/pclzip.lib.php | 23 -
PhpOffice/PhpWord/Shared/Text.php | 252 ++
PhpOffice/PhpWord/Shared/XMLReader.php | 231 ++
PhpOffice/PhpWord/Shared/XMLWriter.php | 187 +
PhpOffice/PhpWord/Shared/ZipArchive.php | 92 +-
PhpOffice/PhpWord/SimpleType/Border.php | 57 +
PhpOffice/PhpWord/SimpleType/DocProtect.php | 15 +-
PhpOffice/PhpWord/SimpleType/Jc.php | 3 +-
PhpOffice/PhpWord/SimpleType/JcTable.php | 2 +-
.../PhpWord/SimpleType/LineSpacingRule.php | 11 +-
PhpOffice/PhpWord/SimpleType/NumberFormat.php | 3 +-
PhpOffice/PhpWord/SimpleType/TblWidth.php | 5 +-
.../PhpWord/SimpleType/TextAlignment.php | 5 +-
PhpOffice/PhpWord/SimpleType/VerticalJc.php | 2 +-
PhpOffice/PhpWord/SimpleType/Zoom.php | 5 +-
PhpOffice/PhpWord/Style.php | 49 +-
PhpOffice/PhpWord/Style/AbstractStyle.php | 116 +-
PhpOffice/PhpWord/Style/Border.php | 143 +-
PhpOffice/PhpWord/Style/Cell.php | 107 +-
PhpOffice/PhpWord/Style/Chart.php | 179 +-
PhpOffice/PhpWord/Style/Extrusion.php | 26 +-
PhpOffice/PhpWord/Style/Fill.php | 15 +-
PhpOffice/PhpWord/Style/Font.php | 374 +-
PhpOffice/PhpWord/Style/Frame.php | 204 +-
PhpOffice/PhpWord/Style/Image.php | 64 +-
PhpOffice/PhpWord/Style/Indentation.php | 60 +-
PhpOffice/PhpWord/Style/Language.php | 64 +-
PhpOffice/PhpWord/Style/Line.php | 75 +-
PhpOffice/PhpWord/Style/LineNumbering.php | 45 +-
PhpOffice/PhpWord/Style/ListItem.php | 108 +-
PhpOffice/PhpWord/Style/Numbering.php | 31 +-
PhpOffice/PhpWord/Style/NumberingLevel.php | 127 +-
PhpOffice/PhpWord/Style/Outline.php | 91 +-
PhpOffice/PhpWord/Style/Paper.php | 43 +-
PhpOffice/PhpWord/Style/Paragraph.php | 308 +-
PhpOffice/PhpWord/Style/Row.php | 63 +-
PhpOffice/PhpWord/Style/Section.php | 205 +-
PhpOffice/PhpWord/Style/Shading.php | 37 +-
PhpOffice/PhpWord/Style/Shadow.php | 22 +-
PhpOffice/PhpWord/Style/Shape.php | 64 +-
PhpOffice/PhpWord/Style/Spacing.php | 81 +-
PhpOffice/PhpWord/Style/TOC.php | 45 +-
PhpOffice/PhpWord/Style/Tab.php | 51 +-
PhpOffice/PhpWord/Style/Table.php | 214 +-
PhpOffice/PhpWord/Style/TablePosition.php | 122 +-
PhpOffice/PhpWord/Style/TextBox.php | 58 +-
PhpOffice/PhpWord/Template.php | 27 -
PhpOffice/PhpWord/TemplateProcessor.php | 472 ++-
PhpOffice/PhpWord/Writer/AbstractWriter.php | 102 +-
PhpOffice/PhpWord/Writer/HTML.php | 42 +-
.../Writer/HTML/Element/AbstractElement.php | 22 +-
.../PhpWord/Writer/HTML/Element/Bookmark.php | 6 +-
.../PhpWord/Writer/HTML/Element/Container.php | 10 +-
.../PhpWord/Writer/HTML/Element/Endnote.php | 6 +-
.../PhpWord/Writer/HTML/Element/Footnote.php | 8 +-
.../PhpWord/Writer/HTML/Element/Image.php | 6 +-
.../PhpWord/Writer/HTML/Element/Link.php | 6 +-
.../PhpWord/Writer/HTML/Element/ListItem.php | 6 +-
.../Writer/HTML/Element/ListItemRun.php | 6 +-
.../PhpWord/Writer/HTML/Element/PageBreak.php | 6 +-
.../PhpWord/Writer/HTML/Element/Table.php | 28 +-
.../PhpWord/Writer/HTML/Element/Text.php | 32 +-
.../PhpWord/Writer/HTML/Element/TextBreak.php | 6 +-
.../PhpWord/Writer/HTML/Element/TextRun.php | 6 +-
.../PhpWord/Writer/HTML/Element/Title.php | 6 +-
.../PhpWord/Writer/HTML/Part/AbstractPart.php | 11 +-
PhpOffice/PhpWord/Writer/HTML/Part/Body.php | 10 +-
PhpOffice/PhpWord/Writer/HTML/Part/Head.php | 66 +-
.../Writer/HTML/Style/AbstractStyle.php | 26 +-
PhpOffice/PhpWord/Writer/HTML/Style/Font.php | 10 +-
.../PhpWord/Writer/HTML/Style/Generic.php | 8 +-
PhpOffice/PhpWord/Writer/HTML/Style/Image.php | 8 +-
.../PhpWord/Writer/HTML/Style/Paragraph.php | 18 +-
PhpOffice/PhpWord/Writer/ODText.php | 30 +-
.../Writer/ODText/Element/AbstractElement.php | 4 +-
.../Writer/ODText/Element/Container.php | 6 +-
.../PhpWord/Writer/ODText/Element/Field.php | 85 +
.../PhpWord/Writer/ODText/Element/Image.php | 10 +-
.../PhpWord/Writer/ODText/Element/Link.php | 10 +-
.../Writer/ODText/Element/PageBreak.php | 10 +-
.../PhpWord/Writer/ODText/Element/Table.php | 24 +-
.../PhpWord/Writer/ODText/Element/Text.php | 69 +-
.../Writer/ODText/Element/TextBreak.php | 8 +-
.../PhpWord/Writer/ODText/Element/TextRun.php | 16 +-
.../PhpWord/Writer/ODText/Element/Title.php | 39 +-
.../Writer/ODText/Part/AbstractPart.php | 18 +-
.../PhpWord/Writer/ODText/Part/Content.php | 217 +-
.../PhpWord/Writer/ODText/Part/Manifest.php | 8 +-
PhpOffice/PhpWord/Writer/ODText/Part/Meta.php | 15 +-
.../PhpWord/Writer/ODText/Part/Mimetype.php | 6 +-
.../PhpWord/Writer/ODText/Part/Styles.php | 156 +-
.../Writer/ODText/Style/AbstractStyle.php | 4 +-
.../PhpWord/Writer/ODText/Style/Font.php | 25 +-
.../PhpWord/Writer/ODText/Style/Image.php | 6 +-
.../PhpWord/Writer/ODText/Style/Paragraph.php | 118 +-
.../PhpWord/Writer/ODText/Style/Section.php | 6 +-
.../PhpWord/Writer/ODText/Style/Table.php | 8 +-
PhpOffice/PhpWord/Writer/PDF.php | 21 +-
.../PhpWord/Writer/PDF/AbstractRenderer.php | 50 +-
PhpOffice/PhpWord/Writer/PDF/DomPDF.php | 22 +-
PhpOffice/PhpWord/Writer/PDF/MPDF.php | 27 +-
PhpOffice/PhpWord/Writer/PDF/TCPDF.php | 25 +-
PhpOffice/PhpWord/Writer/RTF.php | 22 +-
.../Writer/RTF/Element/AbstractElement.php | 23 +-
.../PhpWord/Writer/RTF/Element/Container.php | 6 +-
.../PhpWord/Writer/RTF/Element/Field.php | 4 +-
.../PhpWord/Writer/RTF/Element/Image.php | 6 +-
PhpOffice/PhpWord/Writer/RTF/Element/Link.php | 6 +-
.../PhpWord/Writer/RTF/Element/ListItem.php | 4 +-
.../PhpWord/Writer/RTF/Element/PageBreak.php | 6 +-
.../PhpWord/Writer/RTF/Element/Table.php | 23 +-
PhpOffice/PhpWord/Writer/RTF/Element/Text.php | 8 +-
.../PhpWord/Writer/RTF/Element/TextBreak.php | 6 +-
.../PhpWord/Writer/RTF/Element/TextRun.php | 6 +-
.../PhpWord/Writer/RTF/Element/Title.php | 12 +-
.../PhpWord/Writer/RTF/Part/AbstractPart.php | 6 +-
.../PhpWord/Writer/RTF/Part/Document.php | 40 +-
PhpOffice/PhpWord/Writer/RTF/Part/Header.php | 34 +-
.../Writer/RTF/Style/AbstractStyle.php | 4 +-
PhpOffice/PhpWord/Writer/RTF/Style/Border.php | 25 +-
PhpOffice/PhpWord/Writer/RTF/Style/Font.php | 11 +-
.../PhpWord/Writer/RTF/Style/Indentation.php | 6 +-
.../PhpWord/Writer/RTF/Style/Paragraph.php | 30 +-
.../PhpWord/Writer/RTF/Style/Section.php | 6 +-
PhpOffice/PhpWord/Writer/RTF/Style/Tab.php | 12 +-
PhpOffice/PhpWord/Writer/Word2007.php | 110 +-
.../Word2007/Element/AbstractElement.php | 64 +-
.../Writer/Word2007/Element/Bookmark.php | 8 +-
.../PhpWord/Writer/Word2007/Element/Chart.php | 10 +-
.../Writer/Word2007/Element/CheckBox.php | 6 +-
.../Writer/Word2007/Element/Container.php | 17 +-
.../Writer/Word2007/Element/Endnote.php | 6 +-
.../PhpWord/Writer/Word2007/Element/Field.php | 24 +-
.../Writer/Word2007/Element/Footnote.php | 8 +-
.../Writer/Word2007/Element/FormField.php | 23 +-
.../PhpWord/Writer/Word2007/Element/Image.php | 14 +-
.../PhpWord/Writer/Word2007/Element/Line.php | 6 +-
.../PhpWord/Writer/Word2007/Element/Link.php | 6 +-
.../Writer/Word2007/Element/ListItem.php | 6 +-
.../Writer/Word2007/Element/ListItemRun.php | 20 +-
.../Writer/Word2007/Element/OLEObject.php | 6 +-
.../Writer/Word2007/Element/PageBreak.php | 6 +-
.../Word2007/Element/ParagraphAlignment.php | 4 +-
.../Writer/Word2007/Element/PreserveText.php | 8 +-
.../PhpWord/Writer/Word2007/Element/SDT.php | 28 +-
.../PhpWord/Writer/Word2007/Element/Shape.php | 49 +-
.../PhpWord/Writer/Word2007/Element/TOC.php | 31 +-
.../PhpWord/Writer/Word2007/Element/Table.php | 25 +-
.../Word2007/Element/TableAlignment.php | 4 +-
.../PhpWord/Writer/Word2007/Element/Text.php | 14 +-
.../Writer/Word2007/Element/TextBox.php | 6 +-
.../Writer/Word2007/Element/TextBreak.php | 6 +-
.../Writer/Word2007/Element/TextRun.php | 6 +-
.../PhpWord/Writer/Word2007/Element/Title.php | 6 +-
.../Writer/Word2007/Part/AbstractPart.php | 27 +-
.../PhpWord/Writer/Word2007/Part/Chart.php | 139 +-
.../PhpWord/Writer/Word2007/Part/Comments.php | 18 +-
.../Writer/Word2007/Part/ContentTypes.php | 38 +-
.../Writer/Word2007/Part/DocPropsApp.php | 6 +-
.../Writer/Word2007/Part/DocPropsCore.php | 6 +-
.../Writer/Word2007/Part/DocPropsCustom.php | 15 +-
.../PhpWord/Writer/Word2007/Part/Document.php | 20 +-
.../PhpWord/Writer/Word2007/Part/Endnotes.php | 12 +-
.../Writer/Word2007/Part/FontTable.php | 14 +-
.../PhpWord/Writer/Word2007/Part/Footer.php | 13 +-
.../Writer/Word2007/Part/Footnotes.php | 30 +-
.../PhpWord/Writer/Word2007/Part/Header.php | 6 +-
.../Writer/Word2007/Part/Numbering.php | 44 +-
.../PhpWord/Writer/Word2007/Part/Rels.php | 37 +-
.../Writer/Word2007/Part/RelsDocument.php | 20 +-
.../PhpWord/Writer/Word2007/Part/RelsPart.php | 15 +-
.../PhpWord/Writer/Word2007/Part/Settings.php | 204 +-
.../PhpWord/Writer/Word2007/Part/Styles.php | 36 +-
.../PhpWord/Writer/Word2007/Part/Theme.php | 21 +-
.../Writer/Word2007/Part/WebSettings.php | 10 +-
.../Writer/Word2007/Style/AbstractStyle.php | 61 +-
.../PhpWord/Writer/Word2007/Style/Cell.php | 24 +-
.../Writer/Word2007/Style/Extrusion.php | 6 +-
.../PhpWord/Writer/Word2007/Style/Fill.php | 6 +-
.../PhpWord/Writer/Word2007/Style/Font.php | 16 +-
.../PhpWord/Writer/Word2007/Style/Frame.php | 58 +-
.../PhpWord/Writer/Word2007/Style/Image.php | 4 +-
.../Writer/Word2007/Style/Indentation.php | 10 +-
.../PhpWord/Writer/Word2007/Style/Line.php | 23 +-
.../Writer/Word2007/Style/LineNumbering.php | 8 +-
.../Writer/Word2007/Style/MarginBorder.php | 39 +-
.../PhpWord/Writer/Word2007/Style/Outline.php | 6 +-
.../Writer/Word2007/Style/Paragraph.php | 27 +-
.../PhpWord/Writer/Word2007/Style/Row.php | 8 +-
.../PhpWord/Writer/Word2007/Style/Section.php | 34 +-
.../PhpWord/Writer/Word2007/Style/Shading.php | 12 +-
.../PhpWord/Writer/Word2007/Style/Shadow.php | 6 +-
.../PhpWord/Writer/Word2007/Style/Shape.php | 8 +-
.../PhpWord/Writer/Word2007/Style/Spacing.php | 14 +-
.../PhpWord/Writer/Word2007/Style/Tab.php | 6 +-
.../PhpWord/Writer/Word2007/Style/Table.php | 53 +-
.../Writer/Word2007/Style/TablePosition.php | 32 +-
.../PhpWord/Writer/Word2007/Style/TextBox.php | 8 +-
PhpOffice/PhpWord/Writer/WriterInterface.php | 6 +-
PhpOffice/PhpWord/resources/doc.png | Bin
PhpOffice/PhpWord/resources/ppt.png | Bin
PhpOffice/PhpWord/resources/xls.png | Bin
Psr/SimpleCache/CacheException.php | 10 +
Psr/SimpleCache/CacheInterface.php | 114 +
Psr/SimpleCache/InvalidArgumentException.php | 13 +
Psr/SimpleCache/LICENSE.md | 21 +
ZipStream/Bigint.php | 174 +
ZipStream/DeflateStream.php | 27 +
ZipStream/Exception.php | 12 +
ZipStream/Exception/EncodingException.php | 14 +
ZipStream/Exception/FileNotFoundException.php | 23 +
.../Exception/FileNotReadableException.php | 23 +
.../IncompatibleOptionsException.php | 14 +
ZipStream/Exception/OverflowException.php | 18 +
.../Exception/StreamNotReadableException.php | 23 +
ZipStream/File.php | 470 +++
.../PhpSpreadsheet => ZipStream}/LICENSE | 5 +-
ZipStream/Option/Archive.php | 276 ++
ZipStream/Option/File.php | 122 +
ZipStream/Option/Method.php | 23 +
ZipStream/Option/Version.php | 27 +
ZipStream/Stream.php | 265 ++
ZipStream/ZipStream.php | 608 +++
1010 files changed, 94009 insertions(+), 61354 deletions(-)
create mode 100644 Laminas/Escaper/Escaper.php
create mode 100644 Laminas/Escaper/Exception/ExceptionInterface.php
create mode 100644 Laminas/Escaper/Exception/InvalidArgumentException.php
create mode 100644 Laminas/Escaper/Exception/RuntimeException.php
create mode 100644 Laminas/Escaper/LICENSE.md
create mode 100644 MyCLabs/Enum/Enum.php
create mode 100644 MyCLabs/LICENSE
mode change 100755 => 100644 PhpOffice/Common/Adapter/Zip/PclZipAdapter.php
mode change 100755 => 100644 PhpOffice/Common/Adapter/Zip/ZipArchiveAdapter.php
mode change 100755 => 100644 PhpOffice/Common/Adapter/Zip/ZipInterface.php
mode change 100755 => 100644 PhpOffice/Common/Autoloader.php
delete mode 100755 PhpOffice/Common/COPYING
delete mode 100755 PhpOffice/Common/COPYING.LESSER
mode change 100755 => 100644 PhpOffice/Common/Drawing.php
mode change 100755 => 100644 PhpOffice/Common/File.php
mode change 100755 => 100644 PhpOffice/Common/Font.php
delete mode 100755 PhpOffice/Common/LICENSE
mode change 100755 => 100644 PhpOffice/Common/Microsoft/OLERead.php
mode change 100755 => 100644 PhpOffice/Common/Microsoft/PasswordEncoder.php
mode change 100755 => 100644 PhpOffice/Common/Text.php
mode change 100755 => 100644 PhpOffice/Common/XMLReader.php
mode change 100755 => 100644 PhpOffice/Common/XMLWriter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/AbstractShape.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Autoloader.php
delete mode 100755 PhpOffice/PhpPresentation/COPYING
delete mode 100755 PhpOffice/PhpPresentation/COPYING.LESSER
mode change 100755 => 100644 PhpOffice/PhpPresentation/ComparableInterface.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/DocumentLayout.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/DocumentProperties.php
create mode 100644 PhpOffice/PhpPresentation/Exception/DirectoryNotFoundException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/FeatureNotImplementedException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/FileCopyException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/FileNotFoundException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/FileRemoveException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/InvalidClassException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/InvalidFileFormatException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/InvalidParameterException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/OutOfBoundsException.php
rename PhpOffice/PhpPresentation/{Shape/Drawing.php => Exception/PhpPresentationException.php} (73%)
mode change 100755 => 100644
create mode 100644 PhpOffice/PhpPresentation/Exception/ShapeContainerAlreadyAssignedException.php
create mode 100644 PhpOffice/PhpPresentation/Exception/UnauthorizedMimetypeException.php
rename PhpOffice/PhpPresentation/{Shape/MemoryDrawing.php => Exception/UndefinedChartTypeException.php} (67%)
mode change 100755 => 100644
mode change 100755 => 100644 PhpOffice/PhpPresentation/GeometryCalculator.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/HashTable.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/IOFactory.php
delete mode 100755 PhpOffice/PhpPresentation/LICENSE
mode change 100755 => 100644 PhpOffice/PhpPresentation/PhpPresentation.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/PresentationProperties.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Reader/ODPresentation.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Reader/PowerPoint2007.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Reader/PowerPoint97.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Reader/ReaderInterface.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Reader/Serialized.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/AbstractGraphic.php
create mode 100644 PhpOffice/PhpPresentation/Shape/AutoShape.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Axis.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Gridlines.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Legend.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Marker.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/PlotArea.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Series.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Title.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractType.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php
create mode 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeLine.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Area.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Bar.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Bar3D.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Doughnut.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Line.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Pie.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Pie3D.php
create mode 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Radar.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/Type/Scatter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Chart/View3D.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Comment.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Comment/Author.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Drawing/Base64.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Drawing/File.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Drawing/Gd.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Drawing/ZipFile.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Group.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Hyperlink.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Line.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Media.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Placeholder.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/RichText.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/RichText/BreakElement.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/RichText/Paragraph.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/RichText/Run.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/RichText/TextElement.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/RichText/TextElementInterface.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Table.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Table/Cell.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Shape/Table/Row.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/ShapeContainerInterface.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/AbstractBackground.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/AbstractSlide.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Animation.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Background/Color.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Background/Image.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Background/SchemeColor.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Iterator.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Layout.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Note.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/SlideLayout.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/SlideMaster.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Slide/Transition.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Alignment.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Border.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Borders.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Bullet.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Color.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/ColorMap.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Fill.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Outline.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/SchemeColor.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/Shadow.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Style/TextStyle.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/AbstractDecoratorWriter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/AbstractWriter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/Content.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/Meta.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/Mimetype.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/ObjectsChart.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/Pictures.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/Styles.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsCustom.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php
delete mode 100755 PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php
delete mode 100755 PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php
delete mode 100755 PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptCharts.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptComments.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptMedia.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlides.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptTheme.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/PowerPoint2007/Relationships.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/Serialized.php
mode change 100755 => 100644 PhpOffice/PhpPresentation/Writer/WriterInterface.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/ArrayEnabled.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/BinaryComparison.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Calculation.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Category.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Database.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DAverage.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DCount.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DCountA.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DGet.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DMax.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DMin.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DProduct.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DStDev.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DStDevP.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DSum.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DVar.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DVarP.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTime.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/Logger.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselI.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselK.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselY.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/BitWise.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/Compare.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/Complex.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/Constants.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/Erf.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Engineering/ErfC.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Exception.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/ExceptionHandler.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Amortization.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Constants.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Coupons.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Depreciation.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Dollar.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Helpers.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/InterestRate.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/FormulaParser.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/FormulaToken.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Functions.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Information/ErrorValue.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Information/ExcelError.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Information/Value.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Logical.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Logical/Boolean.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Logical/Conditional.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Logical/Operations.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Address.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Filter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Formula.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Offset.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Selection.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Sort.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Unique.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Angle.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Base.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Exp.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Floor.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Operations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Random.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Roman.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Round.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sign.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sum.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Averages.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Conditional.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Confidence.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Counts.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Deviations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Maximum.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Minimum.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Permutations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Size.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Standardize.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Trends.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Statistical/Variances.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Concatenate.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Extract.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Format.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Helpers.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Replace.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Search.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Text.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/TextData/Trim.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/Token/Stack.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Web.php
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/Web/Service.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Calculation/functionlist.txt
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/Translations.xlsx
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/bg/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/bg/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/cs/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/cs/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/da/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/da/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/de/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/de/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/en/uk/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/es/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/es/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/fi/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/fi/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/fr/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/fr/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/hu/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/hu/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/it/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/it/functions
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/nb/config
create mode 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/nb/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/nl/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/nl/functions
delete mode 100755 PhpOffice/PhpSpreadsheet/Calculation/locale/no/config
delete mode 100755 PhpOffice/PhpSpreadsheet/Calculation/locale/no/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/pl/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/pl/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/pt/br/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/pt/br/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/pt/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/pt/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/ru/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/ru/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/sv/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/sv/functions
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/tr/config
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Calculation/locale/tr/functions
create mode 100644 PhpOffice/PhpSpreadsheet/Cell/AddressHelper.php
create mode 100644 PhpOffice/PhpSpreadsheet/Cell/AddressRange.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/AdvancedValueBinder.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/Cell.php
create mode 100644 PhpOffice/PhpSpreadsheet/Cell/CellAddress.php
create mode 100644 PhpOffice/PhpSpreadsheet/Cell/CellRange.php
create mode 100644 PhpOffice/PhpSpreadsheet/Cell/ColumnRange.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/Coordinate.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/DataType.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/DataValidation.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/DataValidator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/DefaultValueBinder.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/Hyperlink.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/IValueBinder.php
create mode 100644 PhpOffice/PhpSpreadsheet/Cell/RowRange.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Cell/StringValueBinder.php
create mode 100644 PhpOffice/PhpSpreadsheet/CellReferenceHelper.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Axis.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Chart.php
create mode 100644 PhpOffice/PhpSpreadsheet/Chart/ChartColor.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/DataSeries.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/DataSeriesValues.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Exception.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/GridLines.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Layout.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Legend.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/PlotArea.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Properties.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Renderer/IRenderer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Renderer/JpGraph.php
create mode 100644 PhpOffice/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
create mode 100644 PhpOffice/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt
delete mode 100755 PhpOffice/PhpSpreadsheet/Chart/Renderer/Polyfill.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Chart/Title.php
create mode 100644 PhpOffice/PhpSpreadsheet/Chart/TrendLine.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Collection/Cells.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Collection/CellsFactory.php
rename PhpOffice/PhpSpreadsheet/Collection/{Memory.php => Memory/SimpleCache1.php} (58%)
mode change 100755 => 100644
create mode 100644 PhpOffice/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Comment.php
create mode 100644 PhpOffice/PhpSpreadsheet/DefinedName.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Document/Properties.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Document/Security.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Exception.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/HashTable.php
create mode 100644 PhpOffice/PhpSpreadsheet/Helper/Dimension.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Helper/Html.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Helper/Migrator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Helper/Sample.php
create mode 100644 PhpOffice/PhpSpreadsheet/Helper/Size.php
create mode 100644 PhpOffice/PhpSpreadsheet/Helper/TextGrid.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/IComparable.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/IOFactory.php
create mode 100644 PhpOffice/PhpSpreadsheet/NamedFormula.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/NamedRange.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/BaseReader.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Csv.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Csv/Delimiter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/DefaultReadFilter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Exception.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Gnumeric.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Gnumeric/Properties.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Gnumeric/Styles.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Html.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/IReadFilter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/IReader.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Ods.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Ods/AutoFilter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Ods/BaseLoader.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Ods/DefinedNames.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Ods/PageSettings.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Ods/Properties.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Security/XmlScanner.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Slk.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Color.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/ErrorCode.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Escher.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/MD5.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/RC4.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Style/Border.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Style/CellFont.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/Chart.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/Namespaces.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/Properties.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/Styles.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/TableReader.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/Theme.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Reader/Xml.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/PageSettings.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Properties.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style/Alignment.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style/Border.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style/Fill.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style/Font.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php
create mode 100644 PhpOffice/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/ReferenceHelper.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/RichText/ITextElement.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/RichText/RichText.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/RichText/Run.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/RichText/TextElement.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Settings.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/CodePage.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Date.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Drawing.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DgContainer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DggContainer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/File.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Font.php
create mode 100644 PhpOffice/PhpSpreadsheet/Shared/IntOrFloat.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/CholeskyDecomposition.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/Matrix.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php
delete mode 100755 PhpOffice/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/OLE.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/OLE/PPS.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/OLE/PPS/File.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/OLE/PPS/Root.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/OLERead.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/PasswordHasher.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/StringHelper.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/TimeZone.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/BestFit.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Trend/Trend.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/XMLWriter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Shared/Xls.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Spreadsheet.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Alignment.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Border.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Borders.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Color.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Conditional.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardInterface.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Fill.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat/Formatter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Protection.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Style.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Style/Supervisor.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/AutoFilter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/BaseDrawing.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/CellIterator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Column.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/ColumnDimension.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/ColumnIterator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Dimension.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Drawing.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/HeaderFooter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Iterator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/MemoryDrawing.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/PageMargins.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/PageSetup.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Protection.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Row.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/RowCellIterator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/RowDimension.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/RowIterator.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/SheetView.php
create mode 100644 PhpOffice/PhpSpreadsheet/Worksheet/Table.php
create mode 100644 PhpOffice/PhpSpreadsheet/Worksheet/Table/Column.php
create mode 100644 PhpOffice/PhpSpreadsheet/Worksheet/Table/TableStyle.php
create mode 100644 PhpOffice/PhpSpreadsheet/Worksheet/Validations.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Worksheet/Worksheet.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/BaseWriter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Csv.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Exception.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Html.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/IWriter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/AutoFilters.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Cell/Comment.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Cell/Style.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Content.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Formula.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Meta.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/MetaInf.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Mimetype.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/NamedExpressions.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Settings.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Styles.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/Thumbnails.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Ods/WriterPart.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Pdf.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Pdf/Dompdf.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Pdf/Mpdf.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Pdf/Tcpdf.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/CellDataValidation.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/ErrorCode.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Escher.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Font.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Parser.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Style/CellFill.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Workbook.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Worksheet.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xls/Xf.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Chart.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Comments.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/DocProps.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Drawing.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Rels.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/StringTable.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Style.php
create mode 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Table.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Theme.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Workbook.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
mode change 100755 => 100644 PhpOffice/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
delete mode 100755 PhpOffice/PhpWord/COPYING
delete mode 100755 PhpOffice/PhpWord/COPYING.LESSER
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/AbstractCollection.php
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/Bookmarks.php
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/Charts.php
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/Comments.php
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/Endnotes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/Footnotes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Collection/Titles.php
mode change 100755 => 100644 PhpOffice/PhpWord/ComplexType/FootnoteProperties.php
mode change 100755 => 100644 PhpOffice/PhpWord/ComplexType/ProofState.php
mode change 100755 => 100644 PhpOffice/PhpWord/ComplexType/TblWidth.php
mode change 100755 => 100644 PhpOffice/PhpWord/ComplexType/TrackChangesView.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/AbstractContainer.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/AbstractElement.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Bookmark.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Cell.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Chart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/CheckBox.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Comment.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Endnote.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Field.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Footer.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Footnote.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/FormField.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Header.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Line.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Link.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/ListItem.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/ListItemRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/OLEObject.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/PageBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/PreserveText.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Row.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/SDT.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Section.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Shape.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/TOC.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Text.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/TextBox.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/TextBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/TextRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/Title.php
mode change 100755 => 100644 PhpOffice/PhpWord/Element/TrackChange.php
mode change 100755 => 100644 PhpOffice/PhpWord/Escaper/AbstractEscaper.php
mode change 100755 => 100644 PhpOffice/PhpWord/Escaper/EscaperInterface.php
mode change 100755 => 100644 PhpOffice/PhpWord/Escaper/RegExp.php
mode change 100755 => 100644 PhpOffice/PhpWord/Escaper/Rtf.php
mode change 100755 => 100644 PhpOffice/PhpWord/Escaper/Xml.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/CopyFileException.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/CreateTemporaryFileException.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/Exception.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/InvalidImageException.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/InvalidObjectException.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/InvalidStyleException.php
mode change 100755 => 100644 PhpOffice/PhpWord/Exception/UnsupportedImageTypeException.php
mode change 100755 => 100644 PhpOffice/PhpWord/IOFactory.php
delete mode 100755 PhpOffice/PhpWord/LICENSE
mode change 100755 => 100644 PhpOffice/PhpWord/Media.php
mode change 100755 => 100644 PhpOffice/PhpWord/Metadata/Compatibility.php
mode change 100755 => 100644 PhpOffice/PhpWord/Metadata/DocInfo.php
mode change 100755 => 100644 PhpOffice/PhpWord/Metadata/Protection.php
mode change 100755 => 100644 PhpOffice/PhpWord/Metadata/Settings.php
mode change 100755 => 100644 PhpOffice/PhpWord/PhpWord.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/AbstractReader.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/HTML.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/MsDoc.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/ODText.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/ODText/AbstractPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/ODText/Content.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/ODText/Meta.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/RTF.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/RTF/Document.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/ReaderInterface.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/AbstractPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/DocPropsApp.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/DocPropsCore.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/DocPropsCustom.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/Document.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/Endnotes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/Footnotes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/Numbering.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/Settings.php
mode change 100755 => 100644 PhpOffice/PhpWord/Reader/Word2007/Styles.php
mode change 100755 => 100644 PhpOffice/PhpWord/Settings.php
mode change 100755 => 100644 PhpOffice/PhpWord/Shared/AbstractEnum.php
mode change 100755 => 100644 PhpOffice/PhpWord/Shared/Converter.php
create mode 100644 PhpOffice/PhpWord/Shared/Css.php
create mode 100644 PhpOffice/PhpWord/Shared/Drawing.php
mode change 100755 => 100644 PhpOffice/PhpWord/Shared/Html.php
create mode 100644 PhpOffice/PhpWord/Shared/Microsoft/PasswordEncoder.php
mode change 100755 => 100644 PhpOffice/PhpWord/Shared/OLERead.php
mode change 100755 => 100644 PhpOffice/PhpWord/Shared/PCLZip/pclzip.lib.php
create mode 100644 PhpOffice/PhpWord/Shared/Text.php
create mode 100644 PhpOffice/PhpWord/Shared/XMLReader.php
create mode 100644 PhpOffice/PhpWord/Shared/XMLWriter.php
mode change 100755 => 100644 PhpOffice/PhpWord/Shared/ZipArchive.php
create mode 100644 PhpOffice/PhpWord/SimpleType/Border.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/DocProtect.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/Jc.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/JcTable.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/LineSpacingRule.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/NumberFormat.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/TblWidth.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/TextAlignment.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/VerticalJc.php
mode change 100755 => 100644 PhpOffice/PhpWord/SimpleType/Zoom.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/AbstractStyle.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Border.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Cell.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Chart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Extrusion.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Fill.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Frame.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Indentation.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Language.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Line.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/LineNumbering.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/ListItem.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Numbering.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/NumberingLevel.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Outline.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Paper.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Paragraph.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Row.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Section.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Shading.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Shadow.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Shape.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Spacing.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/TOC.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Tab.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/TablePosition.php
mode change 100755 => 100644 PhpOffice/PhpWord/Style/TextBox.php
delete mode 100755 PhpOffice/PhpWord/Template.php
mode change 100755 => 100644 PhpOffice/PhpWord/TemplateProcessor.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/AbstractWriter.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/AbstractElement.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Bookmark.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Container.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Endnote.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Footnote.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Link.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/ListItem.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/ListItemRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/PageBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Text.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/TextBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/TextRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Element/Title.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Part/AbstractPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Part/Body.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Part/Head.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Style/AbstractStyle.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Style/Generic.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Style/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/HTML/Style/Paragraph.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/AbstractElement.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/Container.php
create mode 100644 PhpOffice/PhpWord/Writer/ODText/Element/Field.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/Link.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/PageBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/Text.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/TextBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/TextRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Element/Title.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Part/AbstractPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Part/Content.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Part/Manifest.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Part/Meta.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Part/Mimetype.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Part/Styles.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Style/AbstractStyle.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Style/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Style/Paragraph.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Style/Section.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/ODText/Style/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/PDF.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/PDF/AbstractRenderer.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/PDF/DomPDF.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/PDF/MPDF.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/PDF/TCPDF.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/AbstractElement.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Container.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Field.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Link.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/ListItem.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/PageBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Text.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/TextBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/TextRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Element/Title.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Part/AbstractPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Part/Document.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Part/Header.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/AbstractStyle.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/Border.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/Indentation.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/Paragraph.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/Section.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/RTF/Style/Tab.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/AbstractElement.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Bookmark.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Chart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/CheckBox.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Container.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Endnote.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Field.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Footnote.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/FormField.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Line.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Link.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/ListItem.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/ListItemRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/OLEObject.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/PageBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/ParagraphAlignment.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/PreserveText.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/SDT.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Shape.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/TOC.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/TableAlignment.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Text.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/TextBox.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/TextBreak.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/TextRun.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Element/Title.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/AbstractPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Chart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Comments.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/ContentTypes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/DocPropsApp.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/DocPropsCore.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/DocPropsCustom.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Document.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Endnotes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/FontTable.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Footer.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Footnotes.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Header.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Numbering.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Rels.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/RelsDocument.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/RelsPart.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Settings.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Styles.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/Theme.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Part/WebSettings.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/AbstractStyle.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Cell.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Extrusion.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Fill.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Font.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Frame.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Image.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Indentation.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Line.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/LineNumbering.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/MarginBorder.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Outline.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Paragraph.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Row.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Section.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Shading.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Shadow.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Shape.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Spacing.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Tab.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/Table.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/TablePosition.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/Word2007/Style/TextBox.php
mode change 100755 => 100644 PhpOffice/PhpWord/Writer/WriterInterface.php
mode change 100755 => 100644 PhpOffice/PhpWord/resources/doc.png
mode change 100755 => 100644 PhpOffice/PhpWord/resources/ppt.png
mode change 100755 => 100644 PhpOffice/PhpWord/resources/xls.png
create mode 100644 Psr/SimpleCache/CacheException.php
create mode 100644 Psr/SimpleCache/CacheInterface.php
create mode 100644 Psr/SimpleCache/InvalidArgumentException.php
create mode 100644 Psr/SimpleCache/LICENSE.md
create mode 100644 ZipStream/Bigint.php
create mode 100644 ZipStream/DeflateStream.php
create mode 100644 ZipStream/Exception.php
create mode 100644 ZipStream/Exception/EncodingException.php
create mode 100644 ZipStream/Exception/FileNotFoundException.php
create mode 100644 ZipStream/Exception/FileNotReadableException.php
create mode 100644 ZipStream/Exception/IncompatibleOptionsException.php
create mode 100644 ZipStream/Exception/OverflowException.php
create mode 100644 ZipStream/Exception/StreamNotReadableException.php
create mode 100644 ZipStream/File.php
rename {PhpOffice/PhpSpreadsheet => ZipStream}/LICENSE (81%)
mode change 100755 => 100644
create mode 100644 ZipStream/Option/Archive.php
create mode 100644 ZipStream/Option/File.php
create mode 100644 ZipStream/Option/Method.php
create mode 100644 ZipStream/Option/Version.php
create mode 100644 ZipStream/Stream.php
create mode 100644 ZipStream/ZipStream.php
diff --git a/Laminas/Escaper/Escaper.php b/Laminas/Escaper/Escaper.php
new file mode 100644
index 0000000..d6a02e1
--- /dev/null
+++ b/Laminas/Escaper/Escaper.php
@@ -0,0 +1,412 @@
+
+ */
+ protected static $htmlNamedEntityMap = [
+ 34 => 'quot', // quotation mark
+ 38 => 'amp', // ampersand
+ 60 => 'lt', // less-than sign
+ 62 => 'gt', // greater-than sign
+ ];
+
+ /**
+ * Current encoding for escaping. If not UTF-8, we convert strings from this encoding
+ * pre-escaping and back to this encoding post-escaping.
+ *
+ * @var string
+ */
+ protected $encoding = 'utf-8';
+
+ /**
+ * Holds the value of the special flags passed as second parameter to
+ * htmlspecialchars().
+ *
+ * @var int
+ */
+ protected $htmlSpecialCharsFlags;
+
+ /**
+ * Static Matcher which escapes characters for HTML Attribute contexts
+ *
+ * @var callable
+ * @psalm-var callable(array):string
+ */
+ protected $htmlAttrMatcher;
+
+ /**
+ * Static Matcher which escapes characters for Javascript contexts
+ *
+ * @var callable
+ * @psalm-var callable(array):string
+ */
+ protected $jsMatcher;
+
+ /**
+ * Static Matcher which escapes characters for CSS Attribute contexts
+ *
+ * @var callable
+ * @psalm-var callable(array):string
+ */
+ protected $cssMatcher;
+
+ /**
+ * List of all encoding supported by this class
+ *
+ * @var array
+ */
+ protected $supportedEncodings = [
+ 'iso-8859-1',
+ 'iso8859-1',
+ 'iso-8859-5',
+ 'iso8859-5',
+ 'iso-8859-15',
+ 'iso8859-15',
+ 'utf-8',
+ 'cp866',
+ 'ibm866',
+ '866',
+ 'cp1251',
+ 'windows-1251',
+ 'win-1251',
+ '1251',
+ 'cp1252',
+ 'windows-1252',
+ '1252',
+ 'koi8-r',
+ 'koi8-ru',
+ 'koi8r',
+ 'big5',
+ '950',
+ 'gb2312',
+ '936',
+ 'big5-hkscs',
+ 'shift_jis',
+ 'sjis',
+ 'sjis-win',
+ 'cp932',
+ '932',
+ 'euc-jp',
+ 'eucjp',
+ 'eucjp-win',
+ 'macroman',
+ ];
+
+ /**
+ * Constructor: Single parameter allows setting of global encoding for use by
+ * the current object.
+ *
+ * @throws Exception\InvalidArgumentException
+ */
+ public function __construct(?string $encoding = null)
+ {
+ if ($encoding !== null) {
+ if ($encoding === '') {
+ throw new Exception\InvalidArgumentException(
+ static::class . ' constructor parameter does not allow a blank value'
+ );
+ }
+
+ $encoding = strtolower($encoding);
+ if (! in_array($encoding, $this->supportedEncodings)) {
+ throw new Exception\InvalidArgumentException(
+ 'Value of \'' . $encoding . '\' passed to ' . static::class
+ . ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'
+ );
+ }
+
+ $this->encoding = $encoding;
+ }
+
+ // We take advantage of ENT_SUBSTITUTE flag to correctly deal with invalid UTF-8 sequences.
+ $this->htmlSpecialCharsFlags = ENT_QUOTES | ENT_SUBSTITUTE;
+
+ // set matcher callbacks
+ $this->htmlAttrMatcher = [$this, 'htmlAttrMatcher'];
+ $this->jsMatcher = [$this, 'jsMatcher'];
+ $this->cssMatcher = [$this, 'cssMatcher'];
+ }
+
+ /**
+ * Return the encoding that all output/input is expected to be encoded in.
+ *
+ * @return string
+ */
+ public function getEncoding()
+ {
+ return $this->encoding;
+ }
+
+ /**
+ * Escape a string for the HTML Body context where there are very few characters
+ * of special meaning. Internally this will use htmlspecialchars().
+ *
+ * @return string
+ */
+ public function escapeHtml(string $string)
+ {
+ return htmlspecialchars($string, $this->htmlSpecialCharsFlags, $this->encoding);
+ }
+
+ /**
+ * Escape a string for the HTML Attribute context. We use an extended set of characters
+ * to escape that are not covered by htmlspecialchars() to cover cases where an attribute
+ * might be unquoted or quoted illegally (e.g. backticks are valid quotes for IE).
+ *
+ * @return string
+ */
+ public function escapeHtmlAttr(string $string)
+ {
+ $string = $this->toUtf8($string);
+ if ($string === '' || ctype_digit($string)) {
+ return $string;
+ }
+
+ $result = preg_replace_callback('/[^a-z0-9,\.\-_]/iSu', $this->htmlAttrMatcher, $string);
+ return $this->fromUtf8($result);
+ }
+
+ /**
+ * Escape a string for the Javascript context. This does not use json_encode(). An extended
+ * set of characters are escaped beyond ECMAScript's rules for Javascript literal string
+ * escaping in order to prevent misinterpretation of Javascript as HTML leading to the
+ * injection of special characters and entities. The escaping used should be tolerant
+ * of cases where HTML escaping was not applied on top of Javascript escaping correctly.
+ * Backslash escaping is not used as it still leaves the escaped character as-is and so
+ * is not useful in a HTML context.
+ *
+ * @return string
+ */
+ public function escapeJs(string $string)
+ {
+ $string = $this->toUtf8($string);
+ if ($string === '' || ctype_digit($string)) {
+ return $string;
+ }
+
+ $result = preg_replace_callback('/[^a-z0-9,\._]/iSu', $this->jsMatcher, $string);
+ return $this->fromUtf8($result);
+ }
+
+ /**
+ * Escape a string for the URI or Parameter contexts. This should not be used to escape
+ * an entire URI - only a subcomponent being inserted. The function is a simple proxy
+ * to rawurlencode() which now implements RFC 3986 since PHP 5.3 completely.
+ *
+ * @return string
+ */
+ public function escapeUrl(string $string)
+ {
+ return rawurlencode($string);
+ }
+
+ /**
+ * Escape a string for the CSS context. CSS escaping can be applied to any string being
+ * inserted into CSS and escapes everything except alphanumerics.
+ *
+ * @return string
+ */
+ public function escapeCss(string $string)
+ {
+ $string = $this->toUtf8($string);
+ if ($string === '' || ctype_digit($string)) {
+ return $string;
+ }
+
+ $result = preg_replace_callback('/[^a-z0-9]/iSu', $this->cssMatcher, $string);
+ return $this->fromUtf8($result);
+ }
+
+ /**
+ * Callback function for preg_replace_callback that applies HTML Attribute
+ * escaping to all matches.
+ *
+ * @param array $matches
+ * @return string
+ */
+ protected function htmlAttrMatcher($matches)
+ {
+ $chr = $matches[0];
+ $ord = ord($chr);
+
+ /**
+ * The following replaces characters undefined in HTML with the
+ * hex entity for the Unicode replacement character.
+ */
+ if (
+ ($ord <= 0x1f && $chr !== "\t" && $chr !== "\n" && $chr !== "\r")
+ || ($ord >= 0x7f && $ord <= 0x9f)
+ ) {
+ return '�';
+ }
+
+ /**
+ * Check if the current character to escape has a name entity we should
+ * replace it with while grabbing the integer value of the character.
+ */
+ if (strlen($chr) > 1) {
+ $chr = $this->convertEncoding($chr, 'UTF-32BE', 'UTF-8');
+ }
+
+ $hex = bin2hex($chr);
+ $ord = hexdec($hex);
+ if (isset(static::$htmlNamedEntityMap[$ord])) {
+ return '&' . static::$htmlNamedEntityMap[$ord] . ';';
+ }
+
+ /**
+ * Per OWASP recommendations, we'll use upper hex entities
+ * for any other characters where a named entity does not exist.
+ */
+ if ($ord > 255) {
+ return sprintf('%04X;', $ord);
+ }
+ return sprintf('%02X;', $ord);
+ }
+
+ /**
+ * Callback function for preg_replace_callback that applies Javascript
+ * escaping to all matches.
+ *
+ * @param array $matches
+ * @return string
+ */
+ protected function jsMatcher($matches)
+ {
+ $chr = $matches[0];
+ if (strlen($chr) === 1) {
+ return sprintf('\\x%02X', ord($chr));
+ }
+ $chr = $this->convertEncoding($chr, 'UTF-16BE', 'UTF-8');
+ $hex = strtoupper(bin2hex($chr));
+ if (strlen($hex) <= 4) {
+ return sprintf('\\u%04s', $hex);
+ }
+ $highSurrogate = substr($hex, 0, 4);
+ $lowSurrogate = substr($hex, 4, 4);
+ return sprintf('\\u%04s\\u%04s', $highSurrogate, $lowSurrogate);
+ }
+
+ /**
+ * Callback function for preg_replace_callback that applies CSS
+ * escaping to all matches.
+ *
+ * @param array $matches
+ * @return string
+ */
+ protected function cssMatcher($matches)
+ {
+ $chr = $matches[0];
+ if (strlen($chr) === 1) {
+ $ord = ord($chr);
+ } else {
+ $chr = $this->convertEncoding($chr, 'UTF-32BE', 'UTF-8');
+ $ord = hexdec(bin2hex($chr));
+ }
+ return sprintf('\\%X ', $ord);
+ }
+
+ /**
+ * Converts a string to UTF-8 from the base encoding. The base encoding is set via this
+ *
+ * @param string $string
+ * @throws Exception\RuntimeException
+ * @return string
+ */
+ protected function toUtf8($string)
+ {
+ if ($this->getEncoding() === 'utf-8') {
+ $result = $string;
+ } else {
+ $result = $this->convertEncoding($string, 'UTF-8', $this->getEncoding());
+ }
+
+ if (! $this->isUtf8($result)) {
+ throw new Exception\RuntimeException(
+ sprintf('String to be escaped was not valid UTF-8 or could not be converted: %s', $result)
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Converts a string from UTF-8 to the base encoding. The base encoding is set via this
+ *
+ * @param string $string
+ * @return string
+ */
+ protected function fromUtf8($string)
+ {
+ if ($this->getEncoding() === 'utf-8') {
+ return $string;
+ }
+
+ return $this->convertEncoding($string, $this->getEncoding(), 'UTF-8');
+ }
+
+ /**
+ * Checks if a given string appears to be valid UTF-8 or not.
+ *
+ * @param string $string
+ * @return bool
+ */
+ protected function isUtf8($string)
+ {
+ return $string === '' || preg_match('/^./su', $string);
+ }
+
+ /**
+ * Encoding conversion helper which wraps mb_convert_encoding
+ *
+ * @param string $string
+ * @param string $to
+ * @param array|string $from
+ * @return string
+ */
+ protected function convertEncoding($string, $to, $from)
+ {
+ $result = mb_convert_encoding($string, $to, $from);
+
+ if ($result === false) {
+ return ''; // return non-fatal blank string on encoding errors from users
+ }
+
+ return $result;
+ }
+}
diff --git a/Laminas/Escaper/Exception/ExceptionInterface.php b/Laminas/Escaper/Exception/ExceptionInterface.php
new file mode 100644
index 0000000..8f5fd89
--- /dev/null
+++ b/Laminas/Escaper/Exception/ExceptionInterface.php
@@ -0,0 +1,11 @@
+
+ * @author Daniel Costa
+ * @author Mirosław Filip
+ *
+ * @psalm-template T
+ * @psalm-immutable
+ * @psalm-consistent-constructor
+ */
+abstract class Enum implements \JsonSerializable, \Stringable
+{
+ /**
+ * Enum value
+ *
+ * @var mixed
+ * @psalm-var T
+ */
+ protected $value;
+
+ /**
+ * Enum key, the constant name
+ *
+ * @var string
+ */
+ private $key;
+
+ /**
+ * Store existing constants in a static cache per object.
+ *
+ *
+ * @var array
+ * @psalm-var array>
+ */
+ protected static $cache = [];
+
+ /**
+ * Cache of instances of the Enum class
+ *
+ * @var array
+ * @psalm-var array>
+ */
+ protected static $instances = [];
+
+ /**
+ * Creates a new value of some type
+ *
+ * @psalm-pure
+ * @param mixed $value
+ *
+ * @psalm-param T $value
+ * @throws \UnexpectedValueException if incompatible type is given.
+ */
+ public function __construct($value)
+ {
+ if ($value instanceof static) {
+ /** @psalm-var T */
+ $value = $value->getValue();
+ }
+
+ /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
+ $this->key = static::assertValidValueReturningKey($value);
+
+ /** @psalm-var T */
+ $this->value = $value;
+ }
+
+ /**
+ * This method exists only for the compatibility reason when deserializing a previously serialized version
+ * that didn't had the key property
+ */
+ public function __wakeup()
+ {
+ /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
+ if ($this->key === null) {
+ /**
+ * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
+ * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
+ */
+ $this->key = static::search($this->value);
+ }
+ }
+
+ /**
+ * @param mixed $value
+ * @return static
+ */
+ public static function from($value): self
+ {
+ $key = static::assertValidValueReturningKey($value);
+
+ return self::__callStatic($key, []);
+ }
+
+ /**
+ * @psalm-pure
+ * @return mixed
+ * @psalm-return T
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the enum key (i.e. the constant name).
+ *
+ * @psalm-pure
+ * @return string
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-suppress InvalidCast
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string)$this->value;
+ }
+
+ /**
+ * Determines if Enum should be considered equal with the variable passed as a parameter.
+ * Returns false if an argument is an object of different class or not an object.
+ *
+ * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4
+ *
+ * @psalm-pure
+ * @psalm-param mixed $variable
+ * @return bool
+ */
+ final public function equals($variable = null): bool
+ {
+ return $variable instanceof self
+ && $this->getValue() === $variable->getValue()
+ && static::class === \get_class($variable);
+ }
+
+ /**
+ * Returns the names (keys) of all constants in the Enum class
+ *
+ * @psalm-pure
+ * @psalm-return list
+ * @return array
+ */
+ public static function keys()
+ {
+ return \array_keys(static::toArray());
+ }
+
+ /**
+ * Returns instances of the Enum class of all Enum constants
+ *
+ * @psalm-pure
+ * @psalm-return array
+ * @return static[] Constant name in key, Enum instance in value
+ */
+ public static function values()
+ {
+ $values = array();
+
+ /** @psalm-var T $value */
+ foreach (static::toArray() as $key => $value) {
+ /** @psalm-suppress UnsafeGenericInstantiation */
+ $values[$key] = new static($value);
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns all possible values as an array
+ *
+ * @psalm-pure
+ * @psalm-suppress ImpureStaticProperty
+ *
+ * @psalm-return array
+ * @return array Constant name in key, constant value in value
+ */
+ public static function toArray()
+ {
+ $class = static::class;
+
+ if (!isset(static::$cache[$class])) {
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
+ $reflection = new \ReflectionClass($class);
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
+ static::$cache[$class] = $reflection->getConstants();
+ }
+
+ return static::$cache[$class];
+ }
+
+ /**
+ * Check if is valid enum value
+ *
+ * @param $value
+ * @psalm-param mixed $value
+ * @psalm-pure
+ * @psalm-assert-if-true T $value
+ * @return bool
+ */
+ public static function isValid($value)
+ {
+ return \in_array($value, static::toArray(), true);
+ }
+
+ /**
+ * Asserts valid enum value
+ *
+ * @psalm-pure
+ * @psalm-assert T $value
+ * @param mixed $value
+ */
+ public static function assertValidValue($value): void
+ {
+ self::assertValidValueReturningKey($value);
+ }
+
+ /**
+ * Asserts valid enum value
+ *
+ * @psalm-pure
+ * @psalm-assert T $value
+ * @param mixed $value
+ * @return string
+ */
+ private static function assertValidValueReturningKey($value): string
+ {
+ if (false === ($key = static::search($value))) {
+ throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
+ }
+
+ return $key;
+ }
+
+ /**
+ * Check if is valid enum key
+ *
+ * @param $key
+ * @psalm-param string $key
+ * @psalm-pure
+ * @return bool
+ */
+ public static function isValidKey($key)
+ {
+ $array = static::toArray();
+
+ return isset($array[$key]) || \array_key_exists($key, $array);
+ }
+
+ /**
+ * Return key for value
+ *
+ * @param mixed $value
+ *
+ * @psalm-param mixed $value
+ * @psalm-pure
+ * @return string|false
+ */
+ public static function search($value)
+ {
+ return \array_search($value, static::toArray(), true);
+ }
+
+ /**
+ * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant
+ *
+ * @param string $name
+ * @param array $arguments
+ *
+ * @return static
+ * @throws \BadMethodCallException
+ *
+ * @psalm-pure
+ */
+ public static function __callStatic($name, $arguments)
+ {
+ $class = static::class;
+ if (!isset(self::$instances[$class][$name])) {
+ $array = static::toArray();
+ if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
+ $message = "No static method or enum constant '$name' in class " . static::class;
+ throw new \BadMethodCallException($message);
+ }
+ /** @psalm-suppress UnsafeGenericInstantiation */
+ return self::$instances[$class][$name] = new static($array[$name]);
+ }
+ return clone self::$instances[$class][$name];
+ }
+
+ /**
+ * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode()
+ * natively.
+ *
+ * @return mixed
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->getValue();
+ }
+}
diff --git a/MyCLabs/LICENSE b/MyCLabs/LICENSE
new file mode 100644
index 0000000..2a8cf22
--- /dev/null
+++ b/MyCLabs/LICENSE
@@ -0,0 +1,18 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 My C-Labs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/PhpOffice/Common/Adapter/Zip/PclZipAdapter.php b/PhpOffice/Common/Adapter/Zip/PclZipAdapter.php
old mode 100755
new mode 100644
index 053531f..9d31f1a
--- a/PhpOffice/Common/Adapter/Zip/PclZipAdapter.php
+++ b/PhpOffice/Common/Adapter/Zip/PclZipAdapter.php
@@ -1,4 +1,5 @@
oPclZip = new PclZip($filename);
$this->tmpDir = sys_get_temp_dir();
+
return $this;
}
@@ -31,15 +33,16 @@ class PclZipAdapter implements ZipInterface
{
$pathData = pathinfo($localname);
- $hFile = fopen($this->tmpDir.'/'.$pathData['basename'], "wb");
+ $hFile = fopen($this->tmpDir . '/' . $pathData['basename'], 'wb');
fwrite($hFile, $contents);
fclose($hFile);
- $res = $this->oPclZip->add($this->tmpDir.'/'.$pathData['basename'], PCLZIP_OPT_REMOVE_PATH, $this->tmpDir, PCLZIP_OPT_ADD_PATH, $pathData['dirname']);
+ $res = $this->oPclZip->add($this->tmpDir . '/' . $pathData['basename'], PCLZIP_OPT_REMOVE_PATH, $this->tmpDir, PCLZIP_OPT_ADD_PATH, $pathData['dirname']);
if ($res == 0) {
- throw new \Exception("Error zipping files : " . $this->oPclZip->errorInfo(true));
+ throw new \Exception('Error zipping files : ' . $this->oPclZip->errorInfo(true));
}
- unlink($this->tmpDir.'/'.$pathData['basename']);
+ unlink($this->tmpDir . '/' . $pathData['basename']);
+
return $this;
}
}
diff --git a/PhpOffice/Common/Adapter/Zip/ZipArchiveAdapter.php b/PhpOffice/Common/Adapter/Zip/ZipArchiveAdapter.php
old mode 100755
new mode 100644
index da2c346..a8728eb
--- a/PhpOffice/Common/Adapter/Zip/ZipArchiveAdapter.php
+++ b/PhpOffice/Common/Adapter/Zip/ZipArchiveAdapter.php
@@ -35,13 +35,14 @@ class ZipArchiveAdapter implements ZipInterface
if ($this->oZipArchive->close() === false) {
throw new \Exception("Could not close zip file $this->filename.");
}
+
return $this;
}
public function addFromString($localname, $contents)
{
if ($this->oZipArchive->addFromString($localname, $contents) === false) {
- throw new \Exception("Error zipping files : " . $localname);
+ throw new \Exception('Error zipping files : ' . $localname);
}
return $this;
diff --git a/PhpOffice/Common/Adapter/Zip/ZipInterface.php b/PhpOffice/Common/Adapter/Zip/ZipInterface.php
old mode 100755
new mode 100644
index e8dbc8c..1e22790
--- a/PhpOffice/Common/Adapter/Zip/ZipInterface.php
+++ b/PhpOffice/Common/Adapter/Zip/ZipInterface.php
@@ -6,24 +6,32 @@ interface ZipInterface
{
/**
* Open a ZIP file archive
+ *
* @param string $filename
+ *
* @return $this
+ *
* @throws \Exception
*/
public function open($filename);
/**
* Close the active archive (opened or newly created)
+ *
* @return $this
+ *
* @throws \Exception
*/
public function close();
/**
* Add a file to a ZIP archive using its contents
- * @param string $localname The name of the entry to create.
+ *
+ * @param string $localname the name of the entry to create
* @param string $contents The contents to use to create the entry. It is used in a binary safe mode.
+ *
* @return $this
+ *
* @throws \Exception
*/
public function addFromString($localname, $contents);
diff --git a/PhpOffice/Common/Autoloader.php b/PhpOffice/Common/Autoloader.php
old mode 100755
new mode 100644
index 8b976c9..9269232
--- a/PhpOffice/Common/Autoloader.php
+++ b/PhpOffice/Common/Autoloader.php
@@ -9,7 +9,8 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
@@ -22,16 +23,16 @@ namespace PhpOffice\Common;
class Autoloader
{
/** @const string */
- const NAMESPACE_PREFIX = 'PhpOffice\\Common\\';
+ public const NAMESPACE_PREFIX = 'PhpOffice\\Common\\';
/**
* Register
*
* @return void
*/
- public static function register()
+ public static function register(): void
{
- spl_autoload_register(array(new self, 'autoload'));
+ spl_autoload_register([new self(), 'autoload']);
}
/**
@@ -39,7 +40,7 @@ class Autoloader
*
* @param string $class
*/
- public static function autoload($class)
+ public static function autoload(string $class): void
{
$prefixLength = strlen(self::NAMESPACE_PREFIX);
if (0 === strncmp(self::NAMESPACE_PREFIX, $class, $prefixLength)) {
diff --git a/PhpOffice/Common/COPYING b/PhpOffice/Common/COPYING
deleted file mode 100755
index 94a9ed0..0000000
--- a/PhpOffice/Common/COPYING
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/PhpOffice/Common/COPYING.LESSER b/PhpOffice/Common/COPYING.LESSER
deleted file mode 100755
index 65c5ca8..0000000
--- a/PhpOffice/Common/COPYING.LESSER
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/PhpOffice/Common/Drawing.php b/PhpOffice/Common/Drawing.php
old mode 100755
new mode 100644
index d36c544..2f71865
--- a/PhpOffice/Common/Drawing.php
+++ b/PhpOffice/Common/Drawing.php
@@ -9,27 +9,26 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\Common;
-/**
- * \PhpOffice\Common\Drawing
- */
class Drawing
{
- const DPI_96 = 96;
+ public const DPI_96 = 96;
/**
* Convert pixels to EMU
*
- * @param int $pValue Value in pixels
- * @return int
+ * @param float $pValue Value in pixels
+ *
+ * @return float
*/
- public static function pixelsToEmu($pValue = 0)
+ public static function pixelsToEmu(float $pValue = 0): float
{
return round($pValue * 9525);
}
@@ -37,89 +36,116 @@ class Drawing
/**
* Convert EMU to pixels
*
- * @param int $pValue Value in EMU
+ * @param int $pValue Value in EMU
+ *
* @return int
*/
- public static function emuToPixels($pValue = 0)
+ public static function emuToPixels(int $pValue = 0): int
{
if ($pValue == 0) {
return 0;
}
- return round($pValue / 9525);
+
+ return (int) round($pValue / 9525);
}
/**
* Convert pixels to points
*
- * @param int $pValue Value in pixels
+ * @param int $pValue Value in pixels
+ *
* @return float
*/
- public static function pixelsToPoints($pValue = 0)
+ public static function pixelsToPoints(int $pValue = 0): float
{
- return $pValue * 0.67777777;
+ return $pValue * 0.75;
}
/**
* Convert points width to centimeters
*
- * @param int $pValue Value in points
+ * @param float $pValue Value in points
+ *
* @return float
*/
- public static function pointsToCentimeters($pValue = 0)
+ public static function pointsToCentimeters(float $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
- return ((($pValue * 1.333333333) / self::DPI_96) * 2.54);
+
+ return (($pValue / 0.75) / self::DPI_96) * 2.54;
}
-
+
+ /**
+ * Convert centimeters width to points
+ *
+ * @param float $pValue Value in centimeters
+ *
+ * @return float
+ */
+ public static function centimetersToPoints(float $pValue = 0): float
+ {
+ if ($pValue == 0) {
+ return 0;
+ }
+
+ return ($pValue / 2.54) * self::DPI_96 * 0.75;
+ }
+
/**
* Convert points width to pixels
*
- * @param int $pValue Value in points
+ * @param float $pValue Value in points
+ *
* @return float
*/
- public static function pointsToPixels($pValue = 0)
+ public static function pointsToPixels(float $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
- return $pValue * 1.333333333;
+
+ return $pValue / 0.75;
}
/**
* Convert pixels to centimeters
*
- * @param int $pValue Value in pixels
+ * @param int $pValue Value in pixels
+ *
* @return float
*/
- public static function pixelsToCentimeters($pValue = 0)
+ public static function pixelsToCentimeters(int $pValue = 0): float
{
//return $pValue * 0.028;
- return (($pValue / self::DPI_96) * 2.54);
+ return ($pValue / self::DPI_96) * 2.54;
}
/**
* Convert centimeters width to pixels
*
- * @param int $pValue Value in centimeters
- * @return float
+ * @param float $pValue Value in centimeters
+ *
+ * @return int
*/
- public static function centimetersToPixels($pValue = 0)
+ public static function centimetersToPixels(float $pValue = 0): int
{
if ($pValue == 0) {
return 0;
}
- return ($pValue / 2.54) * self::DPI_96;
+
+ return (int) round((($pValue / 2.54) * self::DPI_96));
}
/**
* Convert degrees to angle
*
- * @param int $pValue Degrees
+ * @param int $pValue Degrees
+ *
* @return int
*/
- public static function degreesToAngle($pValue = 0)
+ public static function degreesToAngle(int $pValue = 0): int
{
return (int) round($pValue * 60000);
}
@@ -127,111 +153,140 @@ class Drawing
/**
* Convert angle to degrees
*
- * @param int $pValue Angle
- * @return int
+ * @param int $pValue Angle
+ *
+ * @return float
*/
- public static function angleToDegrees($pValue = 0)
+ public static function angleToDegrees(int $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
+
return round($pValue / 60000);
}
/**
* Convert centimeters width to twips
*
- * @param integer $pValue
+ * @param int $pValue
+ *
* @return float
*/
- public static function centimetersToTwips($pValue = 0)
+ public static function centimetersToTwips(int $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
+
return $pValue * 566.928;
}
/**
* Convert twips width to centimeters
*
- * @param integer $pValue
+ * @param int $pValue
+ *
* @return float
*/
- public static function twipsToCentimeters($pValue = 0)
+ public static function twipsToCentimeters(int $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
+
return $pValue / 566.928;
}
/**
* Convert inches width to twips
*
- * @param integer $pValue
- * @return float
+ * @param int $pValue
+ *
+ * @return int
*/
- public static function inchesToTwips($pValue = 0)
+ public static function inchesToTwips(int $pValue = 0): int
{
if ($pValue == 0) {
return 0;
}
+
return $pValue * 1440;
}
/**
* Convert twips width to inches
*
- * @param integer $pValue
+ * @param int $pValue
+ *
* @return float
*/
- public static function twipsToInches($pValue = 0)
+ public static function twipsToInches(int $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
+
return $pValue / 1440;
}
/**
* Convert twips width to pixels
*
- * @param integer $pValue
+ * @param int $pValue
+ *
* @return float
*/
- public static function twipsToPixels($pValue = 0)
+ public static function twipsToPixels(int $pValue = 0): float
{
if ($pValue == 0) {
return 0;
}
- return round($pValue / 15.873984);
+
+ return round($pValue / 15);
+ }
+
+ /**
+ * Convert points to emu
+ *
+ * @param float $pValue
+ *
+ * @return int
+ */
+ public static function pointsToEmu(float $pValue = 0): int
+ {
+ if ($pValue == 0) {
+ return 0;
+ }
+
+ return (int) round(($pValue / 0.75) / 9525);
}
/**
* Convert HTML hexadecimal to RGB
*
* @param string $pValue HTML Color in hexadecimal
- * @return array|false Value in RGB
+ *
+ * @return array|null Value in RGB
*/
- public static function htmlToRGB($pValue)
+ public static function htmlToRGB(string $pValue): ?array
{
if ($pValue[0] == '#') {
$pValue = substr($pValue, 1);
}
if (strlen($pValue) == 6) {
- list($colorR, $colorG, $colorB) = array($pValue[0] . $pValue[1], $pValue[2] . $pValue[3], $pValue[4] . $pValue[5]);
+ list($colorR, $colorG, $colorB) = [$pValue[0] . $pValue[1], $pValue[2] . $pValue[3], $pValue[4] . $pValue[5]];
} elseif (strlen($pValue) == 3) {
- list($colorR, $colorG, $colorB) = array($pValue[0] . $pValue[0], $pValue[1] . $pValue[1], $pValue[2] . $pValue[2]);
+ list($colorR, $colorG, $colorB) = [$pValue[0] . $pValue[0], $pValue[1] . $pValue[1], $pValue[2] . $pValue[2]];
} else {
- return false;
+ return null;
}
$colorR = hexdec($colorR);
$colorG = hexdec($colorG);
$colorB = hexdec($colorB);
- return array($colorR, $colorG, $colorB);
+ return [$colorR, $colorG, $colorB];
}
}
diff --git a/PhpOffice/Common/File.php b/PhpOffice/Common/File.php
old mode 100755
new mode 100644
index 8cb5907..8ab931d
--- a/PhpOffice/Common/File.php
+++ b/PhpOffice/Common/File.php
@@ -9,35 +9,36 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\Common;
-/**
- * Drawing
- */
+use ZipArchive;
+
class File
{
/**
* Verify if a file exists
*
- * @param string $pFilename Filename
+ * @param string $pFilename Filename
+ *
* @return bool
*/
- public static function fileExists($pFilename)
+ public static function fileExists(string $pFilename): bool
{
// Sick construction, but it seems that
// file_exists returns strange values when
// doing the original file_exists on ZIP archives...
if (strtolower(substr($pFilename, 0, 3)) == 'zip') {
// Open ZIP file and verify if the file exists
- $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
+ $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
$archiveFile = substr($pFilename, strpos($pFilename, '#') + 1);
- $zip = new \ZipArchive();
+ $zip = new ZipArchive();
if ($zip->open($zipFile) === true) {
$returnValue = ($zip->getFromName($archiveFile) !== false);
$zip->close();
@@ -51,29 +52,33 @@ class File
// Regular file_exists
return file_exists($pFilename);
}
+
/**
* Returns the content of a file
*
- * @param string $pFilename Filename
- * @return string
+ * @param string $pFilename Filename
+ *
+ * @return string|null
*/
- public static function fileGetContents($pFilename)
+ public static function fileGetContents(string $pFilename): ?string
{
if (!self::fileExists($pFilename)) {
- return false;
+ return null;
}
if (strtolower(substr($pFilename, 0, 3)) == 'zip') {
// Open ZIP file and verify if the file exists
- $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
+ $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6);
$archiveFile = substr($pFilename, strpos($pFilename, '#') + 1);
- $zip = new \ZipArchive();
+ $zip = new ZipArchive();
if ($zip->open($zipFile) === true) {
$returnValue = $zip->getFromName($archiveFile);
$zip->close();
+
return $returnValue;
}
- return false;
+
+ return null;
}
// Regular file contents
return file_get_contents($pFilename);
@@ -82,16 +87,17 @@ class File
/**
* Returns canonicalized absolute pathname, also for ZIP archives
*
- * @param string $pFilename
+ * @param string $pFilename
+ *
* @return string
*/
- public static function realpath($pFilename)
+ public static function realpath(string $pFilename): string
{
// Try using realpath()
$returnValue = realpath($pFilename);
// Found something?
- if ($returnValue == '' || is_null($returnValue)) {
+ if (empty($returnValue)) {
$pathArray = explode('/', $pFilename);
while (in_array('..', $pathArray) && $pathArray[0] != '..') {
$numPathArray = count($pathArray);
diff --git a/PhpOffice/Common/Font.php b/PhpOffice/Common/Font.php
old mode 100755
new mode 100644
index 03a8e73..38bc81d
--- a/PhpOffice/Common/Font.php
+++ b/PhpOffice/Common/Font.php
@@ -9,7 +9,8 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
@@ -24,43 +25,47 @@ class Font
/**
* Calculate an (approximate) pixel size, based on a font points size
*
- * @param int $fontSizeInPoints Font size (in points)
- * @return int Font size (in pixels)
+ * @param int $fontSizeInPoints Font size (in points)
+ *
+ * @return float Font size (in pixels)
*/
- public static function fontSizeToPixels($fontSizeInPoints = 12)
+ public static function fontSizeToPixels(int $fontSizeInPoints = 12): float
{
- return ((16 / 12) * $fontSizeInPoints);
+ return (16 / 12) * $fontSizeInPoints;
}
/**
* Calculate an (approximate) pixel size, based on inch size
*
- * @param int $sizeInInch Font size (in inch)
+ * @param int $sizeInInch Font size (in inch)
+ *
* @return int Size (in pixels)
*/
- public static function inchSizeToPixels($sizeInInch = 1)
+ public static function inchSizeToPixels(int $sizeInInch = 1): int
{
- return ($sizeInInch * 96);
+ return $sizeInInch * 96;
}
/**
* Calculate an (approximate) pixel size, based on centimeter size
*
- * @param int $sizeInCm Font size (in centimeters)
- * @return int Size (in pixels)
+ * @param int $sizeInCm Font size (in centimeters)
+ *
+ * @return float Size (in pixels)
*/
- public static function centimeterSizeToPixels($sizeInCm = 1)
+ public static function centimeterSizeToPixels(int $sizeInCm = 1): float
{
- return ($sizeInCm * 37.795275591);
+ return $sizeInCm * 37.795275591;
}
/**
* Convert centimeter to twip
*
* @param int $sizeInCm
- * @return double
+ *
+ * @return float
*/
- public static function centimeterSizeToTwips($sizeInCm = 1)
+ public static function centimeterSizeToTwips(int $sizeInCm = 1): float
{
return $sizeInCm / 2.54 * 1440;
}
@@ -69,9 +74,10 @@ class Font
* Convert inch to twip
*
* @param int $sizeInInch
- * @return double
+ *
+ * @return int
*/
- public static function inchSizeToTwips($sizeInInch = 1)
+ public static function inchSizeToTwips(int $sizeInInch = 1): int
{
return $sizeInInch * 1440;
}
@@ -80,9 +86,10 @@ class Font
* Convert pixel to twip
*
* @param int $sizeInPixel
- * @return double
+ *
+ * @return float
*/
- public static function pixelSizeToTwips($sizeInPixel = 1)
+ public static function pixelSizeToTwips(int $sizeInPixel = 1): float
{
return $sizeInPixel / 96 * 1440;
}
@@ -90,10 +97,11 @@ class Font
/**
* Calculate twip based on point size, used mainly for paragraph spacing
*
- * @param integer $sizeInPoint Size in point
- * @return integer Size (in twips)
+ * @param int $sizeInPoint Size in point
+ *
+ * @return float Size (in twips)
*/
- public static function pointSizeToTwips($sizeInPoint = 1)
+ public static function pointSizeToTwips(int $sizeInPoint = 1): float
{
return $sizeInPoint / 72 * 1440;
}
diff --git a/PhpOffice/Common/LICENSE b/PhpOffice/Common/LICENSE
deleted file mode 100755
index dba7f0e..0000000
--- a/PhpOffice/Common/LICENSE
+++ /dev/null
@@ -1,15 +0,0 @@
-PHPOffice Common, a shared PHP library for PHPOffice Libraries
-
-Copyright (c) 2015-2015 PHPOffice.
-
-PHPOffice Common is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License version 3 as published by
-the Free Software Foundation.
-
-PHPOffice Common is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License version 3 for more details.
-
-You should have received a copy of the GNU Lesser General Public License version 3
-along with PHPOffice Common. If not, see .
diff --git a/PhpOffice/Common/Microsoft/OLERead.php b/PhpOffice/Common/Microsoft/OLERead.php
old mode 100755
new mode 100644
index fa1df12..0bdf7f0
--- a/PhpOffice/Common/Microsoft/OLERead.php
+++ b/PhpOffice/Common/Microsoft/OLERead.php
@@ -9,7 +9,8 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
@@ -17,64 +18,98 @@
namespace PhpOffice\Common\Microsoft;
if (!defined('IDENTIFIER_OLE')) {
- define('IDENTIFIER_OLE', pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1));
+ define('IDENTIFIER_OLE', pack('CCCCCCCC', 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1));
}
class OLERead
{
+ /**
+ * @var string
+ */
private $data = '';
// OLE identifier
- const IDENTIFIER_OLE = IDENTIFIER_OLE;
+ public const IDENTIFIER_OLE = IDENTIFIER_OLE;
// Size of a sector = 512 bytes
- const BIG_BLOCK_SIZE = 0x200;
+ public const BIG_BLOCK_SIZE = 0x200;
// Size of a short sector = 64 bytes
- const SMALL_BLOCK_SIZE = 0x40;
+ public const SMALL_BLOCK_SIZE = 0x40;
// Size of a directory entry always = 128 bytes
- const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
+ public const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
// Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
- const SMALL_BLOCK_THRESHOLD = 0x1000;
+ public const SMALL_BLOCK_THRESHOLD = 0x1000;
// header offsets
- const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
- const ROOT_START_BLOCK_POS = 0x30;
- const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
- const EXTENSION_BLOCK_POS = 0x44;
- const NUM_EXTENSION_BLOCK_POS = 0x48;
- const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
+ public const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2C;
+ public const ROOT_START_BLOCK_POS = 0x30;
+ public const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3C;
+ public const EXTENSION_BLOCK_POS = 0x44;
+ public const NUM_EXTENSION_BLOCK_POS = 0x48;
+ public const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4C;
// property storage offsets (directory offsets)
- const SIZE_OF_NAME_POS = 0x40;
- const TYPE_POS = 0x42;
- const START_BLOCK_POS = 0x74;
- const SIZE_POS = 0x78;
+ public const SIZE_OF_NAME_POS = 0x40;
+ public const TYPE_POS = 0x42;
+ public const START_BLOCK_POS = 0x74;
+ public const SIZE_POS = 0x78;
- public $summaryInformation = null;
- public $docSummaryInfos = null;
- public $powerpointDocument = null;
- public $currentUser = null;
- public $pictures = null;
- public $rootEntry = null;
- public $props = array();
- public $smallBlockChain = null;
- public $bigBlockChain = null;
- public $entry = null;
+ /**
+ * @var int|null
+ */
+ public $summaryInformation;
+ /**
+ * @var int|null
+ */
+ public $docSummaryInfos;
+ /**
+ * @var int|null
+ */
+ public $powerpointDocument;
+ /**
+ * @var int|null
+ */
+ public $currentUser;
+ /**
+ * @var int|null
+ */
+ public $pictures;
+ /**
+ * @var int|null
+ */
+ public $rootEntry;
+ /**
+ * @var array>
+ */
+ public $props = [];
+ /**
+ * @var string|null
+ */
+ public $smallBlockChain;
+ /**
+ * @var string|null
+ */
+ public $bigBlockChain;
+ /**
+ * @var string|null
+ */
+ public $entry;
/**
* Read the file
*
- * @param $sFileName string Filename
+ * @param string $sFileName Filename
+ *
* @throws \Exception
*/
- public function read($sFileName)
+ public function read(string $sFileName): void
{
// Check if file exists and is readable
if (!is_readable($sFileName)) {
- throw new \Exception("Could not open " . $sFileName . " for reading! File does not exist, or it is not readable.");
+ throw new \Exception('Could not open ' . $sFileName . ' for reading! File does not exist, or it is not readable.');
}
// Get the file identifier
@@ -104,18 +139,18 @@ class OLERead
// Total number of sectors used by MSAT
$numExtensionBlocks = self::getInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
- $bigBlockDepotBlocks = array();
+ $bigBlockDepotBlocks = [];
$pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
$bbdBlocks = $numBigBlkDepotBlks;
if ($numExtensionBlocks != 0) {
- $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
+ $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
}
for ($i = 0; $i < $bbdBlocks; ++$i) {
- $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
- $pos += 4;
+ $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
+ $pos += 4;
}
for ($j = 0; $j < $numExtensionBlocks; ++$j) {
@@ -138,8 +173,8 @@ class OLERead
for ($i = 0; $i < $numBigBlkDepotBlks; ++$i) {
$pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
- $this->bigBlockChain .= substr($this->data, $pos, 4*$bbs);
- $pos += 4*$bbs;
+ $this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
+ $pos += 4 * $bbs;
}
$sbdBlock = $sbdStartBlock;
@@ -147,10 +182,10 @@ class OLERead
while ($sbdBlock != -2) {
$pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
- $this->smallBlockChain .= substr($this->data, $pos, 4*$bbs);
- $pos += 4*$bbs;
+ $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
+ $pos += 4 * $bbs;
- $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock*4);
+ $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
}
// read the directory stream
@@ -165,12 +200,8 @@ class OLERead
*
* @return string
*/
- public function getStream($stream)
+ public function getStream(int $stream): ?string
{
- if ($stream === null) {
- return null;
- }
-
$streamData = '';
if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
@@ -179,10 +210,10 @@ class OLERead
$block = $this->props[$stream]['startBlock'];
while ($block != -2) {
- $pos = $block * self::SMALL_BLOCK_SIZE;
+ $pos = $block * self::SMALL_BLOCK_SIZE;
$streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
- $block = self::getInt4d($this->smallBlockChain, $block*4);
+ $block = self::getInt4d($this->smallBlockChain, $block * 4);
}
return $streamData;
@@ -202,7 +233,7 @@ class OLERead
while ($block != -2) {
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
$streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
- $block = self::getInt4d($this->bigBlockChain, $block*4);
+ $block = self::getInt4d($this->bigBlockChain, $block * 4);
}
return $streamData;
@@ -212,9 +243,10 @@ class OLERead
* Read a standard stream (by joining sectors using information from SAT)
*
* @param int $blID Sector ID where the stream starts
+ *
* @return string Data for standard stream
*/
- private function readData($blID)
+ private function readData(int $blID): string
{
$block = $blID;
$data = '';
@@ -222,15 +254,16 @@ class OLERead
while ($block != -2) {
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
$data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
- $block = self::getInt4d($this->bigBlockChain, $block*4);
+ $block = self::getInt4d($this->bigBlockChain, $block * 4);
}
+
return $data;
}
/**
* Read entries in the directory stream.
*/
- private function readPropertySets()
+ private function readPropertySets(): void
{
$offset = 0;
@@ -241,7 +274,7 @@ class OLERead
$data = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
// size in bytes of name
- $nameSize = ord($data[self::SIZE_OF_NAME_POS]) | (ord($data[self::SIZE_OF_NAME_POS+1]) << 8);
+ $nameSize = ord($data[self::SIZE_OF_NAME_POS]) | (ord($data[self::SIZE_OF_NAME_POS + 1]) << 8);
// type of entry
$type = ord($data[self::TYPE_POS]);
@@ -252,13 +285,14 @@ class OLERead
$size = self::getInt4d($data, self::SIZE_POS);
- $name = str_replace("\x00", "", substr($data, 0, $nameSize));
+ $name = str_replace("\x00", '', substr($data, 0, $nameSize));
if ($size > 0) {
- $this->props[] = array (
- 'name' => $name,
- 'type' => $type,
- 'startBlock' => $startBlock,
- 'size' => $size);
+ $this->props[] = [
+ 'name' => $name,
+ 'type' => $type,
+ 'startBlock' => $startBlock,
+ 'size' => $size,
+ ];
// tmp helper to simplify checks
$upName = strtoupper($name);
@@ -268,14 +302,14 @@ class OLERead
case 'R':
$this->rootEntry = count($this->props) - 1;
break;
- case chr(1).'COMPOBJ':
+ case chr(1) . 'COMPOBJ':
break;
- case chr(1).'OLE':
+ case chr(1) . 'OLE':
break;
- case chr(5).'SUMMARYINFORMATION':
+ case chr(5) . 'SUMMARYINFORMATION':
$this->summaryInformation = count($this->props) - 1;
break;
- case chr(5).'DOCUMENTSUMMARYINFORMATION':
+ case chr(5) . 'DOCUMENTSUMMARYINFORMATION':
$this->docSummaryInfos = count($this->props) - 1;
break;
case 'CURRENT USER':
@@ -288,7 +322,7 @@ class OLERead
$this->powerpointDocument = count($this->props) - 1;
break;
default:
- throw new \Exception('OLE Block Not defined: $upName : '.$upName. ' - $name : "'.$name.'"');
+ throw new \Exception('OLE Block Not defined: $upName : ' . $upName . ' - $name : "' . $name . '"');
}
}
@@ -301,6 +335,7 @@ class OLERead
*
* @param string $data
* @param int $pos
+ *
* @return int
*/
private static function getInt4d($data, $pos)
@@ -315,6 +350,7 @@ class OLERead
} else {
$ord24 = ($or24 & 127) << 24;
}
+
return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $ord24;
}
}
diff --git a/PhpOffice/Common/Microsoft/PasswordEncoder.php b/PhpOffice/Common/Microsoft/PasswordEncoder.php
old mode 100755
new mode 100644
index f620f4c..097060b
--- a/PhpOffice/Common/Microsoft/PasswordEncoder.php
+++ b/PhpOffice/Common/Microsoft/PasswordEncoder.php
@@ -9,7 +9,8 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
@@ -21,39 +22,43 @@ namespace PhpOffice\Common\Microsoft;
*/
class PasswordEncoder
{
- const ALGORITHM_MD2 = 'MD2';
- const ALGORITHM_MD4 = 'MD4';
- const ALGORITHM_MD5 = 'MD5';
- const ALGORITHM_SHA_1 = 'SHA-1';
- const ALGORITHM_SHA_256 = 'SHA-256';
- const ALGORITHM_SHA_384 = 'SHA-384';
- const ALGORITHM_SHA_512 = 'SHA-512';
- const ALGORITHM_RIPEMD = 'RIPEMD';
- const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
- const ALGORITHM_MAC = 'MAC';
- const ALGORITHM_HMAC = 'HMAC';
+ public const ALGORITHM_MD2 = 'MD2';
+ public const ALGORITHM_MD4 = 'MD4';
+ public const ALGORITHM_MD5 = 'MD5';
+ public const ALGORITHM_SHA_1 = 'SHA-1';
+ public const ALGORITHM_SHA_256 = 'SHA-256';
+ public const ALGORITHM_SHA_384 = 'SHA-384';
+ public const ALGORITHM_SHA_512 = 'SHA-512';
+ public const ALGORITHM_RIPEMD = 'RIPEMD';
+ public const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
+ public const ALGORITHM_MAC = 'MAC';
+ public const ALGORITHM_HMAC = 'HMAC';
/**
* Mapping between algorithm name and algorithm ID
*
- * @var array
+ * @var array>
+ *
* @see https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.writeprotection.cryptographicalgorithmsid(v=office.14).aspx
*/
- private static $algorithmMapping = array(
- self::ALGORITHM_MD2 => array(1, 'md2'),
- self::ALGORITHM_MD4 => array(2, 'md4'),
- self::ALGORITHM_MD5 => array(3, 'md5'),
- self::ALGORITHM_SHA_1 => array(4, 'sha1'),
- self::ALGORITHM_MAC => array(5, ''), // 'mac' -> not possible with hash()
- self::ALGORITHM_RIPEMD => array(6, 'ripemd'),
- self::ALGORITHM_RIPEMD_160 => array(7, 'ripemd160'),
- self::ALGORITHM_HMAC => array(9, ''), //'hmac' -> not possible with hash()
- self::ALGORITHM_SHA_256 => array(12, 'sha256'),
- self::ALGORITHM_SHA_384 => array(13, 'sha384'),
- self::ALGORITHM_SHA_512 => array(14, 'sha512'),
- );
+ private static $algorithmMapping = [
+ self::ALGORITHM_MD2 => [1, 'md2'],
+ self::ALGORITHM_MD4 => [2, 'md4'],
+ self::ALGORITHM_MD5 => [3, 'md5'],
+ self::ALGORITHM_SHA_1 => [4, 'sha1'],
+ self::ALGORITHM_MAC => [5, ''], // 'mac' -> not possible with hash()
+ self::ALGORITHM_RIPEMD => [6, 'ripemd'],
+ self::ALGORITHM_RIPEMD_160 => [7, 'ripemd160'],
+ self::ALGORITHM_HMAC => [9, ''], //'hmac' -> not possible with hash()
+ self::ALGORITHM_SHA_256 => [12, 'sha256'],
+ self::ALGORITHM_SHA_384 => [13, 'sha384'],
+ self::ALGORITHM_SHA_512 => [14, 'sha512'],
+ ];
- private static $initialCodeArray = array(
+ /**
+ * @var array
+ */
+ private static $initialCodeArray = [
0xE1F0,
0x1D0F,
0xCC9C,
@@ -69,39 +74,47 @@ class PasswordEncoder
0x280C,
0xA96A,
0x4EC3,
- );
+ ];
- private static $encryptionMatrix = array(
- array(0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09),
- array(0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF),
- array(0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0),
- array(0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40),
- array(0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5),
- array(0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A),
- array(0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9),
- array(0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0),
- array(0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC),
- array(0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10),
- array(0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168),
- array(0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C),
- array(0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD),
- array(0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC),
- array(0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4),
- );
+ /**
+ * @var array>
+ */
+ private static $encryptionMatrix = [
+ [0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09],
+ [0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF],
+ [0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0],
+ [0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40],
+ [0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5],
+ [0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A],
+ [0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9],
+ [0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0],
+ [0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC],
+ [0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10],
+ [0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168],
+ [0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C],
+ [0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD],
+ [0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC],
+ [0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4],
+ ];
+ /**
+ * @var int
+ */
private static $passwordMaxLength = 15;
/**
* Create a hashed password that MS Word will be able to work with
+ *
* @see https://blogs.msdn.microsoft.com/vsod/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0/
*
* @param string $password
* @param string $algorithmName
* @param string $salt
* @param int $spinCount
+ *
* @return string
*/
- public static function hashPassword($password, $algorithmName = self::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000)
+ public static function hashPassword(string $password, string $algorithmName = self::ALGORITHM_SHA_1, string $salt = null, int $spinCount = 10000)
{
$origEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
@@ -111,9 +124,9 @@ class PasswordEncoder
// Get the single-byte values by iterating through the Unicode characters of the truncated password.
// For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
$passUtf8 = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
- $byteChars = array();
+ $byteChars = [];
- for ($i = 0; $i < mb_strlen($password); $i++) {
+ for ($i = 0; $i < mb_strlen($password); ++$i) {
$byteChars[$i] = ord(substr($passUtf8, $i * 2, 1));
if ($byteChars[$i] == 0) {
@@ -135,7 +148,7 @@ class PasswordEncoder
$algorithm = self::getAlgorithm($algorithmName);
$generatedKey = hash($algorithm, $salt . $generatedKey, true);
- for ($i = 0; $i < $spinCount; $i++) {
+ for ($i = 0; $i < $spinCount; ++$i) {
$generatedKey = hash($algorithm, $generatedKey . pack('CCCC', $i, $i >> 8, $i >> 16, $i >> 24), true);
}
$generatedKey = base64_encode($generatedKey);
@@ -149,9 +162,10 @@ class PasswordEncoder
* Get algorithm from self::$algorithmMapping
*
* @param string $algorithmName
+ *
* @return string
*/
- private static function getAlgorithm($algorithmName)
+ private static function getAlgorithm(string $algorithmName): string
{
$algorithm = self::$algorithmMapping[$algorithmName][1];
if ($algorithm == '') {
@@ -165,9 +179,10 @@ class PasswordEncoder
* Returns the algorithm ID
*
* @param string $algorithmName
+ *
* @return int
*/
- public static function getAlgorithmId($algorithmName)
+ public static function getAlgorithmId(string $algorithmName): int
{
return self::$algorithmMapping[$algorithmName][0];
}
@@ -175,10 +190,11 @@ class PasswordEncoder
/**
* Build combined key from low-order word and high-order word
*
- * @param array $byteChars byte array representation of password
+ * @param array $byteChars byte array representation of password
+ *
* @return int
*/
- private static function buildCombinedKey($byteChars)
+ private static function buildCombinedKey(array $byteChars): int
{
$byteCharsLength = count($byteChars);
// Compute the high-order word
@@ -189,10 +205,10 @@ class PasswordEncoder
// For every bit in the character, starting with the least significant and progressing to (but excluding)
// the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
// the Encryption Matrix
- for ($i = 0; $i < $byteCharsLength; $i++) {
+ for ($i = 0; $i < $byteCharsLength; ++$i) {
$tmp = self::$passwordMaxLength - $byteCharsLength + $i;
$matrixRow = self::$encryptionMatrix[$tmp];
- for ($intBit = 0; $intBit < 7; $intBit++) {
+ for ($intBit = 0; $intBit < 7; ++$intBit) {
if (($byteChars[$i] & (0x0001 << $intBit)) != 0) {
$highOrderWord = ($highOrderWord ^ $matrixRow[$intBit]);
}
@@ -203,7 +219,7 @@ class PasswordEncoder
// Initialize with 0
$lowOrderWord = 0;
// For each character in the password, going backwards
- for ($i = $byteCharsLength - 1; $i >= 0; $i--) {
+ for ($i = $byteCharsLength - 1; $i >= 0; --$i) {
// low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character
$lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteChars[$i]);
}
@@ -218,7 +234,9 @@ class PasswordEncoder
* Simulate behaviour of (signed) int32
*
* @codeCoverageIgnore
+ *
* @param int $value
+ *
* @return int
*/
private static function int32($value)
diff --git a/PhpOffice/Common/Text.php b/PhpOffice/Common/Text.php
old mode 100755
new mode 100644
index 969b6c1..1410f79
--- a/PhpOffice/Common/Text.php
+++ b/PhpOffice/Common/Text.php
@@ -9,7 +9,8 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
@@ -26,17 +27,17 @@ class Text
*
* @var string[]
*/
- private static $controlCharacters = array();
+ private static $controlCharacters = [];
/**
* Build control characters array
*/
- private static function buildControlCharacters()
+ private static function buildControlCharacters(): void
{
for ($i = 0; $i <= 19; ++$i) {
if ($i != 9 && $i != 10 && $i != 13) {
- $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_';
- $replace = chr($i);
+ $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_';
+ $replace = chr($i);
self::$controlCharacters[$find] = $replace;
}
}
@@ -53,10 +54,11 @@ class Text
* So you could end up with something like _x0008_ in a string (either in a cell value ()
* element or in the shared string element.
*
- * @param string $value Value to escape
+ * @param string $value Value to escape
+ *
* @return string
*/
- public static function controlCharacterPHP2OOXML($value = '')
+ public static function controlCharacterPHP2OOXML(string $value = ''): string
{
if (empty(self::$controlCharacters)) {
self::buildControlCharacters();
@@ -67,35 +69,41 @@ class Text
/**
* Return a number formatted for being integrated in xml files
+ *
* @param float $number
- * @param integer $decimals
+ * @param int $decimals
+ *
* @return string
*/
- public static function numberFormat($number, $decimals)
+ public static function numberFormat(float $number, int $decimals): string
{
return number_format($number, $decimals, '.', '');
}
/**
* @param int $dec
- * @link http://stackoverflow.com/a/7153133/2235790
+ *
+ * @see http://stackoverflow.com/a/7153133/2235790
+ *
* @author velcrow
+ *
* @return string
*/
- public static function chr($dec)
+ public static function chr(int $dec): string
{
- if ($dec<=0x7F) {
+ if ($dec <= 0x7F) {
return chr($dec);
}
- if ($dec<=0x7FF) {
- return chr(($dec>>6)+192).chr(($dec&63)+128);
+ if ($dec <= 0x7FF) {
+ return chr(($dec >> 6) + 192) . chr(($dec & 63) + 128);
}
- if ($dec<=0xFFFF) {
- return chr(($dec>>12)+224).chr((($dec>>6)&63)+128).chr(($dec&63)+128);
+ if ($dec <= 0xFFFF) {
+ return chr(($dec >> 12) + 224) . chr((($dec >> 6) & 63) + 128) . chr(($dec & 63) + 128);
}
- if ($dec<=0x1FFFFF) {
- return chr(($dec>>18)+240).chr((($dec>>12)&63)+128).chr((($dec>>6)&63)+128).chr(($dec&63)+128);
+ if ($dec <= 0x1FFFFF) {
+ return chr(($dec >> 18) + 240) . chr((($dec >> 12) & 63) + 128) . chr((($dec >> 6) & 63) + 128) . chr(($dec & 63) + 128);
}
+
return '';
}
@@ -103,9 +111,10 @@ class Text
* Convert from OpenXML escaped control character to PHP control character
*
* @param string $value Value to unescape
+ *
* @return string
*/
- public static function controlCharacterOOXML2PHP($value = '')
+ public static function controlCharacterOOXML2PHP(string $value = ''): string
{
if (empty(self::$controlCharacters)) {
self::buildControlCharacters();
@@ -118,9 +127,10 @@ class Text
* Check if a string contains UTF-8 data
*
* @param string $value
- * @return boolean
+ *
+ * @return bool
*/
- public static function isUTF8($value = '')
+ public static function isUTF8(string $value = ''): bool
{
return is_string($value) && ($value === '' || preg_match('/^./su', $value) == 1);
}
@@ -128,10 +138,11 @@ class Text
/**
* Return UTF8 encoded value
*
- * @param string $value
- * @return string
+ * @param string|null $value
+ *
+ * @return string|null
*/
- public static function toUTF8($value = '')
+ public static function toUTF8(?string $value = ''): ?string
{
if (!is_null($value) && !self::isUTF8($value)) {
$value = utf8_encode($value);
@@ -146,10 +157,12 @@ class Text
* The function is splitted to reduce cyclomatic complexity
*
* @param string $text UTF8 text
+ *
* @return string Unicode text
+ *
* @since 0.11.0
*/
- public static function toUnicode($text)
+ public static function toUnicode(string $text): string
{
return self::unicodeToEntities(self::utf8ToUnicode($text));
}
@@ -158,18 +171,20 @@ class Text
* Returns unicode array from UTF8 text
*
* @param string $text UTF8 text
- * @return array
+ *
+ * @return array
+ *
* @since 0.11.0
- * @link http://www.randomchaos.com/documents/?source=php_and_unicode
+ * @see http://www.randomchaos.com/documents/?source=php_and_unicode
*/
- public static function utf8ToUnicode($text)
+ public static function utf8ToUnicode(string $text): array
{
- $unicode = array();
- $values = array();
+ $unicode = [];
+ $values = [];
$lookingFor = 1;
// Gets unicode for each character
- for ($i = 0; $i < strlen($text); $i++) {
+ for ($i = 0; $i < strlen($text); ++$i) {
$thisValue = ord($text[$i]);
if ($thisValue < 128) {
$unicode[] = $thisValue;
@@ -185,7 +200,7 @@ class Text
$number = (($values[0] % 32) * 64) + ($values[1] % 64);
}
$unicode[] = $number;
- $values = array();
+ $values = [];
$lookingFor = 1;
}
}
@@ -197,12 +212,14 @@ class Text
/**
* Returns entites from unicode array
*
- * @param array $unicode
+ * @param array $unicode
+ *
* @return string
+ *
* @since 0.11.0
- * @link http://www.randomchaos.com/documents/?source=php_and_unicode
+ * @see http://www.randomchaos.com/documents/?source=php_and_unicode
*/
- private static function unicodeToEntities($unicode)
+ private static function unicodeToEntities(array $unicode): string
{
$entities = '';
@@ -218,10 +235,11 @@ class Text
/**
* Return name without underscore for < 0.10.0 variable name compatibility
*
- * @param string $value
+ * @param string|null $value
+ *
* @return string
*/
- public static function removeUnderscorePrefix($value)
+ public static function removeUnderscorePrefix(?string $value): string
{
if (!is_null($value)) {
if (substr($value, 0, 1) == '_') {
diff --git a/PhpOffice/Common/XMLReader.php b/PhpOffice/Common/XMLReader.php
old mode 100755
new mode 100644
index 5a2fa98..5d9fe81
--- a/PhpOffice/Common/XMLReader.php
+++ b/PhpOffice/Common/XMLReader.php
@@ -9,13 +9,20 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\Common;
+use DOMDocument;
+use DOMElement;
+use DOMNodeList;
+use DOMXpath;
+use ZipArchive;
+
/**
* XML Reader wrapper
*
@@ -26,14 +33,14 @@ class XMLReader
/**
* DOMDocument object
*
- * @var \DOMDocument
+ * @var DOMDocument
*/
private $dom = null;
/**
* DOMXpath object
*
- * @var \DOMXpath
+ * @var DOMXpath
*/
private $xpath = null;
@@ -42,16 +49,18 @@ class XMLReader
*
* @param string $zipFile
* @param string $xmlFile
- * @return \DOMDocument|false
+ *
+ * @return DOMDocument|false
+ *
* @throws \Exception
*/
- public function getDomFromZip($zipFile, $xmlFile)
+ public function getDomFromZip(string $zipFile, string $xmlFile)
{
if (file_exists($zipFile) === false) {
throw new \Exception('Cannot find archive file.');
}
- $zip = new \ZipArchive();
+ $zip = new ZipArchive();
$zip->open($zipFile);
$content = $zip->getFromName($xmlFile);
$zip->close();
@@ -67,14 +76,22 @@ class XMLReader
* Get DOMDocument from content string
*
* @param string $content
- * @return \DOMDocument
+ *
+ * @return DOMDocument
*/
- public function getDomFromString($content)
+ public function getDomFromString(string $content)
{
- //$originalLibXMLEntityValue = libxml_disable_entity_loader(true);
- $this->dom = new \DOMDocument();
+ $originalLibXMLEntityValue = false;
+ if (\PHP_VERSION_ID < 80000) {
+ $originalLibXMLEntityValue = libxml_disable_entity_loader(true);
+ }
+
+ $this->dom = new DOMDocument();
$this->dom->loadXML($content);
- //libxml_disable_entity_loader($originalLibXMLEntityValue);
+
+ if (\PHP_VERSION_ID < 80000) {
+ libxml_disable_entity_loader($originalLibXMLEntityValue);
+ }
return $this->dom;
}
@@ -83,16 +100,17 @@ class XMLReader
* Get elements
*
* @param string $path
- * @param \DOMElement $contextNode
- * @return \DOMNodeList
+ * @param DOMElement $contextNode
+ *
+ * @return DOMNodeList
*/
- public function getElements($path, \DOMElement $contextNode = null)
+ public function getElements(string $path, DOMElement $contextNode = null)
{
if ($this->dom === null) {
- return array();
+ return new DOMNodeList();
}
if ($this->xpath === null) {
- $this->xpath = new \DOMXpath($this->dom);
+ $this->xpath = new DOMXpath($this->dom);
}
if (is_null($contextNode)) {
@@ -107,7 +125,9 @@ class XMLReader
*
* @param string $prefix The prefix
* @param string $namespaceURI The URI of the namespace
+ *
* @return bool true on success or false on failure
+ *
* @throws \InvalidArgumentException If called before having loaded the DOM document
*/
public function registerNamespace($prefix, $namespaceURI)
@@ -116,8 +136,9 @@ class XMLReader
throw new \InvalidArgumentException('Dom needs to be loaded before registering a namespace');
}
if ($this->xpath === null) {
- $this->xpath = new \DOMXpath($this->dom);
+ $this->xpath = new DOMXpath($this->dom);
}
+
return $this->xpath->registerNamespace($prefix, $namespaceURI);
}
@@ -125,14 +146,15 @@ class XMLReader
* Get element
*
* @param string $path
- * @param \DOMElement $contextNode
- * @return \DOMElement|null
+ * @param DOMElement $contextNode
+ *
+ * @return DOMElement|null
*/
- public function getElement($path, \DOMElement $contextNode = null)
+ public function getElement($path, DOMElement $contextNode = null): ?DOMElement
{
$elements = $this->getElements($path, $contextNode);
if ($elements->length > 0) {
- return $elements->item(0);
+ return $elements->item(0) instanceof DOMElement ? $elements->item(0) : null;
}
return null;
@@ -142,17 +164,18 @@ class XMLReader
* Get element attribute
*
* @param string $attribute
- * @param \DOMElement $contextNode
+ * @param DOMElement $contextNode
* @param string $path
+ *
* @return string|null
*/
- public function getAttribute($attribute, \DOMElement $contextNode = null, $path = null)
+ public function getAttribute($attribute, DOMElement $contextNode = null, $path = null)
{
$return = null;
if ($path !== null) {
$elements = $this->getElements($path, $contextNode);
if ($elements->length > 0) {
- /** @var \DOMElement $node Type hint */
+ /** @var DOMElement $node Type hint */
$node = $elements->item(0);
$return = $node->getAttribute($attribute);
}
@@ -169,10 +192,11 @@ class XMLReader
* Get element value
*
* @param string $path
- * @param \DOMElement $contextNode
+ * @param DOMElement $contextNode
+ *
* @return string|null
*/
- public function getValue($path, \DOMElement $contextNode = null)
+ public function getValue($path, DOMElement $contextNode = null)
{
$elements = $this->getElements($path, $contextNode);
if ($elements->length > 0) {
@@ -186,10 +210,11 @@ class XMLReader
* Count elements
*
* @param string $path
- * @param \DOMElement $contextNode
- * @return integer
+ * @param DOMElement $contextNode
+ *
+ * @return int
*/
- public function countElements($path, \DOMElement $contextNode = null)
+ public function countElements($path, DOMElement $contextNode = null)
{
$elements = $this->getElements($path, $contextNode);
@@ -200,10 +225,11 @@ class XMLReader
* Element exists
*
* @param string $path
- * @param \DOMElement $contextNode
- * @return boolean
+ * @param DOMElement $contextNode
+ *
+ * @return bool
*/
- public function elementExists($path, \DOMElement $contextNode = null)
+ public function elementExists($path, DOMElement $contextNode = null)
{
return $this->getElements($path, $contextNode)->length > 0;
}
diff --git a/PhpOffice/Common/XMLWriter.php b/PhpOffice/Common/XMLWriter.php
old mode 100755
new mode 100644
index 4440d65..4c886d6
--- a/PhpOffice/Common/XMLWriter.php
+++ b/PhpOffice/Common/XMLWriter.php
@@ -9,7 +9,8 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/Common/contributors.
*
- * @link https://github.com/PHPOffice/Common
+ * @see https://github.com/PHPOffice/Common
+ *
* @copyright 2009-2016 PHPOffice Common contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
@@ -35,8 +36,8 @@ namespace PhpOffice\Common;
class XMLWriter extends \XMLWriter
{
/** Temporary storage method */
- const STORAGE_MEMORY = 1;
- const STORAGE_DISK = 2;
+ public const STORAGE_MEMORY = 1;
+ public const STORAGE_DISK = 2;
/**
* Temporary filename
@@ -58,7 +59,7 @@ class XMLWriter extends \XMLWriter
if ($pTemporaryStorage == self::STORAGE_MEMORY) {
$this->openMemory();
} else {
- if (!is_dir($pTemporaryStorageDir)) {
+ if ($pTemporaryStorageDir && !is_dir($pTemporaryStorageDir)) {
$pTemporaryStorageDir = sys_get_temp_dir();
}
// Create temporary filename
@@ -87,7 +88,7 @@ class XMLWriter extends \XMLWriter
return;
}
if (PHP_OS != 'WINNT' && @unlink($this->tempFileName) === false) {
- throw new \Exception('The file '.$this->tempFileName.' could not be deleted.');
+ throw new \Exception('The file ' . $this->tempFileName . ' could not be deleted.');
}
}
@@ -103,10 +104,10 @@ class XMLWriter extends \XMLWriter
}
$this->flush();
+
return file_get_contents($this->tempFileName);
}
-
/**
* Write simple element and attribute(s) block
*
@@ -115,15 +116,16 @@ class XMLWriter extends \XMLWriter
* 2. If not, then it's a simple attribute-value pair
*
* @param string $element
- * @param string|array $attributes
+ * @param string|array $attributes
* @param string $value
+ *
* @return void
*/
- public function writeElementBlock($element, $attributes, $value = null)
+ public function writeElementBlock(string $element, $attributes, string $value = null)
{
$this->startElement($element);
if (!is_array($attributes)) {
- $attributes = array($attributes => $value);
+ $attributes = [$attributes => $value];
}
foreach ($attributes as $attribute => $value) {
$this->writeAttribute($attribute, $value);
@@ -136,13 +138,14 @@ class XMLWriter extends \XMLWriter
*
* @param bool $condition
* @param string $element
- * @param string $attribute
+ * @param string|null $attribute
* @param mixed $value
+ *
* @return void
*/
- public function writeElementIf($condition, $element, $attribute = null, $value = null)
+ public function writeElementIf(bool $condition, string $element, ?string $attribute = null, $value = null)
{
- if ($condition == true) {
+ if ($condition) {
if (is_null($attribute)) {
$this->writeElement($element, $value);
} else {
@@ -159,11 +162,12 @@ class XMLWriter extends \XMLWriter
* @param bool $condition
* @param string $attribute
* @param mixed $value
+ *
* @return void
*/
- public function writeAttributeIf($condition, $attribute, $value)
+ public function writeAttributeIf(bool $condition, string $attribute, $value)
{
- if ($condition == true) {
+ if ($condition) {
$this->writeAttribute($attribute, $value);
}
}
@@ -171,13 +175,15 @@ class XMLWriter extends \XMLWriter
/**
* @param string $name
* @param mixed $value
+ *
* @return bool
*/
- public function writeAttribute($name, $value)
+ public function writeAttribute($name, $value): bool
{
if (is_float($value)) {
$value = json_encode($value);
}
- return parent::writeAttribute($name, $value);
+
+ return parent::writeAttribute($name, $value ?? '');
}
}
diff --git a/PhpOffice/PhpPresentation/AbstractShape.php b/PhpOffice/PhpPresentation/AbstractShape.php
old mode 100755
new mode 100644
index 9c588df..c8d3df0
--- a/PhpOffice/PhpPresentation/AbstractShape.php
+++ b/PhpOffice/PhpPresentation/AbstractShape.php
@@ -10,127 +10,120 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
+use PhpOffice\PhpPresentation\Exception\ShapeContainerAlreadyAssignedException;
use PhpOffice\PhpPresentation\Shape\Hyperlink;
use PhpOffice\PhpPresentation\Shape\Placeholder;
+use PhpOffice\PhpPresentation\Style\Border;
use PhpOffice\PhpPresentation\Style\Fill;
use PhpOffice\PhpPresentation\Style\Shadow;
/**
- * Abstract shape
+ * Abstract shape.
*/
abstract class AbstractShape implements ComparableInterface
{
/**
- * Container
+ * Container.
*
- * @var \PhpOffice\PhpPresentation\ShapeContainerInterface
+ * @var ShapeContainerInterface|null
*/
protected $container;
/**
- * Offset X
+ * Offset X.
*
* @var int
*/
protected $offsetX;
/**
- * Offset Y
+ * Offset Y.
*
* @var int
*/
protected $offsetY;
/**
- * Width
+ * Width.
*
* @var int
*/
protected $width;
/**
- * Height
+ * Height.
*
* @var int
*/
protected $height;
/**
- * Fill
- *
- * @var \PhpOffice\PhpPresentation\Style\Fill
+ * @var Fill|null
*/
private $fill;
/**
- * Border
+ * Border.
*
- * @var \PhpOffice\PhpPresentation\Style\Border
+ * @var Border
*/
private $border;
/**
- * Rotation
+ * Rotation.
*
* @var int
*/
protected $rotation;
/**
- * Shadow
+ * Shadow.
*
- * @var \PhpOffice\PhpPresentation\Style\Shadow
+ * @var Shadow|null
*/
protected $shadow;
/**
- * Hyperlink
- *
- * @var \PhpOffice\PhpPresentation\Shape\Hyperlink
+ * @var Hyperlink|null
*/
protected $hyperlink;
/**
- * PlaceHolder
- * @var \PhpOffice\PhpPresentation\Shape\Placeholder
+ * @var Placeholder|null
*/
protected $placeholder;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new self
+ * Create a new self.
*/
public function __construct()
{
- // Initialise values
- $this->container = null;
- $this->offsetX = 0;
- $this->offsetY = 0;
- $this->width = 0;
- $this->height = 0;
- $this->rotation = 0;
- $this->fill = new Style\Fill();
- $this->border = new Style\Border();
- $this->shadow = new Style\Shadow();
-
+ $this->offsetX = $this->offsetY = $this->width = $this->height = $this->rotation = 0;
+ $this->fill = new Fill();
+ $this->shadow = new Shadow();
+ $this->border = new Border();
$this->border->setLineStyle(Style\Border::LINE_NONE);
}
/**
- * Magic Method : clone
+ * Magic Method : clone.
*/
public function __clone()
{
@@ -141,34 +134,34 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Get Container, Slide or Group
- *
- * @return \PhpOffice\PhpPresentation\ShapeContainerInterface
+ * Get Container, Slide or Group.
*/
- public function getContainer()
+ public function getContainer(): ?ShapeContainerInterface
{
return $this->container;
}
/**
- * Set Container, Slide or Group
+ * Set Container, Slide or Group.
+ *
+ * @param ShapeContainerInterface $pValue
+ * @param bool $pOverrideOld If a Slide has already been assigned, overwrite it and remove image from old Slide?
+ *
+ * @throws ShapeContainerAlreadyAssignedException
*
- * @param \PhpOffice\PhpPresentation\ShapeContainerInterface $pValue
- * @param bool $pOverrideOld If a Slide has already been assigned, overwrite it and remove image from old Slide?
- * @throws \Exception
* @return $this
*/
public function setContainer(ShapeContainerInterface $pValue = null, $pOverrideOld = false)
{
if (is_null($this->container)) {
- // Add drawing to \PhpOffice\PhpPresentation\ShapeContainerInterface
+ // Add drawing to ShapeContainerInterface
$this->container = $pValue;
if (!is_null($this->container)) {
$this->container->getShapeCollection()->append($this);
}
} else {
if ($pOverrideOld) {
- // Remove drawing from old \PhpOffice\PhpPresentation\ShapeContainerInterface
+ // Remove drawing from old ShapeContainerInterface
$iterator = $this->container->getShapeCollection()->getIterator();
while ($iterator->valid()) {
@@ -183,7 +176,7 @@ abstract class AbstractShape implements ComparableInterface
// Set new \PhpOffice\PhpPresentation\Slide
$this->setContainer($pValue);
} else {
- throw new \Exception("A \PhpOffice\PhpPresentation\ShapeContainerInterface has already been assigned. Shapes can only exist on one \PhpOffice\PhpPresentation\ShapeContainerInterface.");
+ throw new ShapeContainerAlreadyAssignedException(self::class);
}
}
@@ -191,22 +184,19 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Get OffsetX
- *
- * @return int
+ * Get OffsetX.
*/
- public function getOffsetX()
+ public function getOffsetX(): int
{
return $this->offsetX;
}
/**
- * Set OffsetX
+ * Set OffsetX.
*
- * @param int $pValue
* @return $this
*/
- public function setOffsetX($pValue = 0)
+ public function setOffsetX(int $pValue = 0)
{
$this->offsetX = $pValue;
@@ -214,7 +204,7 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Get OffsetY
+ * Get OffsetY.
*
* @return int
*/
@@ -224,12 +214,11 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Set OffsetY
+ * Set OffsetY.
*
- * @param int $pValue
* @return $this
*/
- public function setOffsetY($pValue = 0)
+ public function setOffsetY(int $pValue = 0)
{
$this->offsetY = $pValue;
@@ -237,7 +226,7 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Get Width
+ * Get Width.
*
* @return int
*/
@@ -247,19 +236,19 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Set Width
+ * Set Width.
*
- * @param int $pValue
* @return $this
*/
- public function setWidth($pValue = 0)
+ public function setWidth(int $pValue = 0)
{
$this->width = $pValue;
+
return $this;
}
/**
- * Get Height
+ * Get Height.
*
* @return int
*/
@@ -269,34 +258,32 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Set Height
+ * Set Height.
*
- * @param int $pValue
* @return $this
*/
- public function setHeight($pValue = 0)
+ public function setHeight(int $pValue = 0)
{
$this->height = $pValue;
+
return $this;
}
/**
- * Set width and height with proportional resize
+ * Set width and height with proportional resize.
*
- * @param int $width
- * @param int $height
- * @example $objDrawing->setWidthAndHeight(160,120);
- * @return $this
+ * @return self
*/
- public function setWidthAndHeight($width = 0, $height = 0)
+ public function setWidthAndHeight(int $width = 0, int $height = 0)
{
$this->width = $width;
$this->height = $height;
+
return $this;
}
/**
- * Get Rotation
+ * Get Rotation.
*
* @return int
*/
@@ -306,75 +293,55 @@ abstract class AbstractShape implements ComparableInterface
}
/**
- * Set Rotation
+ * Set Rotation.
+ *
+ * @param int $pValue
*
- * @param int $pValue
* @return $this
*/
public function setRotation($pValue = 0)
{
$this->rotation = $pValue;
+
return $this;
}
- /**
- * Get Fill
- *
- * @return \PhpOffice\PhpPresentation\Style\Fill
- */
- public function getFill()
+ public function getFill(): ?Fill
{
return $this->fill;
}
- /**
- * Set Fill
- * @param \PhpOffice\PhpPresentation\Style\Fill $pValue
- * @return \PhpOffice\PhpPresentation\AbstractShape
- */
- public function setFill(Fill $pValue = null)
+ public function setFill(Fill $pValue = null): self
{
$this->fill = $pValue;
+
return $this;
}
- /**
- * Get Border
- *
- * @return \PhpOffice\PhpPresentation\Style\Border
- */
- public function getBorder()
+ public function getBorder(): Border
{
return $this->border;
}
- /**
- * Get Shadow
- *
- * @return \PhpOffice\PhpPresentation\Style\Shadow
- */
- public function getShadow()
+ public function getShadow(): ?Shadow
{
return $this->shadow;
}
/**
- * Set Shadow
- *
- * @param \PhpOffice\PhpPresentation\Style\Shadow $pValue
- * @throws \Exception
* @return $this
*/
public function setShadow(Shadow $pValue = null)
{
$this->shadow = $pValue;
+
return $this;
}
/**
* Has Hyperlink?
*
- * @return boolean
+ * @return bool
*/
public function hasHyperlink()
{
@@ -383,87 +350,84 @@ abstract class AbstractShape implements ComparableInterface
/**
* Get Hyperlink
- *
- * @return \PhpOffice\PhpPresentation\Shape\Hyperlink
- * @throws \Exception
*/
- public function getHyperlink()
+ public function getHyperlink(): Hyperlink
{
if (is_null($this->hyperlink)) {
$this->hyperlink = new Hyperlink();
}
+
return $this->hyperlink;
}
/**
* Set Hyperlink
- *
- * @param \PhpOffice\PhpPresentation\Shape\Hyperlink $pHyperlink
- * @throws \Exception
- * @return $this
*/
- public function setHyperlink(Hyperlink $pHyperlink = null)
+ public function setHyperlink(Hyperlink $pHyperlink = null): self
{
$this->hyperlink = $pHyperlink;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5((is_object($this->container) ? $this->container->getHashCode() : '') . $this->offsetX . $this->offsetY . $this->width . $this->height . $this->rotation . (is_null($this->getFill()) ? '' : $this->getFill()->getHashCode()) . (is_null($this->shadow) ? '' : $this->shadow->getHashCode()) . (is_null($this->hyperlink) ? '' : $this->hyperlink->getHashCode()) . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
- public function isPlaceholder()
+ public function isPlaceholder(): bool
{
return !is_null($this->placeholder);
}
- public function getPlaceholder()
+ public function getPlaceholder(): ?Placeholder
{
if (!$this->isPlaceholder()) {
return null;
}
+
return $this->placeholder;
}
- /**
- * @param \PhpOffice\PhpPresentation\Shape\Placeholder $placeholder
- * @return $this
- */
- public function setPlaceHolder(Placeholder $placeholder)
+ public function setPlaceHolder(Placeholder $placeholder): self
{
$this->placeholder = $placeholder;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Autoloader.php b/PhpOffice/PhpPresentation/Autoloader.php
old mode 100755
new mode 100644
index 605981b..6c7211f
--- a/PhpOffice/PhpPresentation/Autoloader.php
+++ b/PhpOffice/PhpPresentation/Autoloader.php
@@ -10,42 +10,44 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
/**
- * Autoloader
+ * Autoloader.
*/
class Autoloader
{
/** @const string */
- const NAMESPACE_PREFIX = 'PhpOffice\\PhpPresentation\\';
+ public const NAMESPACE_PREFIX = 'PhpOffice\\PhpPresentation\\';
/**
- * Register
- *
- * @return void
+ * Register.
*/
- public static function register()
+ public static function register(): void
{
- spl_autoload_register(array(new self, 'autoload'));
+ spl_autoload_register([new self(), 'autoload']);
}
/**
- * Autoload
- *
- * @param string $class
+ * Autoload.
*/
- public static function autoload($class)
+ public static function autoload(string $class): void
{
$prefixLength = strlen(self::NAMESPACE_PREFIX);
if (0 === strncmp(self::NAMESPACE_PREFIX, $class, $prefixLength)) {
$file = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $prefixLength));
$file = realpath(__DIR__ . (empty($file) ? '' : DIRECTORY_SEPARATOR) . $file . '.php');
+ if (!$file) {
+ return;
+ }
if (file_exists($file)) {
/** @noinspection PhpIncludeInspection Dynamic includes */
require_once $file;
diff --git a/PhpOffice/PhpPresentation/COPYING b/PhpOffice/PhpPresentation/COPYING
deleted file mode 100755
index 94a9ed0..0000000
--- a/PhpOffice/PhpPresentation/COPYING
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/PhpOffice/PhpPresentation/COPYING.LESSER b/PhpOffice/PhpPresentation/COPYING.LESSER
deleted file mode 100755
index 65c5ca8..0000000
--- a/PhpOffice/PhpPresentation/COPYING.LESSER
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/PhpOffice/PhpPresentation/ComparableInterface.php b/PhpOffice/PhpPresentation/ComparableInterface.php
old mode 100755
new mode 100644
index 87eae2c..7fefbda
--- a/PhpOffice/PhpPresentation/ComparableInterface.php
+++ b/PhpOffice/PhpPresentation/ComparableInterface.php
@@ -10,42 +10,47 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
/**
- * PhpOffice\PhpPresentation\ComparableInterface
+ * PhpOffice\PhpPresentation\ComparableInterface.
*/
interface ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode();
+ public function getHashCode(): string;
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex();
+ public function getHashIndex(): ?int;
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value);
+ public function setHashIndex(int $value);
}
diff --git a/PhpOffice/PhpPresentation/DocumentLayout.php b/PhpOffice/PhpPresentation/DocumentLayout.php
old mode 100755
new mode 100644
index c8cf385..bf79104
--- a/PhpOffice/PhpPresentation/DocumentLayout.php
+++ b/PhpOffice/PhpPresentation/DocumentLayout.php
@@ -10,83 +10,90 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
use PhpOffice\Common\Drawing;
/**
- * \PhpOffice\PhpPresentation\DocumentLayout
+ * \PhpOffice\PhpPresentation\DocumentLayout.
*/
class DocumentLayout
{
- const LAYOUT_CUSTOM = '';
- const LAYOUT_SCREEN_4X3 = 'screen4x3';
- const LAYOUT_SCREEN_16X10 = 'screen16x10';
- const LAYOUT_SCREEN_16X9 = 'screen16x9';
- const LAYOUT_35MM = '35mm';
- const LAYOUT_A3 = 'A3';
- const LAYOUT_A4 = 'A4';
- const LAYOUT_B4ISO = 'B4ISO';
- const LAYOUT_B5ISO = 'B5ISO';
- const LAYOUT_BANNER = 'banner';
- const LAYOUT_LETTER = 'letter';
- const LAYOUT_OVERHEAD = 'overhead';
+ public const LAYOUT_CUSTOM = '';
+ public const LAYOUT_SCREEN_4X3 = 'screen4x3';
+ public const LAYOUT_SCREEN_16X10 = 'screen16x10';
+ public const LAYOUT_SCREEN_16X9 = 'screen16x9';
+ public const LAYOUT_35MM = '35mm';
+ public const LAYOUT_A3 = 'A3';
+ public const LAYOUT_A4 = 'A4';
+ public const LAYOUT_B4ISO = 'B4ISO';
+ public const LAYOUT_B5ISO = 'B5ISO';
+ public const LAYOUT_BANNER = 'banner';
+ public const LAYOUT_LETTER = 'letter';
+ public const LAYOUT_OVERHEAD = 'overhead';
- const UNIT_EMU = 'emu';
- const UNIT_CENTIMETER = 'cm';
- const UNIT_INCH = 'in';
- const UNIT_MILLIMETER = 'mm';
- const UNIT_PIXEL = 'px';
- const UNIT_POINT = 'pt';
+ public const UNIT_EMU = 'emu';
+ public const UNIT_CENTIMETER = 'cm';
+ public const UNIT_INCH = 'in';
+ public const UNIT_MILLIMETER = 'mm';
+ public const UNIT_PIXEL = 'px';
+ public const UNIT_POINT = 'pt';
/**
- * Dimension types
+ * Dimension types.
*
* 1 px = 9525 EMU @ 96dpi (which is seems to be the default)
* Absolute distances are specified in English Metric Units (EMUs),
* occasionally referred to as A units; there are 360000 EMUs per
* centimeter, 914400 EMUs per inch, 12700 EMUs per point.
+ *
+ * @var array>
*/
- private $dimension = array(
- self::LAYOUT_SCREEN_4X3 => array('cx' => 9144000, 'cy' => 6858000),
- self::LAYOUT_SCREEN_16X10 => array('cx' => 9144000, 'cy' => 5715000),
- self::LAYOUT_SCREEN_16X9 => array('cx' => 9144000, 'cy' => 5143500),
- self::LAYOUT_35MM => array('cx' => 10287000, 'cy' => 6858000),
- self::LAYOUT_A3 => array('cx' => 15120000, 'cy' => 10692000),
- self::LAYOUT_A4 => array('cx' => 10692000, 'cy' => 7560000),
- self::LAYOUT_B4ISO => array('cx' => 10826750, 'cy' => 8120063),
- self::LAYOUT_B5ISO => array('cx' => 7169150, 'cy' => 5376863),
- self::LAYOUT_BANNER => array('cx' => 7315200, 'cy' => 914400),
- self::LAYOUT_LETTER => array('cx' => 9144000, 'cy' => 6858000),
- self::LAYOUT_OVERHEAD => array('cx' => 9144000, 'cy' => 6858000),
- );
+ private $dimension = [
+ self::LAYOUT_SCREEN_4X3 => ['cx' => 9144000, 'cy' => 6858000],
+ self::LAYOUT_SCREEN_16X10 => ['cx' => 9144000, 'cy' => 5715000],
+ self::LAYOUT_SCREEN_16X9 => ['cx' => 9144000, 'cy' => 5143500],
+ self::LAYOUT_35MM => ['cx' => 10287000, 'cy' => 6858000],
+ self::LAYOUT_A3 => ['cx' => 15120000, 'cy' => 10692000],
+ self::LAYOUT_A4 => ['cx' => 10692000, 'cy' => 7560000],
+ self::LAYOUT_B4ISO => ['cx' => 10826750, 'cy' => 8120063],
+ self::LAYOUT_B5ISO => ['cx' => 7169150, 'cy' => 5376863],
+ self::LAYOUT_BANNER => ['cx' => 7315200, 'cy' => 914400],
+ self::LAYOUT_LETTER => ['cx' => 9144000, 'cy' => 6858000],
+ self::LAYOUT_OVERHEAD => ['cx' => 9144000, 'cy' => 6858000],
+ ];
/**
- * Layout name
+ * Layout name.
*
* @var string
*/
private $layout;
/**
- * Layout X dimension
+ * Layout X dimension.
+ *
* @var float
*/
private $dimensionX;
/**
- * Layout Y dimension
+ * Layout Y dimension.
+ *
* @var float
*/
private $dimensionY;
/**
- * Create a new \PhpOffice\PhpPresentation\DocumentLayout
+ * Create a new \PhpOffice\PhpPresentation\DocumentLayout.
*/
public function __construct()
{
@@ -94,23 +101,20 @@ class DocumentLayout
}
/**
- * Get Document Layout
- *
- * @return string
+ * Get Document Layout.
*/
- public function getDocumentLayout()
+ public function getDocumentLayout(): string
{
return $this->layout;
}
/**
- * Set Document Layout
+ * Set Document Layout.
*
- * @param array|string $pValue
- * @param boolean $isLandscape
- * @return \PhpOffice\PhpPresentation\DocumentLayout
+ * @param array|string $pValue
+ * @param bool $isLandscape
*/
- public function setDocumentLayout($pValue = self::LAYOUT_SCREEN_4X3, $isLandscape = true)
+ public function setDocumentLayout($pValue = self::LAYOUT_SCREEN_4X3, $isLandscape = true): self
{
switch ($pValue) {
case self::LAYOUT_SCREEN_4X3:
@@ -146,63 +150,47 @@ class DocumentLayout
}
/**
- * Get Document Layout cx
- *
- * @param string $unit
- * @return integer
+ * Get Document Layout cx.
*/
- public function getCX($unit = self::UNIT_EMU)
+ public function getCX(string $unit = self::UNIT_EMU): float
{
return $this->convertUnit($this->dimensionX, self::UNIT_EMU, $unit);
}
/**
- * Get Document Layout cy
- *
- * @param string $unit
- * @return integer
+ * Get Document Layout cy.
*/
- public function getCY($unit = self::UNIT_EMU)
+ public function getCY(string $unit = self::UNIT_EMU): float
{
return $this->convertUnit($this->dimensionY, self::UNIT_EMU, $unit);
}
/**
- * Get Document Layout cx
- *
- * @param float $value
- * @param string $unit
- * @return DocumentLayout
+ * Get Document Layout cx.
*/
- public function setCX($value, $unit = self::UNIT_EMU)
+ public function setCX(float $value, string $unit = self::UNIT_EMU): self
{
$this->layout = self::LAYOUT_CUSTOM;
$this->dimensionX = $this->convertUnit($value, $unit, self::UNIT_EMU);
+
return $this;
}
/**
- * Get Document Layout cy
- *
- * @param float $value
- * @param string $unit
- * @return DocumentLayout
+ * Get Document Layout cy.
*/
- public function setCY($value, $unit = self::UNIT_EMU)
+ public function setCY(float $value, string $unit = self::UNIT_EMU): self
{
$this->layout = self::LAYOUT_CUSTOM;
$this->dimensionY = $this->convertUnit($value, $unit, self::UNIT_EMU);
+
return $this;
}
/**
- * Convert EMUs to differents units
- * @param float $value
- * @param string $fromUnit
- * @param string $toUnit
- * @return float
+ * Convert EMUs to differents units.
*/
- protected function convertUnit($value, $fromUnit, $toUnit)
+ protected function convertUnit(float $value, string $fromUnit, string $toUnit): float
{
// Convert from $fromUnit to EMU
switch ($fromUnit) {
@@ -238,7 +226,7 @@ class DocumentLayout
$value /= 914400;
break;
case self::UNIT_PIXEL:
- $value = Drawing::emuToPixels($value);
+ $value = Drawing::emuToPixels((int) $value);
break;
case self::UNIT_POINT:
$value /= 12700;
@@ -247,6 +235,7 @@ class DocumentLayout
default:
// no changes
}
+
return $value;
}
}
diff --git a/PhpOffice/PhpPresentation/DocumentProperties.php b/PhpOffice/PhpPresentation/DocumentProperties.php
old mode 100755
new mode 100644
index c9344fe..ab45420
--- a/PhpOffice/PhpPresentation/DocumentProperties.php
+++ b/PhpOffice/PhpPresentation/DocumentProperties.php
@@ -10,108 +10,125 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
/**
- * \PhpOffice\PhpPresentation\DocumentProperties
+ * \PhpOffice\PhpPresentation\DocumentProperties.
*/
class DocumentProperties
{
+ public const PROPERTY_TYPE_BOOLEAN = 'b';
+ public const PROPERTY_TYPE_INTEGER = 'i';
+ public const PROPERTY_TYPE_FLOAT = 'f';
+ public const PROPERTY_TYPE_DATE = 'd';
+ public const PROPERTY_TYPE_STRING = 's';
+ public const PROPERTY_TYPE_UNKNOWN = 'u';
+
/**
- * Creator
+ * Creator.
*
* @var string
*/
private $creator;
/**
- * LastModifiedBy
+ * LastModifiedBy.
*
* @var string
*/
private $lastModifiedBy;
/**
- * Created
+ * Created.
*
* @var int
*/
private $created;
/**
- * Modified
+ * Modified.
*
* @var int
*/
private $modified;
/**
- * Title
+ * Title.
*
* @var string
*/
private $title;
/**
- * Description
+ * Description.
*
* @var string
*/
private $description;
/**
- * Subject
+ * Subject.
*
* @var string
*/
private $subject;
/**
- * Keywords
+ * Keywords.
*
* @var string
*/
private $keywords;
/**
- * Category
+ * Category.
*
* @var string
*/
private $category;
/**
- * Company
+ * Company.
*
* @var string
*/
private $company;
+ /**
+ * Custom Properties.
+ *
+ * @var array>
+ */
+ private $customProperties = [];
+
/**
* Create a new \PhpOffice\PhpPresentation\DocumentProperties
*/
public function __construct()
{
// Initialise values
- $this->creator = 'Unknown Creator';
+ $this->creator = 'Unknown Creator';
$this->lastModifiedBy = $this->creator;
- $this->created = time();
- $this->modified = time();
- $this->title = "Untitled Presentation";
- $this->subject = '';
- $this->description = '';
- $this->keywords = '';
- $this->category = '';
- $this->company = 'Microsoft Corporation';
+ $this->created = time();
+ $this->modified = time();
+ $this->title = 'Untitled Presentation';
+ $this->subject = '';
+ $this->description = '';
+ $this->keywords = '';
+ $this->category = '';
+ $this->company = 'Microsoft Corporation';
}
/**
- * Get Creator
+ * Get Creator.
*
* @return string
*/
@@ -121,9 +138,10 @@ class DocumentProperties
}
/**
- * Set Creator
+ * Set Creator.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setCreator($pValue = '')
@@ -134,7 +152,7 @@ class DocumentProperties
}
/**
- * Get Last Modified By
+ * Get Last Modified By.
*
* @return string
*/
@@ -144,9 +162,10 @@ class DocumentProperties
}
/**
- * Set Last Modified By
+ * Set Last Modified By.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setLastModifiedBy($pValue = '')
@@ -157,7 +176,7 @@ class DocumentProperties
}
/**
- * Get Created
+ * Get Created.
*
* @return int
*/
@@ -167,9 +186,10 @@ class DocumentProperties
}
/**
- * Set Created
+ * Set Created.
*
* @param int $pValue
+ *
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setCreated($pValue = null)
@@ -183,7 +203,7 @@ class DocumentProperties
}
/**
- * Get Modified
+ * Get Modified.
*
* @return int
*/
@@ -193,9 +213,10 @@ class DocumentProperties
}
/**
- * Set Modified
+ * Set Modified.
+ *
+ * @param int $pValue
*
- * @param int $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setModified($pValue = null)
@@ -209,7 +230,7 @@ class DocumentProperties
}
/**
- * Get Title
+ * Get Title.
*
* @return string
*/
@@ -219,9 +240,10 @@ class DocumentProperties
}
/**
- * Set Title
+ * Set Title.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setTitle($pValue = '')
@@ -232,7 +254,7 @@ class DocumentProperties
}
/**
- * Get Description
+ * Get Description.
*
* @return string
*/
@@ -242,9 +264,10 @@ class DocumentProperties
}
/**
- * Set Description
+ * Set Description.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setDescription($pValue = '')
@@ -255,7 +278,7 @@ class DocumentProperties
}
/**
- * Get Subject
+ * Get Subject.
*
* @return string
*/
@@ -265,9 +288,10 @@ class DocumentProperties
}
/**
- * Set Subject
+ * Set Subject.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setSubject($pValue = '')
@@ -278,7 +302,7 @@ class DocumentProperties
}
/**
- * Get Keywords
+ * Get Keywords.
*
* @return string
*/
@@ -288,9 +312,10 @@ class DocumentProperties
}
/**
- * Set Keywords
+ * Set Keywords.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setKeywords($pValue = '')
@@ -301,7 +326,7 @@ class DocumentProperties
}
/**
- * Get Category
+ * Get Category.
*
* @return string
*/
@@ -311,9 +336,10 @@ class DocumentProperties
}
/**
- * Set Category
+ * Set Category.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setCategory($pValue = '')
@@ -324,7 +350,7 @@ class DocumentProperties
}
/**
- * Get Company
+ * Get Company.
*
* @return string
*/
@@ -334,9 +360,10 @@ class DocumentProperties
}
/**
- * Set Company
+ * Set Company.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\DocumentProperties
*/
public function setCompany($pValue = '')
@@ -345,4 +372,99 @@ class DocumentProperties
return $this;
}
+
+ /**
+ * Get a List of Custom Property Names.
+ *
+ * @return array
+ */
+ public function getCustomProperties(): array
+ {
+ return array_keys($this->customProperties);
+ }
+
+ /**
+ * Check if a Custom Property is defined.
+ *
+ * @param string $propertyName
+ *
+ * @return bool
+ */
+ public function isCustomPropertySet(string $propertyName): bool
+ {
+ return isset($this->customProperties[$propertyName]);
+ }
+
+ /**
+ * Get a Custom Property Value.
+ *
+ * @param string $propertyName
+ *
+ * @return mixed|null
+ */
+ public function getCustomPropertyValue(string $propertyName)
+ {
+ if ($this->isCustomPropertySet($propertyName)) {
+ return $this->customProperties[$propertyName]['value'];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a Custom Property Type.
+ *
+ * @param string $propertyName
+ *
+ * @return string|null
+ */
+ public function getCustomPropertyType(string $propertyName): ?string
+ {
+ if ($this->isCustomPropertySet($propertyName)) {
+ return $this->customProperties[$propertyName]['type'];
+ }
+
+ return null;
+ }
+
+ /**
+ * Set a Custom Property.
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @param string|null $propertyType
+ * 'i' : Integer
+ * 'f' : Floating Point
+ * 's' : String
+ * 'd' : Date/Time
+ * 'b' : Boolean
+ *
+ * @return self
+ */
+ public function setCustomProperty(string $propertyName, $propertyValue = '', ?string $propertyType = null): self
+ {
+ if (!in_array($propertyType, [
+ self::PROPERTY_TYPE_INTEGER,
+ self::PROPERTY_TYPE_FLOAT,
+ self::PROPERTY_TYPE_STRING,
+ self::PROPERTY_TYPE_DATE,
+ self::PROPERTY_TYPE_BOOLEAN,
+ ])) {
+ if (is_float($propertyValue)) {
+ $propertyType = self::PROPERTY_TYPE_FLOAT;
+ } elseif (is_int($propertyValue)) {
+ $propertyType = self::PROPERTY_TYPE_INTEGER;
+ } elseif (is_bool($propertyValue)) {
+ $propertyType = self::PROPERTY_TYPE_BOOLEAN;
+ } else {
+ $propertyType = self::PROPERTY_TYPE_STRING;
+ }
+ }
+ $this->customProperties[$propertyName] = [
+ 'value' => $propertyValue,
+ 'type' => $propertyType,
+ ];
+
+ return $this;
+ }
}
diff --git a/PhpOffice/PhpPresentation/Exception/DirectoryNotFoundException.php b/PhpOffice/PhpPresentation/Exception/DirectoryNotFoundException.php
new file mode 100644
index 0000000..bfd444c
--- /dev/null
+++ b/PhpOffice/PhpPresentation/Exception/DirectoryNotFoundException.php
@@ -0,0 +1,32 @@
+ $authorizedMimetypes
+ */
+ public function __construct(string $expectedMimetype, array $authorizedMimetypes)
+ {
+ parent::__construct(sprintf(
+ 'The mime type %s is not found in autorized values (%s)',
+ $expectedMimetype,
+ implode(', ', $authorizedMimetypes)
+ ));
+ }
+}
diff --git a/PhpOffice/PhpPresentation/Shape/MemoryDrawing.php b/PhpOffice/PhpPresentation/Exception/UndefinedChartTypeException.php
old mode 100755
new mode 100644
similarity index 67%
rename from PhpOffice/PhpPresentation/Shape/MemoryDrawing.php
rename to PhpOffice/PhpPresentation/Exception/UndefinedChartTypeException.php
index e4b5c7a..5e77181
--- a/PhpOffice/PhpPresentation/Shape/MemoryDrawing.php
+++ b/PhpOffice/PhpPresentation/Exception/UndefinedChartTypeException.php
@@ -10,19 +10,20 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
-namespace PhpOffice\PhpPresentation\Shape;
+declare(strict_types=1);
-use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
+namespace PhpOffice\PhpPresentation\Exception;
-/**
- * Memory drawing shape
- * @deprecated Drawing\Gd
- */
-class MemoryDrawing extends Gd
+class UndefinedChartTypeException extends PhpPresentationException
{
+ public function __construct()
+ {
+ parent::__construct('The chart type has not been defined');
+ }
}
diff --git a/PhpOffice/PhpPresentation/GeometryCalculator.php b/PhpOffice/PhpPresentation/GeometryCalculator.php
old mode 100755
new mode 100644
index 987a7ba..7eccbf8
--- a/PhpOffice/PhpPresentation/GeometryCalculator.php
+++ b/PhpOffice/PhpPresentation/GeometryCalculator.php
@@ -10,40 +10,42 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
/**
- * PhpOffice\PhpPresentation\GeometryCalculator
+ * PhpOffice\PhpPresentation\GeometryCalculator.
*/
class GeometryCalculator
{
- const X = 'X';
- const Y = 'Y';
+ public const X = 'X';
+ public const Y = 'Y';
/**
- * Calculate X and Y offsets for a set of shapes within a container such as a slide or group.
- *
- * @param \PhpOffice\PhpPresentation\ShapeContainerInterface $container
- * @return array
- */
- public static function calculateOffsets(ShapeContainerInterface $container)
+ * Calculate X and Y offsets for a set of shapes within a container such as a slide or group.
+ *
+ * @return array
+ */
+ public static function calculateOffsets(ShapeContainerInterface $container): array
{
- $offsets = array(self::X => 0, self::Y => 0);
+ $offsets = [self::X => 0, self::Y => 0];
- if ($container !== null && count($container->getShapeCollection()) != 0) {
+ if (null !== $container && 0 != count($container->getShapeCollection())) {
$shapes = $container->getShapeCollection();
- if ($shapes[0] !== null) {
+ if (null !== $shapes[0]) {
$offsets[self::X] = $shapes[0]->getOffsetX();
$offsets[self::Y] = $shapes[0]->getOffsetY();
}
foreach ($shapes as $shape) {
- if ($shape !== null) {
+ if (null !== $shape) {
if ($shape->getOffsetX() < $offsets[self::X]) {
$offsets[self::X] = $shape->getOffsetX();
}
@@ -59,26 +61,26 @@ class GeometryCalculator
}
/**
- * Calculate X and Y extents for a set of shapes within a container such as a slide or group.
- *
- * @param \PhpOffice\PhpPresentation\ShapeContainerInterface $container
- * @return array
- */
- public static function calculateExtents(ShapeContainerInterface $container)
+ * Calculate X and Y extents for a set of shapes within a container such as a slide or group.
+ *
+ * @return array
+ */
+ public static function calculateExtents(ShapeContainerInterface $container): array
{
- $extents = array(self::X => 0, self::Y => 0);
+ /** @var array $extents */
+ $extents = [self::X => 0, self::Y => 0];
- if ($container !== null && count($container->getShapeCollection()) != 0) {
+ if (null !== $container && 0 != count($container->getShapeCollection())) {
$shapes = $container->getShapeCollection();
- if ($shapes[0] !== null) {
- $extents[self::X] = $shapes[0]->getOffsetX() + $shapes[0]->getWidth();
- $extents[self::Y] = $shapes[0]->getOffsetY() + $shapes[0]->getHeight();
+ if (null !== $shapes[0]) {
+ $extents[self::X] = (int) ($shapes[0]->getOffsetX() + $shapes[0]->getWidth());
+ $extents[self::Y] = (int) ($shapes[0]->getOffsetY() + $shapes[0]->getHeight());
}
foreach ($shapes as $shape) {
- if ($shape !== null) {
- $extentX = $shape->getOffsetX() + $shape->getWidth();
- $extentY = $shape->getOffsetY() + $shape->getHeight();
+ if (null !== $shape) {
+ $extentX = (int) ($shape->getOffsetX() + $shape->getWidth());
+ $extentY = (int) ($shape->getOffsetY() + $shape->getHeight());
if ($extentX > $extents[self::X]) {
$extents[self::X] = $extentX;
diff --git a/PhpOffice/PhpPresentation/HashTable.php b/PhpOffice/PhpPresentation/HashTable.php
old mode 100755
new mode 100644
index 54eec91..df5e5a2
--- a/PhpOffice/PhpPresentation/HashTable.php
+++ b/PhpOffice/PhpPresentation/HashTable.php
@@ -10,88 +10,76 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
/**
- * \PhpOffice\PhpPresentation\HashTable
+ * \PhpOffice\PhpPresentation\HashTable.
*/
class HashTable
{
/**
- * HashTable elements
+ * HashTable elements.
*
- * @var array
+ * @var array
*/
- public $items = array();
+ public $items = [];
/**
- * HashTable key map
+ * HashTable key map.
*
- * @var array
+ * @var array
*/
- public $keyMap = array();
+ public $keyMap = [];
/**
- * Create a new \PhpOffice\PhpPresentation\HashTable
+ * Create a new \PhpOffice\PhpPresentation\HashTable.
*
- * @param \PhpOffice\PhpPresentation\ComparableInterface[] $pSource Optional source array to create HashTable from
- * @throws \Exception
+ * @param array $pSource Optional source array to create HashTable from
*/
- public function __construct(array $pSource = null)
+ public function __construct(array $pSource = [])
{
- if (!is_null($pSource)) {
- // Create HashTable
- $this->addFromSource($pSource);
- }
+ $this->addFromSource($pSource);
}
/**
- * Add HashTable items from source
+ * Add HashTable items from source.
*
- * @param \PhpOffice\PhpPresentation\ComparableInterface[] $pSource Source array to create HashTable from
- * @throws \Exception
+ * @param array $pSource Source array to create HashTable from
*/
- public function addFromSource($pSource = null)
+ public function addFromSource(array $pSource = []): void
{
- // Check if an array was passed
- if ($pSource == null) {
- return;
- } elseif (!is_array($pSource)) {
- throw new \Exception('Invalid array parameter passed.');
- }
-
foreach ($pSource as $item) {
$this->add($item);
}
}
/**
- * Add HashTable item
+ * Add HashTable item.
*
- * @param \PhpOffice\PhpPresentation\ComparableInterface $pSource Item to add
+ * @param ComparableInterface $pSource Item to add
*/
- public function add(ComparableInterface $pSource)
+ public function add(ComparableInterface $pSource): void
{
// Determine hashcode
$hashIndex = $pSource->getHashIndex();
$hashCode = $pSource->getHashCode();
-
- if (is_null($hashIndex)) {
- $hashCode = $pSource->getHashCode();
- } elseif (isset($this->keyMap[$hashIndex])) {
+ if (isset($this->keyMap[$hashIndex])) {
$hashCode = $this->keyMap[$hashIndex];
}
// Add value
if (!isset($this->items[$hashCode])) {
$this->items[$hashCode] = $pSource;
- $index = count($this->items) - 1;
- $this->keyMap[$index] = $hashCode;
+ $index = count($this->items) - 1;
+ $this->keyMap[$index] = $hashCode;
$pSource->setHashIndex($index);
} else {
$pSource->setHashIndex($this->items[$hashCode]->getHashIndex());
@@ -99,12 +87,11 @@ class HashTable
}
/**
- * Remove HashTable item
+ * Remove HashTable item.
*
- * @param \PhpOffice\PhpPresentation\ComparableInterface $pSource Item to remove
- * @throws \Exception
+ * @param ComparableInterface $pSource Item to remove
*/
- public function remove(ComparableInterface $pSource)
+ public function remove(ComparableInterface $pSource): void
{
if (isset($this->items[$pSource->getHashCode()])) {
unset($this->items[$pSource->getHashCode()]);
@@ -124,44 +111,38 @@ class HashTable
}
/**
- * Clear HashTable
- *
+ * Clear HashTable.
*/
- public function clear()
+ public function clear(): void
{
- $this->items = array();
- $this->keyMap = array();
+ $this->items = [];
+ $this->keyMap = [];
}
/**
- * Count
- *
- * @return int
+ * Count.
*/
- public function count()
+ public function count(): int
{
return count($this->items);
}
/**
- * Get index for hash code
+ * Get index for hash code.
*
- * @param string $pHashCode
- * @return int Index
+ * @return int Index (-1 if not found)
*/
- public function getIndexForHashCode($pHashCode = '')
+ public function getIndexForHashCode(string $pHashCode = ''): int
{
- return array_search($pHashCode, $this->keyMap);
+ $index = array_search($pHashCode, $this->keyMap);
+
+ return false === $index ? -1 : $index;
}
/**
- * Get by index
- *
- * @param int $pIndex
- * @return \PhpOffice\PhpPresentation\ComparableInterface
- *
+ * Get by index.
*/
- public function getByIndex($pIndex = 0)
+ public function getByIndex(int $pIndex = 0): ?ComparableInterface
{
if (isset($this->keyMap[$pIndex])) {
return $this->getByHashCode($this->keyMap[$pIndex]);
@@ -171,13 +152,9 @@ class HashTable
}
/**
- * Get by hashcode
- *
- * @param string $pHashCode
- * @return \PhpOffice\PhpPresentation\ComparableInterface
- *
+ * Get by hashcode.
*/
- public function getByHashCode($pHashCode = '')
+ public function getByHashCode(string $pHashCode = ''): ?ComparableInterface
{
if (isset($this->items[$pHashCode])) {
return $this->items[$pHashCode];
@@ -187,11 +164,11 @@ class HashTable
}
/**
- * HashTable to array
+ * HashTable to array.
*
- * @return \PhpOffice\PhpPresentation\ComparableInterface[]
+ * @return array
*/
- public function toArray()
+ public function toArray(): array
{
return $this->items;
}
diff --git a/PhpOffice/PhpPresentation/IOFactory.php b/PhpOffice/PhpPresentation/IOFactory.php
old mode 100755
new mode 100644
index 84cb375..0d7f90b
--- a/PhpOffice/PhpPresentation/IOFactory.php
+++ b/PhpOffice/PhpPresentation/IOFactory.php
@@ -10,60 +10,61 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
+use PhpOffice\PhpPresentation\Exception\InvalidClassException;
+use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
+use PhpOffice\PhpPresentation\Reader\ReaderInterface;
+use PhpOffice\PhpPresentation\Writer\WriterInterface;
+use ReflectionClass;
+
/**
- * IOFactory
+ * IOFactory.
*/
class IOFactory
{
/**
- * Autoresolve classes
+ * Autoresolve classes.
*
- * @var array
+ * @var array
*/
- private static $autoResolveClasses = array('Serialized', 'ODPresentation', 'PowerPoint97', 'PowerPoint2007');
+ private static $autoResolveClasses = ['Serialized', 'ODPresentation', 'PowerPoint97', 'PowerPoint2007'];
/**
- * Create writer
+ * Create writer.
*
* @param PhpPresentation $phpPresentation
* @param string $name
- * @return \PhpOffice\PhpPresentation\Writer\WriterInterface
- * @throws \Exception
*/
- public static function createWriter(PhpPresentation $phpPresentation, $name = 'PowerPoint2007')
+ public static function createWriter(PhpPresentation $phpPresentation, string $name = 'PowerPoint2007'): WriterInterface
{
- $class = 'PhpOffice\\PhpPresentation\\Writer\\' . $name;
- return self::loadClass($class, $name, 'writer', $phpPresentation);
+ return self::loadClass('PhpOffice\\PhpPresentation\\Writer\\' . $name, 'Writer', $phpPresentation);
}
/**
- * Create reader
+ * Create reader.
*
- * @param string $name
- * @return \PhpOffice\PhpPresentation\Reader\ReaderInterface
- * @throws \Exception
+ * @param string $name
*/
- public static function createReader($name = '')
+ public static function createReader(string $name): ReaderInterface
{
- $class = 'PhpOffice\\PhpPresentation\\Reader\\' . $name;
- return self::loadClass($class, $name, 'reader');
+ return self::loadClass('PhpOffice\\PhpPresentation\\Reader\\' . $name, 'Reader');
}
/**
- * Loads PhpPresentation from file using automatic \PhpOffice\PhpPresentation\Reader\ReaderInterface resolution
+ * Loads PhpPresentation from file using automatic ReaderInterface resolution.
*
- * @param string $pFilename
- * @return PhpPresentation
- * @throws \Exception
+ * @throws InvalidFileFormatException
*/
- public static function load($pFilename)
+ public static function load(string $pFilename): PhpPresentation
{
// Try loading using self::$autoResolveClasses
foreach (self::$autoResolveClasses as $autoResolveClass) {
@@ -73,42 +74,45 @@ class IOFactory
}
}
- throw new \Exception("Could not automatically determine \PhpOffice\PhpPresentation\Reader\ReaderInterface for file.");
+ throw new InvalidFileFormatException(
+ $pFilename,
+ IOFactory::class,
+ 'Could not automatically determine the good ' . ReaderInterface::class
+ );
}
/**
* Load class
*
* @param string $class
- * @param string $name
* @param string $type
- * @param \PhpOffice\PhpPresentation\PhpPresentation $phpPresentation
- * @return mixed
- * @throws \ReflectionException
+ * @param PhpPresentation|null $phpPresentation
+ *
+ * @return object
+ *
+ * @throws InvalidClassException
*/
- private static function loadClass($class, $name, $type, PhpPresentation $phpPresentation = null)
+ private static function loadClass(string $class, string $type, PhpPresentation $phpPresentation = null)
{
- if (class_exists($class) && self::isConcreteClass($class)) {
- if (is_null($phpPresentation)) {
- return new $class();
- } else {
- return new $class($phpPresentation);
- }
- } else {
- throw new \Exception('"'.$name.'" is not a valid '.$type.'.');
+ if (!class_exists($class)) {
+ throw new InvalidClassException($class, $type . ': The class doesn\'t exist');
}
+ if (!self::isConcreteClass($class)) {
+ throw new InvalidClassException($class, $type . ': The class is an abstract class or an interface');
+ }
+ if (is_null($phpPresentation)) {
+ return new $class();
+ }
+
+ return new $class($phpPresentation);
}
/**
* Is it a concrete class?
- *
- * @param string $class
- * @return bool
- * @throws \ReflectionException
*/
- private static function isConcreteClass($class)
+ private static function isConcreteClass(string $class): bool
{
- $reflection = new \ReflectionClass($class);
+ $reflection = new ReflectionClass($class);
return !$reflection->isAbstract() && !$reflection->isInterface();
}
diff --git a/PhpOffice/PhpPresentation/LICENSE b/PhpOffice/PhpPresentation/LICENSE
deleted file mode 100755
index e67de5a..0000000
--- a/PhpOffice/PhpPresentation/LICENSE
+++ /dev/null
@@ -1,15 +0,0 @@
-PHPPresentation, a pure PHP library for writing presentations files.
-
-Copyright (c) 2010-2015 PHPPresentation.
-
-PHPPresentation is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License version 3 as published by
-the Free Software Foundation.
-
-PHPPresentation is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License version 3 for more details.
-
-You should have received a copy of the GNU Lesser General Public License version 3
-along with PHPPresentation. If not, see .
diff --git a/PhpOffice/PhpPresentation/PhpPresentation.php b/PhpOffice/PhpPresentation/PhpPresentation.php
old mode 100755
new mode 100644
index 172babe..e2db811
--- a/PhpOffice/PhpPresentation/PhpPresentation.php
+++ b/PhpOffice/PhpPresentation/PhpPresentation.php
@@ -10,65 +10,70 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
-use PhpOffice\PhpPresentation\Slide;
+use ArrayObject;
+use PhpOffice\PhpPresentation\Exception\OutOfBoundsException;
use PhpOffice\PhpPresentation\Slide\Iterator;
use PhpOffice\PhpPresentation\Slide\SlideMaster;
/**
- * PhpPresentation
+ * PhpPresentation.
*/
class PhpPresentation
{
/**
- * Document properties
+ * Document properties.
*
- * @var \PhpOffice\PhpPresentation\DocumentProperties
+ * @var DocumentProperties
*/
protected $documentProperties;
/**
- * Presentation properties
+ * Presentation properties.
*
- * @var \PhpOffice\PhpPresentation\PresentationProperties
+ * @var PresentationProperties
*/
protected $presentationProps;
/**
- * Document layout
+ * Document layout.
*
- * @var \PhpOffice\PhpPresentation\DocumentLayout
+ * @var DocumentLayout
*/
protected $layout;
/**
- * Collection of Slide objects
+ * Collection of Slide objects.
*
- * @var \PhpOffice\PhpPresentation\Slide[]
+ * @var array
*/
- protected $slideCollection = array();
+ protected $slideCollection = [];
/**
- * Active slide index
+ * Active slide index.
*
* @var int
*/
protected $activeSlideIndex = 0;
/**
- * Collection of Master Slides
- * @var \ArrayObject|\PhpOffice\PhpPresentation\Slide\SlideMaster[]
+ * Collection of Master Slides.
+ *
+ * @var array|ArrayObject
*/
protected $slideMasters;
/**
- * Create a new PhpPresentation with one Slide
+ * Create a new PhpPresentation with one Slide.
*/
public function __construct()
{
@@ -86,45 +91,17 @@ class PhpPresentation
}
/**
- * Get properties
- *
- * @return \PhpOffice\PhpPresentation\DocumentProperties
- * @deprecated for getDocumentProperties
+ * Get properties.
*/
- public function getProperties()
- {
- return $this->getDocumentProperties();
- }
-
- /**
- * Set properties
- *
- * @param \PhpOffice\PhpPresentation\DocumentProperties $value
- * @deprecated for setDocumentProperties
- * @return PhpPresentation
- */
- public function setProperties(DocumentProperties $value)
- {
- return $this->setDocumentProperties($value);
- }
-
- /**
- * Get properties
- *
- * @return \PhpOffice\PhpPresentation\DocumentProperties
- */
- public function getDocumentProperties()
+ public function getDocumentProperties(): DocumentProperties
{
return $this->documentProperties;
}
/**
- * Set properties
- *
- * @param \PhpOffice\PhpPresentation\DocumentProperties $value
- * @return PhpPresentation
+ * Set properties.
*/
- public function setDocumentProperties(DocumentProperties $value)
+ public function setDocumentProperties(DocumentProperties $value): self
{
$this->documentProperties = $value;
@@ -132,44 +109,37 @@ class PhpPresentation
}
/**
- * Get presentation properties
- *
- * @return \PhpOffice\PhpPresentation\PresentationProperties
+ * Get presentation properties.
*/
- public function getPresentationProperties()
+ public function getPresentationProperties(): PresentationProperties
{
return $this->presentationProps;
}
/**
- * Set presentation properties
+ * Set presentation properties.
*
- * @param \PhpOffice\PhpPresentation\PresentationProperties $value
* @return PhpPresentation
*/
- public function setPresentationProperties(PresentationProperties $value)
+ public function setPresentationProperties(PresentationProperties $value): self
{
$this->presentationProps = $value;
+
return $this;
}
/**
- * Get layout
- *
- * @return \PhpOffice\PhpPresentation\DocumentLayout
+ * Get layout.
*/
- public function getLayout()
+ public function getLayout(): DocumentLayout
{
return $this->layout;
}
/**
- * Set layout
- *
- * @param \PhpOffice\PhpPresentation\DocumentLayout $value
- * @return PhpPresentation
+ * Set layout.
*/
- public function setLayout(DocumentLayout $value)
+ public function setLayout(DocumentLayout $value): self
{
$this->layout = $value;
@@ -177,36 +147,28 @@ class PhpPresentation
}
/**
- * Get active slide
- *
- * @return \PhpOffice\PhpPresentation\Slide
+ * Get active slide.
*/
- public function getActiveSlide()
+ public function getActiveSlide(): Slide
{
return $this->slideCollection[$this->activeSlideIndex];
}
/**
- * Create slide and add it to this presentation
- *
- * @return \PhpOffice\PhpPresentation\Slide
- * @throws \Exception
+ * Create slide and add it to this presentation.
*/
- public function createSlide()
+ public function createSlide(): Slide
{
$newSlide = new Slide($this);
$this->addSlide($newSlide);
+
return $newSlide;
}
/**
- * Add slide
- *
- * @param \PhpOffice\PhpPresentation\Slide $slide
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Slide
+ * Add slide.
*/
- public function addSlide(Slide $slide = null)
+ public function addSlide(Slide $slide): Slide
{
$this->slideCollection[] = $slide;
@@ -214,16 +176,16 @@ class PhpPresentation
}
/**
- * Remove slide by index
+ * Remove slide by index.
*
- * @param int $index Slide index
- * @throws \Exception
- * @return PhpPresentation
+ * @param int $index Slide index
+ *
+ * @throws OutOfBoundsException
*/
- public function removeSlideByIndex($index = 0)
+ public function removeSlideByIndex(int $index = 0): self
{
if ($index > count($this->slideCollection) - 1) {
- throw new \Exception("Slide index is out of bounds.");
+ throw new OutOfBoundsException(0, count($this->slideCollection) - 1, $index);
}
array_splice($this->slideCollection, $index, 1);
@@ -231,80 +193,77 @@ class PhpPresentation
}
/**
- * Get slide by index
+ * Get slide by index.
*
- * @param int $index Slide index
- * @return \PhpOffice\PhpPresentation\Slide
- * @throws \Exception
+ * @param int $index Slide index
+ *
+ * @throws OutOfBoundsException
*/
- public function getSlide($index = 0)
+ public function getSlide(int $index = 0): Slide
{
if ($index > count($this->slideCollection) - 1) {
- throw new \Exception("Slide index is out of bounds.");
+ throw new OutOfBoundsException(0, count($this->slideCollection) - 1, $index);
}
+
return $this->slideCollection[$index];
}
/**
- * Get all slides
+ * Get all slides.
*
- * @return \PhpOffice\PhpPresentation\Slide[]
+ * @return array
*/
- public function getAllSlides()
+ public function getAllSlides(): array
{
return $this->slideCollection;
}
/**
- * Get index for slide
- *
- * @param \PhpOffice\PhpPresentation\Slide\AbstractSlide $slide
- * @return int
- * @throws \Exception
+ * Get index for slide.
*/
- public function getIndex(Slide\AbstractSlide $slide)
+ public function getIndex(Slide\AbstractSlide $slide): ?int
{
- $index = null;
+ if (empty($this->slideCollection)) {
+ return null;
+ }
foreach ($this->slideCollection as $key => $value) {
if ($value->getHashCode() == $slide->getHashCode()) {
- $index = $key;
- break;
+ return $key;
}
}
- return $index;
+
+ return null;
}
/**
- * Get slide count
- *
- * @return int
+ * Get slide count.
*/
- public function getSlideCount()
+ public function getSlideCount(): int
{
return count($this->slideCollection);
}
/**
- * Get active slide index
+ * Get active slide index.
*
* @return int Active slide index
*/
- public function getActiveSlideIndex()
+ public function getActiveSlideIndex(): int
{
return $this->activeSlideIndex;
}
/**
- * Set active slide index
+ * Set active slide index.
*
- * @param int $index Active slide index
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Slide
+ * @param int $index Active slide index
+ *
+ * @throws OutOfBoundsException
*/
- public function setActiveSlideIndex($index = 0)
+ public function setActiveSlideIndex(int $index = 0): Slide
{
if ($index > count($this->slideCollection) - 1) {
- throw new \Exception("Active slide index is out of bounds.");
+ throw new OutOfBoundsException(0, count($this->slideCollection) - 1, $index);
}
$this->activeSlideIndex = $index;
@@ -312,13 +271,11 @@ class PhpPresentation
}
/**
- * Add external slide
+ * Add external slide.
*
- * @param \PhpOffice\PhpPresentation\Slide $slide External slide to add
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Slide
+ * @param Slide $slide External slide to add
*/
- public function addExternalSlide(Slide $slide)
+ public function addExternalSlide(Slide $slide): Slide
{
$slide->rebindParent($this);
@@ -328,36 +285,28 @@ class PhpPresentation
}
/**
- * Get slide iterator
- *
- * @return \PhpOffice\PhpPresentation\Slide\Iterator
+ * Get slide iterator.
*/
- public function getSlideIterator()
+ public function getSlideIterator(): Iterator
{
return new Iterator($this);
}
/**
- * Create a masterslide and add it to this presentation
- *
- * @return \PhpOffice\PhpPresentation\Slide\SlideMaster
- * @throws \Exception
+ * Create a masterslide and add it to this presentation.
*/
- public function createMasterSlide()
+ public function createMasterSlide(): SlideMaster
{
$newMasterSlide = new SlideMaster($this);
$this->addMasterSlide($newMasterSlide);
+
return $newMasterSlide;
}
/**
- * Add masterslide
- *
- * @param \PhpOffice\PhpPresentation\Slide\SlideMaster $slide
- * @return \PhpOffice\PhpPresentation\Slide\SlideMaster
- * @throws \Exception
+ * Add masterslide.
*/
- public function addMasterSlide(SlideMaster $slide = null)
+ public function addMasterSlide(SlideMaster $slide): SlideMaster
{
$this->slideMasters[] = $slide;
@@ -365,12 +314,9 @@ class PhpPresentation
}
/**
- * Copy presentation (!= clone!)
- *
- * @return PhpPresentation
- * @throws \Exception
+ * Copy presentation (!= clone!).
*/
- public function copy()
+ public function copy(): PhpPresentation
{
$copied = clone $this;
@@ -384,49 +330,7 @@ class PhpPresentation
}
/**
- * Mark a document as final
- * @param bool $state
- * @return PresentationProperties
- * @deprecated for getPresentationProperties()->markAsFinal()
- */
- public function markAsFinal($state = true)
- {
- return $this->getPresentationProperties()->markAsFinal($state);
- }
-
- /**
- * Return if this document is marked as final
- * @return bool
- * @deprecated for getPresentationProperties()->isMarkedAsFinal()
- */
- public function isMarkedAsFinal()
- {
- return $this->getPresentationProperties()->isMarkedAsFinal();
- }
-
- /**
- * Set the zoom of the document (in percentage)
- * @param float $zoom
- * @return PresentationProperties
- * @deprecated for getPresentationProperties()->setZoom()
- */
- public function setZoom($zoom = 1.0)
- {
- return $this->getPresentationProperties()->setZoom($zoom);
- }
-
- /**
- * Return the zoom (in percentage)
- * @return float
- * @deprecated for getPresentationProperties()->getZoom()
- */
- public function getZoom()
- {
- return $this->getPresentationProperties()->getZoom();
- }
-
- /**
- * @return \ArrayObject|Slide\SlideMaster[]
+ * @return array|ArrayObject
*/
public function getAllMasterSlides()
{
@@ -434,14 +338,14 @@ class PhpPresentation
}
/**
- * @param \ArrayObject|Slide\SlideMaster[] $slideMasters
- * @return $this
+ * @param array|ArrayObject $slideMasters
*/
- public function setAllMasterSlides($slideMasters = array())
+ public function setAllMasterSlides($slideMasters = []): self
{
- if ($slideMasters instanceof \ArrayObject || is_array($slideMasters)) {
+ if ($slideMasters instanceof ArrayObject || is_array($slideMasters)) {
$this->slideMasters = $slideMasters;
}
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/PresentationProperties.php b/PhpOffice/PhpPresentation/PresentationProperties.php
old mode 100755
new mode 100644
index 05b6dbf..a8ee8ef
--- a/PhpOffice/PhpPresentation/PresentationProperties.php
+++ b/PhpOffice/PhpPresentation/PresentationProperties.php
@@ -10,27 +10,31 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
-/**
- * \PhpOffice\PhpPresentation\PresentationProperties
- */
class PresentationProperties
{
- const VIEW_HANDOUT = 'handoutView';
- const VIEW_NOTES = 'notesView';
- const VIEW_NOTES_MASTER = 'notesMasterView';
- const VIEW_OUTLINE = 'outlineView';
- const VIEW_SLIDE = 'sldView';
- const VIEW_SLIDE_MASTER = 'sldMasterView';
- const VIEW_SLIDE_SORTER = 'sldSorterView';
- const VIEW_SLIDE_THUMBNAIL = 'sldThumbnailView';
+ public const VIEW_HANDOUT = 'handoutView';
+ public const VIEW_NOTES = 'notesView';
+ public const VIEW_NOTES_MASTER = 'notesMasterView';
+ public const VIEW_OUTLINE = 'outlineView';
+ public const VIEW_SLIDE = 'sldView';
+ public const VIEW_SLIDE_MASTER = 'sldMasterView';
+ public const VIEW_SLIDE_SORTER = 'sldSorterView';
+ public const VIEW_SLIDE_THUMBNAIL = 'sldThumbnailView';
- protected $arrayView = array(
+ /**
+ * @var array
+ */
+ protected $arrayView = [
self::VIEW_HANDOUT,
self::VIEW_NOTES,
self::VIEW_NOTES_MASTER,
@@ -39,163 +43,189 @@ class PresentationProperties
self::VIEW_SLIDE_MASTER,
self::VIEW_SLIDE_SORTER,
self::VIEW_SLIDE_THUMBNAIL,
- );
+ ];
- /*
- * @var boolean
+ public const SLIDESHOW_TYPE_PRESENT = 'present';
+ public const SLIDESHOW_TYPE_BROWSE = 'browse';
+ public const SLIDESHOW_TYPE_KIOSK = 'kiosk';
+
+ /**
+ * @var array
+ */
+ protected $arraySlideshowTypes = [
+ self::SLIDESHOW_TYPE_PRESENT,
+ self::SLIDESHOW_TYPE_BROWSE,
+ self::SLIDESHOW_TYPE_KIOSK,
+ ];
+
+ /**
+ * @var bool
*/
protected $isLoopUntilEsc = false;
/**
- * Mark as final
+ * Mark as final.
+ *
* @var bool
*/
protected $markAsFinal = false;
- /*
- * @var string
+ /**
+ * @var string|null
*/
protected $thumbnail;
/**
- * Zoom
+ * Zoom.
+ *
* @var float
*/
- protected $zoom = 1;
+ protected $zoom = 1.0;
- /*
+ /**
* @var string
*/
protected $lastView = self::VIEW_SLIDE;
- /*
- * @var boolean
+ /**
+ * @var string
+ */
+ protected $slideshowType = self::SLIDESHOW_TYPE_PRESENT;
+
+ /**
+ * @var bool
*/
protected $isCommentVisible = false;
-
- /**
- * @return bool
- */
- public function isLoopContinuouslyUntilEsc()
+
+ public function isLoopContinuouslyUntilEsc(): bool
{
return $this->isLoopUntilEsc;
}
-
- /**
- * @param bool $value
- * @return \PhpOffice\PhpPresentation\PresentationProperties
- */
- public function setLoopContinuouslyUntilEsc($value = false)
+
+ public function setLoopContinuouslyUntilEsc(bool $value = false): self
{
- if (is_bool($value)) {
- $this->isLoopUntilEsc = $value;
- }
+ $this->isLoopUntilEsc = $value;
+
return $this;
}
-
+
/**
- * Return the thumbnail file path
- * @return string
+ * Return the thumbnail file path.
+ *
+ * @return string|null
*/
- public function getThumbnailPath()
+ public function getThumbnailPath(): ?string
{
return $this->thumbnail;
}
-
+
/**
- * Define the path for the thumbnail file / preview picture
+ * Define the path for the thumbnail file / preview picture.
+ *
* @param string $path
- * @return \PhpOffice\PhpPresentation\PresentationProperties
+ *
+ * @return self
*/
- public function setThumbnailPath($path = '')
+ public function setThumbnailPath(string $path = ''): self
{
if (file_exists($path)) {
$this->thumbnail = $path;
}
+
return $this;
}
/**
- * Mark a document as final
- * @param bool $state
- * @return PresentationProperties
+ * Mark a document as final.
*/
- public function markAsFinal($state = true)
+ public function markAsFinal(bool $state = true): self
{
- if (is_bool($state)) {
- $this->markAsFinal = $state;
- }
+ $this->markAsFinal = $state;
+
return $this;
}
/**
- * Return if this document is marked as final
+ * Return if this document is marked as final.
+ *
* @return bool
*/
- public function isMarkedAsFinal()
+ public function isMarkedAsFinal(): bool
{
return $this->markAsFinal;
}
/**
- * Set the zoom of the document (in percentage)
- * @param float $zoom
- * @return PresentationProperties
+ * Set the zoom of the document (in percentage).
*/
- public function setZoom($zoom = 1.0)
+ public function setZoom(float $zoom = 1.0): self
{
- if (is_numeric($zoom)) {
- $this->zoom = (float)$zoom;
- }
+ $this->zoom = $zoom;
+
return $this;
}
/**
- * Return the zoom (in percentage)
- * @return float
+ * Return the zoom (in percentage).
*/
- public function getZoom()
+ public function getZoom(): float
{
return $this->zoom;
}
/**
* @param string $value
- * @return $this
+ *
+ * @return self
*/
- public function setLastView($value = self::VIEW_SLIDE)
+ public function setLastView(string $value = self::VIEW_SLIDE): self
{
if (in_array($value, $this->arrayView)) {
$this->lastView = $value;
}
+
return $this;
}
/**
* @return string
*/
- public function getLastView()
+ public function getLastView(): string
{
return $this->lastView;
}
- /**
- * @param bool $value
- * @return $this
- */
- public function setCommentVisible($value = false)
+ public function setCommentVisible(bool $value = false): self
{
- if (is_bool($value)) {
- $this->isCommentVisible = $value;
- }
+ $this->isCommentVisible = $value;
+
return $this;
}
+ public function isCommentVisible(): bool
+ {
+ return $this->isCommentVisible;
+ }
+
/**
* @return string
*/
- public function isCommentVisible()
+ public function getSlideshowType(): string
{
- return $this->isCommentVisible;
+ return $this->slideshowType;
+ }
+
+ /**
+ * @param string $value
+ *
+ * @return self
+ */
+ public function setSlideshowType(string $value = self::SLIDESHOW_TYPE_PRESENT): self
+ {
+ if (in_array($value, $this->arraySlideshowTypes)) {
+ $this->slideshowType = $value;
+ }
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Reader/ODPresentation.php b/PhpOffice/PhpPresentation/Reader/ODPresentation.php
old mode 100755
new mode 100644
index d658322..d4fd67c
--- a/PhpOffice/PhpPresentation/Reader/ODPresentation.php
+++ b/PhpOffice/PhpPresentation/Reader/ODPresentation.php
@@ -10,64 +10,76 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Reader;
-use ZipArchive;
-use PhpOffice\Common\XMLReader;
+use DateTime;
+use DOMElement;
use PhpOffice\Common\Drawing as CommonDrawing;
+use PhpOffice\Common\XMLReader;
+use PhpOffice\PhpPresentation\DocumentProperties;
+use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
+use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
use PhpOffice\PhpPresentation\PhpPresentation;
+use PhpOffice\PhpPresentation\PresentationProperties;
+use PhpOffice\PhpPresentation\Shape\Drawing\Base64;
use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
use PhpOffice\PhpPresentation\Slide\Background\Image;
+use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Style\Bullet;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Fill;
use PhpOffice\PhpPresentation\Style\Font;
use PhpOffice\PhpPresentation\Style\Shadow;
-use PhpOffice\PhpPresentation\Style\Alignment;
+use ZipArchive;
/**
- * Serialized format reader
+ * Serialized format reader.
*/
class ODPresentation implements ReaderInterface
{
/**
- * Output Object
+ * Output Object.
+ *
* @var PhpPresentation
*/
protected $oPhpPresentation;
/**
- * Output Object
+ * Output Object.
+ *
* @var \ZipArchive
*/
protected $oZip;
/**
- * @var array[]
+ * @var array
*/
- protected $arrayStyles = array();
+ protected $arrayStyles = [];
/**
- * @var array[]
+ * @var array>
*/
- protected $arrayCommonStyles = array();
+ protected $arrayCommonStyles = [];
/**
* @var \PhpOffice\Common\XMLReader
*/
protected $oXMLReader;
+ /**
+ * @var int
+ */
+ protected $levelParagraph = 0;
/**
* Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
- *
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
*/
- public function canRead($pFilename)
+ public function canRead(string $pFilename): bool
{
return $this->fileSupportsUnserializePhpPresentation($pFilename);
}
@@ -75,83 +87,81 @@ class ODPresentation implements ReaderInterface
/**
* Does a file support UnserializePhpPresentation ?
*
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
+ * @throws FileNotFoundException
*/
- public function fileSupportsUnserializePhpPresentation($pFilename = '')
+ public function fileSupportsUnserializePhpPresentation(string $pFilename = ''): bool
{
// Check if file exists
if (!file_exists($pFilename)) {
- throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ throw new FileNotFoundException($pFilename);
}
-
+
$oZip = new ZipArchive();
// Is it a zip ?
- if ($oZip->open($pFilename) === true) {
+ if (true === $oZip->open($pFilename)) {
// Is it an OpenXML Document ?
// Is it a Presentation ?
- if (is_array($oZip->statName('META-INF/manifest.xml')) && is_array($oZip->statName('mimetype')) && $oZip->getFromName('mimetype') == 'application/vnd.oasis.opendocument.presentation') {
+ if (is_array($oZip->statName('META-INF/manifest.xml')) && is_array($oZip->statName('mimetype')) && 'application/vnd.oasis.opendocument.presentation' == $oZip->getFromName('mimetype')) {
return true;
}
}
+
return false;
}
/**
- * Loads PhpPresentation Serialized file
+ * Loads PhpPresentation Serialized file.
*
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * @throws InvalidFileFormatException
*/
- public function load($pFilename)
+ public function load(string $pFilename): PhpPresentation
{
// Unserialize... First make sure the file supports it!
if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
- throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\ODPresentation: " . $pFilename . ".");
+ throw new InvalidFileFormatException($pFilename, ODPresentation::class);
}
return $this->loadFile($pFilename);
}
/**
- * Load PhpPresentation Serialized file
+ * Load PhpPresentation Serialized file.
*
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * @param string $pFilename
+ *
+ * @return PhpPresentation
*/
protected function loadFile($pFilename)
{
$this->oPhpPresentation = new PhpPresentation();
$this->oPhpPresentation->removeSlideByIndex();
-
+
$this->oZip = new ZipArchive();
$this->oZip->open($pFilename);
-
+
$this->oXMLReader = new XMLReader();
- if ($this->oXMLReader->getDomFromZip($pFilename, 'meta.xml') !== false) {
+ if (false !== $this->oXMLReader->getDomFromZip($pFilename, 'meta.xml')) {
$this->loadDocumentProperties();
}
$this->oXMLReader = new XMLReader();
- if ($this->oXMLReader->getDomFromZip($pFilename, 'styles.xml') !== false) {
+ if (false !== $this->oXMLReader->getDomFromZip($pFilename, 'styles.xml')) {
$this->loadStylesFile();
}
$this->oXMLReader = new XMLReader();
- if ($this->oXMLReader->getDomFromZip($pFilename, 'content.xml') !== false) {
+ if (false !== $this->oXMLReader->getDomFromZip($pFilename, 'content.xml')) {
$this->loadSlides();
+ $this->loadPresentationProperties();
}
return $this->oPhpPresentation;
}
-
+
/**
* Read Document Properties
*/
- protected function loadDocumentProperties()
+ protected function loadDocumentProperties(): void
{
- $arrayProperties = array(
+ $arrayProperties = [
'/office:document-meta/office:meta/meta:initial-creator' => 'setCreator',
'/office:document-meta/office:meta/dc:creator' => 'setLastModifiedBy',
'/office:document-meta/office:meta/dc:title' => 'setTitle',
@@ -160,62 +170,99 @@ class ODPresentation implements ReaderInterface
'/office:document-meta/office:meta/meta:keyword' => 'setKeywords',
'/office:document-meta/office:meta/meta:creation-date' => 'setCreated',
'/office:document-meta/office:meta/dc:date' => 'setModified',
- );
- $oProperties = $this->oPhpPresentation->getDocumentProperties();
+ ];
+ $properties = $this->oPhpPresentation->getDocumentProperties();
foreach ($arrayProperties as $path => $property) {
$oElement = $this->oXMLReader->getElement($path);
- if ($oElement instanceof \DOMElement) {
- if (in_array($property, array('setCreated', 'setModified'))) {
- $oDateTime = new \DateTime();
- $oDateTime->createFromFormat(\DateTime::W3C, $oElement->nodeValue);
- $oProperties->{$property}($oDateTime->getTimestamp());
- } else {
- $oProperties->{$property}($oElement->nodeValue);
+ if ($oElement instanceof DOMElement) {
+ $value = $oElement->nodeValue;
+ if (in_array($property, ['setCreated', 'setModified'])) {
+ $dateTime = DateTime::createFromFormat(DateTime::W3C, $value);
+ if (!$dateTime) {
+ $dateTime = new DateTime();
+ }
+ $value = $dateTime->getTimestamp();
}
+ $properties->{$property}($value);
}
}
+
+ foreach ($this->oXMLReader->getElements('/office:document-meta/office:meta/meta:user-defined') as $element) {
+ if (!($element instanceof DOMElement)
+ || !$element->hasAttribute('meta:name')) {
+ continue;
+ }
+ $propertyName = $element->getAttribute('meta:name');
+ $propertyValue = (string) $element->nodeValue;
+ $propertyType = $element->getAttribute('meta:value-type');
+ switch ($propertyType) {
+ case 'boolean':
+ $propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
+ break;
+ case 'float':
+ $propertyType = filter_var($propertyValue, FILTER_VALIDATE_INT) === false
+ ? DocumentProperties::PROPERTY_TYPE_FLOAT
+ : DocumentProperties::PROPERTY_TYPE_INTEGER;
+ break;
+ case 'date':
+ $propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
+ break;
+ case 'string':
+ default:
+ $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
+ break;
+ }
+ $properties->setCustomProperty($propertyName, $propertyValue, $propertyType);
+ }
}
-
+
/**
* Extract all slides
*/
- protected function loadSlides()
+ protected function loadSlides(): void
{
foreach ($this->oXMLReader->getElements('/office:document-content/office:automatic-styles/*') as $oElement) {
- if ($oElement->hasAttribute('style:name')) {
+ if ($oElement instanceof DOMElement && $oElement->hasAttribute('style:name')) {
$this->loadStyle($oElement);
}
}
foreach ($this->oXMLReader->getElements('/office:document-content/office:body/office:presentation/draw:page') as $oElement) {
- if ($oElement->nodeName == 'draw:page') {
+ if ($oElement instanceof DOMElement && 'draw:page' == $oElement->nodeName) {
$this->loadSlide($oElement);
}
}
}
+ protected function loadPresentationProperties(): void
+ {
+ $element = $this->oXMLReader->getElement('/office:document-content/office:body/office:presentation/presentation:settings');
+ if ($element instanceof DOMElement) {
+ if ($element->getAttribute('presentation:full-screen') === 'false') {
+ $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(PresentationProperties::SLIDESHOW_TYPE_BROWSE);
+ }
+ }
+ }
+
/**
* Extract style
- * @param \DOMElement $nodeStyle
- * @return bool
- * @throws \Exception
*/
- protected function loadStyle(\DOMElement $nodeStyle)
+ protected function loadStyle(DOMElement $nodeStyle): bool
{
$keyStyle = $nodeStyle->getAttribute('style:name');
$nodeDrawingPageProps = $this->oXMLReader->getElement('style:drawing-page-properties', $nodeStyle);
- if ($nodeDrawingPageProps instanceof \DOMElement) {
+ if ($nodeDrawingPageProps instanceof DOMElement) {
// Read Background Color
- if ($nodeDrawingPageProps->hasAttribute('draw:fill-color') && $nodeDrawingPageProps->getAttribute('draw:fill') == 'solid') {
+ if ($nodeDrawingPageProps->hasAttribute('draw:fill-color') && 'solid' == $nodeDrawingPageProps->getAttribute('draw:fill')) {
$oBackground = new \PhpOffice\PhpPresentation\Slide\Background\Color();
$oColor = new Color();
$oColor->setRGB(substr($nodeDrawingPageProps->getAttribute('draw:fill-color'), -6));
$oBackground->setColor($oColor);
}
// Read Background Image
- if ($nodeDrawingPageProps->getAttribute('draw:fill') == 'bitmap' && $nodeDrawingPageProps->hasAttribute('draw:fill-image-name')) {
+ if ('bitmap' == $nodeDrawingPageProps->getAttribute('draw:fill') && $nodeDrawingPageProps->hasAttribute('draw:fill-image-name')) {
$nameStyle = $nodeDrawingPageProps->getAttribute('draw:fill-image-name');
- if (!empty($this->arrayCommonStyles[$nameStyle]) && $this->arrayCommonStyles[$nameStyle]['type'] == 'image' && !empty($this->arrayCommonStyles[$nameStyle]['path'])) {
+ if (!empty($this->arrayCommonStyles[$nameStyle]) && 'image' == $this->arrayCommonStyles[$nameStyle]['type'] && !empty($this->arrayCommonStyles[$nameStyle]['path'])) {
$tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderODPBkg');
$contentImg = $this->oZip->getFromName($this->arrayCommonStyles[$nameStyle]['path']);
file_put_contents($tmpBkgImg, $contentImg);
@@ -227,27 +274,27 @@ class ODPresentation implements ReaderInterface
}
$nodeGraphicProps = $this->oXMLReader->getElement('style:graphic-properties', $nodeStyle);
- if ($nodeGraphicProps instanceof \DOMElement) {
+ if ($nodeGraphicProps instanceof DOMElement) {
// Read Shadow
- if ($nodeGraphicProps->hasAttribute('draw:shadow') && $nodeGraphicProps->getAttribute('draw:shadow') == 'visible') {
+ if ($nodeGraphicProps->hasAttribute('draw:shadow') && 'visible' == $nodeGraphicProps->getAttribute('draw:shadow')) {
$oShadow = new Shadow();
$oShadow->setVisible(true);
if ($nodeGraphicProps->hasAttribute('draw:shadow-color')) {
$oShadow->getColor()->setRGB(substr($nodeGraphicProps->getAttribute('draw:shadow-color'), -6));
}
if ($nodeGraphicProps->hasAttribute('draw:shadow-opacity')) {
- $oShadow->setAlpha(100 - (int)substr($nodeGraphicProps->getAttribute('draw:shadow-opacity'), 0, -1));
+ $oShadow->setAlpha(100 - (int) substr($nodeGraphicProps->getAttribute('draw:shadow-opacity'), 0, -1));
}
if ($nodeGraphicProps->hasAttribute('draw:shadow-offset-x') && $nodeGraphicProps->hasAttribute('draw:shadow-offset-y')) {
- $offsetX = substr($nodeGraphicProps->getAttribute('draw:shadow-offset-x'), 0, -2);
- $offsetY = substr($nodeGraphicProps->getAttribute('draw:shadow-offset-y'), 0, -2);
+ $offsetX = (float) substr($nodeGraphicProps->getAttribute('draw:shadow-offset-x'), 0, -2);
+ $offsetY = (float) substr($nodeGraphicProps->getAttribute('draw:shadow-offset-y'), 0, -2);
$distance = 0;
- if ($offsetX != 0) {
+ if (0 != $offsetX) {
$distance = ($offsetX < 0 ? $offsetX * -1 : $offsetX);
- } elseif ($offsetY != 0) {
+ } elseif (0 != $offsetY) {
$distance = ($offsetY < 0 ? $offsetY * -1 : $offsetY);
}
- $oShadow->setDirection(rad2deg(atan2($offsetY, $offsetX)));
+ $oShadow->setDirection((int) rad2deg(atan2($offsetY, $offsetX)));
$oShadow->setDistance(CommonDrawing::centimetersToPixels($distance));
}
}
@@ -272,91 +319,177 @@ class ODPresentation implements ReaderInterface
}
}
}
-
+
$nodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $nodeStyle);
- if ($nodeTextProperties instanceof \DOMElement) {
+ if ($nodeTextProperties instanceof DOMElement) {
$oFont = new Font();
if ($nodeTextProperties->hasAttribute('fo:color')) {
$oFont->getColor()->setRGB(substr($nodeTextProperties->getAttribute('fo:color'), -6));
}
+ // Font Latin
if ($nodeTextProperties->hasAttribute('fo:font-family')) {
- $oFont->setName($nodeTextProperties->getAttribute('fo:font-family'));
+ $oFont
+ ->setName($nodeTextProperties->getAttribute('fo:font-family'))
+ ->setFormat(Font::FORMAT_LATIN);
}
- if ($nodeTextProperties->hasAttribute('fo:font-weight') && $nodeTextProperties->getAttribute('fo:font-weight') == 'bold') {
- $oFont->setBold(true);
+ if ($nodeTextProperties->hasAttribute('fo:font-weight') && 'bold' == $nodeTextProperties->getAttribute('fo:font-weight')) {
+ $oFont
+ ->setBold(true)
+ ->setFormat(Font::FORMAT_LATIN);
}
if ($nodeTextProperties->hasAttribute('fo:font-size')) {
- $oFont->setSize(substr($nodeTextProperties->getAttribute('fo:font-size'), 0, -2));
+ $oFont
+ ->setSize((int) substr($nodeTextProperties->getAttribute('fo:font-size'), 0, -2))
+ ->setFormat(Font::FORMAT_LATIN);
+ }
+ // Font East Asian
+ if ($nodeTextProperties->hasAttribute('style:font-family-asian')) {
+ $oFont
+ ->setName($nodeTextProperties->getAttribute('style:font-family-asian'))
+ ->setFormat(Font::FORMAT_EAST_ASIAN);
+ }
+ if ($nodeTextProperties->hasAttribute('style:font-weight-asian') && 'bold' == $nodeTextProperties->getAttribute('style:font-weight-asian')) {
+ $oFont
+ ->setBold(true)
+ ->setFormat(Font::FORMAT_EAST_ASIAN);
+ }
+ if ($nodeTextProperties->hasAttribute('style:font-size-asian')) {
+ $oFont
+ ->setSize((int) substr($nodeTextProperties->getAttribute('style:font-size-asian'), 0, -2))
+ ->setFormat(Font::FORMAT_EAST_ASIAN);
+ }
+ // Font Complex Script
+ if ($nodeTextProperties->hasAttribute('style:font-family-complex')) {
+ $oFont
+ ->setName($nodeTextProperties->getAttribute('style:font-family-complex'))
+ ->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
+ }
+ if ($nodeTextProperties->hasAttribute('style:font-weight-complex') && 'bold' == $nodeTextProperties->getAttribute('style:font-weight-complex')) {
+ $oFont
+ ->setBold(true)
+ ->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
+ }
+ if ($nodeTextProperties->hasAttribute('style:font-size-complex')) {
+ $oFont
+ ->setSize((int) substr($nodeTextProperties->getAttribute('style:font-size-complex'), 0, -2))
+ ->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
+ }
+ if ($nodeTextProperties->hasAttribute('style:script-type')) {
+ switch ($nodeTextProperties->getAttribute('style:script-type')) {
+ case 'latin':
+ $oFont->setFormat(Font::FORMAT_LATIN);
+ break;
+ case 'asian':
+ $oFont->setFormat(Font::FORMAT_EAST_ASIAN);
+ break;
+ case 'complex':
+ $oFont->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
+ break;
+ }
}
}
$nodeParagraphProps = $this->oXMLReader->getElement('style:paragraph-properties', $nodeStyle);
- if ($nodeParagraphProps instanceof \DOMElement) {
+ if ($nodeParagraphProps instanceof DOMElement) {
+ if ($nodeParagraphProps->hasAttribute('fo:line-height')) {
+ $lineHeightUnit = $this->getExpressionUnit($nodeParagraphProps->getAttribute('fo:margin-bottom'));
+ $lineSpacingMode = $lineHeightUnit == '%' ? Paragraph::LINE_SPACING_MODE_PERCENT : Paragraph::LINE_SPACING_MODE_POINT;
+ $lineSpacing = $this->getExpressionValue($nodeParagraphProps->getAttribute('fo:margin-bottom'));
+ }
+ if ($nodeParagraphProps->hasAttribute('fo:margin-bottom')) {
+ $spacingAfter = (float) substr($nodeParagraphProps->getAttribute('fo:margin-bottom'), 0, -2);
+ $spacingAfter = CommonDrawing::centimetersToPoints($spacingAfter);
+ }
+ if ($nodeParagraphProps->hasAttribute('fo:margin-top')) {
+ $spacingBefore = (float) substr($nodeParagraphProps->getAttribute('fo:margin-top'), 0, -2);
+ $spacingBefore = CommonDrawing::centimetersToPoints($spacingBefore);
+ }
$oAlignment = new Alignment();
if ($nodeParagraphProps->hasAttribute('fo:text-align')) {
$oAlignment->setHorizontal($nodeParagraphProps->getAttribute('fo:text-align'));
}
+ if ($nodeParagraphProps->hasAttribute('style:writing-mode')) {
+ switch ($nodeParagraphProps->getAttribute('style:writing-mode')) {
+ case 'lr-tb':
+ case 'tb-lr':
+ case 'lr':
+ $oAlignment->setIsRTL(false);
+ break;
+ case 'rl-tb':
+ case 'tb-rl':
+ case 'rl':
+ $oAlignment->setIsRTL(false);
+ break;
+ case 'tb':
+ case 'page':
+ default:
+ break;
+ }
+ }
}
-
- if ($nodeStyle->nodeName == 'text:list-style') {
- $arrayListStyle = array();
+
+ if ('text:list-style' == $nodeStyle->nodeName) {
+ $arrayListStyle = [];
foreach ($this->oXMLReader->getElements('text:list-level-style-bullet', $nodeStyle) as $oNodeListLevel) {
$oAlignment = new Alignment();
$oBullet = new Bullet();
$oBullet->setBulletType(Bullet::TYPE_NONE);
- if ($oNodeListLevel->hasAttribute('text:level')) {
- $oAlignment->setLevel((int) $oNodeListLevel->getAttribute('text:level') - 1);
- }
- if ($oNodeListLevel->hasAttribute('text:bullet-char')) {
- $oBullet->setBulletChar($oNodeListLevel->getAttribute('text:bullet-char'));
- $oBullet->setBulletType(Bullet::TYPE_BULLET);
- }
-
- $oNodeListProperties = $this->oXMLReader->getElement('style:list-level-properties', $oNodeListLevel);
- if ($oNodeListProperties instanceof \DOMElement) {
- if ($oNodeListProperties->hasAttribute('text:min-label-width')) {
- $oAlignment->setIndent((int)round(CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:min-label-width'), 0, -2))));
+ if ($oNodeListLevel instanceof DOMElement) {
+ if ($oNodeListLevel->hasAttribute('text:level')) {
+ $oAlignment->setLevel((int) $oNodeListLevel->getAttribute('text:level') - 1);
}
- if ($oNodeListProperties->hasAttribute('text:space-before')) {
- $iSpaceBefore = CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:space-before'), 0, -2));
- $iMarginLeft = $iSpaceBefore + $oAlignment->getIndent();
- $oAlignment->setMarginLeft($iMarginLeft);
+ if ($oNodeListLevel->hasAttribute('text:bullet-char')) {
+ $oBullet->setBulletChar($oNodeListLevel->getAttribute('text:bullet-char'));
+ $oBullet->setBulletType(Bullet::TYPE_BULLET);
+ }
+
+ $oNodeListProperties = $this->oXMLReader->getElement('style:list-level-properties', $oNodeListLevel);
+ if ($oNodeListProperties instanceof DOMElement) {
+ if ($oNodeListProperties->hasAttribute('text:min-label-width')) {
+ $oAlignment->setIndent(CommonDrawing::centimetersToPixels((float) substr($oNodeListProperties->getAttribute('text:min-label-width'), 0, -2)));
+ }
+ if ($oNodeListProperties->hasAttribute('text:space-before')) {
+ $iSpaceBefore = CommonDrawing::centimetersToPixels((float) substr($oNodeListProperties->getAttribute('text:space-before'), 0, -2));
+ $iMarginLeft = $iSpaceBefore + $oAlignment->getIndent();
+ $oAlignment->setMarginLeft($iMarginLeft);
+ }
+ }
+
+ $oNodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $oNodeListLevel);
+ if ($oNodeTextProperties instanceof DOMElement) {
+ if ($oNodeTextProperties->hasAttribute('fo:font-family')) {
+ $oBullet->setBulletFont($oNodeTextProperties->getAttribute('fo:font-family'));
+ }
}
}
- $oNodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $oNodeListLevel);
- if ($oNodeTextProperties instanceof \DOMElement) {
- if ($oNodeTextProperties->hasAttribute('fo:font-family')) {
- $oBullet->setBulletFont($oNodeTextProperties->getAttribute('fo:font-family'));
- }
- }
-
- $arrayListStyle[$oAlignment->getLevel()] = array(
+
+ $arrayListStyle[$oAlignment->getLevel()] = [
'alignment' => $oAlignment,
'bullet' => $oBullet,
- );
+ ];
}
}
-
- $this->arrayStyles[$keyStyle] = array(
- 'alignment' => isset($oAlignment) ? $oAlignment : null,
- 'background' => isset($oBackground) ? $oBackground : null,
- 'fill' => isset($oFill) ? $oFill : null,
- 'font' => isset($oFont) ? $oFont : null,
- 'shadow' => isset($oShadow) ? $oShadow : null,
- 'listStyle' => isset($arrayListStyle) ? $arrayListStyle : null,
- );
-
+
+ $this->arrayStyles[$keyStyle] = [
+ 'alignment' => $oAlignment ?? null,
+ 'background' => $oBackground ?? null,
+ 'fill' => $oFill ?? null,
+ 'font' => $oFont ?? null,
+ 'shadow' => $oShadow ?? null,
+ 'listStyle' => $arrayListStyle ?? null,
+ 'spacingAfter' => $spacingAfter ?? null,
+ 'spacingBefore' => $spacingBefore ?? null,
+ 'lineSpacingMode' => $lineSpacingMode ?? null,
+ 'lineSpacing' => $lineSpacing ?? null,
+ ];
+
return true;
}
/**
* Read Slide
- *
- * @param \DOMElement $nodeSlide
- * @return bool
- * @throws \Exception
*/
- protected function loadSlide(\DOMElement $nodeSlide)
+ protected function loadSlide(DOMElement $nodeSlide): bool
{
// Core
$this->oPhpPresentation->createSlide();
@@ -371,128 +504,151 @@ class ODPresentation implements ReaderInterface
}
}
foreach ($this->oXMLReader->getElements('draw:frame', $nodeSlide) as $oNodeFrame) {
- if ($this->oXMLReader->getElement('draw:image', $oNodeFrame)) {
- $this->loadShapeDrawing($oNodeFrame);
- continue;
- }
- if ($this->oXMLReader->getElement('draw:text-box', $oNodeFrame)) {
- $this->loadShapeRichText($oNodeFrame);
- continue;
+ if ($oNodeFrame instanceof DOMElement) {
+ if ($this->oXMLReader->getElement('draw:image', $oNodeFrame)) {
+ $this->loadShapeDrawing($oNodeFrame);
+ continue;
+ }
+ if ($this->oXMLReader->getElement('draw:text-box', $oNodeFrame)) {
+ $this->loadShapeRichText($oNodeFrame);
+ continue;
+ }
}
}
+
return true;
}
/**
* Read Shape Drawing
- *
- * @param \DOMElement $oNodeFrame
- * @throws \Exception
*/
- protected function loadShapeDrawing(\DOMElement $oNodeFrame)
+ protected function loadShapeDrawing(DOMElement $oNodeFrame): void
{
// Core
- $oShape = new Gd();
- $oShape->getShadow()->setVisible(false);
+ $mimetype = '';
$oNodeImage = $this->oXMLReader->getElement('draw:image', $oNodeFrame);
- if ($oNodeImage instanceof \DOMElement) {
+ if ($oNodeImage instanceof DOMElement) {
+ if ($oNodeImage->hasAttribute('loext:mime-type')) {
+ $mimetype = $oNodeImage->getAttribute('loext:mime-type');
+ }
if ($oNodeImage->hasAttribute('xlink:href')) {
$sFilename = $oNodeImage->getAttribute('xlink:href');
// svm = StarView Metafile
- if (pathinfo($sFilename, PATHINFO_EXTENSION) == 'svm') {
+ if ('svm' == pathinfo($sFilename, PATHINFO_EXTENSION)) {
return;
}
$imageFile = $this->oZip->getFromName($sFilename);
- if (!empty($imageFile)) {
- $oShape->setImageResource(imagecreatefromstring($imageFile));
- }
}
}
-
- $oShape->setName($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
- $oShape->setDescription($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
- $oShape->setResizeProportional(false);
- $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
- $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
- $oShape->setResizeProportional(true);
- $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
- $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
-
+
+ if (empty($imageFile)) {
+ return;
+ }
+
+ // Contents of file
+ if (empty($mimetype)) {
+ $shape = new Gd();
+ $shape->setImageResource(imagecreatefromstring($imageFile));
+ } else {
+ $shape = new Base64();
+ $shape->setData('data:' . $mimetype . ';base64,' . base64_encode($imageFile));
+ }
+
+ $shape->getShadow()->setVisible(false);
+ $shape->setName($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
+ $shape->setDescription($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
+ $shape->setResizeProportional(false);
+ $shape->setWidth($oNodeFrame->hasAttribute('svg:width') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:width'), 0, -2)) : 0);
+ $shape->setHeight($oNodeFrame->hasAttribute('svg:height') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:height'), 0, -2)) : 0);
+ $shape->setResizeProportional(true);
+ $shape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:x'), 0, -2)) : 0);
+ $shape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:y'), 0, -2)) : 0);
+
if ($oNodeFrame->hasAttribute('draw:style-name')) {
$keyStyle = $oNodeFrame->getAttribute('draw:style-name');
if (isset($this->arrayStyles[$keyStyle])) {
- $oShape->setShadow($this->arrayStyles[$keyStyle]['shadow']);
- $oShape->setFill($this->arrayStyles[$keyStyle]['fill']);
+ $shape->setShadow($this->arrayStyles[$keyStyle]['shadow']);
+ $shape->setFill($this->arrayStyles[$keyStyle]['fill']);
}
}
-
- $this->oPhpPresentation->getActiveSlide()->addShape($oShape);
+
+ $this->oPhpPresentation->getActiveSlide()->addShape($shape);
}
/**
* Read Shape RichText
- *
- * @param \DOMElement $oNodeFrame
- * @throws \Exception
*/
- protected function loadShapeRichText(\DOMElement $oNodeFrame)
+ protected function loadShapeRichText(DOMElement $oNodeFrame): void
{
// Core
$oShape = $this->oPhpPresentation->getActiveSlide()->createRichTextShape();
- $oShape->setParagraphs(array());
-
- $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
- $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
- $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
- $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
-
+ $oShape->setParagraphs([]);
+
+ $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:width'), 0, -2)) : 0);
+ $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:height'), 0, -2)) : 0);
+ $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:x'), 0, -2)) : 0);
+ $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? CommonDrawing::centimetersToPixels((float) substr($oNodeFrame->getAttribute('svg:y'), 0, -2)) : 0);
+
foreach ($this->oXMLReader->getElements('draw:text-box/*', $oNodeFrame) as $oNodeParagraph) {
$this->levelParagraph = 0;
- if ($oNodeParagraph->nodeName == 'text:p') {
- $this->readParagraph($oShape, $oNodeParagraph);
- }
- if ($oNodeParagraph->nodeName == 'text:list') {
- $this->readList($oShape, $oNodeParagraph);
+ if ($oNodeParagraph instanceof DOMElement) {
+ if ('text:p' == $oNodeParagraph->nodeName) {
+ $this->readParagraph($oShape, $oNodeParagraph);
+ }
+ if ('text:list' == $oNodeParagraph->nodeName) {
+ $this->readList($oShape, $oNodeParagraph);
+ }
}
}
-
+
if (count($oShape->getParagraphs()) > 0) {
$oShape->setActiveParagraph(0);
}
}
-
- protected $levelParagraph = 0;
/**
* Read Paragraph
- * @param RichText $oShape
- * @param \DOMElement $oNodeParent
- * @throws \Exception
*/
- protected function readParagraph(RichText $oShape, \DOMElement $oNodeParent)
+ protected function readParagraph(RichText $oShape, DOMElement $oNodeParent): void
{
$oParagraph = $oShape->createParagraph();
+ if ($oNodeParent->hasAttribute('text:style-name')) {
+ $keyStyle = $oNodeParent->getAttribute('text:style-name');
+ if (isset($this->arrayStyles[$keyStyle])) {
+ if (!empty($this->arrayStyles[$keyStyle]['spacingAfter'])) {
+ $oParagraph->setSpacingAfter($this->arrayStyles[$keyStyle]['spacingAfter']);
+ }
+ if (!empty($this->arrayStyles[$keyStyle]['spacingBefore'])) {
+ $oParagraph->setSpacingBefore($this->arrayStyles[$keyStyle]['spacingBefore']);
+ }
+ if (!empty($this->arrayStyles[$keyStyle]['lineSpacingMode'])) {
+ $oParagraph->setLineSpacingMode($this->arrayStyles[$keyStyle]['lineSpacingMode']);
+ }
+ if (!empty($this->arrayStyles[$keyStyle]['lineSpacing'])) {
+ $oParagraph->setLineSpacing($this->arrayStyles[$keyStyle]['lineSpacing']);
+ }
+ }
+ }
$oDomList = $this->oXMLReader->getElements('text:span', $oNodeParent);
$oDomTextNodes = $this->oXMLReader->getElements('text()', $oNodeParent);
foreach ($oDomTextNodes as $oDomTextNode) {
- if (trim($oDomTextNode->nodeValue) != '') {
+ if ('' != trim($oDomTextNode->nodeValue)) {
$oTextRun = $oParagraph->createTextRun();
$oTextRun->setText(trim($oDomTextNode->nodeValue));
}
}
foreach ($oDomList as $oNodeRichTextElement) {
- $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
+ if ($oNodeRichTextElement instanceof DOMElement) {
+ $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
+ }
}
}
/**
* Read Paragraph Item
- * @param Paragraph $oParagraph
- * @param \DOMElement $oNodeParent
- * @throws \Exception
*/
- protected function readParagraphItem(Paragraph $oParagraph, \DOMElement $oNodeParent)
+ protected function readParagraphItem(Paragraph $oParagraph, DOMElement $oNodeParent): void
{
if ($this->oXMLReader->elementExists('text:line-break', $oNodeParent)) {
$oParagraph->createBreak();
@@ -505,7 +661,7 @@ class ODPresentation implements ReaderInterface
}
}
$oTextRunLink = $this->oXMLReader->getElement('text:a', $oNodeParent);
- if ($oTextRunLink instanceof \DOMElement) {
+ if ($oTextRunLink instanceof DOMElement) {
$oTextRun->setText($oTextRunLink->nodeValue);
if ($oTextRunLink->hasAttribute('xlink:href')) {
$oTextRun->getHyperlink()->setUrl($oTextRunLink->getAttribute('xlink:href'));
@@ -518,33 +674,27 @@ class ODPresentation implements ReaderInterface
/**
* Read List
- *
- * @param RichText $oShape
- * @param \DOMElement $oNodeParent
- * @throws \Exception
*/
- protected function readList(RichText $oShape, \DOMElement $oNodeParent)
+ protected function readList(RichText $oShape, DOMElement $oNodeParent): void
{
foreach ($this->oXMLReader->getElements('text:list-item/*', $oNodeParent) as $oNodeListItem) {
- if ($oNodeListItem->nodeName == 'text:p') {
- $this->readListItem($oShape, $oNodeListItem, $oNodeParent);
- }
- if ($oNodeListItem->nodeName == 'text:list') {
- $this->levelParagraph++;
- $this->readList($oShape, $oNodeListItem);
- $this->levelParagraph--;
+ if ($oNodeListItem instanceof DOMElement) {
+ if ('text:p' == $oNodeListItem->nodeName) {
+ $this->readListItem($oShape, $oNodeListItem, $oNodeParent);
+ }
+ if ('text:list' == $oNodeListItem->nodeName) {
+ ++$this->levelParagraph;
+ $this->readList($oShape, $oNodeListItem);
+ --$this->levelParagraph;
+ }
}
}
}
/**
* Read List Item
- * @param RichText $oShape
- * @param \DOMElement $oNodeParent
- * @param \DOMElement $oNodeParagraph
- * @throws \Exception
*/
- protected function readListItem(RichText $oShape, \DOMElement $oNodeParent, \DOMElement $oNodeParagraph)
+ protected function readListItem(RichText $oShape, DOMElement $oNodeParent, DOMElement $oNodeParagraph): void
{
$oParagraph = $oShape->createParagraph();
if ($oNodeParagraph->hasAttribute('text:style-name')) {
@@ -555,22 +705,52 @@ class ODPresentation implements ReaderInterface
}
}
foreach ($this->oXMLReader->getElements('text:span', $oNodeParent) as $oNodeRichTextElement) {
- $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
+ if ($oNodeRichTextElement instanceof DOMElement) {
+ $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
+ }
}
}
/**
- * Load file 'styles.xml'
+ * Load file 'styles.xml'.
*/
- protected function loadStylesFile()
+ protected function loadStylesFile(): void
{
foreach ($this->oXMLReader->getElements('/office:document-styles/office:styles/*') as $oElement) {
- if ($oElement->nodeName == 'draw:fill-image') {
- $this->arrayCommonStyles[$oElement->getAttribute('draw:name')] = array(
+ if ($oElement instanceof DOMElement && 'draw:fill-image' == $oElement->nodeName) {
+ $this->arrayCommonStyles[$oElement->getAttribute('draw:name')] = [
'type' => 'image',
- 'path' => $oElement->hasAttribute('xlink:href') ? $oElement->getAttribute('xlink:href') : null
- );
+ 'path' => $oElement->hasAttribute('xlink:href') ? $oElement->getAttribute('xlink:href') : null,
+ ];
}
}
}
+
+ /**
+ * @param string $expr
+ *
+ * @return string
+ */
+ private function getExpressionUnit(string $expr): string
+ {
+ if (substr($expr, -1) == '%') {
+ return '%';
+ }
+
+ return substr($expr, -2);
+ }
+
+ /**
+ * @param string $expr
+ *
+ * @return string
+ */
+ private function getExpressionValue(string $expr): string
+ {
+ if (substr($expr, -1) == '%') {
+ return substr($expr, 0, -1);
+ }
+
+ return substr($expr, 0, -2);
+ }
}
diff --git a/PhpOffice/PhpPresentation/Reader/PowerPoint2007.php b/PhpOffice/PhpPresentation/Reader/PowerPoint2007.php
old mode 100755
new mode 100644
index aa3b847..a0a8957
--- a/PhpOffice/PhpPresentation/Reader/PowerPoint2007.php
+++ b/PhpOffice/PhpPresentation/Reader/PowerPoint2007.php
@@ -10,75 +10,89 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Reader;
+use DateTime;
+use DOMElement;
+use DOMNode;
+use DOMNodeList;
+use PhpOffice\Common\Drawing as CommonDrawing;
+use PhpOffice\Common\XMLReader;
use PhpOffice\PhpPresentation\DocumentLayout;
+use PhpOffice\PhpPresentation\DocumentProperties;
+use PhpOffice\PhpPresentation\Exception\FeatureNotImplementedException;
+use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
+use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
use PhpOffice\PhpPresentation\PhpPresentation;
+use PhpOffice\PhpPresentation\PresentationProperties;
+use PhpOffice\PhpPresentation\Shape\Drawing\Base64;
+use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
+use PhpOffice\PhpPresentation\Shape\Hyperlink;
use PhpOffice\PhpPresentation\Shape\Placeholder;
use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
use PhpOffice\PhpPresentation\Shape\Table\Cell;
use PhpOffice\PhpPresentation\Slide;
use PhpOffice\PhpPresentation\Slide\AbstractSlide;
+use PhpOffice\PhpPresentation\Slide\Note;
use PhpOffice\PhpPresentation\Slide\SlideLayout;
use PhpOffice\PhpPresentation\Slide\SlideMaster;
-use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
-use PhpOffice\PhpPresentation\Style\Bullet;
use PhpOffice\PhpPresentation\Style\Border;
use PhpOffice\PhpPresentation\Style\Borders;
+use PhpOffice\PhpPresentation\Style\Bullet;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Fill;
+use PhpOffice\PhpPresentation\Style\Font;
use PhpOffice\PhpPresentation\Style\SchemeColor;
use PhpOffice\PhpPresentation\Style\TextStyle;
-use PhpOffice\Common\XMLReader;
-use PhpOffice\Common\Drawing as CommonDrawing;
use ZipArchive;
/**
- * Serialized format reader
+ * Serialized format reader.
*/
class PowerPoint2007 implements ReaderInterface
{
/**
- * Output Object
+ * Output Object.
+ *
* @var PhpPresentation
*/
protected $oPhpPresentation;
/**
- * Output Object
+ * Output Object.
+ *
* @var \ZipArchive
*/
protected $oZip;
/**
- * @var array[]
+ * @var array>>
*/
- protected $arrayRels = array();
+ protected $arrayRels = [];
/**
* @var SlideLayout[]
*/
- protected $arraySlideLayouts = array();
- /*
+ protected $arraySlideLayouts = [];
+ /**
* @var string
*/
protected $filename;
- /*
+ /**
* @var string
*/
protected $fileRels;
/**
* Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
- *
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
*/
- public function canRead($pFilename)
+ public function canRead(string $pFilename): bool
{
return $this->fileSupportsUnserializePhpPresentation($pFilename);
}
@@ -86,103 +100,101 @@ class PowerPoint2007 implements ReaderInterface
/**
* Does a file support UnserializePhpPresentation ?
*
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
+ * @throws FileNotFoundException
*/
- public function fileSupportsUnserializePhpPresentation($pFilename = '')
+ public function fileSupportsUnserializePhpPresentation(string $pFilename = ''): bool
{
// Check if file exists
if (!file_exists($pFilename)) {
- throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ throw new FileNotFoundException($pFilename);
}
$oZip = new ZipArchive();
// Is it a zip ?
- if ($oZip->open($pFilename) === true) {
+ if (true === $oZip->open($pFilename)) {
// Is it an OpenXML Document ?
// Is it a Presentation ?
if (is_array($oZip->statName('[Content_Types].xml')) && is_array($oZip->statName('ppt/presentation.xml'))) {
return true;
}
}
+
return false;
}
/**
- * Loads PhpPresentation Serialized file
+ * Loads PhpPresentation Serialized file.
*
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * @throws InvalidFileFormatException
*/
- public function load($pFilename)
+ public function load(string $pFilename): PhpPresentation
{
// Unserialize... First make sure the file supports it!
if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
- throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\PowerPoint2007: " . $pFilename . ".");
+ throw new InvalidFileFormatException($pFilename, PowerPoint2007::class);
}
return $this->loadFile($pFilename);
}
/**
- * Load PhpPresentation Serialized file
- *
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * Load PhpPresentation Serialized file.
*/
- protected function loadFile($pFilename)
+ protected function loadFile(string $pFilename): PhpPresentation
{
$this->oPhpPresentation = new PhpPresentation();
$this->oPhpPresentation->removeSlideByIndex();
- $this->oPhpPresentation->setAllMasterSlides(array());
+ $this->oPhpPresentation->setAllMasterSlides([]);
$this->filename = $pFilename;
$this->oZip = new ZipArchive();
$this->oZip->open($this->filename);
$docPropsCore = $this->oZip->getFromName('docProps/core.xml');
- if ($docPropsCore !== false) {
+ if (false !== $docPropsCore) {
$this->loadDocumentProperties($docPropsCore);
}
$docPropsCustom = $this->oZip->getFromName('docProps/custom.xml');
- if ($docPropsCustom !== false) {
+ if (false !== $docPropsCustom) {
$this->loadCustomProperties($docPropsCustom);
}
$pptViewProps = $this->oZip->getFromName('ppt/viewProps.xml');
- if ($pptViewProps !== false) {
+ if (false !== $pptViewProps) {
$this->loadViewProperties($pptViewProps);
}
$pptPresentation = $this->oZip->getFromName('ppt/presentation.xml');
- if ($pptPresentation !== false) {
+ if (false !== $pptPresentation) {
$this->loadDocumentLayout($pptPresentation);
$this->loadSlides($pptPresentation);
}
+ $pptPresProps = $this->oZip->getFromName('ppt/presProps.xml');
+ if (false !== $pptPresProps) {
+ $this->loadPresentationProperties($pptPresentation);
+ }
+
return $this->oPhpPresentation;
}
/**
- * Read Document Layout
- * @param $sPart
+ * Read Document Layout.
*/
- protected function loadDocumentLayout($sPart)
+ protected function loadDocumentLayout(string $sPart): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
foreach ($xmlReader->getElements('/p:presentation/p:sldSz') as $oElement) {
- if (!($oElement instanceof \DOMElement)) {
+ if (!($oElement instanceof DOMElement)) {
continue;
}
$type = $oElement->getAttribute('type');
$oLayout = $this->oPhpPresentation->getLayout();
- if ($type == DocumentLayout::LAYOUT_CUSTOM) {
- $oLayout->setCX($oElement->getAttribute('cx'));
- $oLayout->setCY($oElement->getAttribute('cy'));
+ if (DocumentLayout::LAYOUT_CUSTOM == $type) {
+ $oLayout->setCX((float) $oElement->getAttribute('cx'));
+ $oLayout->setCY((float) $oElement->getAttribute('cy'));
} else {
$oLayout->setDocumentLayout($type, true);
if ($oElement->getAttribute('cx') < $oElement->getAttribute('cy')) {
@@ -194,14 +206,14 @@ class PowerPoint2007 implements ReaderInterface
}
/**
- * Read Document Properties
- * @param string $sPart
+ * Read Document Properties.
*/
- protected function loadDocumentProperties($sPart)
+ protected function loadDocumentProperties(string $sPart): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
- $arrayProperties = array(
+ $arrayProperties = [
'/cp:coreProperties/dc:creator' => 'setCreator',
'/cp:coreProperties/cp:lastModifiedBy' => 'setLastModifiedBy',
'/cp:coreProperties/dc:title' => 'setTitle',
@@ -211,15 +223,14 @@ class PowerPoint2007 implements ReaderInterface
'/cp:coreProperties/cp:category' => 'setCategory',
'/cp:coreProperties/dcterms:created' => 'setCreated',
'/cp:coreProperties/dcterms:modified' => 'setModified',
- );
+ ];
$oProperties = $this->oPhpPresentation->getDocumentProperties();
foreach ($arrayProperties as $path => $property) {
$oElement = $xmlReader->getElement($path);
- if ($oElement instanceof \DOMElement) {
- if ($oElement->hasAttribute('xsi:type') && $oElement->getAttribute('xsi:type') == 'dcterms:W3CDTF') {
- $oDateTime = new \DateTime();
- $oDateTime->createFromFormat(\DateTime::W3C, $oElement->nodeValue);
- $oProperties->{$property}($oDateTime->getTimestamp());
+ if ($oElement instanceof DOMElement) {
+ if ($oElement->hasAttribute('xsi:type') && 'dcterms:W3CDTF' == $oElement->getAttribute('xsi:type')) {
+ $dateTime = DateTime::createFromFormat(DateTime::W3C, $oElement->nodeValue);
+ $oProperties->{$property}($dateTime->getTimestamp());
} else {
$oProperties->{$property}($oElement->nodeValue);
}
@@ -229,34 +240,99 @@ class PowerPoint2007 implements ReaderInterface
}
/**
- * Read Custom Properties
- * @param string $sPart
+ * Read Custom Properties.
*/
- protected function loadCustomProperties($sPart)
+ protected function loadCustomProperties(string $sPart): void
{
$xmlReader = new XMLReader();
$sPart = str_replace(' xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"', '', $sPart);
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
- $pathMarkAsFinal = '/Properties/property[@pid="2"][@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"][@name="_MarkAsFinal"]/vt:bool';
- if (is_object($oElement = $xmlReader->getElement($pathMarkAsFinal))) {
- if ($oElement->nodeValue == 'true') {
- $this->oPhpPresentation->getPresentationProperties()->markAsFinal(true);
+ foreach ($xmlReader->getElements('/Properties/property[@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"]') as $element) {
+ if (!$element->hasAttribute('name')) {
+ continue;
+ }
+ $propertyName = $element->getAttribute('name');
+ if ($propertyName == '_MarkAsFinal') {
+ $attributeElement = $xmlReader->getElement('vt:bool', $element);
+ if ($attributeElement && 'true' == $attributeElement->nodeValue) {
+ $this->oPhpPresentation->getPresentationProperties()->markAsFinal(true);
+ }
+ } else {
+ $attributeTypeInt = $xmlReader->getElement('vt:i4', $element);
+ $attributeTypeFloat = $xmlReader->getElement('vt:r8', $element);
+ $attributeTypeBoolean = $xmlReader->getElement('vt:bool', $element);
+ $attributeTypeDate = $xmlReader->getElement('vt:filetime', $element);
+ $attributeTypeString = $xmlReader->getElement('vt:lpwstr', $element);
+
+ if ($attributeTypeInt) {
+ $propertyType = DocumentProperties::PROPERTY_TYPE_INTEGER;
+ $propertyValue = (int) $attributeTypeInt->nodeValue;
+ } elseif ($attributeTypeFloat) {
+ $propertyType = DocumentProperties::PROPERTY_TYPE_FLOAT;
+ $propertyValue = (float) $attributeTypeFloat->nodeValue;
+ } elseif ($attributeTypeBoolean) {
+ $propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
+ $propertyValue = $attributeTypeBoolean->nodeValue == 'true' ? true : false;
+ } elseif ($attributeTypeDate) {
+ $propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
+ $propertyValue = strtotime($attributeTypeDate->nodeValue);
+ } else {
+ $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
+ $propertyValue = $attributeTypeString->nodeValue;
+ }
+
+ $this->oPhpPresentation->getDocumentProperties()->setCustomProperty($propertyName, $propertyValue, $propertyType);
}
}
}
}
/**
- * Read View Properties
- * @param string $sPart
+ * Read Presentation Properties
*/
- protected function loadViewProperties($sPart)
+ protected function loadPresentationProperties(string $sPart): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
+ if ($xmlReader->getDomFromString($sPart)) {
+ $element = $xmlReader->getElement('/p:presentationPr/p:showPr');
+ if ($element instanceof DOMElement) {
+ if ($element->hasAttribute('loop')) {
+ $this->oPhpPresentation->getPresentationProperties()->setLoopContinuouslyUntilEsc(
+ (bool) $element->getAttribute('loop')
+ );
+ }
+ if (null !== $xmlReader->getElement('p:present', $element)) {
+ $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
+ PresentationProperties::SLIDESHOW_TYPE_PRESENT
+ );
+ }
+ if (null !== $xmlReader->getElement('p:browse', $element)) {
+ $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
+ PresentationProperties::SLIDESHOW_TYPE_BROWSE
+ );
+ }
+ if (null !== $xmlReader->getElement('p:kiosk', $element)) {
+ $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
+ PresentationProperties::SLIDESHOW_TYPE_KIOSK
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Read View Properties.
+ */
+ protected function loadViewProperties(string $sPart): void
+ {
+ $xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
$pathZoom = '/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sx';
$oElement = $xmlReader->getElement($pathZoom);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('d') && $oElement->hasAttribute('n')) {
$this->oPhpPresentation->getPresentationProperties()->setZoom($oElement->getAttribute('n') / $oElement->getAttribute('d'));
}
@@ -266,12 +342,11 @@ class PowerPoint2007 implements ReaderInterface
/**
* Extract all slides
- * @param $sPart
- * @throws \Exception
*/
- protected function loadSlides($sPart)
+ protected function loadSlides(string $sPart): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
$fileRels = 'ppt/_rels/presentation.xml.rels';
$this->loadRels($fileRels);
@@ -279,19 +354,19 @@ class PowerPoint2007 implements ReaderInterface
$this->loadMasterSlides($xmlReader, $fileRels);
// Continue with loading the slides
foreach ($xmlReader->getElements('/p:presentation/p:sldIdLst/p:sldId') as $oElement) {
- if (!($oElement instanceof \DOMElement)) {
+ if (!($oElement instanceof DOMElement)) {
continue;
}
$rId = $oElement->getAttribute('r:id');
$pathSlide = isset($this->arrayRels[$fileRels][$rId]) ? $this->arrayRels[$fileRels][$rId]['Target'] : '';
if (!empty($pathSlide)) {
$pptSlide = $this->oZip->getFromName('ppt/' . $pathSlide);
- if ($pptSlide !== false) {
+ if (false !== $pptSlide) {
$slideRels = 'ppt/slides/_rels/' . basename($pathSlide) . '.rels';
$this->loadRels($slideRels);
$this->loadSlide($pptSlide, basename($pathSlide));
foreach ($this->arrayRels[$slideRels] as $rel) {
- if ($rel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide') {
+ if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide' == $rel['Type']) {
$this->loadSlideNote(basename($rel['Target']), $this->oPhpPresentation->getActiveSlide());
}
}
@@ -303,15 +378,12 @@ class PowerPoint2007 implements ReaderInterface
/**
* Extract all MasterSlides
- * @param XMLReader $xmlReader
- * @param string $fileRels
- * @throws \Exception
*/
- protected function loadMasterSlides(XMLReader $xmlReader, $fileRels)
+ protected function loadMasterSlides(XMLReader $xmlReader, string $fileRels): void
{
// Get all the MasterSlide Id's from the presentation.xml file
foreach ($xmlReader->getElements('/p:presentation/p:sldMasterIdLst/p:sldMasterId') as $oElement) {
- if (!($oElement instanceof \DOMElement)) {
+ if (!($oElement instanceof DOMElement)) {
continue;
}
$rId = $oElement->getAttribute('r:id');
@@ -320,7 +392,7 @@ class PowerPoint2007 implements ReaderInterface
$this->arrayRels[$fileRels][$rId]['Target'] : '';
if (!empty($pathMasterSlide)) {
$pptMasterSlide = $this->oZip->getFromName('ppt/' . $pathMasterSlide);
- if ($pptMasterSlide !== false) {
+ if (false !== $pptMasterSlide) {
$this->loadRels('ppt/slideMasters/_rels/' . basename($pathMasterSlide) . '.rels');
$this->loadMasterSlide($pptMasterSlide, basename($pathMasterSlide));
}
@@ -330,13 +402,11 @@ class PowerPoint2007 implements ReaderInterface
/**
* Extract data from slide
- * @param string $sPart
- * @param string $baseFile
- * @throws \Exception
*/
- protected function loadSlide($sPart, $baseFile)
+ protected function loadSlide(string $sPart, string $baseFile): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
// Core
$oSlide = $this->oPhpPresentation->createSlide();
@@ -345,9 +415,9 @@ class PowerPoint2007 implements ReaderInterface
// Background
$oElement = $xmlReader->getElement('/p:sld/p:cSld/p:bg/p:bgPr');
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
$oElementColor = $xmlReader->getElement('a:solidFill/a:srgbClr', $oElement);
- if ($oElementColor instanceof \DOMElement) {
+ if ($oElementColor instanceof DOMElement) {
// Color
$oColor = new Color();
$oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
@@ -359,7 +429,7 @@ class PowerPoint2007 implements ReaderInterface
$oSlide->setBackground($oBackground);
}
$oElementColor = $xmlReader->getElement('a:solidFill/a:schemeClr', $oElement);
- if ($oElementColor instanceof \DOMElement) {
+ if ($oElementColor instanceof DOMElement) {
// Color
$oColor = new SchemeColor();
$oColor->setValue($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
@@ -371,14 +441,14 @@ class PowerPoint2007 implements ReaderInterface
$oSlide->setBackground($oBackground);
}
$oElementImage = $xmlReader->getElement('a:blipFill/a:blip', $oElement);
- if ($oElementImage instanceof \DOMElement) {
+ if ($oElementImage instanceof DOMElement) {
$relImg = $this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'][$oElementImage->getAttribute('r:embed')];
if (is_array($relImg)) {
// File
$pathImage = 'ppt/slides/' . $relImg['Target'];
$pathImage = explode('/', $pathImage);
foreach ($pathImage as $key => $partPath) {
- if ($partPath == '..') {
+ if ('..' == $partPath) {
unset($pathImage[$key - 1]);
unset($pathImage[$key]);
}
@@ -400,14 +470,12 @@ class PowerPoint2007 implements ReaderInterface
// Shapes
$arrayElements = $xmlReader->getElements('/p:sld/p:cSld/p:spTree/*');
- if ($arrayElements) {
- $this->loadSlideShapes($oSlide, $arrayElements, $xmlReader);
- }
+ $this->loadSlideShapes($oSlide, $arrayElements, $xmlReader);
// Layout
$oSlide = $this->oPhpPresentation->getActiveSlide();
foreach ($this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'] as $valueRel) {
- if ($valueRel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout') {
+ if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout' == $valueRel['Type']) {
$layoutBasename = basename($valueRel['Target']);
if (array_key_exists($layoutBasename, $this->arraySlideLayouts)) {
$oSlide->setSlideLayout($this->arraySlideLayouts[$layoutBasename]);
@@ -418,14 +486,10 @@ class PowerPoint2007 implements ReaderInterface
}
}
- /**
- * @param string $sPart
- * @param string $baseFile
- * @throws \Exception
- */
- protected function loadMasterSlide($sPart, $baseFile)
+ protected function loadMasterSlide(string $sPart, string $baseFile): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
// Core
$oSlideMaster = $this->oPhpPresentation->createMasterSlide();
@@ -434,19 +498,18 @@ class PowerPoint2007 implements ReaderInterface
// Background
$oElement = $xmlReader->getElement('/p:sldMaster/p:cSld/p:bg');
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
$this->loadSlideBackground($xmlReader, $oElement, $oSlideMaster);
}
// Shapes
$arrayElements = $xmlReader->getElements('/p:sldMaster/p:cSld/p:spTree/*');
- if ($arrayElements) {
- $this->loadSlideShapes($oSlideMaster, $arrayElements, $xmlReader);
- }
+ $this->loadSlideShapes($oSlideMaster, $arrayElements, $xmlReader);
+
// Header & Footer
// ColorMapping
- $colorMap = array();
+ $colorMap = [];
$oElement = $xmlReader->getElement('/p:sldMaster/p:clrMap');
if ($oElement->hasAttributes()) {
foreach ($oElement->attributes as $attr) {
@@ -457,81 +520,80 @@ class PowerPoint2007 implements ReaderInterface
// TextStyles
$arrayElementTxStyles = $xmlReader->getElements('/p:sldMaster/p:txStyles/*');
- if ($arrayElementTxStyles) {
- foreach ($arrayElementTxStyles as $oElementTxStyle) {
- $arrayElementsLvl = $xmlReader->getElements('/p:sldMaster/p:txStyles/' . $oElementTxStyle->nodeName . '/*');
- foreach ($arrayElementsLvl as $oElementLvl) {
- if (!($oElementLvl instanceof \DOMElement) || $oElementLvl->nodeName == 'a:extLst') {
- continue;
- }
- $oRTParagraph = new Paragraph();
+ foreach ($arrayElementTxStyles as $oElementTxStyle) {
+ $arrayElementsLvl = $xmlReader->getElements('/p:sldMaster/p:txStyles/' . $oElementTxStyle->nodeName . '/*');
+ foreach ($arrayElementsLvl as $oElementLvl) {
+ if (!($oElementLvl instanceof DOMElement) || 'a:extLst' == $oElementLvl->nodeName) {
+ continue;
+ }
+ $oRTParagraph = new Paragraph();
- if ($oElementLvl->nodeName == 'a:defPPr') {
- $level = 0;
- } else {
- $level = str_replace('a:lvl', '', $oElementLvl->nodeName);
- $level = str_replace('pPr', '', $level);
- }
+ if ('a:defPPr' == $oElementLvl->nodeName) {
+ $level = 0;
+ } else {
+ $level = str_replace('a:lvl', '', $oElementLvl->nodeName);
+ $level = str_replace('pPr', '', $level);
+ $level = intval($level);
+ }
- if ($oElementLvl->hasAttribute('algn')) {
- $oRTParagraph->getAlignment()->setHorizontal($oElementLvl->getAttribute('algn'));
+ if ($oElementLvl->hasAttribute('algn')) {
+ $oRTParagraph->getAlignment()->setHorizontal($oElementLvl->getAttribute('algn'));
+ }
+ if ($oElementLvl->hasAttribute('marL')) {
+ $val = (int) $oElementLvl->getAttribute('marL');
+ $val = CommonDrawing::emuToPixels((int) $val);
+ $oRTParagraph->getAlignment()->setMarginLeft($val);
+ }
+ if ($oElementLvl->hasAttribute('marR')) {
+ $val = (int) $oElementLvl->getAttribute('marR');
+ $val = CommonDrawing::emuToPixels((int) $val);
+ $oRTParagraph->getAlignment()->setMarginRight($val);
+ }
+ if ($oElementLvl->hasAttribute('indent')) {
+ $val = (int) $oElementLvl->getAttribute('indent');
+ $val = CommonDrawing::emuToPixels((int) $val);
+ $oRTParagraph->getAlignment()->setIndent($val);
+ }
+ $oElementLvlDefRPR = $xmlReader->getElement('a:defRPr', $oElementLvl);
+ if ($oElementLvlDefRPR instanceof DOMElement) {
+ if ($oElementLvlDefRPR->hasAttribute('sz')) {
+ $oRTParagraph->getFont()->setSize($oElementLvlDefRPR->getAttribute('sz') / 100);
}
- if ($oElementLvl->hasAttribute('marL')) {
- $val = $oElementLvl->getAttribute('marL');
- $val = CommonDrawing::emuToPixels($val);
- $oRTParagraph->getAlignment()->setMarginLeft($val);
+ if ($oElementLvlDefRPR->hasAttribute('b') && 1 == $oElementLvlDefRPR->getAttribute('b')) {
+ $oRTParagraph->getFont()->setBold(true);
}
- if ($oElementLvl->hasAttribute('marR')) {
- $val = $oElementLvl->getAttribute('marR');
- $val = CommonDrawing::emuToPixels($val);
- $oRTParagraph->getAlignment()->setMarginRight($val);
+ if ($oElementLvlDefRPR->hasAttribute('i') && 1 == $oElementLvlDefRPR->getAttribute('i')) {
+ $oRTParagraph->getFont()->setItalic(true);
}
- if ($oElementLvl->hasAttribute('indent')) {
- $val = $oElementLvl->getAttribute('indent');
- $val = CommonDrawing::emuToPixels($val);
- $oRTParagraph->getAlignment()->setIndent($val);
- }
- $oElementLvlDefRPR = $xmlReader->getElement('a:defRPr', $oElementLvl);
- if ($oElementLvlDefRPR instanceof \DOMElement) {
- if ($oElementLvlDefRPR->hasAttribute('sz')) {
- $oRTParagraph->getFont()->setSize($oElementLvlDefRPR->getAttribute('sz') / 100);
- }
- if ($oElementLvlDefRPR->hasAttribute('b') && $oElementLvlDefRPR->getAttribute('b') == 1) {
- $oRTParagraph->getFont()->setBold(true);
- }
- if ($oElementLvlDefRPR->hasAttribute('i') && $oElementLvlDefRPR->getAttribute('i') == 1) {
- $oRTParagraph->getFont()->setItalic(true);
- }
- }
- $oElementSchemeColor = $xmlReader->getElement('a:defRPr/a:solidFill/a:schemeClr', $oElementLvl);
- if ($oElementSchemeColor instanceof \DOMElement) {
- if ($oElementSchemeColor->hasAttribute('val')) {
- $oSchemeColor = new SchemeColor();
- $oSchemeColor->setValue($oElementSchemeColor->getAttribute('val'));
- $oRTParagraph->getFont()->setColor($oSchemeColor);
- }
+ }
+ $oElementSchemeColor = $xmlReader->getElement('a:defRPr/a:solidFill/a:schemeClr', $oElementLvl);
+ if ($oElementSchemeColor instanceof DOMElement) {
+ if ($oElementSchemeColor->hasAttribute('val')) {
+ $oSchemeColor = new SchemeColor();
+ $oSchemeColor->setValue($oElementSchemeColor->getAttribute('val'));
+ $oRTParagraph->getFont()->setColor($oSchemeColor);
}
+ }
- switch ($oElementTxStyle->nodeName) {
- case 'p:bodyStyle':
- $oSlideMaster->getTextStyles()->setBodyStyleAtLvl($oRTParagraph, $level);
- break;
- case 'p:otherStyle':
- $oSlideMaster->getTextStyles()->setOtherStyleAtLvl($oRTParagraph, $level);
- break;
- case 'p:titleStyle':
- $oSlideMaster->getTextStyles()->setTitleStyleAtLvl($oRTParagraph, $level);
- break;
- }
+ switch ($oElementTxStyle->nodeName) {
+ case 'p:bodyStyle':
+ $oSlideMaster->getTextStyles()->setBodyStyleAtLvl($oRTParagraph, $level);
+ break;
+ case 'p:otherStyle':
+ $oSlideMaster->getTextStyles()->setOtherStyleAtLvl($oRTParagraph, $level);
+ break;
+ case 'p:titleStyle':
+ $oSlideMaster->getTextStyles()->setTitleStyleAtLvl($oRTParagraph, $level);
+ break;
}
}
}
// Load the theme
foreach ($this->arrayRels[$oSlideMaster->getRelsIndex()] as $arrayRel) {
- if ($arrayRel['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme') {
+ if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme' == $arrayRel['Type']) {
$pptTheme = $this->oZip->getFromName('ppt/' . substr($arrayRel['Target'], strrpos($arrayRel['Target'], '../') + 3));
- if ($pptTheme !== false) {
+ if (false !== $pptTheme) {
$this->loadTheme($pptTheme, $oSlideMaster);
}
break;
@@ -540,7 +602,7 @@ class PowerPoint2007 implements ReaderInterface
// Load the Layoutslide
foreach ($xmlReader->getElements('/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId') as $oElement) {
- if (!($oElement instanceof \DOMElement)) {
+ if (!($oElement instanceof DOMElement)) {
continue;
}
$rId = $oElement->getAttribute('r:id');
@@ -549,7 +611,7 @@ class PowerPoint2007 implements ReaderInterface
$this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]['Target'] : '';
if (!empty($pathLayoutSlide)) {
$pptLayoutSlide = $this->oZip->getFromName('ppt/' . substr($pathLayoutSlide, strrpos($pathLayoutSlide, '../') + 3));
- if ($pptLayoutSlide !== false) {
+ if (false !== $pptLayoutSlide) {
$this->loadRels('ppt/slideLayouts/_rels/' . basename($pathLayoutSlide) . '.rels');
$oSlideMaster->addSlideLayout(
$this->loadLayoutSlide($pptLayoutSlide, basename($pathLayoutSlide), $oSlideMaster)
@@ -560,16 +622,10 @@ class PowerPoint2007 implements ReaderInterface
}
}
- /**
- * @param string $sPart
- * @param string $baseFile
- * @param SlideMaster $oSlideMaster
- * @return SlideLayout|null
- * @throws \Exception
- */
- protected function loadLayoutSlide($sPart, $baseFile, SlideMaster $oSlideMaster)
+ protected function loadLayoutSlide(string $sPart, string $baseFile, SlideMaster $oSlideMaster): ?SlideLayout
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
// Core
$oSlideLayout = new SlideLayout($oSlideMaster);
@@ -577,20 +633,20 @@ class PowerPoint2007 implements ReaderInterface
// Name
$oElement = $xmlReader->getElement('/p:sldLayout/p:cSld');
- if ($oElement instanceof \DOMElement && $oElement->hasAttribute('name')) {
+ if ($oElement instanceof DOMElement && $oElement->hasAttribute('name')) {
$oSlideLayout->setLayoutName($oElement->getAttribute('name'));
}
// Background
$oElement = $xmlReader->getElement('/p:sldLayout/p:cSld/p:bg');
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
$this->loadSlideBackground($xmlReader, $oElement, $oSlideLayout);
}
// ColorMapping
$oElement = $xmlReader->getElement('/p:sldLayout/p:clrMapOvr/a:overrideClrMapping');
- if ($oElement instanceof \DOMElement && $oElement->hasAttributes()) {
- $colorMap = array();
+ if ($oElement instanceof DOMElement && $oElement->hasAttributes()) {
+ $colorMap = [];
foreach ($oElement->attributes as $attr) {
$colorMap[$attr->nodeName] = $attr->nodeValue;
}
@@ -599,30 +655,27 @@ class PowerPoint2007 implements ReaderInterface
// Shapes
$oElements = $xmlReader->getElements('/p:sldLayout/p:cSld/p:spTree/*');
- if ($oElements) {
- $this->loadSlideShapes($oSlideLayout, $oElements, $xmlReader);
- }
+ $this->loadSlideShapes($oSlideLayout, $oElements, $xmlReader);
$this->arraySlideLayouts[$baseFile] = &$oSlideLayout;
+
return $oSlideLayout;
}
+ /* @phpstan-ignore-next-line */
return null;
}
- /**
- * @param string $sPart
- * @param SlideMaster $oSlideMaster
- */
- protected function loadTheme($sPart, SlideMaster $oSlideMaster)
+ protected function loadTheme(string $sPart, SlideMaster $oSlideMaster): void
{
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
$oElements = $xmlReader->getElements('/a:theme/a:themeElements/a:clrScheme/*');
- if ($oElements) {
- foreach ($oElements as $oElement) {
+ foreach ($oElements as $oElement) {
+ if ($oElement instanceof DOMElement) {
$oSchemeColor = new SchemeColor();
$oSchemeColor->setValue(str_replace('a:', '', $oElement->tagName));
$colorElement = $xmlReader->getElement('*', $oElement);
- if ($colorElement instanceof \DOMElement) {
+ if ($colorElement instanceof DOMElement) {
if ($colorElement->hasAttribute('lastClr')) {
$oSchemeColor->setRGB($colorElement->getAttribute('lastClr'));
} elseif ($colorElement->hasAttribute('val')) {
@@ -635,17 +688,11 @@ class PowerPoint2007 implements ReaderInterface
}
}
- /**
- * @param XMLReader $xmlReader
- * @param \DOMElement $oElement
- * @param AbstractSlide $oSlide
- * @throws \Exception
- */
- protected function loadSlideBackground(XMLReader $xmlReader, \DOMElement $oElement, AbstractSlide $oSlide)
+ protected function loadSlideBackground(XMLReader $xmlReader, DOMElement $oElement, AbstractSlide $oSlide): void
{
// Background color
$oElementColor = $xmlReader->getElement('p:bgPr/a:solidFill/a:srgbClr', $oElement);
- if ($oElementColor instanceof \DOMElement) {
+ if ($oElementColor instanceof DOMElement) {
// Color
$oColor = new Color();
$oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
@@ -658,7 +705,7 @@ class PowerPoint2007 implements ReaderInterface
// Background scheme color
$oElementSchemeColor = $xmlReader->getElement('p:bgRef/a:schemeClr', $oElement);
- if ($oElementSchemeColor instanceof \DOMElement) {
+ if ($oElementSchemeColor instanceof DOMElement) {
// Color
$oColor = new SchemeColor();
$oColor->setValue($oElementSchemeColor->hasAttribute('val') ? $oElementSchemeColor->getAttribute('val') : null);
@@ -671,14 +718,14 @@ class PowerPoint2007 implements ReaderInterface
// Background image
$oElementImage = $xmlReader->getElement('p:bgPr/a:blipFill/a:blip', $oElement);
- if ($oElementImage instanceof \DOMElement) {
+ if ($oElementImage instanceof DOMElement) {
$relImg = $this->arrayRels[$oSlide->getRelsIndex()][$oElementImage->getAttribute('r:embed')];
if (is_array($relImg)) {
// File
$pathImage = 'ppt/slides/' . $relImg['Target'];
$pathImage = explode('/', $pathImage);
foreach ($pathImage as $key => $partPath) {
- if ($partPath == '..') {
+ if ('..' == $partPath) {
unset($pathImage[$key - 1]);
unset($pathImage[$key]);
}
@@ -697,63 +744,53 @@ class PowerPoint2007 implements ReaderInterface
}
}
- /**
- * @param string $baseFile
- * @param Slide $oSlide
- * @throws \Exception
- */
- protected function loadSlideNote($baseFile, Slide $oSlide)
+ protected function loadSlideNote(string $baseFile, Slide $oSlide): void
{
$sPart = $this->oZip->getFromName('ppt/notesSlides/' . $baseFile);
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
$oNote = $oSlide->getNote();
$arrayElements = $xmlReader->getElements('/p:notes/p:cSld/p:spTree/*');
- if ($arrayElements) {
- $this->loadSlideShapes($oNote, $arrayElements, $xmlReader);
- }
+ $this->loadSlideShapes($oNote, $arrayElements, $xmlReader);
}
}
- /**
- * @param XMLReader $document
- * @param \DOMElement $node
- * @param AbstractSlide $oSlide
- * @throws \Exception
- */
- protected function loadShapeDrawing(XMLReader $document, \DOMElement $node, AbstractSlide $oSlide)
+ protected function loadShapeDrawing(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
{
// Core
- $oShape = new Gd();
+ $document->registerNamespace('asvg', 'http://schemas.microsoft.com/office/drawing/2016/SVG/main');
+ if ($document->getElement('p:blipFill/a:blip/a:extLst/a:ext/asvg:svgBlip', $node)) {
+ $oShape = new Base64();
+ } else {
+ $oShape = new Gd();
+ }
$oShape->getShadow()->setVisible(false);
// Variables
$fileRels = $oSlide->getRelsIndex();
$oElement = $document->getElement('p:nvPicPr/p:cNvPr', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
$oShape->setName($oElement->hasAttribute('name') ? $oElement->getAttribute('name') : '');
$oShape->setDescription($oElement->hasAttribute('descr') ? $oElement->getAttribute('descr') : '');
// Hyperlink
$oElementHlinkClick = $document->getElement('a:hlinkClick', $oElement);
if (is_object($oElementHlinkClick)) {
- if ($oElementHlinkClick->hasAttribute('tooltip')) {
- $oShape->getHyperlink()->setTooltip($oElementHlinkClick->getAttribute('tooltip'));
- }
- if ($oElementHlinkClick->hasAttribute('r:id') && isset($this->arrayRels[$fileRels][$oElementHlinkClick->getAttribute('r:id')]['Target'])) {
- $oShape->getHyperlink()->setUrl($this->arrayRels[$fileRels][$oElementHlinkClick->getAttribute('r:id')]['Target']);
- }
+ $oShape->setHyperlink(
+ $this->loadHyperlink($document, $oElementHlinkClick, $oShape->getHyperlink())
+ );
}
}
$oElement = $document->getElement('p:blipFill/a:blip', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('r:embed') && isset($this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'])) {
$pathImage = 'ppt/slides/' . $this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'];
$pathImage = explode('/', $pathImage);
foreach ($pathImage as $key => $partPath) {
- if ($partPath == '..') {
+ if ('..' == $partPath) {
unset($pathImage[$key - 1]);
unset($pathImage[$key]);
}
@@ -761,61 +798,65 @@ class PowerPoint2007 implements ReaderInterface
$pathImage = implode('/', $pathImage);
$imageFile = $this->oZip->getFromName($pathImage);
if (!empty($imageFile)) {
- $info = getimagesizefromstring($imageFile);
- $oShape->setMimeType($info['mime']);
- $oShape->setRenderingFunction(str_replace('/', '', $info['mime']));
- $oShape->setImageResource(imagecreatefromstring($imageFile));
+ if ($oShape instanceof Gd) {
+ $info = getimagesizefromstring($imageFile);
+ $oShape->setMimeType($info['mime']);
+ $oShape->setRenderingFunction(str_replace('/', '', $info['mime']));
+ $oShape->setImageResource(imagecreatefromstring($imageFile));
+ } elseif ($oShape instanceof Base64) {
+ $oShape->setData('data:image/svg+xml;base64,' . base64_encode($imageFile));
+ }
}
}
}
$oElement = $document->getElement('p:spPr', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
$oFill = $this->loadStyleFill($document, $oElement);
$oShape->setFill($oFill);
}
$oElement = $document->getElement('p:spPr/a:xfrm', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('rot')) {
- $oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
+ $oShape->setRotation((int) CommonDrawing::angleToDegrees((int) $oElement->getAttribute('rot')));
}
}
$oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('x')) {
- $oShape->setOffsetX(CommonDrawing::emuToPixels($oElement->getAttribute('x')));
+ $oShape->setOffsetX(CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
}
if ($oElement->hasAttribute('y')) {
- $oShape->setOffsetY(CommonDrawing::emuToPixels($oElement->getAttribute('y')));
+ $oShape->setOffsetY(CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
}
}
$oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('cx')) {
- $oShape->setWidth(CommonDrawing::emuToPixels($oElement->getAttribute('cx')));
+ $oShape->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
}
if ($oElement->hasAttribute('cy')) {
- $oShape->setHeight(CommonDrawing::emuToPixels($oElement->getAttribute('cy')));
+ $oShape->setHeight(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
}
}
$oElement = $document->getElement('p:spPr/a:effectLst', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
$oShape->getShadow()->setVisible(true);
$oSubElement = $document->getElement('a:outerShdw', $oElement);
- if ($oSubElement instanceof \DOMElement) {
+ if ($oSubElement instanceof DOMElement) {
if ($oSubElement->hasAttribute('blurRad')) {
- $oShape->getShadow()->setBlurRadius(CommonDrawing::emuToPixels($oSubElement->getAttribute('blurRad')));
+ $oShape->getShadow()->setBlurRadius(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('blurRad')));
}
if ($oSubElement->hasAttribute('dist')) {
- $oShape->getShadow()->setDistance(CommonDrawing::emuToPixels($oSubElement->getAttribute('dist')));
+ $oShape->getShadow()->setDistance(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('dist')));
}
if ($oSubElement->hasAttribute('dir')) {
- $oShape->getShadow()->setDirection(CommonDrawing::angleToDegrees($oSubElement->getAttribute('dir')));
+ $oShape->getShadow()->setDirection((int) CommonDrawing::angleToDegrees((int) $oSubElement->getAttribute('dir')));
}
if ($oSubElement->hasAttribute('algn')) {
$oShape->getShadow()->setAlignment($oSubElement->getAttribute('algn'));
@@ -823,7 +864,7 @@ class PowerPoint2007 implements ReaderInterface
}
$oSubElement = $document->getElement('a:outerShdw/a:srgbClr', $oElement);
- if ($oSubElement instanceof \DOMElement) {
+ if ($oSubElement instanceof DOMElement) {
if ($oSubElement->hasAttribute('val')) {
$oColor = new Color();
$oColor->setRGB($oSubElement->getAttribute('val'));
@@ -832,9 +873,9 @@ class PowerPoint2007 implements ReaderInterface
}
$oSubElement = $document->getElement('a:outerShdw/a:srgbClr/a:alpha', $oElement);
- if ($oSubElement instanceof \DOMElement) {
+ if ($oSubElement instanceof DOMElement) {
if ($oSubElement->hasAttribute('val')) {
- $oShape->getShadow()->setAlpha((int)$oSubElement->getAttribute('val') / 1000);
+ $oShape->getShadow()->setAlpha((int) $oSubElement->getAttribute('val') / 1000);
}
}
}
@@ -842,52 +883,46 @@ class PowerPoint2007 implements ReaderInterface
$oSlide->addShape($oShape);
}
- /**
- * @param XMLReader $document
- * @param \DOMElement $node
- * @param AbstractSlide $oSlide
- * @throws \Exception
- */
- protected function loadShapeRichText(XMLReader $document, \DOMElement $node, $oSlide)
+ protected function loadShapeRichText(XMLReader $document, DOMElement $node, $oSlide): void
{
- if (!$document->elementExists('p:txBody/a:p/a:r', $node)) {
+ if (!$document->elementExists('p:txBody/a:p/a:r', $node) || !$oSlide instanceof AbstractSlide) {
return;
}
// Core
$oShape = $oSlide->createRichTextShape();
- $oShape->setParagraphs(array());
+ $oShape->setParagraphs([]);
// Variables
if ($oSlide instanceof AbstractSlide) {
$this->fileRels = $oSlide->getRelsIndex();
}
$oElement = $document->getElement('p:spPr/a:xfrm', $node);
- if ($oElement instanceof \DOMElement && $oElement->hasAttribute('rot')) {
- $oShape->setRotation(CommonDrawing::angleToDegrees($oElement->getAttribute('rot')));
+ if ($oElement instanceof DOMElement && $oElement->hasAttribute('rot')) {
+ $oShape->setRotation((int) CommonDrawing::angleToDegrees((int) $oElement->getAttribute('rot')));
}
$oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('x')) {
- $oShape->setOffsetX(CommonDrawing::emuToPixels($oElement->getAttribute('x')));
+ $oShape->setOffsetX(CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
}
if ($oElement->hasAttribute('y')) {
- $oShape->setOffsetY(CommonDrawing::emuToPixels($oElement->getAttribute('y')));
+ $oShape->setOffsetY(CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
}
}
$oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('cx')) {
- $oShape->setWidth(CommonDrawing::emuToPixels($oElement->getAttribute('cx')));
+ $oShape->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
}
if ($oElement->hasAttribute('cy')) {
- $oShape->setHeight(CommonDrawing::emuToPixels($oElement->getAttribute('cy')));
+ $oShape->setHeight(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
}
}
$oElement = $document->getElement('p:nvSpPr/p:nvPr/p:ph', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('type')) {
$placeholder = new Placeholder($oElement->getAttribute('type'));
$oShape->setPlaceHolder($placeholder);
@@ -896,7 +931,9 @@ class PowerPoint2007 implements ReaderInterface
$arrayElements = $document->getElements('p:txBody/a:p', $node);
foreach ($arrayElements as $oElement) {
- $this->loadParagraph($document, $oElement, $oShape);
+ if ($oElement instanceof DOMElement) {
+ $this->loadParagraph($document, $oElement, $oShape);
+ }
}
if (count($oShape->getParagraphs()) > 0) {
@@ -904,20 +941,14 @@ class PowerPoint2007 implements ReaderInterface
}
}
- /**
- * @param XMLReader $document
- * @param \DOMElement $node
- * @param AbstractSlide $oSlide
- * @throws \Exception
- */
- protected function loadShapeTable(XMLReader $document, \DOMElement $node, AbstractSlide $oSlide)
+ protected function loadShapeTable(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
{
$this->fileRels = $oSlide->getRelsIndex();
$oShape = $oSlide->createTableShape();
$oElement = $document->getElement('p:cNvPr', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('name')) {
$oShape->setName($oElement->getAttribute('name'));
}
@@ -927,22 +958,22 @@ class PowerPoint2007 implements ReaderInterface
}
$oElement = $document->getElement('p:xfrm/a:off', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('x')) {
- $oShape->setOffsetX(CommonDrawing::emuToPixels($oElement->getAttribute('x')));
+ $oShape->setOffsetX(CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
}
if ($oElement->hasAttribute('y')) {
- $oShape->setOffsetY(CommonDrawing::emuToPixels($oElement->getAttribute('y')));
+ $oShape->setOffsetY(CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
}
}
$oElement = $document->getElement('p:xfrm/a:ext', $node);
- if ($oElement instanceof \DOMElement) {
+ if ($oElement instanceof DOMElement) {
if ($oElement->hasAttribute('cx')) {
- $oShape->setWidth(CommonDrawing::emuToPixels($oElement->getAttribute('cx')));
+ $oShape->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
}
if ($oElement->hasAttribute('cy')) {
- $oShape->setHeight(CommonDrawing::emuToPixels($oElement->getAttribute('cy')));
+ $oShape->setHeight(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
}
}
@@ -950,43 +981,46 @@ class PowerPoint2007 implements ReaderInterface
$oShape->setNumColumns($arrayElements->length);
$oShape->createRow();
foreach ($arrayElements as $key => $oElement) {
- if ($oElement instanceof \DOMElement && $oElement->getAttribute('w')) {
- $oShape->getRow(0)->getCell($key)->setWidth(CommonDrawing::emuToPixels($oElement->getAttribute('w')));
+ if ($oElement instanceof DOMElement && $oElement->getAttribute('w')) {
+ $oShape->getRow(0)->getCell($key)->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('w')));
}
}
$arrayElements = $document->getElements('a:graphic/a:graphicData/a:tbl/a:tr', $node);
foreach ($arrayElements as $keyRow => $oElementRow) {
- if (!($oElementRow instanceof \DOMElement)) {
+ if (!($oElementRow instanceof DOMElement)) {
continue;
}
- $oRow = $oShape->getRow($keyRow, true);
- if (is_null($oRow)) {
+ if ($oShape->hasRow($keyRow)) {
+ $oRow = $oShape->getRow($keyRow);
+ } else {
$oRow = $oShape->createRow();
}
if ($oElementRow->hasAttribute('h')) {
- $oRow->setHeight(CommonDrawing::emuToPixels($oElementRow->getAttribute('h')));
+ $oRow->setHeight(CommonDrawing::emuToPixels((int) $oElementRow->getAttribute('h')));
}
$arrayElementsCell = $document->getElements('a:tc', $oElementRow);
foreach ($arrayElementsCell as $keyCell => $oElementCell) {
- if (!($oElementCell instanceof \DOMElement)) {
+ if (!($oElementCell instanceof DOMElement)) {
continue;
}
$oCell = $oRow->getCell($keyCell);
- $oCell->setParagraphs(array());
+ $oCell->setParagraphs([]);
if ($oElementCell->hasAttribute('gridSpan')) {
- $oCell->setColSpan($oElementCell->getAttribute('gridSpan'));
+ $oCell->setColSpan((int) $oElementCell->getAttribute('gridSpan'));
}
if ($oElementCell->hasAttribute('rowSpan')) {
- $oCell->setRowSpan($oElementCell->getAttribute('rowSpan'));
+ $oCell->setRowSpan((int) $oElementCell->getAttribute('rowSpan'));
}
foreach ($document->getElements('a:txBody/a:p', $oElementCell) as $oElementPara) {
- $this->loadParagraph($document, $oElementPara, $oCell);
+ if ($oElementPara instanceof DOMElement) {
+ $this->loadParagraph($document, $oElementPara, $oCell);
+ }
}
$oElementTcPr = $document->getElement('a:tcPr', $oElementCell);
- if ($oElementTcPr instanceof \DOMElement) {
+ if ($oElementTcPr instanceof DOMElement) {
$numParagraphs = count($oCell->getParagraphs());
if ($numParagraphs > 0) {
if ($oElementTcPr->hasAttribute('vert')) {
@@ -996,16 +1030,16 @@ class PowerPoint2007 implements ReaderInterface
$oCell->getParagraph(0)->getAlignment()->setVertical($oElementTcPr->getAttribute('anchor'));
}
if ($oElementTcPr->hasAttribute('marB')) {
- $oCell->getParagraph(0)->getAlignment()->setMarginBottom($oElementTcPr->getAttribute('marB'));
+ $oCell->getParagraph(0)->getAlignment()->setMarginBottom((int) $oElementTcPr->getAttribute('marB'));
}
if ($oElementTcPr->hasAttribute('marL')) {
- $oCell->getParagraph(0)->getAlignment()->setMarginLeft($oElementTcPr->getAttribute('marL'));
+ $oCell->getParagraph(0)->getAlignment()->setMarginLeft((int) $oElementTcPr->getAttribute('marL'));
}
if ($oElementTcPr->hasAttribute('marR')) {
- $oCell->getParagraph(0)->getAlignment()->setMarginRight($oElementTcPr->getAttribute('marR'));
+ $oCell->getParagraph(0)->getAlignment()->setMarginRight((int) $oElementTcPr->getAttribute('marR'));
}
if ($oElementTcPr->hasAttribute('marT')) {
- $oCell->getParagraph(0)->getAlignment()->setMarginTop($oElementTcPr->getAttribute('marT'));
+ $oCell->getParagraph(0)->getAlignment()->setMarginTop((int) $oElementTcPr->getAttribute('marT'));
}
}
@@ -1016,27 +1050,27 @@ class PowerPoint2007 implements ReaderInterface
$oBorders = new Borders();
$oElementBorderL = $document->getElement('a:lnL', $oElementTcPr);
- if ($oElementBorderL instanceof \DOMElement) {
+ if ($oElementBorderL instanceof DOMElement) {
$this->loadStyleBorder($document, $oElementBorderL, $oBorders->getLeft());
}
$oElementBorderR = $document->getElement('a:lnR', $oElementTcPr);
- if ($oElementBorderR instanceof \DOMElement) {
+ if ($oElementBorderR instanceof DOMElement) {
$this->loadStyleBorder($document, $oElementBorderR, $oBorders->getRight());
}
$oElementBorderT = $document->getElement('a:lnT', $oElementTcPr);
- if ($oElementBorderT instanceof \DOMElement) {
+ if ($oElementBorderT instanceof DOMElement) {
$this->loadStyleBorder($document, $oElementBorderT, $oBorders->getTop());
}
$oElementBorderB = $document->getElement('a:lnB', $oElementTcPr);
- if ($oElementBorderB instanceof \DOMElement) {
+ if ($oElementBorderB instanceof DOMElement) {
$this->loadStyleBorder($document, $oElementBorderB, $oBorders->getBottom());
}
$oElementBorderDiagDown = $document->getElement('a:lnTlToBr', $oElementTcPr);
- if ($oElementBorderDiagDown instanceof \DOMElement) {
+ if ($oElementBorderDiagDown instanceof DOMElement) {
$this->loadStyleBorder($document, $oElementBorderDiagDown, $oBorders->getDiagonalDown());
}
$oElementBorderDiagUp = $document->getElement('a:lnBlToTr', $oElementTcPr);
- if ($oElementBorderDiagUp instanceof \DOMElement) {
+ if ($oElementBorderDiagUp instanceof DOMElement) {
$this->loadStyleBorder($document, $oElementBorderDiagUp, $oBorders->getDiagonalUp());
}
$oCell->setBorders($oBorders);
@@ -1046,19 +1080,16 @@ class PowerPoint2007 implements ReaderInterface
}
/**
- * @param XMLReader $document
- * @param \DOMElement $oElement
* @param Cell|RichText $oShape
- * @throws \Exception
*/
- protected function loadParagraph(XMLReader $document, \DOMElement $oElement, $oShape)
+ protected function loadParagraph(XMLReader $document, DOMElement $oElement, $oShape): void
{
// Core
$oParagraph = $oShape->createParagraph();
- $oParagraph->setRichTextElements(array());
+ $oParagraph->setRichTextElements([]);
$oSubElement = $document->getElement('a:pPr', $oElement);
- if ($oSubElement instanceof \DOMElement) {
+ if ($oSubElement instanceof DOMElement) {
if ($oSubElement->hasAttribute('algn')) {
$oParagraph->getAlignment()->setHorizontal($oSubElement->getAttribute('algn'));
}
@@ -1066,51 +1097,73 @@ class PowerPoint2007 implements ReaderInterface
$oParagraph->getAlignment()->setVertical($oSubElement->getAttribute('fontAlgn'));
}
if ($oSubElement->hasAttribute('marL')) {
- $oParagraph->getAlignment()->setMarginLeft(CommonDrawing::emuToPixels($oSubElement->getAttribute('marL')));
+ $oParagraph->getAlignment()->setMarginLeft(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('marL')));
}
if ($oSubElement->hasAttribute('marR')) {
- $oParagraph->getAlignment()->setMarginRight(CommonDrawing::emuToPixels($oSubElement->getAttribute('marR')));
+ $oParagraph->getAlignment()->setMarginRight(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('marR')));
}
if ($oSubElement->hasAttribute('indent')) {
- $oParagraph->getAlignment()->setIndent(CommonDrawing::emuToPixels($oSubElement->getAttribute('indent')));
+ $oParagraph->getAlignment()->setIndent(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('indent')));
}
if ($oSubElement->hasAttribute('lvl')) {
- $oParagraph->getAlignment()->setLevel($oSubElement->getAttribute('lvl'));
+ $oParagraph->getAlignment()->setLevel((int) $oSubElement->getAttribute('lvl'));
+ }
+ if ($oSubElement->hasAttribute('rtl')) {
+ $oParagraph->getAlignment()->setIsRTL((bool) $oSubElement->getAttribute('rtl'));
+ }
+
+ $oElementLineSpacingPoints = $document->getElement('a:lnSpc/a:spcPts', $oSubElement);
+ if ($oElementLineSpacingPoints instanceof DOMElement) {
+ $oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_POINT);
+ $oParagraph->setLineSpacing($oElementLineSpacingPoints->getAttribute('val') / 100);
+ }
+ $oElementLineSpacingPercent = $document->getElement('a:lnSpc/a:spcPct', $oSubElement);
+ if ($oElementLineSpacingPercent instanceof DOMElement) {
+ $oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_PERCENT);
+ $oParagraph->setLineSpacing($oElementLineSpacingPercent->getAttribute('val') / 1000);
+ }
+ $oElementSpacingBefore = $document->getElement('a:spcBef/a:spcPts', $oSubElement);
+ if ($oElementSpacingBefore instanceof DOMElement) {
+ $oParagraph->setSpacingBefore($oElementSpacingBefore->getAttribute('val') / 100);
+ }
+ $oElementSpacingAfter = $document->getElement('a:spcAft/a:spcPts', $oSubElement);
+ if ($oElementSpacingAfter instanceof DOMElement) {
+ $oParagraph->setSpacingAfter($oElementSpacingAfter->getAttribute('val') / 100);
}
$oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NONE);
$oElementBuFont = $document->getElement('a:buFont', $oSubElement);
- if ($oElementBuFont instanceof \DOMElement) {
+ if ($oElementBuFont instanceof DOMElement) {
if ($oElementBuFont->hasAttribute('typeface')) {
$oParagraph->getBulletStyle()->setBulletFont($oElementBuFont->getAttribute('typeface'));
}
}
$oElementBuChar = $document->getElement('a:buChar', $oSubElement);
- if ($oElementBuChar instanceof \DOMElement) {
+ if ($oElementBuChar instanceof DOMElement) {
$oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
if ($oElementBuChar->hasAttribute('char')) {
$oParagraph->getBulletStyle()->setBulletChar($oElementBuChar->getAttribute('char'));
}
}
$oElementBuAutoNum = $document->getElement('a:buAutoNum', $oSubElement);
- if ($oElementBuAutoNum instanceof \DOMElement) {
+ if ($oElementBuAutoNum instanceof DOMElement) {
$oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NUMERIC);
if ($oElementBuAutoNum->hasAttribute('type')) {
$oParagraph->getBulletStyle()->setBulletNumericStyle($oElementBuAutoNum->getAttribute('type'));
}
- if ($oElementBuAutoNum->hasAttribute('startAt') && $oElementBuAutoNum->getAttribute('startAt') != 1) {
+ if ($oElementBuAutoNum->hasAttribute('startAt') && 1 != $oElementBuAutoNum->getAttribute('startAt')) {
$oParagraph->getBulletStyle()->setBulletNumericStartAt($oElementBuAutoNum->getAttribute('startAt'));
}
}
$oElementBuClr = $document->getElement('a:buClr', $oSubElement);
- if ($oElementBuClr instanceof \DOMElement) {
+ if ($oElementBuClr instanceof DOMElement) {
$oColor = new Color();
/**
* @todo Create protected for reading Color
*/
$oElementColor = $document->getElement('a:srgbClr', $oElementBuClr);
- if ($oElementColor instanceof \DOMElement) {
+ if ($oElementColor instanceof DOMElement) {
$oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
}
$oParagraph->getBulletStyle()->setBulletColor($oColor);
@@ -1118,27 +1171,30 @@ class PowerPoint2007 implements ReaderInterface
}
$arraySubElements = $document->getElements('(a:r|a:br)', $oElement);
foreach ($arraySubElements as $oSubElement) {
- if ($oSubElement->tagName == 'a:br') {
+ if (!($oSubElement instanceof DOMElement)) {
+ continue;
+ }
+ if ('a:br' == $oSubElement->tagName) {
$oParagraph->createBreak();
}
- if ($oSubElement->tagName == 'a:r') {
+ if ('a:r' == $oSubElement->tagName) {
$oElementrPr = $document->getElement('a:rPr', $oSubElement);
if (is_object($oElementrPr)) {
$oText = $oParagraph->createTextRun();
if ($oElementrPr->hasAttribute('b')) {
$att = $oElementrPr->getAttribute('b');
- $oText->getFont()->setBold($att == 'true' || $att == '1' ? true : false);
+ $oText->getFont()->setBold('true' == $att || '1' == $att ? true : false);
}
if ($oElementrPr->hasAttribute('i')) {
$att = $oElementrPr->getAttribute('i');
- $oText->getFont()->setItalic($att == 'true' || $att == '1' ? true : false);
+ $oText->getFont()->setItalic('true' == $att || '1' == $att ? true : false);
}
if ($oElementrPr->hasAttribute('strike')) {
- $oText->getFont()->setStrikethrough($oElementrPr->getAttribute('strike') == 'noStrike' ? false : true);
+ $oText->getFont()->setStrikethrough('noStrike' == $oElementrPr->getAttribute('strike') ? false : true);
}
if ($oElementrPr->hasAttribute('sz')) {
- $oText->getFont()->setSize((int)($oElementrPr->getAttribute('sz') / 100));
+ $oText->getFont()->setSize((int) ($oElementrPr->getAttribute('sz') / 100));
}
if ($oElementrPr->hasAttribute('u')) {
$oText->getFont()->setUnderline($oElementrPr->getAttribute('u'));
@@ -1153,13 +1209,31 @@ class PowerPoint2007 implements ReaderInterface
// Hyperlink
$oElementHlinkClick = $document->getElement('a:hlinkClick', $oElementrPr);
if (is_object($oElementHlinkClick)) {
- if ($oElementHlinkClick->hasAttribute('tooltip')) {
- $oText->getHyperlink()->setTooltip($oElementHlinkClick->getAttribute('tooltip'));
- }
- if ($oElementHlinkClick->hasAttribute('r:id') && isset($this->arrayRels[$this->fileRels][$oElementHlinkClick->getAttribute('r:id')]['Target'])) {
- $oText->getHyperlink()->setUrl($this->arrayRels[$this->fileRels][$oElementHlinkClick->getAttribute('r:id')]['Target']);
- }
+ $oText->setHyperlink(
+ $this->loadHyperlink($document, $oElementHlinkClick, $oText->getHyperlink())
+ );
}
+ // Font
+ $oElementFontFormat = null;
+ $oElementFontFormatLatin = $document->getElement('a:latin', $oElementrPr);
+ if (is_object($oElementFontFormatLatin)) {
+ $oText->getFont()->setFormat(Font::FORMAT_LATIN);
+ $oElementFontFormat = $oElementFontFormatLatin;
+ }
+ $oElementFontFormatEastAsian = $document->getElement('a:ea', $oElementrPr);
+ if (is_object($oElementFontFormatEastAsian)) {
+ $oText->getFont()->setFormat(Font::FORMAT_EAST_ASIAN);
+ $oElementFontFormat = $oElementFontFormatEastAsian;
+ }
+ $oElementFontFormatComplexScript = $document->getElement('a:cs', $oElementrPr);
+ if (is_object($oElementFontFormatComplexScript)) {
+ $oText->getFont()->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
+ $oElementFontFormat = $oElementFontFormatComplexScript;
+ }
+ if (is_object($oElementFontFormat) && $oElementFontFormat->hasAttribute('typeface')) {
+ $oText->getFont()->setName($oElementFontFormat->getAttribute('typeface'));
+ }
+
//} else {
// $oText = $oParagraph->createText();
@@ -1170,13 +1244,24 @@ class PowerPoint2007 implements ReaderInterface
}
}
- /**
- * @param XMLReader $xmlReader
- * @param \DOMElement $oElement
- * @param Border $oBorder
- * @throws \Exception
- */
- protected function loadStyleBorder(XMLReader $xmlReader, \DOMElement $oElement, Border $oBorder)
+ protected function loadHyperlink(XMLReader $xmlReader, DOMElement $element, Hyperlink $hyperlink): Hyperlink
+ {
+ if ($element->hasAttribute('tooltip')) {
+ $hyperlink->setTooltip($element->getAttribute('tooltip'));
+ }
+ if ($element->hasAttribute('r:id') && isset($this->arrayRels[$this->fileRels][$element->getAttribute('r:id')]['Target'])) {
+ $hyperlink->setUrl($this->arrayRels[$this->fileRels][$element->getAttribute('r:id')]['Target']);
+ }
+ if ($subElementExt = $xmlReader->getElement('a:extLst/a:ext', $element)) {
+ if ($subElementExt->hasAttribute('uri') && $subElementExt->getAttribute('uri') == '{A12FA001-AC4F-418D-AE19-62706E023703}') {
+ $hyperlink->setIsTextColorUsed(true);
+ }
+ }
+
+ return $hyperlink;
+ }
+
+ protected function loadStyleBorder(XMLReader $xmlReader, DOMElement $oElement, Border $oBorder): void
{
if ($oElement->hasAttribute('w')) {
$oBorder->setLineWidth($oElement->getAttribute('w') / 12700);
@@ -1186,116 +1271,111 @@ class PowerPoint2007 implements ReaderInterface
}
$oElementNoFill = $xmlReader->getElement('a:noFill', $oElement);
- if ($oElementNoFill instanceof \DOMElement && $oBorder->getLineStyle() == Border::LINE_SINGLE) {
+ if ($oElementNoFill instanceof DOMElement && Border::LINE_SINGLE == $oBorder->getLineStyle()) {
$oBorder->setLineStyle(Border::LINE_NONE);
}
$oElementColor = $xmlReader->getElement('a:solidFill/a:srgbClr', $oElement);
- if ($oElementColor instanceof \DOMElement) {
+ if ($oElementColor instanceof DOMElement) {
$oBorder->setColor($this->loadStyleColor($xmlReader, $oElementColor));
}
$oElementDashStyle = $xmlReader->getElement('a:prstDash', $oElement);
- if ($oElementDashStyle instanceof \DOMElement && $oElementDashStyle->hasAttribute('val')) {
+ if ($oElementDashStyle instanceof DOMElement && $oElementDashStyle->hasAttribute('val')) {
$oBorder->setDashStyle($oElementDashStyle->getAttribute('val'));
}
}
- /**
- * @param XMLReader $xmlReader
- * @param \DOMElement $oElement
- * @return Color
- */
- protected function loadStyleColor(XMLReader $xmlReader, \DOMElement $oElement)
+ protected function loadStyleColor(XMLReader $xmlReader, DOMElement $oElement): Color
{
$oColor = new Color();
$oColor->setRGB($oElement->getAttribute('val'));
$oElementAlpha = $xmlReader->getElement('a:alpha', $oElement);
- if ($oElementAlpha instanceof \DOMElement && $oElementAlpha->hasAttribute('val')) {
+ if ($oElementAlpha instanceof DOMElement && $oElementAlpha->hasAttribute('val')) {
$alpha = strtoupper(dechex((($oElementAlpha->getAttribute('val') / 1000) / 100) * 255));
$oColor->setRGB($oElement->getAttribute('val'), $alpha);
}
+
return $oColor;
}
- /**
- * @param XMLReader $xmlReader
- * @param \DOMElement $oElement
- * @return null|Fill
- * @throws \Exception
- */
- protected function loadStyleFill(XMLReader $xmlReader, \DOMElement $oElement)
+ protected function loadStyleFill(XMLReader $xmlReader, DOMElement $oElement): ?Fill
{
// Gradient fill
$oElementFill = $xmlReader->getElement('a:gradFill', $oElement);
- if ($oElementFill instanceof \DOMElement) {
+ if ($oElementFill instanceof DOMElement) {
$oFill = new Fill();
$oFill->setFillType(Fill::FILL_GRADIENT_LINEAR);
$oElementColor = $xmlReader->getElement('a:gsLst/a:gs[@pos="0"]/a:srgbClr', $oElementFill);
- if ($oElementColor instanceof \DOMElement && $oElementColor->hasAttribute('val')) {
+ if ($oElementColor instanceof DOMElement && $oElementColor->hasAttribute('val')) {
$oFill->setStartColor($this->loadStyleColor($xmlReader, $oElementColor));
}
$oElementColor = $xmlReader->getElement('a:gsLst/a:gs[@pos="100000"]/a:srgbClr', $oElementFill);
- if ($oElementColor instanceof \DOMElement && $oElementColor->hasAttribute('val')) {
+ if ($oElementColor instanceof DOMElement && $oElementColor->hasAttribute('val')) {
$oFill->setEndColor($this->loadStyleColor($xmlReader, $oElementColor));
}
$oRotation = $xmlReader->getElement('a:lin', $oElementFill);
- if ($oRotation instanceof \DOMElement && $oRotation->hasAttribute('ang')) {
- $oFill->setRotation(CommonDrawing::angleToDegrees($oRotation->getAttribute('ang')));
+ if ($oRotation instanceof DOMElement && $oRotation->hasAttribute('ang')) {
+ $oFill->setRotation(CommonDrawing::angleToDegrees((int) $oRotation->getAttribute('ang')));
}
+
return $oFill;
}
// Solid fill
$oElementFill = $xmlReader->getElement('a:solidFill', $oElement);
- if ($oElementFill instanceof \DOMElement) {
+ if ($oElementFill instanceof DOMElement) {
$oFill = new Fill();
$oFill->setFillType(Fill::FILL_SOLID);
$oElementColor = $xmlReader->getElement('a:srgbClr', $oElementFill);
- if ($oElementColor instanceof \DOMElement) {
+ if ($oElementColor instanceof DOMElement) {
$oFill->setStartColor($this->loadStyleColor($xmlReader, $oElementColor));
}
+
return $oFill;
}
+
return null;
}
- /**
- * @param string $fileRels
- */
- protected function loadRels($fileRels)
+ protected function loadRels(string $fileRels): void
{
$sPart = $this->oZip->getFromName($fileRels);
- if ($sPart !== false) {
+ if (false !== $sPart) {
$xmlReader = new XMLReader();
+ /* @phpstan-ignore-next-line */
if ($xmlReader->getDomFromString($sPart)) {
foreach ($xmlReader->getElements('*') as $oNode) {
- if (!($oNode instanceof \DOMElement)) {
+ if (!($oNode instanceof DOMElement)) {
continue;
}
- $this->arrayRels[$fileRels][$oNode->getAttribute('Id')] = array(
+ $this->arrayRels[$fileRels][$oNode->getAttribute('Id')] = [
'Target' => $oNode->getAttribute('Target'),
'Type' => $oNode->getAttribute('Type'),
- );
+ ];
}
}
}
}
/**
- * @param $oSlide
- * @param \DOMNodeList $oElements
- * @param XMLReader $xmlReader
- * @throws \Exception
+ * @param AbstractSlide|Note $oSlide
+ * @param DOMNodeList $oElements
+ *
+ * @throws FeatureNotImplementedException
+ *
* @internal param $baseFile
*/
- protected function loadSlideShapes($oSlide, $oElements, $xmlReader)
+ protected function loadSlideShapes($oSlide, DOMNodeList $oElements, XMLReader $xmlReader): void
{
foreach ($oElements as $oNode) {
+ if (!($oNode instanceof DOMElement)) {
+ continue;
+ }
switch ($oNode->tagName) {
case 'p:graphicFrame':
$this->loadShapeTable($xmlReader, $oNode, $oSlide);
@@ -1307,7 +1387,7 @@ class PowerPoint2007 implements ReaderInterface
$this->loadShapeRichText($xmlReader, $oNode, $oSlide);
break;
default:
- //var_export($oNode->tagName);
+ //throw new FeatureNotImplementedException();
}
}
}
diff --git a/PhpOffice/PhpPresentation/Reader/PowerPoint97.php b/PhpOffice/PhpPresentation/Reader/PowerPoint97.php
old mode 100755
new mode 100644
index 385638c..b28b9fa
--- a/PhpOffice/PhpPresentation/Reader/PowerPoint97.php
+++ b/PhpOffice/PhpPresentation/Reader/PowerPoint97.php
@@ -10,17 +10,24 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Reader;
+use Exception;
use PhpOffice\Common\Microsoft\OLERead;
use PhpOffice\Common\Text;
-use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\AbstractShape;
+use PhpOffice\PhpPresentation\Exception\FeatureNotImplementedException;
+use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
+use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
+use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\Shape\Drawing;
use PhpOffice\PhpPresentation\Shape\Group;
@@ -28,350 +35,356 @@ use PhpOffice\PhpPresentation\Shape\Hyperlink;
use PhpOffice\PhpPresentation\Shape\Line;
use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Style\Alignment;
-use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Bullet;
+use PhpOffice\PhpPresentation\Style\Color;
+use PhpOffice\PhpPresentation\Style\Font;
/**
- * Serialized format reader
+ * Serialized format reader.
*/
class PowerPoint97 implements ReaderInterface
{
- const OFFICEARTBLIPEMF = 0xF01A;
- const OFFICEARTBLIPWMF = 0xF01B;
- const OFFICEARTBLIPPICT = 0xF01C;
- const OFFICEARTBLIPJPG = 0xF01D;
- const OFFICEARTBLIPPNG = 0xF01E;
- const OFFICEARTBLIPDIB = 0xF01F;
- const OFFICEARTBLIPTIFF = 0xF029;
- const OFFICEARTBLIPJPEG = 0xF02A;
+ public const OFFICEARTBLIPEMF = 0xF01A;
+ public const OFFICEARTBLIPWMF = 0xF01B;
+ public const OFFICEARTBLIPPICT = 0xF01C;
+ public const OFFICEARTBLIPJPG = 0xF01D;
+ public const OFFICEARTBLIPPNG = 0xF01E;
+ public const OFFICEARTBLIPDIB = 0xF01F;
+ public const OFFICEARTBLIPTIFF = 0xF029;
+ public const OFFICEARTBLIPJPEG = 0xF02A;
/**
- * @link http://msdn.microsoft.com/en-us/library/dd945336(v=office.12).aspx
+ * @see http://msdn.microsoft.com/en-us/library/dd945336(v=office.12).aspx
*/
- const RT_ANIMATIONINFO = 0x1014;
- const RT_ANIMATIONINFOATOM = 0x0FF1;
- const RT_BINARYTAGDATABLOB = 0x138B;
- const RT_BLIPCOLLECTION9 = 0x07F8;
- const RT_BLIPENTITY9ATOM = 0x07F9;
- const RT_BOOKMARKCOLLECTION = 0x07E3;
- const RT_BOOKMARKENTITYATOM = 0x0FD0;
- const RT_BOOKMARKSEEDATOM = 0x07E9;
- const RT_BROADCASTDOCINFO9 = 0x177E;
- const RT_BROADCASTDOCINFO9ATOM = 0x177F;
- const RT_BUILDATOM = 0x2B03;
- const RT_BUILDLIST = 0x2B02;
- const RT_CHARTBUILD = 0x2B04;
- const RT_CHARTBUILDATOM = 0x2B05;
- const RT_COLORSCHEMEATOM = 0x07F0;
- const RT_COMMENT10 = 0x2EE0;
- const RT_COMMENT10ATOM = 0x2EE1;
- const RT_COMMENTINDEX10 = 0x2EE4;
- const RT_COMMENTINDEX10ATOM = 0x2EE5;
- const RT_CRYPTSESSION10CONTAINER = 0x2F14;
- const RT_CURRENTUSERATOM = 0x0FF6;
- const RT_CSTRING = 0x0FBA;
- const RT_DATETIMEMETACHARATOM = 0x0FF7;
- const RT_DEFAULTRULERATOM = 0x0FAB;
- const RT_DOCROUTINGSLIPATOM = 0x0406;
- const RT_DIAGRAMBUILD = 0x2B06;
- const RT_DIAGRAMBUILDATOM = 0x2B07;
- const RT_DIFF10 = 0x2EED;
- const RT_DIFF10ATOM = 0x2EEE;
- const RT_DIFFTREE10 = 0x2EEC;
- const RT_DOCTOOLBARSTATES10ATOM = 0x36B1;
- const RT_DOCUMENT = 0x03E8;
- const RT_DOCUMENTATOM = 0x03E9;
- const RT_DRAWING = 0x040C;
- const RT_DRAWINGGROUP = 0x040B;
- const RT_ENDDOCUMENTATOM = 0x03EA;
- const RT_EXTERNALAVIMOVIE = 0x1006;
- const RT_EXTERNALCDAUDIO = 0x100E;
- const RT_EXTERNALCDAUDIOATOM = 0x1012;
- const RT_EXTERNALHYPERLINK = 0x0FD7;
- const RT_EXTERNALHYPERLINK9 = 0x0FE4;
- const RT_EXTERNALHYPERLINKATOM = 0x0FD3;
- const RT_EXTERNALHYPERLINKFLAGSATOM = 0x1018;
- const RT_EXTERNALMCIMOVIE = 0x1007;
- const RT_EXTERNALMEDIAATOM = 0x1004;
- const RT_EXTERNALMIDIAUDIO = 0x100D;
- const RT_EXTERNALOBJECTLIST = 0x0409;
- const RT_EXTERNALOBJECTLISTATOM = 0x040A;
- const RT_EXTERNALOBJECTREFATOM = 0x0BC1;
- const RT_EXTERNALOLECONTROL = 0x0FEE;
- const RT_EXTERNALOLECONTROLATOM = 0x0FFB;
- const RT_EXTERNALOLEEMBED = 0x0FCC;
- const RT_EXTERNALOLEEMBEDATOM = 0x0FCD;
- const RT_EXTERNALOLELINK = 0x0FCE;
- const RT_EXTERNALOLELINKATOM = 0x0FD1;
- const RT_EXTERNALOLEOBJECTATOM = 0x0FC3;
- const RT_EXTERNALOLEOBJECTSTG = 0x1011;
- const RT_EXTERNALVIDEO = 0x1005;
- const RT_EXTERNALWAVAUDIOEMBEDDED = 0x100F;
- const RT_EXTERNALWAVAUDIOEMBEDDEDATOM = 0x1013;
- const RT_EXTERNALWAVAUDIOLINK = 0x1010;
- const RT_ENVELOPEDATA9ATOM = 0x1785;
- const RT_ENVELOPEFLAGS9ATOM = 0x1784;
- const RT_ENVIRONMENT = 0x03F2;
- const RT_FONTCOLLECTION = 0x07D5;
- const RT_FONTCOLLECTION10 = 0x07D6;
- const RT_FONTEMBEDDATABLOB = 0x0FB8;
- const RT_FONTEMBEDFLAGS10ATOM = 0x32C8;
- const RT_FILTERPRIVACYFLAGS10ATOM = 0x36B0;
- const RT_FONTENTITYATOM = 0x0FB7;
- const RT_FOOTERMETACHARATOM = 0x0FFA;
- const RT_GENERICDATEMETACHARATOM = 0x0FF8;
- const RT_GRIDSPACING10ATOM = 0x040D;
- const RT_GUIDEATOM = 0x03FB;
- const RT_HANDOUT = 0x0FC9;
- const RT_HASHCODEATOM = 0x2B00;
- const RT_HEADERSFOOTERS = 0x0FD9;
- const RT_HEADERSFOOTERSATOM = 0x0FDA;
- const RT_HEADERMETACHARATOM = 0x0FF9;
- const RT_HTMLDOCINFO9ATOM = 0x177B;
- const RT_HTMLPUBLISHINFOATOM = 0x177C;
- const RT_HTMLPUBLISHINFO9 = 0x177D;
- const RT_INTERACTIVEINFO = 0x0FF2;
- const RT_INTERACTIVEINFOATOM = 0x0FF3;
- const RT_KINSOKU = 0x0FC8;
- const RT_KINSOKUATOM = 0x0FD2;
- const RT_LEVELINFOATOM = 0x2B0A;
- const RT_LINKEDSHAPE10ATOM = 0x2EE6;
- const RT_LINKEDSLIDE10ATOM = 0x2EE7;
- const RT_LIST = 0x07D0;
- const RT_MAINMASTER = 0x03F8;
- const RT_MASTERTEXTPROPATOM = 0x0FA2;
- const RT_METAFILE = 0x0FC1;
- const RT_NAMEDSHOW = 0x0411;
- const RT_NAMEDSHOWS = 0x0410;
- const RT_NAMEDSHOWSLIDESATOM = 0x0412;
- const RT_NORMALVIEWSETINFO9 = 0x0414;
- const RT_NORMALVIEWSETINFO9ATOM = 0x0415;
- const RT_NOTES= 0x03F0;
- const RT_NOTESATOM = 0x03F1;
- const RT_NOTESTEXTVIEWINFO9 = 0x0413;
- const RT_OUTLINETEXTPROPS9 = 0x0FAE;
- const RT_OUTLINETEXTPROPS10 = 0x0FB3;
- const RT_OUTLINETEXTPROPS11 = 0x0FB5;
- const RT_OUTLINETEXTPROPSHEADER9ATOM = 0x0FAF;
- const RT_OUTLINETEXTREFATOM = 0x0F9E;
- const RT_OUTLINEVIEWINFO = 0x0407;
- const RT_PERSISTDIRECTORYATOM = 0x1772;
- const RT_PARABUILD = 0x2B08;
- const RT_PARABUILDATOM = 0x2B09;
- const RT_PHOTOALBUMINFO10ATOM = 0x36B2;
- const RT_PLACEHOLDERATOM = 0x0BC3;
- const RT_PRESENTATIONADVISORFLAGS9ATOM = 0x177A;
- const RT_PRINTOPTIONSATOM = 0x1770;
- const RT_PROGBINARYTAG = 0x138A;
- const RT_PROGSTRINGTAG = 0x1389;
- const RT_PROGTAGS = 0x1388;
- const RT_RECOLORINFOATOM = 0x0FE7;
- const RT_RTFDATETIMEMETACHARATOM = 0x1015;
- const RT_ROUNDTRIPANIMATIONATOM12ATOM = 0x2B0B;
- const RT_ROUNDTRIPANIMATIONHASHATOM12ATOM = 0x2B0D;
- const RT_ROUNDTRIPCOLORMAPPING12ATOM = 0x040F;
- const RT_ROUNDTRIPCOMPOSITEMASTERID12ATOM = 0x041D;
- const RT_ROUNDTRIPCONTENTMASTERID12ATOM = 0x0422;
- const RT_ROUNDTRIPCONTENTMASTERINFO12ATOM = 0x041E;
- const RT_ROUNDTRIPCUSTOMTABLESTYLES12ATOM = 0x0428;
- const RT_ROUNDTRIPDOCFLAGS12ATOM = 0x0425;
- const RT_ROUNDTRIPHEADERFOOTERDEFAULTS12ATOM = 0x0424;
- const RT_ROUNDTRIPHFPLACEHOLDER12ATOM = 0x0420;
- const RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM = 0x0BDD;
- const RT_ROUNDTRIPNOTESMASTERTEXTSTYLES12ATOM = 0x0427;
- const RT_ROUNDTRIPOARTTEXTSTYLES12ATOM = 0x0423;
- const RT_ROUNDTRIPORIGINALMAINMASTERID12ATOM = 0x041C;
- const RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM = 0x0426;
- const RT_ROUNDTRIPSHAPEID12ATOM = 0x041F;
- const RT_ROUNDTRIPSLIDESYNCINFO12 = 0x3714;
- const RT_ROUNDTRIPSLIDESYNCINFOATOM12 = 0x3715;
- const RT_ROUNDTRIPTHEME12ATOM = 0x040E;
- const RT_SHAPEATOM = 0x0BDB;
- const RT_SHAPEFLAGS10ATOM = 0x0BDC;
- const RT_SLIDE = 0x03EE;
- const RT_SLIDEATOM = 0x03EF;
- const RT_SLIDEFLAGS10ATOM = 0x2EEA;
- const RT_SLIDELISTENTRY10ATOM = 0x2EF0;
- const RT_SLIDELISTTABLE10 = 0x2EF1;
- const RT_SLIDELISTWITHTEXT = 0x0FF0;
- const RT_SLIDELISTTABLESIZE10ATOM = 0x2EEF;
- const RT_SLIDENUMBERMETACHARATOM = 0x0FD8;
- const RT_SLIDEPERSISTATOM = 0x03F3;
- const RT_SLIDESHOWDOCINFOATOM = 0x0401;
- const RT_SLIDESHOWSLIDEINFOATOM = 0x03F9;
- const RT_SLIDETIME10ATOM = 0x2EEB;
- const RT_SLIDEVIEWINFO = 0x03FA;
- const RT_SLIDEVIEWINFOATOM = 0x03FE;
- const RT_SMARTTAGSTORE11CONTAINER = 0x36B3;
- const RT_SOUND = 0x07E6;
- const RT_SOUNDCOLLECTION = 0x07E4;
- const RT_SOUNDCOLLECTIONATOM = 0x07E5;
- const RT_SOUNDDATABLOB = 0x07E7;
- const RT_SORTERVIEWINFO = 0x0408;
- const RT_STYLETEXTPROPATOM = 0x0FA1;
- const RT_STYLETEXTPROP10ATOM = 0x0FB1;
- const RT_STYLETEXTPROP11ATOM = 0x0FB6;
- const RT_STYLETEXTPROP9ATOM = 0x0FAC;
- const RT_SUMMARY = 0x0402;
- const RT_TEXTBOOKMARKATOM = 0x0FA7;
- const RT_TEXTBYTESATOM = 0x0FA8;
- const RT_TEXTCHARFORMATEXCEPTIONATOM = 0x0FA4;
- const RT_TEXTCHARSATOM = 0x0FA0;
- const RT_TEXTDEFAULTS10ATOM = 0x0FB4;
- const RT_TEXTDEFAULTS9ATOM = 0x0FB0;
- const RT_TEXTHEADERATOM = 0x0F9F;
- const RT_TEXTINTERACTIVEINFOATOM = 0x0FDF;
- const RT_TEXTMASTERSTYLEATOM = 0x0FA3;
- const RT_TEXTMASTERSTYLE10ATOM = 0x0FB2;
- const RT_TEXTMASTERSTYLE9ATOM = 0x0FAD;
- const RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM = 0x0FA5;
- const RT_TEXTRULERATOM = 0x0FA6;
- const RT_TEXTSPECIALINFOATOM = 0x0FAA;
- const RT_TEXTSPECIALINFODEFAULTATOM = 0x0FA9;
- const RT_TIMEANIMATEBEHAVIOR = 0xF134;
- const RT_TIMEANIMATEBEHAVIORCONTAINER = 0xF12B;
- const RT_TIMEANIMATIONVALUE = 0xF143;
- const RT_TIMEANIMATIONVALUELIST = 0xF13F;
- const RT_TIMEBEHAVIOR = 0xF133;
- const RT_TIMEBEHAVIORCONTAINER = 0xF12A;
- const RT_TIMECOLORBEHAVIOR = 0xF135;
- const RT_TIMECOLORBEHAVIORCONTAINER = 0xF12C;
- const RT_TIMECLIENTVISUALELEMENT = 0xF13C;
- const RT_TIMECOMMANDBEHAVIOR = 0xF13B;
- const RT_TIMECOMMANDBEHAVIORCONTAINER = 0xF132;
- const RT_TIMECONDITION = 0xF128;
- const RT_TIMECONDITIONCONTAINER = 0xF125;
- const RT_TIMEEFFECTBEHAVIOR = 0xF136;
- const RT_TIMEEFFECTBEHAVIORCONTAINER = 0xF12D;
- const RT_TIMEEXTTIMENODECONTAINER = 0xF144;
- const RT_TIMEITERATEDATA = 0xF140;
- const RT_TIMEMODIFIER = 0xF129;
- const RT_TIMEMOTIONBEHAVIOR = 0xF137;
- const RT_TIMEMOTIONBEHAVIORCONTAINER = 0xF12E;
- const RT_TIMENODE = 0xF127;
- const RT_TIMEPROPERTYLIST = 0xF13D;
- const RT_TIMEROTATIONBEHAVIOR = 0xF138;
- const RT_TIMEROTATIONBEHAVIORCONTAINER = 0xF12F;
- const RT_TIMESCALEBEHAVIOR = 0xF139;
- const RT_TIMESCALEBEHAVIORCONTAINER = 0xF130;
- const RT_TIMESEQUENCEDATA = 0xF141;
- const RT_TIMESETBEHAVIOR = 0xF13A;
- const RT_TIMESETBEHAVIORCONTAINER = 0xF131;
- const RT_TIMESUBEFFECTCONTAINER = 0xF145;
- const RT_TIMEVARIANT = 0xF142;
- const RT_TIMEVARIANTLIST = 0xF13E;
- const RT_USEREDITATOM = 0x0FF5;
- const RT_VBAINFO = 0x03FF;
- const RT_VBAINFOATOM = 0x0400;
- const RT_VIEWINFOATOM = 0x03FD;
- const RT_VISUALPAGEATOM = 0x2B01;
- const RT_VISUALSHAPEATOM = 0x2AFB;
+ public const RT_ANIMATIONINFO = 0x1014;
+ public const RT_ANIMATIONINFOATOM = 0x0FF1;
+ public const RT_BINARYTAGDATABLOB = 0x138B;
+ public const RT_BLIPCOLLECTION9 = 0x07F8;
+ public const RT_BLIPENTITY9ATOM = 0x07F9;
+ public const RT_BOOKMARKCOLLECTION = 0x07E3;
+ public const RT_BOOKMARKENTITYATOM = 0x0FD0;
+ public const RT_BOOKMARKSEEDATOM = 0x07E9;
+ public const RT_BROADCASTDOCINFO9 = 0x177E;
+ public const RT_BROADCASTDOCINFO9ATOM = 0x177F;
+ public const RT_BUILDATOM = 0x2B03;
+ public const RT_BUILDLIST = 0x2B02;
+ public const RT_CHARTBUILD = 0x2B04;
+ public const RT_CHARTBUILDATOM = 0x2B05;
+ public const RT_COLORSCHEMEATOM = 0x07F0;
+ public const RT_COMMENT10 = 0x2EE0;
+ public const RT_COMMENT10ATOM = 0x2EE1;
+ public const RT_COMMENTINDEX10 = 0x2EE4;
+ public const RT_COMMENTINDEX10ATOM = 0x2EE5;
+ public const RT_CRYPTSESSION10CONTAINER = 0x2F14;
+ public const RT_CURRENTUSERATOM = 0x0FF6;
+ public const RT_CSTRING = 0x0FBA;
+ public const RT_DATETIMEMETACHARATOM = 0x0FF7;
+ public const RT_DEFAULTRULERATOM = 0x0FAB;
+ public const RT_DOCROUTINGSLIPATOM = 0x0406;
+ public const RT_DIAGRAMBUILD = 0x2B06;
+ public const RT_DIAGRAMBUILDATOM = 0x2B07;
+ public const RT_DIFF10 = 0x2EED;
+ public const RT_DIFF10ATOM = 0x2EEE;
+ public const RT_DIFFTREE10 = 0x2EEC;
+ public const RT_DOCTOOLBARSTATES10ATOM = 0x36B1;
+ public const RT_DOCUMENT = 0x03E8;
+ public const RT_DOCUMENTATOM = 0x03E9;
+ public const RT_DRAWING = 0x040C;
+ public const RT_DRAWINGGROUP = 0x040B;
+ public const RT_ENDDOCUMENTATOM = 0x03EA;
+ public const RT_EXTERNALAVIMOVIE = 0x1006;
+ public const RT_EXTERNALCDAUDIO = 0x100E;
+ public const RT_EXTERNALCDAUDIOATOM = 0x1012;
+ public const RT_EXTERNALHYPERLINK = 0x0FD7;
+ public const RT_EXTERNALHYPERLINK9 = 0x0FE4;
+ public const RT_EXTERNALHYPERLINKATOM = 0x0FD3;
+ public const RT_EXTERNALHYPERLINKFLAGSATOM = 0x1018;
+ public const RT_EXTERNALMCIMOVIE = 0x1007;
+ public const RT_EXTERNALMEDIAATOM = 0x1004;
+ public const RT_EXTERNALMIDIAUDIO = 0x100D;
+ public const RT_EXTERNALOBJECTLIST = 0x0409;
+ public const RT_EXTERNALOBJECTLISTATOM = 0x040A;
+ public const RT_EXTERNALOBJECTREFATOM = 0x0BC1;
+ public const RT_EXTERNALOLECONTROL = 0x0FEE;
+ public const RT_EXTERNALOLECONTROLATOM = 0x0FFB;
+ public const RT_EXTERNALOLEEMBED = 0x0FCC;
+ public const RT_EXTERNALOLEEMBEDATOM = 0x0FCD;
+ public const RT_EXTERNALOLELINK = 0x0FCE;
+ public const RT_EXTERNALOLELINKATOM = 0x0FD1;
+ public const RT_EXTERNALOLEOBJECTATOM = 0x0FC3;
+ public const RT_EXTERNALOLEOBJECTSTG = 0x1011;
+ public const RT_EXTERNALVIDEO = 0x1005;
+ public const RT_EXTERNALWAVAUDIOEMBEDDED = 0x100F;
+ public const RT_EXTERNALWAVAUDIOEMBEDDEDATOM = 0x1013;
+ public const RT_EXTERNALWAVAUDIOLINK = 0x1010;
+ public const RT_ENVELOPEDATA9ATOM = 0x1785;
+ public const RT_ENVELOPEFLAGS9ATOM = 0x1784;
+ public const RT_ENVIRONMENT = 0x03F2;
+ public const RT_FONTCOLLECTION = 0x07D5;
+ public const RT_FONTCOLLECTION10 = 0x07D6;
+ public const RT_FONTEMBEDDATABLOB = 0x0FB8;
+ public const RT_FONTEMBEDFLAGS10ATOM = 0x32C8;
+ public const RT_FILTERPRIVACYFLAGS10ATOM = 0x36B0;
+ public const RT_FONTENTITYATOM = 0x0FB7;
+ public const RT_FOOTERMETACHARATOM = 0x0FFA;
+ public const RT_GENERICDATEMETACHARATOM = 0x0FF8;
+ public const RT_GRIDSPACING10ATOM = 0x040D;
+ public const RT_GUIDEATOM = 0x03FB;
+ public const RT_HANDOUT = 0x0FC9;
+ public const RT_HASHCODEATOM = 0x2B00;
+ public const RT_HEADERSFOOTERS = 0x0FD9;
+ public const RT_HEADERSFOOTERSATOM = 0x0FDA;
+ public const RT_HEADERMETACHARATOM = 0x0FF9;
+ public const RT_HTMLDOCINFO9ATOM = 0x177B;
+ public const RT_HTMLPUBLISHINFOATOM = 0x177C;
+ public const RT_HTMLPUBLISHINFO9 = 0x177D;
+ public const RT_INTERACTIVEINFO = 0x0FF2;
+ public const RT_INTERACTIVEINFOATOM = 0x0FF3;
+ public const RT_KINSOKU = 0x0FC8;
+ public const RT_KINSOKUATOM = 0x0FD2;
+ public const RT_LEVELINFOATOM = 0x2B0A;
+ public const RT_LINKEDSHAPE10ATOM = 0x2EE6;
+ public const RT_LINKEDSLIDE10ATOM = 0x2EE7;
+ public const RT_LIST = 0x07D0;
+ public const RT_MAINMASTER = 0x03F8;
+ public const RT_MASTERTEXTPROPATOM = 0x0FA2;
+ public const RT_METAFILE = 0x0FC1;
+ public const RT_NAMEDSHOW = 0x0411;
+ public const RT_NAMEDSHOWS = 0x0410;
+ public const RT_NAMEDSHOWSLIDESATOM = 0x0412;
+ public const RT_NORMALVIEWSETINFO9 = 0x0414;
+ public const RT_NORMALVIEWSETINFO9ATOM = 0x0415;
+ public const RT_NOTES = 0x03F0;
+ public const RT_NOTESATOM = 0x03F1;
+ public const RT_NOTESTEXTVIEWINFO9 = 0x0413;
+ public const RT_OUTLINETEXTPROPS9 = 0x0FAE;
+ public const RT_OUTLINETEXTPROPS10 = 0x0FB3;
+ public const RT_OUTLINETEXTPROPS11 = 0x0FB5;
+ public const RT_OUTLINETEXTPROPSHEADER9ATOM = 0x0FAF;
+ public const RT_OUTLINETEXTREFATOM = 0x0F9E;
+ public const RT_OUTLINEVIEWINFO = 0x0407;
+ public const RT_PERSISTDIRECTORYATOM = 0x1772;
+ public const RT_PARABUILD = 0x2B08;
+ public const RT_PARABUILDATOM = 0x2B09;
+ public const RT_PHOTOALBUMINFO10ATOM = 0x36B2;
+ public const RT_PLACEHOLDERATOM = 0x0BC3;
+ public const RT_PRESENTATIONADVISORFLAGS9ATOM = 0x177A;
+ public const RT_PRINTOPTIONSATOM = 0x1770;
+ public const RT_PROGBINARYTAG = 0x138A;
+ public const RT_PROGSTRINGTAG = 0x1389;
+ public const RT_PROGTAGS = 0x1388;
+ public const RT_RECOLORINFOATOM = 0x0FE7;
+ public const RT_RTFDATETIMEMETACHARATOM = 0x1015;
+ public const RT_ROUNDTRIPANIMATIONATOM12ATOM = 0x2B0B;
+ public const RT_ROUNDTRIPANIMATIONHASHATOM12ATOM = 0x2B0D;
+ public const RT_ROUNDTRIPCOLORMAPPING12ATOM = 0x040F;
+ public const RT_ROUNDTRIPCOMPOSITEMASTERID12ATOM = 0x041D;
+ public const RT_ROUNDTRIPCONTENTMASTERID12ATOM = 0x0422;
+ public const RT_ROUNDTRIPCONTENTMASTERINFO12ATOM = 0x041E;
+ public const RT_ROUNDTRIPCUSTOMTABLESTYLES12ATOM = 0x0428;
+ public const RT_ROUNDTRIPDOCFLAGS12ATOM = 0x0425;
+ public const RT_ROUNDTRIPHEADERFOOTERDEFAULTS12ATOM = 0x0424;
+ public const RT_ROUNDTRIPHFPLACEHOLDER12ATOM = 0x0420;
+ public const RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM = 0x0BDD;
+ public const RT_ROUNDTRIPNOTESMASTERTEXTSTYLES12ATOM = 0x0427;
+ public const RT_ROUNDTRIPOARTTEXTSTYLES12ATOM = 0x0423;
+ public const RT_ROUNDTRIPORIGINALMAINMASTERID12ATOM = 0x041C;
+ public const RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM = 0x0426;
+ public const RT_ROUNDTRIPSHAPEID12ATOM = 0x041F;
+ public const RT_ROUNDTRIPSLIDESYNCINFO12 = 0x3714;
+ public const RT_ROUNDTRIPSLIDESYNCINFOATOM12 = 0x3715;
+ public const RT_ROUNDTRIPTHEME12ATOM = 0x040E;
+ public const RT_SHAPEATOM = 0x0BDB;
+ public const RT_SHAPEFLAGS10ATOM = 0x0BDC;
+ public const RT_SLIDE = 0x03EE;
+ public const RT_SLIDEATOM = 0x03EF;
+ public const RT_SLIDEFLAGS10ATOM = 0x2EEA;
+ public const RT_SLIDELISTENTRY10ATOM = 0x2EF0;
+ public const RT_SLIDELISTTABLE10 = 0x2EF1;
+ public const RT_SLIDELISTWITHTEXT = 0x0FF0;
+ public const RT_SLIDELISTTABLESIZE10ATOM = 0x2EEF;
+ public const RT_SLIDENUMBERMETACHARATOM = 0x0FD8;
+ public const RT_SLIDEPERSISTATOM = 0x03F3;
+ public const RT_SLIDESHOWDOCINFOATOM = 0x0401;
+ public const RT_SLIDESHOWSLIDEINFOATOM = 0x03F9;
+ public const RT_SLIDETIME10ATOM = 0x2EEB;
+ public const RT_SLIDEVIEWINFO = 0x03FA;
+ public const RT_SLIDEVIEWINFOATOM = 0x03FE;
+ public const RT_SMARTTAGSTORE11CONTAINER = 0x36B3;
+ public const RT_SOUND = 0x07E6;
+ public const RT_SOUNDCOLLECTION = 0x07E4;
+ public const RT_SOUNDCOLLECTIONATOM = 0x07E5;
+ public const RT_SOUNDDATABLOB = 0x07E7;
+ public const RT_SORTERVIEWINFO = 0x0408;
+ public const RT_STYLETEXTPROPATOM = 0x0FA1;
+ public const RT_STYLETEXTPROP10ATOM = 0x0FB1;
+ public const RT_STYLETEXTPROP11ATOM = 0x0FB6;
+ public const RT_STYLETEXTPROP9ATOM = 0x0FAC;
+ public const RT_SUMMARY = 0x0402;
+ public const RT_TEXTBOOKMARKATOM = 0x0FA7;
+ public const RT_TEXTBYTESATOM = 0x0FA8;
+ public const RT_TEXTCHARFORMATEXCEPTIONATOM = 0x0FA4;
+ public const RT_TEXTCHARSATOM = 0x0FA0;
+ public const RT_TEXTDEFAULTS10ATOM = 0x0FB4;
+ public const RT_TEXTDEFAULTS9ATOM = 0x0FB0;
+ public const RT_TEXTHEADERATOM = 0x0F9F;
+ public const RT_TEXTINTERACTIVEINFOATOM = 0x0FDF;
+ public const RT_TEXTMASTERSTYLEATOM = 0x0FA3;
+ public const RT_TEXTMASTERSTYLE10ATOM = 0x0FB2;
+ public const RT_TEXTMASTERSTYLE9ATOM = 0x0FAD;
+ public const RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM = 0x0FA5;
+ public const RT_TEXTRULERATOM = 0x0FA6;
+ public const RT_TEXTSPECIALINFOATOM = 0x0FAA;
+ public const RT_TEXTSPECIALINFODEFAULTATOM = 0x0FA9;
+ public const RT_TIMEANIMATEBEHAVIOR = 0xF134;
+ public const RT_TIMEANIMATEBEHAVIORCONTAINER = 0xF12B;
+ public const RT_TIMEANIMATIONVALUE = 0xF143;
+ public const RT_TIMEANIMATIONVALUELIST = 0xF13F;
+ public const RT_TIMEBEHAVIOR = 0xF133;
+ public const RT_TIMEBEHAVIORCONTAINER = 0xF12A;
+ public const RT_TIMECOLORBEHAVIOR = 0xF135;
+ public const RT_TIMECOLORBEHAVIORCONTAINER = 0xF12C;
+ public const RT_TIMECLIENTVISUALELEMENT = 0xF13C;
+ public const RT_TIMECOMMANDBEHAVIOR = 0xF13B;
+ public const RT_TIMECOMMANDBEHAVIORCONTAINER = 0xF132;
+ public const RT_TIMECONDITION = 0xF128;
+ public const RT_TIMECONDITIONCONTAINER = 0xF125;
+ public const RT_TIMEEFFECTBEHAVIOR = 0xF136;
+ public const RT_TIMEEFFECTBEHAVIORCONTAINER = 0xF12D;
+ public const RT_TIMEEXTTIMENODECONTAINER = 0xF144;
+ public const RT_TIMEITERATEDATA = 0xF140;
+ public const RT_TIMEMODIFIER = 0xF129;
+ public const RT_TIMEMOTIONBEHAVIOR = 0xF137;
+ public const RT_TIMEMOTIONBEHAVIORCONTAINER = 0xF12E;
+ public const RT_TIMENODE = 0xF127;
+ public const RT_TIMEPROPERTYLIST = 0xF13D;
+ public const RT_TIMEROTATIONBEHAVIOR = 0xF138;
+ public const RT_TIMEROTATIONBEHAVIORCONTAINER = 0xF12F;
+ public const RT_TIMESCALEBEHAVIOR = 0xF139;
+ public const RT_TIMESCALEBEHAVIORCONTAINER = 0xF130;
+ public const RT_TIMESEQUENCEDATA = 0xF141;
+ public const RT_TIMESETBEHAVIOR = 0xF13A;
+ public const RT_TIMESETBEHAVIORCONTAINER = 0xF131;
+ public const RT_TIMESUBEFFECTCONTAINER = 0xF145;
+ public const RT_TIMEVARIANT = 0xF142;
+ public const RT_TIMEVARIANTLIST = 0xF13E;
+ public const RT_USEREDITATOM = 0x0FF5;
+ public const RT_VBAINFO = 0x03FF;
+ public const RT_VBAINFOATOM = 0x0400;
+ public const RT_VIEWINFOATOM = 0x03FD;
+ public const RT_VISUALPAGEATOM = 0x2B01;
+ public const RT_VISUALSHAPEATOM = 0x2AFB;
/**
- * @var http://msdn.microsoft.com/en-us/library/dd926394(v=office.12).aspx
+ * @see http://msdn.microsoft.com/en-us/library/dd926394(v=office.12).aspx
*/
- const SL_BIGOBJECT = 0x0000000F;
- const SL_BLANK = 0x00000010;
- const SL_COLUMNTWOROWS = 0x0000000A;
- const SL_FOUROBJECTS = 0x0000000E;
- const SL_MASTERTITLE = 0x00000002;
- const SL_TITLEBODY = 0x00000001;
- const SL_TITLEONLY = 0x00000007;
- const SL_TITLESLIDE = 0x00000000;
- const SL_TWOCOLUMNS = 0x00000008;
- const SL_TWOCOLUMNSROW = 0x0000000D;
- const SL_TWOROWS = 0x00000009;
- const SL_TWOROWSCOLUMN = 0x0000000B;
- const SL_VERTICALTITLEBODY = 0x00000011;
- const SL_VERTICALTWOROWS = 0x00000012;
+ public const SL_BIGOBJECT = 0x0000000F;
+ public const SL_BLANK = 0x00000010;
+ public const SL_COLUMNTWOROWS = 0x0000000A;
+ public const SL_FOUROBJECTS = 0x0000000E;
+ public const SL_MASTERTITLE = 0x00000002;
+ public const SL_TITLEBODY = 0x00000001;
+ public const SL_TITLEONLY = 0x00000007;
+ public const SL_TITLESLIDE = 0x00000000;
+ public const SL_TWOCOLUMNS = 0x00000008;
+ public const SL_TWOCOLUMNSROW = 0x0000000D;
+ public const SL_TWOROWS = 0x00000009;
+ public const SL_TWOROWSCOLUMN = 0x0000000B;
+ public const SL_VERTICALTITLEBODY = 0x00000011;
+ public const SL_VERTICALTWOROWS = 0x00000012;
/**
- * Array with Fonts
+ * Array with Fonts.
+ *
+ * @var array
*/
- private $arrayFonts = array();
+ private $arrayFonts = [];
/**
- * Array with Hyperlinks
+ * Array with Hyperlinks.
+ *
+ * @var array>
*/
- private $arrayHyperlinks = array();
+ private $arrayHyperlinks = [];
/**
- * Array with Notes
+ * Array with Notes.
+ *
+ * @var array
*/
- private $arrayNotes = array();
+ private $arrayNotes = [];
/**
- * Array with Pictures
+ * Array with Pictures.
+ *
+ * @var array
*/
- private $arrayPictures = array();
+ private $arrayPictures = [];
/**
* Offset (in bytes) from the beginning of the PowerPoint Document Stream to the UserEditAtom record for the most recent user edit.
+ *
* @var int
*/
private $offsetToCurrentEdit;
/**
* A structure that specifies a compressed table of sequential persist object identifiers and stream offsets to associated persist objects.
- * @var int[]
+ *
+ * @var array
*/
private $rgPersistDirEntry;
/**
- * Offset (in bytes) from the beginning of the PowerPoint Document Stream to the PersistDirectoryAtom record for this user edit
+ * Offset (in bytes) from the beginning of the PowerPoint Document Stream to the PersistDirectoryAtom record for this user edit.
+ *
* @var int
*/
private $offsetPersistDirectory;
/**
- * Output Object
+ * Output Object.
+ *
* @var PhpPresentation
*/
private $oPhpPresentation;
/**
- * Group Object
- * @var Group
+ * @var Group|null
*/
private $oCurrentGroup;
/**
- * @var boolean
+ * @var bool
*/
private $bFirstShapeGroup = false;
/**
- * Stream "Powerpoint Document"
+ * Stream "Powerpoint Document".
+ *
* @var string
*/
private $streamPowerpointDocument;
/**
- * Stream "Current User"
+ * Stream "Current User".
+ *
* @var string
*/
private $streamCurrentUser;
/**
- * Stream "Summary Information"
- * @var string
- */
- private $streamSummaryInformation;
- /**
- * Stream "Document Summary Information"
- * @var string
- */
- private $streamDocumentSummaryInformation;
- /**
- * Stream "Pictures"
+ * Stream "Pictures".
+ *
* @var string
*/
private $streamPictures;
/**
- * @var integer
+ * @var int
*/
private $inMainType;
/**
- * @var integer
+ * @var int|null
*/
private $currentNote;
/**
- * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
- *
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
+ * @var string|null
*/
- public function canRead($pFilename)
+ private $filename;
+
+ /**
+ * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
+ */
+ public function canRead(string $pFilename): bool
{
return $this->fileSupportsUnserializePhpPresentation($pFilename);
}
@@ -379,15 +392,13 @@ class PowerPoint97 implements ReaderInterface
/**
* Does a file support UnserializePhpPresentation ?
*
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
+ * @throws FileNotFoundException
*/
- public function fileSupportsUnserializePhpPresentation($pFilename = '')
+ public function fileSupportsUnserializePhpPresentation(string $pFilename = ''): bool
{
// Check if file exists
if (!file_exists($pFilename)) {
- throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ throw new FileNotFoundException($pFilename);
}
try {
@@ -395,43 +406,40 @@ class PowerPoint97 implements ReaderInterface
$ole = new OLERead();
// get excel data
$ole->read($pFilename);
+
return true;
- } catch (\Exception $e) {
+ } catch (Exception $e) {
return false;
}
}
/**
- * Loads PhpPresentation Serialized file
+ * Loads PhpPresentation Serialized file.
*
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * @throws InvalidFileFormatException
*/
- public function load($pFilename)
+ public function load(string $pFilename): PhpPresentation
{
// Unserialize... First make sure the file supports it!
if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
- throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\PowerPoint97: " . $pFilename . ".");
+ throw new InvalidFileFormatException($pFilename, PowerPoint97::class);
}
- return $this->loadFile($pFilename);
+ $this->filename = $pFilename;
+
+ return $this->loadFile();
}
/**
* Load PhpPresentation Serialized file
- *
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
*/
- private function loadFile($pFilename)
+ private function loadFile(): PhpPresentation
{
$this->oPhpPresentation = new PhpPresentation();
$this->oPhpPresentation->removeSlideByIndex();
// Read OLE Blocks
- $this->loadOLE($pFilename);
+ $this->loadOLE();
// Read pictures in the Pictures Stream
$this->loadPicturesStream();
// Read information in the Current User Stream
@@ -444,14 +452,12 @@ class PowerPoint97 implements ReaderInterface
/**
* Read OLE Part
- * @param string $pFilename
- * @throws \Exception
*/
- private function loadOLE($pFilename)
+ private function loadOLE(): void
{
// OLE reader
$oOLE = new OLERead();
- $oOLE->read($pFilename);
+ $oOLE->read($this->filename);
// PowerPoint Document Stream
$this->streamPowerpointDocument = $oOLE->getStream($oOLE->powerpointDocument);
@@ -459,21 +465,18 @@ class PowerPoint97 implements ReaderInterface
// Current User Stream
$this->streamCurrentUser = $oOLE->getStream($oOLE->currentUser);
- // Get summary information data
- $this->streamSummaryInformation = $oOLE->getStream($oOLE->summaryInformation);
-
- // Get additional document summary information data
- $this->streamDocumentSummaryInformation = $oOLE->getStream($oOLE->docSummaryInfos);
-
// Get pictures data
$this->streamPictures = $oOLE->getStream($oOLE->pictures);
}
/**
- * Stream Pictures
- * @link http://msdn.microsoft.com/en-us/library/dd920746(v=office.12).aspx
+ * Stream Pictures.
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd920746(v=office.12).aspx
*/
- private function loadPicturesStream()
+ private function loadPicturesStream(): void
{
$stream = $this->streamPictures;
@@ -482,11 +485,11 @@ class PowerPoint97 implements ReaderInterface
$arrayRH = $this->loadRecordHeader($stream, $pos);
$pos += 8;
$readSuccess = false;
- if ($arrayRH['recVer'] == 0x00 && ($arrayRH['recType'] == 0xF007 || ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117))) {
+ if (0x00 == $arrayRH['recVer'] && (0xF007 == $arrayRH['recType'] || ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117))) {
//@link : http://msdn.microsoft.com/en-us/library/dd950560(v=office.12).aspx
- if ($arrayRH['recType'] == 0xF007) {
+ if (0xF007 == $arrayRH['recType']) {
// OfficeArtFBSE
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ throw new FeatureNotImplementedException();
}
if ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117) {
$arrayRecord = $this->readRecordOfficeArtBlip($stream, $pos - 8);
@@ -497,39 +500,44 @@ class PowerPoint97 implements ReaderInterface
}
$readSuccess = true;
}
- } while ($readSuccess === true);
+ } while (true === $readSuccess);
}
/**
- * Stream Current User
- * @link http://msdn.microsoft.com/en-us/library/dd908567(v=office.12).aspx
+ * Stream Current User.
+ *
+ * @throws FeatureNotImplementedException
+ * @throws InvalidFileFormatException
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd908567(v=office.12).aspx
*/
- private function loadCurrentUserStream()
+ private function loadCurrentUserStream(): void
{
$pos = 0;
/**
- * CurrentUserAtom : http://msdn.microsoft.com/en-us/library/dd948895(v=office.12).aspx
+ * CurrentUserAtom : http://msdn.microsoft.com/en-us/library/dd948895(v=office.12).aspx.
*/
// RecordHeader : http://msdn.microsoft.com/en-us/library/dd926377(v=office.12).aspx
$rHeader = $this->loadRecordHeader($this->streamCurrentUser, $pos);
$pos += 8;
- if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_CURRENTUSERATOM) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > RecordHeader).');
+ if (0x0 != $rHeader['recVer'] || 0x000 != $rHeader['recInstance'] || self::RT_CURRENTUSERATOM != $rHeader['recType']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > RecordHeader');
}
// Size
$size = self::getInt4d($this->streamCurrentUser, $pos);
$pos += 4;
- if ($size != 0x00000014) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > Size).');
+ if (0x00000014 != $size) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > Size');
}
// headerToken
$headerToken = self::getInt4d($this->streamCurrentUser, $pos);
$pos += 4;
- if ($headerToken == 0xF3D1C4DF && $headerToken != 0xE391C05F) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.') : Encrypted file');
+ if (0xF3D1C4DF == $headerToken && 0xE391C05F != $headerToken) {
+ // Encrypted file
+ throw new FeatureNotImplementedException();
}
// offsetToCurrentEdit
@@ -540,69 +548,70 @@ class PowerPoint97 implements ReaderInterface
$lenUserName = self::getInt2d($this->streamCurrentUser, $pos);
$pos += 2;
if ($lenUserName > 255) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > lenUserName).');
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > lenUserName');
}
// docFileVersion
$docFileVersion = self::getInt2d($this->streamCurrentUser, $pos);
$pos += 2;
- if ($docFileVersion != 0x03F4) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > docFileVersion).');
+ if (0x03F4 != $docFileVersion) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > docFileVersion');
}
// majorVersion
$majorVersion = self::getInt1d($this->streamCurrentUser, $pos);
- $pos += 1;
- if ($majorVersion != 0x03) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > majorVersion).');
+ ++$pos;
+ if (0x03 != $majorVersion) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > majorVersion');
}
// minorVersion
$minorVersion = self::getInt1d($this->streamCurrentUser, $pos);
- $pos += 1;
- if ($minorVersion != 0x00) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > minorVersion).');
+ ++$pos;
+ if (0x00 != $minorVersion) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > minorVersion');
}
// unused
$pos += 2;
// ansiUserName
- $ansiUserName = '';
+ // $ansiUserName = '';
do {
$char = self::getInt1d($this->streamCurrentUser, $pos);
if (($char >= 0x00 && $char <= 0x1F) || ($char >= 0x7F && $char <= 0x9F)) {
$char = false;
} else {
- $ansiUserName .= chr($char);
- $pos += 1;
+ // $ansiUserName .= chr($char);
+ ++$pos;
}
- } while ($char !== false);
+ } while (false !== $char);
// relVersion
$relVersion = self::getInt4d($this->streamCurrentUser, $pos);
$pos += 4;
- if ($relVersion != 0x00000008 && $relVersion != 0x00000009) {
- throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > relVersion).');
+ if (0x00000008 != $relVersion && 0x00000009 != $relVersion) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > relVersion');
}
// unicodeUserName
- $unicodeUserName = '';
- for ($inc = 0; $inc < $lenUserName; $inc++) {
+ // $unicodeUserName = '';
+ for ($inc = 0; $inc < $lenUserName; ++$inc) {
$char = self::getInt2d($this->streamCurrentUser, $pos);
if (($char >= 0x00 && $char <= 0x1F) || ($char >= 0x7F && $char <= 0x9F)) {
break;
}
- $unicodeUserName .= chr($char);
+ // $unicodeUserName .= chr($char);
$pos += 2;
}
}
/**
- * Stream Powerpoint Document
- * @link http://msdn.microsoft.com/en-us/library/dd921564(v=office.12).aspx
+ * Stream Powerpoint Document.
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd921564(v=office.12).aspx
*/
- private function loadPowerpointDocumentStream()
+ private function loadPowerpointDocumentStream(): void
{
$this->readRecordUserEditAtom($this->streamPowerpointDocument, $this->offsetToCurrentEdit);
@@ -611,11 +620,11 @@ class PowerPoint97 implements ReaderInterface
foreach ($this->rgPersistDirEntry as $offsetDir) {
$pos = $offsetDir;
- $rh = $this->loadRecordHeader($this->streamPowerpointDocument, $pos);
+ $rHeader = $this->loadRecordHeader($this->streamPowerpointDocument, $pos);
$pos += 8;
- $this->inMainType = $rh['recType'];
+ $this->inMainType = $rHeader['recType'];
$this->currentNote = null;
- switch ($rh['recType']) {
+ switch ($rHeader['recType']) {
case self::RT_DOCUMENT:
$this->readRecordDocumentContainer($this->streamPowerpointDocument, $pos);
break;
@@ -626,63 +635,50 @@ class PowerPoint97 implements ReaderInterface
$this->readRecordSlideContainer($this->streamPowerpointDocument, $pos);
break;
default:
- // throw new \Exception('Feature not implemented : l.'.__LINE__.'('.dechex($rh['recType']).')');
break;
}
}
}
/**
- * Read a record header
- * @param string $stream
- * @param integer $pos
- * @return array
+ * Read a record header.
+ *
+ * @return array
*/
- private function loadRecordHeader($stream, $pos)
+ private function loadRecordHeader(string $stream, int $pos): array
{
$rec = self::getInt2d($stream, $pos);
$recType = self::getInt2d($stream, $pos + 2);
$recLen = self::getInt4d($stream, $pos + 4);
- return array(
+
+ return [
'recVer' => ($rec >> 0) & bindec('1111'),
'recInstance' => ($rec >> 4) & bindec('111111111111'),
'recType' => $recType,
'recLen' => $recLen,
- );
+ ];
}
/**
- * Read 8-bit unsigned integer
- *
- * @param string $data
- * @param int $pos
- * @return int
+ * Read 8-bit unsigned integer.
*/
- public static function getInt1d($data, $pos)
+ public static function getInt1d(string $data, int $pos): int
{
return ord($data[$pos]);
}
/**
- * Read 16-bit unsigned integer
- *
- * @param string $data
- * @param int $pos
- * @return int
+ * Read 16-bit unsigned integer.
*/
- public static function getInt2d($data, $pos)
+ public static function getInt2d(string $data, int $pos): int
{
- return ord($data[$pos]) | (ord($data[$pos+1]) << 8);
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
}
/**
- * Read 32-bit signed integer
- *
- * @param string $data
- * @param int $pos
- * @return int
+ * Read 32-bit signed integer.
*/
- public static function getInt4d($data, $pos)
+ public static function getInt4d(string $data, int $pos): int
{
// FIX: represent numbers correctly on 64-bit system
// http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
@@ -694,30 +690,32 @@ class PowerPoint97 implements ReaderInterface
// negative number
$ord24 = -abs((256 - $or24) << 24);
}
- return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $ord24;
+
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $ord24;
}
/**
* A container record that specifies the animation and sound information for a shape.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd772900(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd772900(v=office.12).aspx
*/
- private function readRecordAnimationInfoContainer($stream, $pos)
+ private function readRecordAnimationInfoContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ANIMATIONINFO) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ANIMATIONINFO == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// animationAtom
// animationSound
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ throw new FeatureNotImplementedException();
}
return $arrayReturn;
@@ -725,27 +723,28 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies information about the document.
- * @param string $stream
- * @param integer $pos
- * @throws \Exception
- * @link http://msdn.microsoft.com/en-us/library/dd947357(v=office.12).aspx
+ *
+ * @throws FeatureNotImplementedException
+ * @throws InvalidFileFormatException
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd947357(v=office.12).aspx
*/
- private function readRecordDocumentContainer($stream, $pos)
+ private function readRecordDocumentContainer(string $stream, int $pos): void
{
$documentAtom = $this->loadRecordHeader($stream, $pos);
$pos += 8;
- if ($documentAtom['recVer'] != 0x1 || $documentAtom['recInstance'] != 0x000 || $documentAtom['recType'] != self::RT_DOCUMENTATOM) {
- throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom).');
+ if (0x1 != $documentAtom['recVer'] || 0x000 != $documentAtom['recInstance'] || self::RT_DOCUMENTATOM != $documentAtom['recType']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : RTDocument > DocumentAtom');
}
$pos += $documentAtom['recLen'];
$exObjList = $this->loadRecordHeader($stream, $pos);
- if ($exObjList['recVer'] == 0xF && $exObjList['recInstance'] == 0x000 && $exObjList['recType'] == self::RT_EXTERNALOBJECTLIST) {
+ if (0xF == $exObjList['recVer'] && 0x000 == $exObjList['recInstance'] && self::RT_EXTERNALOBJECTLIST == $exObjList['recType']) {
$pos += 8;
// exObjListAtom > rh
$exObjListAtom = $this->loadRecordHeader($stream, $pos);
- if ($exObjListAtom['recVer'] != 0x0 || $exObjListAtom['recInstance'] != 0x000 || $exObjListAtom['recType'] != self::RT_EXTERNALOBJECTLISTATOM || $exObjListAtom['recLen'] != 0x00000004) {
- throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom > exObjList > exObjListAtom).');
+ if (0x0 != $exObjListAtom['recVer'] || 0x000 != $exObjListAtom['recInstance'] || self::RT_EXTERNALOBJECTLISTATOM != $exObjListAtom['recType'] || 0x00000004 != $exObjListAtom['recLen']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : RTDocument > DocumentAtom > exObjList > exObjListAtom');
}
$pos += 8;
// exObjListAtom > exObjIdSeed
@@ -761,8 +760,8 @@ class PowerPoint97 implements ReaderInterface
//@link : http://msdn.microsoft.com/en-us/library/dd944995(v=office.12).aspx
// exHyperlinkAtom > rh
$exHyperlinkAtom = $this->loadRecordHeader($stream, $pos);
- if ($exHyperlinkAtom['recVer'] != 0x0 || $exHyperlinkAtom['recInstance'] != 0x000 || $exHyperlinkAtom['recType'] != self::RT_EXTERNALHYPERLINKATOM || $exObjListAtom['recLen'] != 0x00000004) {
- throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom > exObjList > rgChildRec > RT_ExternalHyperlink).');
+ if (0x0 != $exHyperlinkAtom['recVer'] || 0x000 != $exHyperlinkAtom['recInstance'] || self::RT_EXTERNALHYPERLINKATOM != $exHyperlinkAtom['recType'] || 0x00000004 != $exObjListAtom['recLen']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : RTDocument > DocumentAtom > exObjList > rgChildRec > RT_ExternalHyperlink');
}
$pos += 8;
$exObjList['recLen'] -= 8;
@@ -771,14 +770,14 @@ class PowerPoint97 implements ReaderInterface
$pos += 4;
$exObjList['recLen'] -= 4;
- $this->arrayHyperlinks[$exHyperlinkId] = array();
+ $this->arrayHyperlinks[$exHyperlinkId] = [];
// friendlyNameAtom
- $friendlyNameAtom = $this->loadRecordHeader($stream, $pos);
- if ($friendlyNameAtom['recVer'] == 0x0 && $friendlyNameAtom['recInstance'] == 0x000 && $friendlyNameAtom['recType'] == self::RT_CSTRING && $friendlyNameAtom['recLen'] % 2 == 0) {
+ $friendlyNameAtom = $this->loadRecordHeader($stream, $pos);
+ if (0x0 == $friendlyNameAtom['recVer'] && 0x000 == $friendlyNameAtom['recInstance'] && self::RT_CSTRING == $friendlyNameAtom['recType'] && $friendlyNameAtom['recLen'] % 2 == 0) {
$pos += 8;
$exObjList['recLen'] -= 8;
$this->arrayHyperlinks[$exHyperlinkId]['text'] = '';
- for ($inc = 0; $inc < ($friendlyNameAtom['recLen'] / 2); $inc++) {
+ for ($inc = 0; $inc < ($friendlyNameAtom['recLen'] / 2); ++$inc) {
$char = self::getInt2d($stream, $pos);
$pos += 2;
$exObjList['recLen'] -= 2;
@@ -786,12 +785,12 @@ class PowerPoint97 implements ReaderInterface
}
}
// targetAtom
- $targetAtom = $this->loadRecordHeader($stream, $pos);
- if ($targetAtom['recVer'] == 0x0 && $targetAtom['recInstance'] == 0x001 && $targetAtom['recType'] == self::RT_CSTRING && $targetAtom['recLen'] % 2 == 0) {
+ $targetAtom = $this->loadRecordHeader($stream, $pos);
+ if (0x0 == $targetAtom['recVer'] && 0x001 == $targetAtom['recInstance'] && self::RT_CSTRING == $targetAtom['recType'] && $targetAtom['recLen'] % 2 == 0) {
$pos += 8;
$exObjList['recLen'] -= 8;
$this->arrayHyperlinks[$exHyperlinkId]['url'] = '';
- for ($inc = 0; $inc < ($targetAtom['recLen'] / 2); $inc++) {
+ for ($inc = 0; $inc < ($targetAtom['recLen'] / 2); ++$inc) {
$char = self::getInt2d($stream, $pos);
$pos += 2;
$exObjList['recLen'] -= 2;
@@ -799,12 +798,12 @@ class PowerPoint97 implements ReaderInterface
}
}
// locationAtom
- $locationAtom = $this->loadRecordHeader($stream, $pos);
- if ($locationAtom['recVer'] == 0x0 && $locationAtom['recInstance'] == 0x003 && $locationAtom['recType'] == self::RT_CSTRING && $locationAtom['recLen'] % 2 == 0) {
+ $locationAtom = $this->loadRecordHeader($stream, $pos);
+ if (0x0 == $locationAtom['recVer'] && 0x003 == $locationAtom['recInstance'] && self::RT_CSTRING == $locationAtom['recType'] && $locationAtom['recLen'] % 2 == 0) {
$pos += 8;
$exObjList['recLen'] -= 8;
$string = '';
- for ($inc = 0; $inc < ($locationAtom['recLen'] / 2); $inc++) {
+ for ($inc = 0; $inc < ($locationAtom['recLen'] / 2); ++$inc) {
$char = self::getInt2d($stream, $pos);
$pos += 2;
$exObjList['recLen'] -= 2;
@@ -813,35 +812,36 @@ class PowerPoint97 implements ReaderInterface
}
break;
default:
- throw new \Exception('Feature not implemented (l.'.__LINE__.' : '.dechex($childRec['recType'].')'));
+ // var_dump(dechex((int) $childRec['recType']));
+ throw new FeatureNotImplementedException();
}
} while ($exObjList['recLen'] > 0);
}
//@link : http://msdn.microsoft.com/en-us/library/dd907813(v=office.12).aspx
$documentTextInfo = $this->loadRecordHeader($stream, $pos);
- if ($documentTextInfo['recVer'] == 0xF && $documentTextInfo['recInstance'] == 0x000 && $documentTextInfo['recType'] == self::RT_ENVIRONMENT) {
+ if (0xF == $documentTextInfo['recVer'] && 0x000 == $documentTextInfo['recInstance'] && self::RT_ENVIRONMENT == $documentTextInfo['recType']) {
$pos += 8;
//@link : http://msdn.microsoft.com/en-us/library/dd952717(v=office.12).aspx
$kinsoku = $this->loadRecordHeader($stream, $pos);
- if ($kinsoku['recVer'] == 0xF && $kinsoku['recInstance'] == 0x002 && $kinsoku['recType'] == self::RT_KINSOKU) {
+ if (0xF == $kinsoku['recVer'] && 0x002 == $kinsoku['recInstance'] && self::RT_KINSOKU == $kinsoku['recType']) {
$pos += 8;
$pos += $kinsoku['recLen'];
}
//@link : http://msdn.microsoft.com/en-us/library/dd948152(v=office.12).aspx
$fontCollection = $this->loadRecordHeader($stream, $pos);
- if ($fontCollection['recVer'] == 0xF && $fontCollection['recInstance'] == 0x000 && $fontCollection['recType'] == self::RT_FONTCOLLECTION) {
+ if (0xF == $fontCollection['recVer'] && 0x000 == $fontCollection['recInstance'] && self::RT_FONTCOLLECTION == $fontCollection['recType']) {
$pos += 8;
do {
$fontEntityAtom = $this->loadRecordHeader($stream, $pos);
$pos += 8;
$fontCollection['recLen'] -= 8;
- if ($fontEntityAtom['recVer'] != 0x0 || $fontEntityAtom['recInstance'] > 128 || $fontEntityAtom['recType'] != self::RT_FONTENTITYATOM) {
- throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > RT_Environment > RT_FontCollection > RT_FontEntityAtom).');
+ if (0x0 != $fontEntityAtom['recVer'] || $fontEntityAtom['recInstance'] > 128 || self::RT_FONTENTITYATOM != $fontEntityAtom['recType']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : RTDocument > RT_Environment > RT_FontCollection > RT_FontEntityAtom');
}
$string = '';
- for ($inc = 0; $inc < 32; $inc++) {
+ for ($inc = 0; $inc < 32; ++$inc) {
$char = self::getInt2d($stream, $pos);
$pos += 2;
$fontCollection['recLen'] -= 2;
@@ -850,28 +850,28 @@ class PowerPoint97 implements ReaderInterface
$this->arrayFonts[] = $string;
// lfCharSet (1 byte)
- $pos += 1;
- $fontCollection['recLen'] -= 1;
+ ++$pos;
+ --$fontCollection['recLen'];
// fEmbedSubsetted (1 bit)
// unused (7 bits)
- $pos += 1;
- $fontCollection['recLen'] -= 1;
+ ++$pos;
+ --$fontCollection['recLen'];
// rasterFontType (1 bit)
// deviceFontType (1 bit)
// truetypeFontType (1 bit)
// fNoFontSubstitution (1 bit)
// reserved (4 bits)
- $pos += 1;
- $fontCollection['recLen'] -= 1;
+ ++$pos;
+ --$fontCollection['recLen'];
// lfPitchAndFamily (1 byte)
- $pos += 1;
- $fontCollection['recLen'] -= 1;
+ ++$pos;
+ --$fontCollection['recLen'];
$fontEmbedData1 = $this->loadRecordHeader($stream, $pos);
- if ($fontEmbedData1['recVer'] == 0x0 && $fontEmbedData1['recInstance'] >= 0x000 && $fontEmbedData1['recInstance'] <= 0x003 && $fontEmbedData1['recType'] == self::RT_FONTEMBEDDATABLOB) {
+ if (0x0 == $fontEmbedData1['recVer'] && $fontEmbedData1['recInstance'] >= 0x000 && $fontEmbedData1['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData1['recType']) {
$pos += 8;
$fontCollection['recLen'] -= 8;
$pos += $fontEmbedData1['recLen'];
@@ -879,7 +879,7 @@ class PowerPoint97 implements ReaderInterface
}
$fontEmbedData2 = $this->loadRecordHeader($stream, $pos);
- if ($fontEmbedData2['recVer'] == 0x0 && $fontEmbedData2['recInstance'] >= 0x000 && $fontEmbedData2['recInstance'] <= 0x003 && $fontEmbedData2['recType'] == self::RT_FONTEMBEDDATABLOB) {
+ if (0x0 == $fontEmbedData2['recVer'] && $fontEmbedData2['recInstance'] >= 0x000 && $fontEmbedData2['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData2['recType']) {
$pos += 8;
$fontCollection['recLen'] -= 8;
$pos += $fontEmbedData2['recLen'];
@@ -887,7 +887,7 @@ class PowerPoint97 implements ReaderInterface
}
$fontEmbedData3 = $this->loadRecordHeader($stream, $pos);
- if ($fontEmbedData3['recVer'] == 0x0 && $fontEmbedData3['recInstance'] >= 0x000 && $fontEmbedData3['recInstance'] <= 0x003 && $fontEmbedData3['recType'] == self::RT_FONTEMBEDDATABLOB) {
+ if (0x0 == $fontEmbedData3['recVer'] && $fontEmbedData3['recInstance'] >= 0x000 && $fontEmbedData3['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData3['recType']) {
$pos += 8;
$fontCollection['recLen'] -= 8;
$pos += $fontEmbedData3['recLen'];
@@ -895,7 +895,7 @@ class PowerPoint97 implements ReaderInterface
}
$fontEmbedData4 = $this->loadRecordHeader($stream, $pos);
- if ($fontEmbedData4['recVer'] == 0x0 && $fontEmbedData4['recInstance'] >= 0x000 && $fontEmbedData4['recInstance'] <= 0x003 && $fontEmbedData4['recType'] == self::RT_FONTEMBEDDATABLOB) {
+ if (0x0 == $fontEmbedData4['recVer'] && $fontEmbedData4['recInstance'] >= 0x000 && $fontEmbedData4['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData4['recType']) {
$pos += 8;
$fontCollection['recLen'] -= 8;
$pos += $fontEmbedData4['recLen'];
@@ -905,81 +905,81 @@ class PowerPoint97 implements ReaderInterface
}
$textCFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
- if ($textCFDefaultsAtom['recVer'] == 0x0 && $textCFDefaultsAtom['recInstance'] == 0x000 && $textCFDefaultsAtom['recType'] == self::RT_TEXTCHARFORMATEXCEPTIONATOM) {
+ if (0x0 == $textCFDefaultsAtom['recVer'] && 0x000 == $textCFDefaultsAtom['recInstance'] && self::RT_TEXTCHARFORMATEXCEPTIONATOM == $textCFDefaultsAtom['recType']) {
$pos += 8;
$pos += $textCFDefaultsAtom['recLen'];
}
$textPFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
- if ($textPFDefaultsAtom['recVer'] == 0x0 && $textPFDefaultsAtom['recInstance'] == 0x000 && $textPFDefaultsAtom['recType'] == self::RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM) {
+ if (0x0 == $textPFDefaultsAtom['recVer'] && 0x000 == $textPFDefaultsAtom['recInstance'] && self::RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM == $textPFDefaultsAtom['recType']) {
$pos += 8;
$pos += $textPFDefaultsAtom['recLen'];
}
$defaultRulerAtom = $this->loadRecordHeader($stream, $pos);
- if ($defaultRulerAtom['recVer'] == 0x0 && $defaultRulerAtom['recInstance'] == 0x000 && $defaultRulerAtom['recType'] == self::RT_DEFAULTRULERATOM) {
+ if (0x0 == $defaultRulerAtom['recVer'] && 0x000 == $defaultRulerAtom['recInstance'] && self::RT_DEFAULTRULERATOM == $defaultRulerAtom['recType']) {
$pos += 8;
$pos += $defaultRulerAtom['recLen'];
}
$textSIDefaultsAtom = $this->loadRecordHeader($stream, $pos);
- if ($textSIDefaultsAtom['recVer'] == 0x0 && $textSIDefaultsAtom['recInstance'] == 0x000 && $textSIDefaultsAtom['recType'] == self::RT_TEXTSPECIALINFODEFAULTATOM) {
+ if (0x0 == $textSIDefaultsAtom['recVer'] && 0x000 == $textSIDefaultsAtom['recInstance'] && self::RT_TEXTSPECIALINFODEFAULTATOM == $textSIDefaultsAtom['recType']) {
$pos += 8;
$pos += $textSIDefaultsAtom['recLen'];
}
$textMasterStyleAtom = $this->loadRecordHeader($stream, $pos);
- if ($textMasterStyleAtom['recVer'] == 0x0 && $textMasterStyleAtom['recType'] == self::RT_TEXTMASTERSTYLEATOM) {
+ if (0x0 == $textMasterStyleAtom['recVer'] && self::RT_TEXTMASTERSTYLEATOM == $textMasterStyleAtom['recType']) {
$pos += 8;
$pos += $textMasterStyleAtom['recLen'];
}
}
$soundCollection = $this->loadRecordHeader($stream, $pos);
- if ($soundCollection['recVer'] == 0xF && $soundCollection['recInstance'] == 0x005 && $soundCollection['recType'] == self::RT_SOUNDCOLLECTION) {
+ if (0xF == $soundCollection['recVer'] && 0x005 == $soundCollection['recInstance'] && self::RT_SOUNDCOLLECTION == $soundCollection['recType']) {
$pos += 8;
$pos += $soundCollection['recLen'];
}
$drawingGroup = $this->loadRecordHeader($stream, $pos);
- if ($drawingGroup['recVer'] == 0xF && $drawingGroup['recInstance'] == 0x000 && $drawingGroup['recType'] == self::RT_DRAWINGGROUP) {
+ if (0xF == $drawingGroup['recVer'] && 0x000 == $drawingGroup['recInstance'] && self::RT_DRAWINGGROUP == $drawingGroup['recType']) {
$drawing = $this->readRecordDrawingGroupContainer($stream, $pos);
$pos += 8;
$pos += $drawing['length'];
}
$masterList = $this->loadRecordHeader($stream, $pos);
- if ($masterList['recVer'] == 0xF && $masterList['recInstance'] == 0x001 && $masterList['recType'] == self::RT_SLIDELISTWITHTEXT) {
+ if (0xF == $masterList['recVer'] && 0x001 == $masterList['recInstance'] && self::RT_SLIDELISTWITHTEXT == $masterList['recType']) {
$pos += 8;
$pos += $masterList['recLen'];
}
$docInfoList = $this->loadRecordHeader($stream, $pos);
- if ($docInfoList['recVer'] == 0xF && $docInfoList['recInstance'] == 0x000 && $docInfoList['recType'] == self::RT_LIST) {
+ if (0xF == $docInfoList['recVer'] && 0x000 == $docInfoList['recInstance'] && self::RT_LIST == $docInfoList['recType']) {
$pos += 8;
$pos += $docInfoList['recLen'];
}
$slideHF = $this->loadRecordHeader($stream, $pos);
- if ($slideHF['recVer'] == 0xF && $slideHF['recInstance'] == 0x003 && $slideHF['recType'] == self::RT_HEADERSFOOTERS) {
+ if (0xF == $slideHF['recVer'] && 0x003 == $slideHF['recInstance'] && self::RT_HEADERSFOOTERS == $slideHF['recType']) {
$pos += 8;
$pos += $slideHF['recLen'];
}
$notesHF = $this->loadRecordHeader($stream, $pos);
- if ($notesHF['recVer'] == 0xF && $notesHF['recInstance'] == 0x004 && $notesHF['recType'] == self::RT_HEADERSFOOTERS) {
+ if (0xF == $notesHF['recVer'] && 0x004 == $notesHF['recInstance'] && self::RT_HEADERSFOOTERS == $notesHF['recType']) {
$pos += 8;
$pos += $notesHF['recLen'];
}
// SlideListWithTextContainer
$slideList = $this->loadRecordHeader($stream, $pos);
- if ($slideList['recVer'] == 0xF && $slideList['recInstance'] == 0x000 && $slideList['recType'] == self::RT_SLIDELISTWITHTEXT) {
+ if (0xF == $slideList['recVer'] && 0x000 == $slideList['recInstance'] && self::RT_SLIDELISTWITHTEXT == $slideList['recType']) {
$pos += 8;
do {
// SlideListWithTextSubContainerOrAtom
$rhSlideList = $this->loadRecordHeader($stream, $pos);
- if ($rhSlideList['recVer'] == 0x0 && $rhSlideList['recInstance'] == 0x000 && $rhSlideList['recType'] == self::RT_SLIDEPERSISTATOM && $rhSlideList['recLen'] == 0x00000014) {
+ if (0x0 == $rhSlideList['recVer'] && 0x000 == $rhSlideList['recInstance'] && self::RT_SLIDEPERSISTATOM == $rhSlideList['recType'] && 0x00000014 == $rhSlideList['recLen']) {
$pos += 8;
$slideList['recLen'] -= 8;
// persistIdRef
@@ -993,7 +993,7 @@ class PowerPoint97 implements ReaderInterface
$slideList['recLen'] -= 4;
// slideId
$slideId = self::getInt4d($stream, $pos);
- if ($slideId == -2147483648) {
+ if (-2147483648 == $slideId) {
$slideId = 0;
}
if ($slideId > 0) {
@@ -1011,60 +1011,63 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies information about a slide.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
*/
- private function readRecordDrawingContainer($stream, $pos)
+ private function readRecordDrawingContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_DRAWING) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_DRAWING == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
$officeArtDg = $this->readRecordOfficeArtDgContainer($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $officeArtDg['length'];
}
+
return $arrayReturn;
}
- private function readRecordDrawingGroupContainer($stream, $pos)
+ /**
+ * @return array
+ */
+ private function readRecordDrawingGroupContainer(string $stream, int $pos): array
{
-
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_DRAWINGGROUP) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_DRAWINGGROUP == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
$arrayReturn['length'] += $data['recLen'];
}
+
return $arrayReturn;
}
/**
* An atom record that specifies a reference to an external object.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd910388(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd910388(v=office.12).aspx
*/
- private function readRecordExObjRefAtom($stream, $pos)
+ private function readRecordExObjRefAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_EXTERNALOBJECTREFATOM && $data['recLen'] == 0x00000004) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_EXTERNALOBJECTREFATOM == $data['recType'] && 0x00000004 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -1076,19 +1079,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies a type of action to be performed.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd953300(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd953300(v=office.12).aspx
*/
- private function readRecordInteractiveInfoAtom($stream, $pos)
+ private function readRecordInteractiveInfoAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_INTERACTIVEINFOATOM && $data['recLen'] == 0x00000010) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_INTERACTIVEINFOATOM == $data['recType'] && 0x00000010 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// soundIdRef
@@ -1097,19 +1100,19 @@ class PowerPoint97 implements ReaderInterface
$arrayReturn['exHyperlinkIdRef'] = self::getInt4d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
// action
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
// oleVerb
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
// jump
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
// fAnimated (1 bit)
// fStopSound (1 bit)
// fCustomShowReturn (1 bit)
// fVisited (1 bit)
// reserved (4 bits)
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
// hyperlinkType
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
// unused
$arrayReturn['length'] += 3;
}
@@ -1119,19 +1122,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies the name of a macro, a file name, or a named show.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd925121(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd925121(v=office.12).aspx
*/
- private function readRecordMacroNameAtom($stream, $pos)
+ private function readRecordMacroNameAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x002 && $data['recType'] == self::RT_CSTRING && $data['recLen'] % 2 == 0) {
+ if (0x0 == $data['recVer'] && 0x002 == $data['recInstance'] && self::RT_CSTRING == $data['recType'] && $data['recLen'] % 2 == 0) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -1143,19 +1146,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies what actions to perform when interacting with an object by means of a mouse click.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd952348(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd952348(v=office.12).aspx
*/
- private function readRecordMouseClickInteractiveInfoContainer($stream, $pos)
+ private function readRecordMouseClickInteractiveInfoContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_INTERACTIVEINFO) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_INTERACTIVEINFO == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// interactiveInfoAtom
@@ -1174,25 +1177,26 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies what actions to perform when interacting with an object by moving the mouse cursor over it.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd925811(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd925811(v=office.12).aspx
*/
- private function readRecordMouseOverInteractiveInfoContainer($stream, $pos)
+ private function readRecordMouseOverInteractiveInfoContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x001 && $data['recType'] == self::RT_INTERACTIVEINFO) {
+ if (0xF == $data['recVer'] && 0x001 == $data['recInstance'] && self::RT_INTERACTIVEINFO == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// interactiveInfoAtom
// macroNameAtom
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ throw new FeatureNotImplementedException();
}
return $arrayReturn;
@@ -1200,21 +1204,22 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtBlip record specifies BLIP file data.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd910081(v=office.12).aspx
+ *
+ * @return array{'length': int, 'picture': null|string}
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd910081(v=office.12).aspx
*/
- private function readRecordOfficeArtBlip($stream, $pos)
+ private function readRecordOfficeArtBlip(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- 'picture' => null
- );
+ 'picture' => null,
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && ($data['recType'] >= 0xF018 && $data['recType'] <= 0xF117)) {
+ if (0x0 == $data['recVer'] && ($data['recType'] >= 0xF018 && $data['recType'] <= 0xF117)) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -1224,20 +1229,21 @@ class PowerPoint97 implements ReaderInterface
// rgbUid1
$arrayReturn['length'] += 16;
$data['recLen'] -= 16;
- if ($data['recInstance'] == 0x6E1) {
+ if (0x6E1 == $data['recInstance']) {
// rgbUid2
$arrayReturn['length'] += 16;
$data['recLen'] -= 16;
}
// tag
- $arrayReturn['length'] += 1;
- $data['recLen'] -= 1;
+ ++$arrayReturn['length'];
+ --$data['recLen'];
// BLIPFileData
$arrayReturn['picture'] = substr($this->streamPictures, $pos + $arrayReturn['length'], $data['recLen']);
$arrayReturn['length'] += $data['recLen'];
break;
default:
- throw new \Exception('Feature not implemented (l.'.__LINE__.' : '.dechex($data['recType'].')'));
+ // var_dump(dechex((int) $data['recType']))
+ throw new FeatureNotImplementedException();
}
}
@@ -1246,19 +1252,19 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtChildAnchor record specifies four signed integers that specify the anchor for the shape that contains this record.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd922720(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd922720(v=office.12).aspx
*/
- private function readRecordOfficeArtChildAnchor($stream, $pos)
+ private function readRecordOfficeArtChildAnchor(string $stream, int $pos)
{
- $arrayReturn = array(
- 'length' => 0
- );
+ $arrayReturn = [
+ 'length' => 0,
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF00F && $data['recLen'] == 0x00000010) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF00F == $data['recType'] && 0x00000010 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -1277,20 +1283,21 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies the location of a shape.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd922797(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd922797(v=office.12).aspx
*/
- private function readRecordOfficeArtClientAnchor($stream, $pos)
+ private function readRecordOfficeArtClientAnchor(string $stream, int $pos)
{
- $arrayReturn = array(
- 'length' => 0
- );
+ $arrayReturn = [
+ 'length' => 0,
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF010 && ($data['recLen'] == 0x00000008 || $data['recLen'] == 0x00000010)) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF010 == $data['recType'] && (0x00000008 == $data['recLen'] || 0x00000010 == $data['recLen'])) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -1307,7 +1314,8 @@ class PowerPoint97 implements ReaderInterface
$pos += 8;
break;
case 0x00000010:
- throw new \Exception('PowerPoint97 Reader : record OfficeArtClientAnchor (0x00000010)');
+ // record OfficeArtClientAnchor (0x00000010)
+ throw new FeatureNotImplementedException();
}
}
@@ -1316,46 +1324,47 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies text related data for a shape.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
+ *
+ * @return array{'length': int, 'alignH': string|null, 'text': string, 'numParts': int, 'numTexts': int, 'hyperlink': array>, 'part': array{'length': int, 'strLenRT': int, 'partLength': int|float, 'bold': bool, 'italic': bool, 'underline': bool, 'fontName': string, 'fontSize': int, 'color': Color}}
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
*/
- private function readRecordOfficeArtClientTextbox($stream, $pos)
+ private function readRecordOfficeArtClientTextbox(string $stream, int $pos)
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
'text' => '',
'numParts' => 0,
'numTexts' => 0,
- 'hyperlink' => array(),
- );
+ 'hyperlink' => [],
+ ];
$data = $this->loadRecordHeader($stream, $pos);
// recVer 0xF
// Doc : 0x0 https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
// Sample : 0xF https://msdn.microsoft.com/en-us/library/dd953497(v=office.12).aspx
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF00D) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF00D == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
$strLen = 0;
do {
$rhChild = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
- /**
+ /*
* @link : https://msdn.microsoft.com/en-us/library/dd947039(v=office.12).aspx
*/
// echo dechex($rhChild['recType']).'-'.$rhChild['recType'].EOL;
switch ($rhChild['recType']) {
case self::RT_INTERACTIVEINFO:
//@link : http://msdn.microsoft.com/en-us/library/dd948623(v=office.12).aspx
- if ($rhChild['recInstance'] == 0x0000) {
+ if (0x0000 == $rhChild['recInstance']) {
$mouseClickInfo = $this->readRecordMouseClickInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $mouseClickInfo['length'];
$arrayReturn['hyperlink'][]['id'] = $mouseClickInfo['exHyperlinkIdRef'];
}
- if ($rhChild['recInstance'] == 0x0001) {
+ if (0x0001 == $rhChild['recInstance']) {
$mouseOverInfo = $this->readRecordMouseOverInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $mouseOverInfo['length'];
}
@@ -1367,8 +1376,8 @@ class PowerPoint97 implements ReaderInterface
$strLenRT = $strLen + 1;
do {
$strucTextPFRun = $this->readStructureTextPFRun($stream, $pos + $arrayReturn['length'], $strLenRT);
- $arrayReturn['numTexts']++;
- $arrayReturn['text'.$arrayReturn['numTexts']] = $strucTextPFRun;
+ ++$arrayReturn['numTexts'];
+ $arrayReturn['text' . $arrayReturn['numTexts']] = $strucTextPFRun;
if (isset($strucTextPFRun['alignH'])) {
$arrayReturn['alignH'] = $strucTextPFRun['alignH'];
}
@@ -1379,8 +1388,8 @@ class PowerPoint97 implements ReaderInterface
$strLenRT = $strLen + 1;
do {
$strucTextCFRun = $this->readStructureTextCFRun($stream, $pos + $arrayReturn['length'], $strLenRT);
- $arrayReturn['numParts']++;
- $arrayReturn['part'.$arrayReturn['numParts']] = $strucTextCFRun;
+ ++$arrayReturn['numParts'];
+ $arrayReturn['part' . $arrayReturn['numParts']] = $strucTextCFRun;
$strLenRT = $strucTextCFRun['strLenRT'];
$arrayReturn['length'] += $strucTextCFRun['length'];
} while ($strLenRT > 0);
@@ -1388,23 +1397,23 @@ class PowerPoint97 implements ReaderInterface
case self::RT_TEXTBYTESATOM:
$arrayReturn['length'] += 8;
// @link : https://msdn.microsoft.com/en-us/library/dd947905(v=office.12).aspx
- $strLen = (int)$rhChild['recLen'];
- for ($inc = 0; $inc < $strLen; $inc++) {
+ $strLen = (int) $rhChild['recLen'];
+ for ($inc = 0; $inc < $strLen; ++$inc) {
$char = self::getInt1d($stream, $pos + $arrayReturn['length']);
- if ($char == 0x0B) {
+ if (0x0B == $char) {
$char = 0x20;
}
$arrayReturn['text'] .= Text::chr($char);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
}
break;
case self::RT_TEXTCHARSATOM:
$arrayReturn['length'] += 8;
// @link : http://msdn.microsoft.com/en-us/library/dd772921(v=office.12).aspx
- $strLen = (int)($rhChild['recLen']/2);
- for ($inc = 0; $inc < $strLen; $inc++) {
+ $strLen = (int) ($rhChild['recLen'] / 2);
+ for ($inc = 0; $inc < $strLen; ++$inc) {
$char = self::getInt2d($stream, $pos + $arrayReturn['length']);
- if ($char == 0x0B) {
+ if (0x0B == $char) {
$char = 0x20;
}
$arrayReturn['text'] .= Text::chr($char);
@@ -1420,16 +1429,16 @@ class PowerPoint97 implements ReaderInterface
case self::RT_TEXTINTERACTIVEINFOATOM:
$arrayReturn['length'] += 8;
//@link : http://msdn.microsoft.com/en-us/library/dd947973(v=office.12).aspx
- if ($rhChild['recInstance'] == 0x0000) {
+ if (0x0000 == $rhChild['recInstance']) {
//@todo : MouseClickTextInteractiveInfoAtom
- $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['start'] = self::getInt4d($stream, $pos + + $arrayReturn['length']);
+ $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['start'] = self::getInt4d($stream, $pos + +$arrayReturn['length']);
$arrayReturn['length'] += 4;
- $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['end'] = self::getInt4d($stream, $pos + + $arrayReturn['length']);
+ $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['end'] = self::getInt4d($stream, $pos + +$arrayReturn['length']);
$arrayReturn['length'] += 4;
}
- if ($rhChild['recInstance'] == 0x0001) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (0x0001 == $rhChild['recInstance']) {
+ throw new FeatureNotImplementedException();
}
break;
case self::RT_TEXTSPECIALINFOATOM:
@@ -1455,30 +1464,31 @@ class PowerPoint97 implements ReaderInterface
default:
$arrayReturn['length'] += 8;
$arrayReturn['length'] += $rhChild['recLen'];
- // throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($rhChild['recType']).')');
}
} while (($data['recLen'] - $arrayReturn['length']) > 0);
}
+
return $arrayReturn;
}
/**
* The OfficeArtSpContainer record specifies a shape container.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd943794(v=office.12).aspx
+ *
+ * @return array{'length': int, 'shape': null|AbstractShape}
+ *
+ * @throws InvalidFileFormatException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd943794(v=office.12).aspx
*/
- private function readRecordOfficeArtSpContainer($stream, $pos)
+ private function readRecordOfficeArtSpContainer(string $stream, int $pos)
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
'shape' => null,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF004) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF004 == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// shapeGroup
@@ -1487,12 +1497,12 @@ class PowerPoint97 implements ReaderInterface
// shapeProp
$shapeProp = $this->readRecordOfficeArtFSP($stream, $pos + $arrayReturn['length']);
- if ($shapeProp['length'] == 0) {
- throw new \Exception('PowerPoint97 Reader : record OfficeArtFSP');
+ if (0 == $shapeProp['length']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
}
$arrayReturn['length'] += $shapeProp['length'];
- if ($shapeProp['fDeleted'] == 0x1 && $shapeProp['fChild'] == 0x0) {
+ if (0x1 == $shapeProp['fDeleted'] && 0x0 == $shapeProp['fChild']) {
// deletedShape
$deletedShape = $this->readRecordOfficeArtFPSPL($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $deletedShape['length'];
@@ -1527,20 +1537,20 @@ class PowerPoint97 implements ReaderInterface
$arrayReturn['length'] += $clientTextbox['length'];
// shapeSecondaryOptions2
- if ($shpSecondaryOptions1['length'] == 0) {
+ if (0 == $shpSecondaryOptions1['length']) {
$shpSecondaryOptions2 = $this->readRecordOfficeArtSecondaryFOPT($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $shpSecondaryOptions2['length'];
}
// shapeTertiaryOptions2
- if ($shpTertiaryOptions1['length'] == 0) {
+ if (0 == $shpTertiaryOptions1['length']) {
$shpTertiaryOptions2 = $this->readRecordOfficeArtTertiaryFOPT($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $shpTertiaryOptions2['length'];
}
// Core : Shape
// Informations about group are not defined
- $arrayDimensions = array();
+ $arrayDimensions = [];
$bIsGroup = false;
if (is_object($this->oCurrentGroup)) {
if (!$this->bFirstShapeGroup) {
@@ -1584,39 +1594,40 @@ class PowerPoint97 implements ReaderInterface
$start = 0;
$lastLevel = -1;
$lastMarginLeft = 0;
- for ($inc = 1; $inc <= $clientTextbox['numParts']; $inc++) {
- if ($clientTextbox['numParts'] == $clientTextbox['numTexts'] && isset($clientTextbox['text'.$inc])) {
- if (isset($clientTextbox['text'.$inc]['bulletChar'])) {
+ /* @phpstan-ignore-next-line */
+ for ($inc = 1; $inc <= $clientTextbox['numParts']; ++$inc) {
+ if ($clientTextbox['numParts'] == $clientTextbox['numTexts'] && isset($clientTextbox['text' . $inc])) {
+ if (isset($clientTextbox['text' . $inc]['bulletChar'])) {
$arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
- $arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletChar($clientTextbox['text'.$inc]['bulletChar']);
+ $arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletChar($clientTextbox['text' . $inc]['bulletChar']);
}
// Indent
$indent = 0;
- if (isset($clientTextbox['text'.$inc]['indent'])) {
- $indent = $clientTextbox['text'.$inc]['indent'];
+ if (isset($clientTextbox['text' . $inc]['indent'])) {
+ $indent = $clientTextbox['text' . $inc]['indent'];
}
- if (isset($clientTextbox['text'.$inc]['leftMargin'])) {
- if ($lastMarginLeft > $clientTextbox['text'.$inc]['leftMargin']) {
- $lastLevel--;
+ if (isset($clientTextbox['text' . $inc]['leftMargin'])) {
+ if ($lastMarginLeft > $clientTextbox['text' . $inc]['leftMargin']) {
+ --$lastLevel;
}
- if ($lastMarginLeft < $clientTextbox['text'.$inc]['leftMargin']) {
- $lastLevel++;
+ if ($lastMarginLeft < $clientTextbox['text' . $inc]['leftMargin']) {
+ ++$lastLevel;
}
$arrayReturn['shape']->getActiveParagraph()->getAlignment()->setLevel($lastLevel);
- $lastMarginLeft = $clientTextbox['text'.$inc]['leftMargin'];
+ $lastMarginLeft = $clientTextbox['text' . $inc]['leftMargin'];
- $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setMarginLeft($clientTextbox['text'.$inc]['leftMargin']);
- $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setIndent($indent - $clientTextbox['text'.$inc]['leftMargin']);
+ $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setMarginLeft($clientTextbox['text' . $inc]['leftMargin']);
+ $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setIndent($indent - $clientTextbox['text' . $inc]['leftMargin']);
}
}
// Texte
- $sText = substr(isset($clientTextbox['text']) ? $clientTextbox['text'] : '', $start, $clientTextbox['part'.$inc]['partLength']);
+ $sText = substr(isset($clientTextbox['text']) ? $clientTextbox['text'] : '', $start, $clientTextbox['part' . $inc]['partLength']);
$sHyperlinkURL = '';
if (empty($sText)) {
// Is there a hyperlink ?
- if (isset($clientTextbox['hyperlink']) && is_array($clientTextbox['hyperlink']) && !empty($clientTextbox['hyperlink'])) {
+ if (!empty($clientTextbox['hyperlink'])) {
foreach ($clientTextbox['hyperlink'] as $itmHyperlink) {
- if ($itmHyperlink['start'] == $start && ($itmHyperlink['end'] - $itmHyperlink['start']) == $clientTextbox['part'.$inc]['partLength']) {
+ if ($itmHyperlink['start'] == $start && ($itmHyperlink['end'] - $itmHyperlink['start']) == (float) $clientTextbox['part' . $inc]['partLength']) {
$sText = $this->arrayHyperlinks[$itmHyperlink['id']]['text'];
$sHyperlinkURL = $this->arrayHyperlinks[$itmHyperlink['id']]['url'];
break;
@@ -1626,36 +1637,38 @@ class PowerPoint97 implements ReaderInterface
}
// New paragraph
$bCreateParagraph = false;
- if (strpos($sText, "\r") !== false) {
+ if (false !== strpos($sText, "\r")) {
$bCreateParagraph = true;
$sText = str_replace("\r", '', $sText);
}
// TextRun
$txtRun = $arrayReturn['shape']->createTextRun($sText);
- if (isset($clientTextbox['part'.$inc]['bold'])) {
- $txtRun->getFont()->setBold($clientTextbox['part'.$inc]['bold']);
+ if (isset($clientTextbox['part' . $inc]['bold'])) {
+ $txtRun->getFont()->setBold($clientTextbox['part' . $inc]['bold']);
}
- if (isset($clientTextbox['part'.$inc]['italic'])) {
- $txtRun->getFont()->setItalic($clientTextbox['part'.$inc]['italic']);
+ if (isset($clientTextbox['part' . $inc]['italic'])) {
+ $txtRun->getFont()->setItalic($clientTextbox['part' . $inc]['italic']);
}
- if (isset($clientTextbox['part'.$inc]['underline'])) {
- $txtRun->getFont()->setUnderline($clientTextbox['part'.$inc]['underline']);
+ if (isset($clientTextbox['part' . $inc]['underline'])) {
+ $txtRun->getFont()->setUnderline(
+ $clientTextbox['part' . $inc]['underline'] ? Font::UNDERLINE_SINGLE : Font::UNDERLINE_NONE
+ );
}
- if (isset($clientTextbox['part'.$inc]['fontName'])) {
- $txtRun->getFont()->setName($clientTextbox['part'.$inc]['fontName']);
+ if (isset($clientTextbox['part' . $inc]['fontName'])) {
+ $txtRun->getFont()->setName($clientTextbox['part' . $inc]['fontName']);
}
- if (isset($clientTextbox['part'.$inc]['fontSize'])) {
- $txtRun->getFont()->setSize($clientTextbox['part'.$inc]['fontSize']);
+ if (isset($clientTextbox['part' . $inc]['fontSize'])) {
+ $txtRun->getFont()->setSize($clientTextbox['part' . $inc]['fontSize']);
}
- if (isset($clientTextbox['part'.$inc]['color'])) {
- $txtRun->getFont()->setColor($clientTextbox['part'.$inc]['color']);
+ if (isset($clientTextbox['part' . $inc]['color'])) {
+ $txtRun->getFont()->setColor($clientTextbox['part' . $inc]['color']);
}
// Hyperlink
if (!empty($sHyperlinkURL)) {
$txtRun->setHyperlink(new Hyperlink($sHyperlinkURL));
}
- $start += $clientTextbox['part'.$inc]['partLength'];
+ $start += $clientTextbox['part' . $inc]['partLength'];
if ($bCreateParagraph) {
$arrayReturn['shape']->createParagraph();
}
@@ -1677,10 +1690,10 @@ class PowerPoint97 implements ReaderInterface
$arrayReturn['shape']->setRotation($rotation);
}
// Shadow
- if (isset($shpPrimaryOptions['shadowOffsetX']) && isset($shpPrimaryOptions['shadowOffsetY'])) {
+ if (isset($shpPrimaryOptions['shadowOffsetX'], $shpPrimaryOptions['shadowOffsetY'])) {
$shadowOffsetX = $shpPrimaryOptions['shadowOffsetX'];
$shadowOffsetY = $shpPrimaryOptions['shadowOffsetY'];
- if ($shadowOffsetX != 0 && $shadowOffsetX != 0) {
+ if (0 != $shadowOffsetX && 0 != $shadowOffsetX) {
$arrayReturn['shape']->getShadow()->setVisible(true);
if ($shadowOffsetX > 0 && $shadowOffsetX == $shadowOffsetY) {
$arrayReturn['shape']->getShadow()->setDistance($shadowOffsetX)->setDirection(45);
@@ -1690,7 +1703,7 @@ class PowerPoint97 implements ReaderInterface
// Specific Line
if ($arrayReturn['shape'] instanceof Line) {
if (isset($shpPrimaryOptions['lineColor'])) {
- $arrayReturn['shape']->getBorder()->getColor()->setARGB('FF'.$shpPrimaryOptions['lineColor']);
+ $arrayReturn['shape']->getBorder()->getColor()->setARGB('FF' . $shpPrimaryOptions['lineColor']);
}
if (isset($shpPrimaryOptions['lineWidth'])) {
$arrayReturn['shape']->setHeight($shpPrimaryOptions['lineWidth']);
@@ -1726,27 +1739,31 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtSpgrContainer record specifies a container for groups of shapes.
+ *
* @param string $stream
- * @param integer $pos
- * @param boolean $bInGroup
- * @return array
- * @throws \Exception
- * @link : https://msdn.microsoft.com/en-us/library/dd910416(v=office.12).aspx
+ * @param int $pos
+ * @param bool $bInGroup
+ *
+ * @return array
+ *
+ * @throws InvalidFileFormatException
+ *
+ * @see : https://msdn.microsoft.com/en-us/library/dd910416(v=office.12).aspx
*/
private function readRecordOfficeArtSpgrContainer($stream, $pos, $bInGroup = false)
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF003) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF003 == $data['recType']) {
$arrayReturn['length'] += 8;
do {
$rhFileBlock = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
- if (!($rhFileBlock['recVer'] == 0xF && $rhFileBlock['recInstance'] == 0x0000 && ($rhFileBlock['recType'] == 0xF003 || $rhFileBlock['recType'] == 0xF004))) {
- throw new \Exception('PowerPoint97 Reader : readRecordOfficeArtSpgrContainer.');
+ if (!(0xF == $rhFileBlock['recVer'] && 0x0000 == $rhFileBlock['recInstance'] && (0xF003 == $rhFileBlock['recType'] || 0xF004 == $rhFileBlock['recType']))) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
}
switch ($rhFileBlock['recType']) {
@@ -1776,7 +1793,7 @@ class PowerPoint97 implements ReaderInterface
$arrayIdxSlide = array_flip($this->arrayNotes);
if ($this->currentNote > 0 && isset($arrayIdxSlide[$this->currentNote])) {
$oSlide = $this->oPhpPresentation->getSlide($arrayIdxSlide[$this->currentNote]);
- if ($oSlide->getNote()->getShapeCollection()->count() == 0) {
+ if (0 == $oSlide->getNote()->getShapeCollection()->count()) {
$oSlide->getNote()->addShape($fileBlock['shape']);
}
}
@@ -1795,40 +1812,42 @@ class PowerPoint97 implements ReaderInterface
}
} while ($data['recLen'] > 0);
}
+
return $arrayReturn;
}
/**
* The OfficeArtTertiaryFOPT record specifies a table of OfficeArtRGFOPTE records,.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd950206(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd950206(v=office.12).aspx
*/
- private function readRecordOfficeArtTertiaryFOPT($stream, $pos)
+ private function readRecordOfficeArtTertiaryFOPT(string $stream, int $pos)
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x3 && $data['recType'] == 0xF122) {
+ if (0x3 == $data['recVer'] && 0xF122 == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
- $officeArtFOPTE = array();
- for ($inc = 0; $inc < $data['recInstance']; $inc++) {
+ $officeArtFOPTE = [];
+ for ($inc = 0; $inc < $data['recInstance']; ++$inc) {
$opid = self::getInt2d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
$optOp = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
- $officeArtFOPTE[] = array(
+ $officeArtFOPTE[] = [
'opid' => ($opid >> 0) & bindec('11111111111111'),
'fBid' => ($opid >> 14) & bindec('1'),
'fComplex' => ($opid >> 15) & bindec('1'),
'op' => $optOp,
- );
+ ];
}
//@link : http://code.metager.de/source/xref/kde/calligra/filters/libmso/OPID
foreach ($officeArtFOPTE as $opt) {
@@ -1840,14 +1859,14 @@ class PowerPoint97 implements ReaderInterface
case 0x03A0:
// Table Row Properties
//@link : https://msdn.microsoft.com/en-us/library/dd923419(v=office.12).aspx
- if ($opt['fComplex'] == 0x1) {
+ if (0x1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
}
break;
case 0x03A9:
// GroupShape : metroBlob
//@link : https://msdn.microsoft.com/en-us/library/dd943388(v=office.12).aspx
- if ($opt['fComplex'] == 0x1) {
+ if (0x1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
}
break;
@@ -1856,33 +1875,34 @@ class PowerPoint97 implements ReaderInterface
//@link : https://msdn.microsoft.com/en-us/library/dd951605(v=office.12).aspx
break;
default:
- throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($opt['opid']).')');
+ // var_dump('0x' . dechex($opt['opid']));
+ throw new FeatureNotImplementedException();
}
}
}
+
return $arrayReturn;
}
/**
* The OfficeArtDgContainer record specifies the container for all the file records for the objects in a drawing.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link : https://msdn.microsoft.com/en-us/library/dd924455(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see : https://msdn.microsoft.com/en-us/library/dd924455(v=office.12).aspx
*/
- private function readRecordOfficeArtDgContainer($stream, $pos)
+ private function readRecordOfficeArtDgContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF002) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF002 == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// drawingData
- $drawingData = $this->readRecordOfficeArtFDG($stream, $pos + $arrayReturn['length']);
+ $drawingData = $this->readRecordOfficeArtFDG($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += $drawingData['length'];
// regroupItems
//@todo
@@ -1905,19 +1925,19 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtFDG record specifies the number of shapes, the drawing identifier, and the shape identifier of the last shape in a drawing.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link : https://msdn.microsoft.com/en-us/library/dd946757(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see : https://msdn.microsoft.com/en-us/library/dd946757(v=office.12).aspx
*/
- private function readRecordOfficeArtFDG($stream, $pos)
+ private function readRecordOfficeArtFDG(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] <= 0xFFE && $data['recType'] == 0xF008 && $data['recLen'] == 0x00000008) {
+ if (0x0 == $data['recVer'] && $data['recInstance'] <= 0xFFE && 0xF008 == $data['recType'] && 0x00000008 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
@@ -1929,37 +1949,37 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd943404(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd943404(v=office.12).aspx
*/
- private function readRecordOfficeArtFOPT($stream, $pos)
+ private function readRecordOfficeArtFOPT(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x3 && $data['recType'] == 0xF00B) {
+ if (0x3 == $data['recVer'] && 0xF00B == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
//@link : http://msdn.microsoft.com/en-us/library/dd906086(v=office.12).aspx
- $officeArtFOPTE = array();
- for ($inc = 0; $inc < $data['recInstance']; $inc++) {
+ $officeArtFOPTE = [];
+ for ($inc = 0; $inc < $data['recInstance']; ++$inc) {
$opid = self::getInt2d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
$data['recLen'] -= 2;
$optOp = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
$data['recLen'] -= 4;
- $officeArtFOPTE[] = array(
+ $officeArtFOPTE[] = [
'opid' => ($opid >> 0) & bindec('11111111111111'),
'fBid' => ($opid >> 14) & bindec('1'),
'fComplex' => ($opid >> 15) & bindec('1'),
'op' => $optOp,
- );
+ ];
}
//@link : http://code.metager.de/source/xref/kde/calligra/filters/libmso/OPID
foreach ($officeArtFOPTE as $opt) {
@@ -1981,22 +2001,22 @@ class PowerPoint97 implements ReaderInterface
case 0x0081:
// Text : dxTextLeft
//@link : http://msdn.microsoft.com/en-us/library/dd953234(v=office.12).aspx
- $arrayReturn['insetLeft'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['insetLeft'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x0082:
// Text : dyTextTop
//@link : http://msdn.microsoft.com/en-us/library/dd925068(v=office.12).aspx
- $arrayReturn['insetTop'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['insetTop'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x0083:
// Text : dxTextRight
//@link : http://msdn.microsoft.com/en-us/library/dd906782(v=office.12).aspx
- $arrayReturn['insetRight'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['insetRight'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x0084:
// Text : dyTextBottom
//@link : http://msdn.microsoft.com/en-us/library/dd772858(v=office.12).aspx
- $arrayReturn['insetBottom'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['insetBottom'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x0085:
// Text : WrapText
@@ -2013,7 +2033,7 @@ class PowerPoint97 implements ReaderInterface
case 0x0104:
// Blip : pib
//@link : http://msdn.microsoft.com/en-us/library/dd772837(v=office.12).aspx
- if ($opt['fComplex'] == 0) {
+ if (0 == $opt['fComplex']) {
$arrayReturn['pib'] = $opt['op'];
$data['recLen'] -= $opt['op'];
} else {
@@ -2052,7 +2072,7 @@ class PowerPoint97 implements ReaderInterface
case 0x145:
// Geometry : pVertices
//@link : http://msdn.microsoft.com/en-us/library/dd949814(v=office.12).aspx
- if ($opt['fComplex'] == 1) {
+ if (1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
$data['recLen'] -= $opt['op'];
}
@@ -2060,7 +2080,7 @@ class PowerPoint97 implements ReaderInterface
case 0x146:
// Geometry : pSegmentInfo
//@link : http://msdn.microsoft.com/en-us/library/dd905742(v=office.12).aspx
- if ($opt['fComplex'] == 1) {
+ if (1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
$data['recLen'] -= $opt['op'];
}
@@ -2068,7 +2088,7 @@ class PowerPoint97 implements ReaderInterface
case 0x155:
// Geometry : pAdjustHandles
//@link : http://msdn.microsoft.com/en-us/library/dd905890(v=office.12).aspx
- if ($opt['fComplex'] == 1) {
+ if (1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
$data['recLen'] -= $opt['op'];
}
@@ -2076,7 +2096,7 @@ class PowerPoint97 implements ReaderInterface
case 0x156:
// Geometry : pGuides
//@link : http://msdn.microsoft.com/en-us/library/dd910801(v=office.12).aspx
- if ($opt['fComplex'] == 1) {
+ if (1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
$data['recLen'] -= $opt['op'];
}
@@ -2084,7 +2104,7 @@ class PowerPoint97 implements ReaderInterface
case 0x157:
// Geometry : pInscribe
//@link : http://msdn.microsoft.com/en-us/library/dd904889(v=office.12).aspx
- if ($opt['fComplex'] == 1) {
+ if (1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
$data['recLen'] -= $opt['op'];
}
@@ -2100,28 +2120,28 @@ class PowerPoint97 implements ReaderInterface
case 0x0181:
// Fill : fillColor
//@link : http://msdn.microsoft.com/en-us/library/dd921332(v=office.12).aspx
- $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
+ $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
// echo 'fillColor : '.$strColor.EOL;
break;
case 0x0183:
// Fill : fillBackColor
//@link : http://msdn.microsoft.com/en-us/library/dd950634(v=office.12).aspx
- $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
+ $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
// echo 'fillBackColor : '.$strColor.EOL;
break;
case 0x0193:
// Fill : fillRectRight
//@link : http://msdn.microsoft.com/en-us/library/dd951294(v=office.12).aspx
- // echo 'fillRectRight : '.\PhpOffice\Common\Drawing::emuToPixels($opt['op']).EOL;
+ // echo 'fillRectRight : '.\PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']).EOL;
break;
case 0x0194:
// Fill : fillRectBottom
//@link : http://msdn.microsoft.com/en-us/library/dd910194(v=office.12).aspx
- // echo 'fillRectBottom : '.\PhpOffice\Common\Drawing::emuToPixels($opt['op']).EOL;
+ // echo 'fillRectBottom : '.\PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']).EOL;
break;
case 0x01BF:
// Fill : Fill Style Boolean Properties
@@ -2130,9 +2150,9 @@ class PowerPoint97 implements ReaderInterface
case 0x01C0:
// Line Style : lineColor
//@link : http://msdn.microsoft.com/en-us/library/dd920397(v=office.12).aspx
- $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
+ $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, '0', STR_PAD_LEFT);
$arrayReturn['lineColor'] = $strColor;
break;
case 0x01C1:
@@ -2147,7 +2167,7 @@ class PowerPoint97 implements ReaderInterface
case 0x01CB:
// Line Style : lineWidth
//@link : http://msdn.microsoft.com/en-us/library/dd926964(v=office.12).aspx
- $arrayReturn['lineWidth'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['lineWidth'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x01D6:
// Line Style : lineJoinStyle
@@ -2172,12 +2192,12 @@ class PowerPoint97 implements ReaderInterface
case 0x0205:
// Shadow Style : shadowOffsetX
//@link : http://msdn.microsoft.com/en-us/library/dd945280(v=office.12).aspx
- $arrayReturn['shadowOffsetX'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['shadowOffsetX'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x0206:
// Shadow Style : shadowOffsetY
//@link : http://msdn.microsoft.com/en-us/library/dd907855(v=office.12).aspx
- $arrayReturn['shadowOffsetY'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
+ $arrayReturn['shadowOffsetY'] = \PhpOffice\Common\Drawing::emuToPixels((int) $opt['op']);
break;
case 0x023F:
// Shadow Style : Shadow Style Boolean Properties
@@ -2194,7 +2214,7 @@ class PowerPoint97 implements ReaderInterface
case 0x0380:
// Group Shape Property Set : wzName
//@link : http://msdn.microsoft.com/en-us/library/dd950681(v=office.12).aspx
- if ($opt['fComplex'] == 1) {
+ if (1 == $opt['fComplex']) {
$arrayReturn['length'] += $opt['op'];
$data['recLen'] -= $opt['op'];
}
@@ -2204,7 +2224,6 @@ class PowerPoint97 implements ReaderInterface
//@link : http://msdn.microsoft.com/en-us/library/dd949807(v=office.12).aspx
break;
default:
- // throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($opt['opid']).')');
}
}
if ($data['recLen'] > 0) {
@@ -2217,19 +2236,19 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtFPSPL record specifies the former hierarchical position of the containing object that is either a shape or a group of shapes.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd947479(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd947479(v=office.12).aspx
*/
- private function readRecordOfficeArtFPSPL($stream, $pos)
+ private function readRecordOfficeArtFPSPL(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF11D && $data['recLen'] == 0x00000004) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF11D == $data['recType'] && 0x00000004 == $data['recLen']) {
$arrayReturn['length'] += 8;
$arrayReturn['length'] += $data['recLen'];
}
@@ -2239,19 +2258,19 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtFSP record specifies an instance of a shape.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd925898(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd925898(v=office.12).aspx
*/
- private function readRecordOfficeArtFSP($stream, $pos)
+ private function readRecordOfficeArtFSP(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x2 && $data['recType'] == 0xF00A && $data['recLen'] == 0x00000008) {
+ if (0x2 == $data['recVer'] && 0xF00A == $data['recType'] && 0x00000008 == $data['recLen']) {
$arrayReturn['length'] += 8;
// spid
$arrayReturn['length'] += 4;
@@ -2269,19 +2288,19 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtFSPGR record specifies the coordinate system of the group shape that the anchors of the child shape are expressed in.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd925381(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd925381(v=office.12).aspx
*/
- private function readRecordOfficeArtFSPGR($stream, $pos)
+ private function readRecordOfficeArtFSPGR(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x1 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF009 && $data['recLen'] == 0x00000010) {
+ if (0x1 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF009 == $data['recType'] && 0x00000010 == $data['recLen']) {
$arrayReturn['length'] += 8;
//$arrShapeGroup['xLeft'] = self::getInt4d($this->streamPowerpointDocument, $pos);
$arrayReturn['length'] += 4;
@@ -2298,43 +2317,45 @@ class PowerPoint97 implements ReaderInterface
/**
* The OfficeArtSecondaryFOPT record specifies a table of OfficeArtRGFOPTE records.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd950259(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd950259(v=office.12).aspx
*/
- private function readRecordOfficeArtSecondaryFOPT($stream, $pos)
+ private function readRecordOfficeArtSecondaryFOPT(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x3 && $data['recType'] == 0xF121) {
+ if (0x3 == $data['recVer'] && 0xF121 == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
$arrayReturn['length'] += $data['recLen'];
}
+
return $arrayReturn;
}
/**
* A container record that specifies information about a shape.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link : https://msdn.microsoft.com/en-us/library/dd950927(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see : https://msdn.microsoft.com/en-us/library/dd950927(v=office.12).aspx
*/
- private function readRecordOfficeArtClientData($stream, $pos)
+ private function readRecordOfficeArtClientData(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF011) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF011 == $data['recType']) {
$arrayReturn['length'] += 8;
// shapeFlagsAtom (9 bytes)
$dataShapeFlagsAtom = $this->readRecordShapeFlagsAtom($stream, $pos + $arrayReturn['length']);
@@ -2369,13 +2390,13 @@ class PowerPoint97 implements ReaderInterface
$arrayReturn['length'] += $dataRecolorInfo['length'];
// rgShapeClientRoundtripData (variable)
- $array = array(
+ $array = [
self::RT_PROGTAGS,
self::RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM,
self::RT_ROUNDTRIPSHAPEID12ATOM,
self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM,
self::RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM,
- );
+ ];
do {
$dataHeaderRG = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
if (in_array($dataHeaderRG['recType'], $array)) {
@@ -2393,7 +2414,8 @@ class PowerPoint97 implements ReaderInterface
$arrayReturn['length'] += $dataRG['length'];
break;
default:
- throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($dataHeaderRG['recType']).')');
+ // var_dump('0x' . dechex($dataHeaderRG['recType']));
+ throw new FeatureNotImplementedException();
}
}
} while (in_array($dataHeaderRG['recType'], $array));
@@ -2404,17 +2426,17 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies a persist object directory. Each persist object identifier specified MUST be unique in that persist object directory.
- * @link http://msdn.microsoft.com/en-us/library/dd952680(v=office.12).aspx
- * @param string $stream
- * @param integer $pos
- * @throws \Exception
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd952680(v=office.12).aspx
+ *
+ * @throws InvalidFileFormatException
*/
- private function readRecordPersistDirectoryAtom($stream, $pos)
+ private function readRecordPersistDirectoryAtom(string $stream, int $pos): void
{
$rHeader = $this->loadRecordHeader($stream, $pos);
$pos += 8;
- if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_PERSISTDIRECTORYATOM) {
- throw new \Exception('File PowerPoint 97 in error (Location : PersistDirectoryAtom > RecordHeader).');
+ if (0x0 != $rHeader['recVer'] || 0x000 != $rHeader['recInstance'] || self::RT_PERSISTDIRECTORYATOM != $rHeader['recType']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : PersistDirectoryAtom > RecordHeader');
}
// rgPersistDirEntry
// @link : http://msdn.microsoft.com/en-us/library/dd947347(v=office.12).aspx
@@ -2423,10 +2445,10 @@ class PowerPoint97 implements ReaderInterface
$pos += 4;
$rHeader['recLen'] -= 4;
//$persistId = ($data >> 0) & bindec('11111111111111111111');
- $cPersist = ($data >> 20) & bindec('111111111111');
+ $cPersist = ($data >> 20) & bindec('111111111111');
- $rgPersistOffset = array();
- for ($inc = 0; $inc < $cPersist; $inc++) {
+ $rgPersistOffset = [];
+ for ($inc = 0; $inc < $cPersist; ++$inc) {
$rgPersistOffset[] = self::getInt4d($stream, $pos);
$pos += 4;
$rHeader['recLen'] -= 4;
@@ -2437,19 +2459,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies information about the headers (1) and footers within a slide.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd904856(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd904856(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordPerSlideHeadersFootersContainer($stream, $pos)
+ private function readRecordPerSlideHeadersFootersContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_HEADERSFOOTERS) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_HEADERSFOOTERS == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
@@ -2461,19 +2483,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies whether a shape is a placeholder shape.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd923930(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd923930(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordPlaceholderAtom($stream, $pos)
+ private function readRecordPlaceholderAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PLACEHOLDERATOM && $data['recLen'] == 0x00000008) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PLACEHOLDERATOM == $data['recType'] && 0x00000008 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2485,19 +2507,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies a collection of re-color mappings for a metafile ([MS-WMF]).
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd904899(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd904899(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordRecolorInfoAtom($stream, $pos)
+ private function readRecordRecolorInfoAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_RECOLORINFOATOM) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_RECOLORINFOATOM == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2509,19 +2531,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies that a shape is a header or footerplaceholder shape.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd910800(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd910800(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordRoundTripHFPlaceholder12Atom($stream, $pos)
+ private function readRecordRoundTripHFPlaceholder12Atom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM && $data['recLen'] == 0x00000001) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM == $data['recType'] && 0x00000001 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2533,19 +2555,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies a shape identifier.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd772926(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd772926(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordRoundTripShapeId12Atom($stream, $pos)
+ private function readRecordRoundTripShapeId12Atom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPSHAPEID12ATOM && $data['recLen'] == 0x00000004) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ROUNDTRIPSHAPEID12ATOM == $data['recType'] && 0x00000004 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
@@ -2557,19 +2579,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies information about a slide that synchronizes to a slide in a slide library.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordRoundTripSlideSyncInfo12Container($stream, $pos)
+ private function readRecordRoundTripSlideSyncInfo12Container(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPSLIDESYNCINFO12) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ROUNDTRIPSLIDESYNCINFO12 == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
@@ -2581,19 +2603,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies shape-level Boolean flags.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd908949(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd908949(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordShapeFlags10Atom($stream, $pos)
+ private function readRecordShapeFlags10Atom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SHAPEFLAGS10ATOM && $data['recLen'] == 0x00000001) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SHAPEFLAGS10ATOM == $data['recType'] && 0x00000001 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2605,19 +2627,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies shape-level Boolean flags.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd925824(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd925824(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordShapeFlagsAtom($stream, $pos)
+ private function readRecordShapeFlagsAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
- 'length' => 0,
- );
+ $arrayReturn = [
+ 'length' => 0,
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SHAPEATOM && $data['recLen'] == 0x00000001) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SHAPEATOM == $data['recType'] && 0x00000001 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2629,19 +2651,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies programmable tags with additional binary shape data.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd911033(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd911033(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordShapeProgBinaryTagContainer($stream, $pos)
+ private function readRecordShapeProgBinaryTagContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGBINARYTAG) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PROGBINARYTAG == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2653,20 +2675,21 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies programmable tags with additional shape data.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd911266(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd911266(v=office.12).aspx
*/
- private function readRecordShapeProgTagsContainer($stream, $pos)
+ private function readRecordShapeProgTagsContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGTAGS) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PROGTAGS == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
@@ -2680,7 +2703,8 @@ class PowerPoint97 implements ReaderInterface
break;
//case self::RT_PROGSTRINGTAG:
default:
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ // var_dump('0x' . dechex($dataHeaderRG['recType']));
+ throw new FeatureNotImplementedException();
}
} while ($length < $data['recLen']);
// Datas
@@ -2692,28 +2716,28 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies information about a slide.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
*/
- private function readRecordSlideAtom($stream, $pos)
+ private function readRecordSlideAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x2 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDEATOM) {
+ if (0x2 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SLIDEATOM == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// slideAtom > geom
$arrayReturn['length'] += 4;
// slideAtom > rgPlaceholderTypes
- $rgPlaceholderTypes = array();
- for ($inc = 0; $inc < 8; $inc++) {
+ $rgPlaceholderTypes = [];
+ for ($inc = 0; $inc < 8; ++$inc) {
$rgPlaceholderTypes[] = self::getInt1d($this->streamPowerpointDocument, $pos);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
}
// slideAtom > masterIdRef
@@ -2731,12 +2755,12 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies a presentation slide or title master slide.
- * @param string $stream
- * @param int $pos
- * @throws \Exception
- * @link http://msdn.microsoft.com/en-us/library/dd946323(v=office.12).aspx
+ *
+ * @throws InvalidFileFormatException
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd946323(v=office.12).aspx
*/
- private function readRecordSlideContainer($stream, $pos)
+ private function readRecordSlideContainer(string $stream, int $pos): void
{
// Core
$this->oPhpPresentation->createSlide();
@@ -2744,8 +2768,8 @@ class PowerPoint97 implements ReaderInterface
// *** slideAtom (32 bytes)
$slideAtom = $this->readRecordSlideAtom($stream, $pos);
- if ($slideAtom['length'] == 0) {
- throw new \Exception('PowerPoint97 Reader : record SlideAtom');
+ if (0 == $slideAtom['length']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
}
$pos += $slideAtom['length'];
@@ -2767,8 +2791,9 @@ class PowerPoint97 implements ReaderInterface
// *** slideSchemeColorSchemeAtom (40 bytes)
$slideSchemeColorAtom = $this->readRecordSlideSchemeColorSchemeAtom($stream, $pos);
- if ($slideSchemeColorAtom['length'] == 0) {
- throw new \Exception('PowerPoint97 Reader : record SlideSchemeColorSchemeAtom');
+ if (0 == $slideSchemeColorAtom['length']) {
+ // Record SlideSchemeColorSchemeAtom
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
}
$pos += $slideSchemeColorAtom['length'];
@@ -2785,25 +2810,25 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies the name of a slide.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd906297(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd906297(v=office.12).aspx
+ *
+ * @return array{'length': int, 'slideName': string}
*/
- private function readRecordSlideNameAtom($stream, $pos)
+ private function readRecordSlideNameAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
'slideName' => '',
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x003 && $data['recType'] == self::RT_CSTRING && $data['recLen'] % 2 == 0) {
+ if (0x0 == $data['recVer'] && 0x003 == $data['recInstance'] && self::RT_CSTRING == $data['recType'] && $data['recLen'] % 2 == 0) {
// Record Header
$arrayReturn['length'] += 8;
// Length
$strLen = ($data['recLen'] / 2);
- for ($inc = 0; $inc < $strLen; $inc++) {
+ for ($inc = 0; $inc < $strLen; ++$inc) {
$char = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
$arrayReturn['slideName'] .= Text::chr($char);
@@ -2815,19 +2840,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies a slide number metacharacter.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd945703(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd945703(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordSlideNumberMCAtom($stream, $pos)
+ private function readRecordSlideNumberMCAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDENUMBERMETACHARATOM && $data['recLen'] == 0x00000004) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SLIDENUMBERMETACHARATOM == $data['recType'] && 0x00000004 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Datas
@@ -2839,19 +2864,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies programmable tags with additional slide data.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd951946(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd951946(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordSlideProgTagsContainer($stream, $pos)
+ private function readRecordSlideProgTagsContainer(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGTAGS) {
+ if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PROGTAGS == $data['recType']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
@@ -2863,29 +2888,29 @@ class PowerPoint97 implements ReaderInterface
/**
* A container record that specifies the color scheme used by a slide.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd949420(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd949420(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordSlideSchemeColorSchemeAtom($stream, $pos)
+ private function readRecordSlideSchemeColorSchemeAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x001 && $data['recType'] == self::RT_COLORSCHEMEATOM && $data['recLen'] == 0x00000020) {
+ if (0x0 == $data['recVer'] && 0x001 == $data['recInstance'] && self::RT_COLORSCHEMEATOM == $data['recType'] && 0x00000020 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Length
- $rgSchemeColor = array();
- for ($inc = 0; $inc <= 7; $inc++) {
- $rgSchemeColor[] = array(
+ $rgSchemeColor = [];
+ for ($inc = 0; $inc <= 7; ++$inc) {
+ $rgSchemeColor[] = [
'red' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4),
'green' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4 + 1),
'blue' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4 + 2),
- );
+ ];
}
$arrayReturn['length'] += (8 * 4);
}
@@ -2895,19 +2920,19 @@ class PowerPoint97 implements ReaderInterface
/**
* An atom record that specifies what transition effect to perform during a slide show, and how to advance to the next presentation slide.
- * @param string $stream
- * @param integer $pos
- * @link https://msdn.microsoft.com/en-us/library/dd943408(v=office.12).aspx
- * @return array
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd943408(v=office.12).aspx
+ *
+ * @return array
*/
- private function readRecordSlideShowSlideInfoAtom($stream, $pos)
+ private function readRecordSlideShowSlideInfoAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDESHOWSLIDEINFOATOM && $data['recLen'] == 0x00000010) {
+ if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SLIDESHOWSLIDEINFOATOM == $data['recType'] && 0x00000010 == $data['recLen']) {
// Record Header
$arrayReturn['length'] += 8;
// Length;
@@ -2918,18 +2943,18 @@ class PowerPoint97 implements ReaderInterface
}
/**
- * UserEditAtom
- * @link http://msdn.microsoft.com/en-us/library/dd945746(v=office.12).aspx
- * @param string $stream
- * @param integer $pos
- * @throws \Exception
+ * UserEditAtom.
+ *
+ * @see http://msdn.microsoft.com/en-us/library/dd945746(v=office.12).aspx
+ *
+ * @throws InvalidFileFormatException
*/
- private function readRecordUserEditAtom($stream, $pos)
+ private function readRecordUserEditAtom(string $stream, int $pos): void
{
$rHeader = $this->loadRecordHeader($stream, $pos);
$pos += 8;
- if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_USEREDITATOM || ($rHeader['recLen'] != 0x0000001C && $rHeader['recLen'] != 0x00000020)) {
- throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > RecordHeader).');
+ if (0x0 != $rHeader['recVer'] || 0x000 != $rHeader['recInstance'] || self::RT_USEREDITATOM != $rHeader['recType'] || (0x0000001C != $rHeader['recLen'] && 0x00000020 != $rHeader['recLen'])) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > RecordHeader');
}
// lastSlideIdRef
@@ -2939,16 +2964,16 @@ class PowerPoint97 implements ReaderInterface
// minorVersion
$minorVersion = self::getInt1d($stream, $pos);
- $pos += 1;
- if ($minorVersion != 0x00) {
- throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > minorVersion).');
+ ++$pos;
+ if (0x00 != $minorVersion) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > minorVersion');
}
// majorVersion
$majorVersion = self::getInt1d($stream, $pos);
- $pos += 1;
- if ($majorVersion != 0x03) {
- throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > majorVersion).');
+ ++$pos;
+ if (0x03 != $majorVersion) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > majorVersion');
}
// offsetLastEdit
@@ -2958,10 +2983,10 @@ class PowerPoint97 implements ReaderInterface
$pos += 4;
// docPersistIdRef
- $docPersistIdRef = self::getInt4d($stream, $pos);
+ $docPersistIdRef = self::getInt4d($stream, $pos);
$pos += 4;
- if ($docPersistIdRef != 0x00000001) {
- throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > docPersistIdRef).');
+ if (0x00000001 != $docPersistIdRef) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > docPersistIdRef');
}
// persistIdSeed
@@ -2974,19 +2999,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A structure that specifies the character-level formatting of a run of text.
- * @param string $stream
- * @param int $pos
- * @param int $strLenRT
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd945870(v=office.12).aspx
+ *
+ * @return array{'length': int, 'strLenRT': int, 'partLength': int, 'bold': bool, 'italic': bool, 'underline': bool, 'fontName': string, 'fontSize': int, 'color': Color}
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd945870(v=office.12).aspx
*/
- private function readStructureTextCFRun($stream, $pos, $strLenRT)
+ private function readStructureTextCFRun(string $stream, int $pos, int $strLenRT): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
'strLenRT' => $strLenRT,
- );
+ ];
// rgTextCFRun
$countRgTextCFRun = self::getInt4d($stream, $pos + $arrayReturn['length']);
@@ -2997,7 +3022,7 @@ class PowerPoint97 implements ReaderInterface
$masks = self::getInt4d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
- $masksData = array();
+ $masksData = [];
$masksData['bold'] = ($masks >> 0) & bindec('1');
$masksData['italic'] = ($masks >> 1) & bindec('1');
$masksData['underline'] = ($masks >> 2) & bindec('1');
@@ -3021,11 +3046,11 @@ class PowerPoint97 implements ReaderInterface
$masksData['newEATypeface'] = ($masks >> 24) & bindec('1');
$masksData['csTypeface'] = ($masks >> 25) & bindec('1');
$masksData['pp11ext'] = ($masks >> 26) & bindec('1');
- if ($masksData['bold'] == 1 || $masksData['italic'] == 1 || $masksData['underline'] == 1 || $masksData['shadow'] == 1 || $masksData['fehint'] == 1 || $masksData['kumi'] == 1 || $masksData['emboss'] == 1 || $masksData['fHasStyle'] == 1) {
+ if (1 == $masksData['bold'] || 1 == $masksData['italic'] || 1 == $masksData['underline'] || 1 == $masksData['shadow'] || 1 == $masksData['fehint'] || 1 == $masksData['kumi'] || 1 == $masksData['emboss'] || 1 == $masksData['fHasStyle']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
- $fontStyleFlags = array();
+ $fontStyleFlags = [];
$fontStyleFlags['bold'] = ($data >> 0) & bindec('1');
$fontStyleFlags['italic'] = ($data >> 1) & bindec('1');
$fontStyleFlags['underline'] = ($data >> 2) & bindec('1');
@@ -3039,51 +3064,51 @@ class PowerPoint97 implements ReaderInterface
$fontStyleFlags['pp9rt'] = ($data >> 10) & bindec('1111');
$fontStyleFlags['unused4'] = ($data >> 14) & bindec('11');
- $arrayReturn['bold'] = ($fontStyleFlags['bold'] == 1) ? true : false;
- $arrayReturn['italic'] = ($fontStyleFlags['italic'] == 1) ? true : false;
- $arrayReturn['underline'] = ($fontStyleFlags['underline'] == 1) ? true : false;
+ $arrayReturn['bold'] = (1 == $fontStyleFlags['bold']) ? true : false;
+ $arrayReturn['italic'] = (1 == $fontStyleFlags['italic']) ? true : false;
+ $arrayReturn['underline'] = (1 == $fontStyleFlags['underline']) ? true : false;
}
- if ($masksData['typeface'] == 1) {
+ if (1 == $masksData['typeface']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
$arrayReturn['fontName'] = isset($this->arrayFonts[$data]) ? $this->arrayFonts[$data] : '';
}
- if ($masksData['oldEATypeface'] == 1) {
+ if (1 == $masksData['oldEATypeface']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['ansiTypeface'] == 1) {
+ if (1 == $masksData['ansiTypeface']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['symbolTypeface'] == 1) {
+ if (1 == $masksData['symbolTypeface']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['size'] == 1) {
+ if (1 == $masksData['size']) {
$arrayReturn['fontSize'] = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['color'] == 1) {
+ if (1 == $masksData['color']) {
$red = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
$green = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
$blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
$index = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
- if ($index == 0xFE) {
- $strColor = str_pad(dechex($red), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex($green), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex($blue), 2, STR_PAD_LEFT, '0');
+ if (0xFE == $index) {
+ $strColor = str_pad(dechex($red), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex($green), 2, '0', STR_PAD_LEFT);
+ $strColor .= str_pad(dechex($blue), 2, '0', STR_PAD_LEFT);
- $arrayReturn['color'] = new Color('FF'.$strColor);
+ $arrayReturn['color'] = new Color('FF' . $strColor);
}
}
- if ($masksData['position'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['position']) {
+ throw new FeatureNotImplementedException();
}
return $arrayReturn;
@@ -3091,19 +3116,19 @@ class PowerPoint97 implements ReaderInterface
/**
* A structure that specifies the paragraph-level formatting of a run of text.
- * @param string $stream
- * @param integer $pos
- * @param integer $strLenRT
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd923535(v=office.12).aspx
+ *
+ * @return array{'length': int, 'strLenRT': int, 'alignH': string|null, 'bulletChar': string, 'leftMargin': int, 'indent': int}
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd923535(v=office.12).aspx
*/
- private function readStructureTextPFRun($stream, $pos, $strLenRT)
+ private function readStructureTextPFRun(string $stream, int $pos, int $strLenRT): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
'strLenRT' => $strLenRT,
- );
+ ];
// rgTextPFRun
$countRgTextPFRun = self::getInt4d($stream, $pos + $arrayReturn['length']);
@@ -3116,7 +3141,7 @@ class PowerPoint97 implements ReaderInterface
$masks = self::getInt4d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
- $masksData = array();
+ $masksData = [];
$masksData['hasBullet'] = ($masks >> 0) & bindec('1');
$masksData['bulletHasFont'] = ($masks >> 1) & bindec('1');
$masksData['bulletHasColor'] = ($masks >> 2) & bindec('1');
@@ -3144,8 +3169,8 @@ class PowerPoint97 implements ReaderInterface
$masksData['bulletScheme'] = ($masks >> 24) & bindec('1');
$masksData['bulletHasScheme'] = ($masks >> 25) & bindec('1');
- $bulletFlags = array();
- if ($masksData['hasBullet'] == 1 || $masksData['bulletHasFont'] == 1 || $masksData['bulletHasColor'] == 1 || $masksData['bulletHasSize'] == 1) {
+ $bulletFlags = [];
+ if (1 == $masksData['hasBullet'] || 1 == $masksData['bulletHasFont'] || 1 == $masksData['bulletHasColor'] || 1 == $masksData['bulletHasSize']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
@@ -3154,36 +3179,36 @@ class PowerPoint97 implements ReaderInterface
$bulletFlags['fBulletHasColor'] = ($data >> 2) & bindec('1');
$bulletFlags['fBulletHasSize'] = ($data >> 3) & bindec('1');
}
- if ($masksData['bulletChar'] == 1) {
+ if (1 == $masksData['bulletChar']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
$arrayReturn['bulletChar'] = chr($data);
}
- if ($masksData['bulletFont'] == 1) {
+ if (1 == $masksData['bulletFont']) {
// $data = self::getInt2d($stream, $pos);
$arrayReturn['length'] += 2;
}
- if ($masksData['bulletSize'] == 1) {
+ if (1 == $masksData['bulletSize']) {
// $data = self::getInt2d($stream, $pos);
$arrayReturn['length'] += 2;
}
- if ($masksData['bulletColor'] == 1) {
- $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
- $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
- $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
+ if (1 == $masksData['bulletColor']) {
+ // $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
+ ++$arrayReturn['length'];
+ // $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
+ ++$arrayReturn['length'];
+ // $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
+ ++$arrayReturn['length'];
$index = self::getInt1d($stream, $pos + $arrayReturn['length']);
- $arrayReturn['length'] += 1;
+ ++$arrayReturn['length'];
- if ($index == 0xFE) {
- $strColor = str_pad(dechex($red), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex($green), 2, STR_PAD_LEFT, '0');
- $strColor .= str_pad(dechex($blue), 2, STR_PAD_LEFT, '0');
+ if (0xFE == $index) {
+ // $strColor = str_pad(dechex($red), 2, '0', STR_PAD_LEFT);
+ // $strColor .= str_pad(dechex($green), 2, '0', STR_PAD_LEFT);
+ // $strColor .= str_pad(dechex($blue), 2, '0', STR_PAD_LEFT);
}
}
- if ($masksData['align'] == 1) {
+ if (1 == $masksData['align']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
switch ($data) {
@@ -3212,45 +3237,45 @@ class PowerPoint97 implements ReaderInterface
break;
}
}
- if ($masksData['lineSpacing'] == 1) {
+ if (1 == $masksData['lineSpacing']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['spaceBefore'] == 1) {
+ if (1 == $masksData['spaceBefore']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['spaceAfter'] == 1) {
+ if (1 == $masksData['spaceAfter']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['leftMargin'] == 1) {
+ if (1 == $masksData['leftMargin']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
- $arrayReturn['leftMargin'] = (int)round($data/6);
+ $arrayReturn['leftMargin'] = (int) round($data / 6);
}
- if ($masksData['indent'] == 1) {
+ if (1 == $masksData['indent']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
- $arrayReturn['indent'] = (int)round($data/6);
+ $arrayReturn['indent'] = (int) round($data / 6);
}
- if ($masksData['defaultTabSize'] == 1) {
+ if (1 == $masksData['defaultTabSize']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['tabStops'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['tabStops']) {
+ throw new FeatureNotImplementedException();
}
- if ($masksData['fontAlign'] == 1) {
+ if (1 == $masksData['fontAlign']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['charWrap'] == 1 || $masksData['wordWrap'] == 1 || $masksData['overflow'] == 1) {
+ if (1 == $masksData['charWrap'] || 1 == $masksData['wordWrap'] || 1 == $masksData['overflow']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['textDirection'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['textDirection']) {
+ throw new FeatureNotImplementedException();
}
return $arrayReturn;
@@ -3258,26 +3283,26 @@ class PowerPoint97 implements ReaderInterface
/**
* A structure that specifies language and spelling information for a run of text.
- * @param string $stream
- * @param integer $pos
- * @param string $strLenRT
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd909603(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd909603(v=office.12).aspx
*/
- private function readStructureTextSIRun($stream, $pos, $strLenRT)
+ private function readStructureTextSIRun(string $stream, int $pos, int $strLenRT): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
'strLenRT' => $strLenRT,
- );
+ ];
$arrayReturn['strLenRT'] -= self::getInt4d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
$data = self::getInt4d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
- $masksData = array();
+ $masksData = [];
$masksData['spell'] = ($data >> 0) & bindec('1');
$masksData['lang'] = ($data >> 1) & bindec('1');
$masksData['altLang'] = ($data >> 2) & bindec('1');
@@ -3289,30 +3314,30 @@ class PowerPoint97 implements ReaderInterface
$masksData['reserved1'] = ($data >> 8) & bindec('1');
$masksData['smartTag'] = ($data >> 9) & bindec('1');
- if ($masksData['spell'] == 1) {
+ if (1 == $masksData['spell']) {
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
- $masksSpell = array();
+ $masksSpell = [];
$masksSpell['error'] = ($data >> 0) & bindec('1');
$masksSpell['clean'] = ($data >> 1) & bindec('1');
$masksSpell['grammar'] = ($data >> 2) & bindec('1');
}
- if ($masksData['lang'] == 1) {
+ if (1 == $masksData['lang']) {
// $data = self::getInt2d($stream, $pos);
$arrayReturn['length'] += 2;
}
- if ($masksData['altLang'] == 1) {
+ if (1 == $masksData['altLang']) {
// $data = self::getInt2d($stream, $pos);
$arrayReturn['length'] += 2;
}
- if ($masksData['fBidi'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['fBidi']) {
+ throw new FeatureNotImplementedException();
}
- if ($masksData['fPp10ext'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['fPp10ext']) {
+ throw new FeatureNotImplementedException();
}
- if ($masksData['smartTag'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['smartTag']) {
+ throw new FeatureNotImplementedException();
}
return $arrayReturn;
@@ -3320,22 +3345,23 @@ class PowerPoint97 implements ReaderInterface
/**
* A structure that specifies tabbing, margins, and indentation for text.
- * @param string $stream
- * @param integer $pos
- * @return array
- * @throws \Exception
- * @link https://msdn.microsoft.com/en-us/library/dd922749(v=office.12).aspx
+ *
+ * @return array
+ *
+ * @throws FeatureNotImplementedException
+ *
+ * @see https://msdn.microsoft.com/en-us/library/dd922749(v=office.12).aspx
*/
- private function readStructureTextRuler($stream, $pos)
+ private function readStructureTextRuler(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = self::getInt4d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 4;
- $masksData = array();
+ $masksData = [];
$masksData['fDefaultTabSize'] = ($data >> 0) & bindec('1');
$masksData['fCLevels'] = ($data >> 1) & bindec('1');
$masksData['fTabStops'] = ($data >> 2) & bindec('1');
@@ -3350,64 +3376,64 @@ class PowerPoint97 implements ReaderInterface
$masksData['fIndent4'] = ($data >> 11) & bindec('1');
$masksData['fIndent5'] = ($data >> 12) & bindec('1');
- if ($masksData['fCLevels'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['fCLevels']) {
+ throw new FeatureNotImplementedException();
}
- if ($masksData['fDefaultTabSize'] == 1) {
- throw new \Exception('Feature not implemented (l.'.__LINE__.')');
+ if (1 == $masksData['fDefaultTabSize']) {
+ throw new FeatureNotImplementedException();
}
- if ($masksData['fTabStops'] == 1) {
+ if (1 == $masksData['fTabStops']) {
$count = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
- $arrayTabStops = array();
- for ($inc = 0; $inc < $count; $inc++) {
+ $arrayTabStops = [];
+ for ($inc = 0; $inc < $count; ++$inc) {
$position = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
$type = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
- $arrayTabStops[] = array(
+ $arrayTabStops[] = [
'position' => $position,
'type' => $type,
- );
+ ];
}
}
- if ($masksData['fLeftMargin1'] == 1) {
+ if (1 == $masksData['fLeftMargin1']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fIndent1'] == 1) {
+ if (1 == $masksData['fIndent1']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fLeftMargin2'] == 1) {
+ if (1 == $masksData['fLeftMargin2']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fIndent2'] == 1) {
+ if (1 == $masksData['fIndent2']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fLeftMargin3'] == 1) {
+ if (1 == $masksData['fLeftMargin3']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fIndent3'] == 1) {
+ if (1 == $masksData['fIndent3']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fLeftMargin4'] == 1) {
+ if (1 == $masksData['fLeftMargin4']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fIndent4'] == 1) {
+ if (1 == $masksData['fIndent4']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fLeftMargin5'] == 1) {
+ if (1 == $masksData['fLeftMargin5']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
- if ($masksData['fIndent5'] == 1) {
+ if (1 == $masksData['fIndent5']) {
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
$arrayReturn['length'] += 2;
}
@@ -3415,12 +3441,7 @@ class PowerPoint97 implements ReaderInterface
return $arrayReturn;
}
- /**
- * @param $stream
- * @param int $pos
- * @throws \Exception
- */
- private function readRecordNotesContainer($stream, $pos)
+ private function readRecordNotesContainer(string $stream, int $pos): void
{
// notesAtom
$notesAtom = $this->readRecordNotesAtom($stream, $pos);
@@ -3437,26 +3458,25 @@ class PowerPoint97 implements ReaderInterface
}
/**
- * @param $stream
- * @param int $pos
- * @return array
- * @throws \Exception
+ * @return array
+ *
+ * @throws InvalidFileFormatException
*/
- private function readRecordNotesAtom($stream, $pos)
+ private function readRecordNotesAtom(string $stream, int $pos): array
{
- $arrayReturn = array(
+ $arrayReturn = [
'length' => 0,
- );
+ ];
$data = $this->loadRecordHeader($stream, $pos);
- if ($data['recVer'] != 0x1 || $data['recInstance'] != 0x000 || $data['recType'] != self::RT_NOTESATOM || $data['recLen'] != 0x00000008) {
- throw new \Exception('File PowerPoint 97 in error (Location : NotesAtom > RecordHeader)');
+ if (0x1 != $data['recVer'] || 0x000 != $data['recInstance'] || self::RT_NOTESATOM != $data['recType'] || 0x00000008 != $data['recLen']) {
+ throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : NotesAtom > RecordHeader)');
}
// Record Header
$arrayReturn['length'] += 8;
// NotesAtom > slideIdRef
$notesIdRef = self::getInt4d($stream, $pos + $arrayReturn['length']);
- if ($notesIdRef == -2147483648) {
+ if (-2147483648 == $notesIdRef) {
$notesIdRef = 0;
}
$this->currentNote = $notesIdRef;
diff --git a/PhpOffice/PhpPresentation/Reader/ReaderInterface.php b/PhpOffice/PhpPresentation/Reader/ReaderInterface.php
old mode 100755
new mode 100644
index c5149e9..d3e06d3
--- a/PhpOffice/PhpPresentation/Reader/ReaderInterface.php
+++ b/PhpOffice/PhpPresentation/Reader/ReaderInterface.php
@@ -10,32 +10,30 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Reader;
+use PhpOffice\PhpPresentation\PhpPresentation;
+
/**
- * Reader interface
+ * Reader interface.
*/
interface ReaderInterface
{
/**
* Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
- *
- * @param string $pFilename
- * @return boolean
*/
- public function canRead($pFilename);
+ public function canRead(string $pFilename): bool;
/**
- * Loads PhpPresentation from file
- *
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * Loads PhpPresentation from file.
*/
- public function load($pFilename);
+ public function load(string $pFilename): PhpPresentation;
}
diff --git a/PhpOffice/PhpPresentation/Reader/Serialized.php b/PhpOffice/PhpPresentation/Reader/Serialized.php
old mode 100755
new mode 100644
index 7349ecd..33de31b
--- a/PhpOffice/PhpPresentation/Reader/Serialized.php
+++ b/PhpOffice/PhpPresentation/Reader/Serialized.php
@@ -10,29 +10,33 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Reader;
use PhpOffice\Common\File;
+use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
+use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
+use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter;
+use PhpOffice\PhpPresentation\Shape\Drawing\File as DrawingFile;
+use ZipArchive;
/**
- * Serialized format reader
+ * Serialized format reader.
*/
class Serialized implements ReaderInterface
{
/**
* Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
- *
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
*/
- public function canRead($pFilename)
+ public function canRead(string $pFilename): bool
{
return $this->fileSupportsUnserializePhpPresentation($pFilename);
}
@@ -40,15 +44,13 @@ class Serialized implements ReaderInterface
/**
* Does a file support UnserializePhpPresentation ?
*
- * @param string $pFilename
- * @throws \Exception
- * @return boolean
+ * @throws FileNotFoundException
*/
- public function fileSupportsUnserializePhpPresentation($pFilename = '')
+ public function fileSupportsUnserializePhpPresentation(string $pFilename): bool
{
// Check if file exists
if (!file_exists($pFilename)) {
- throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ throw new FileNotFoundException($pFilename);
}
// File exists, does it contain PhpPresentation.xml?
@@ -56,58 +58,63 @@ class Serialized implements ReaderInterface
}
/**
- * Loads PhpPresentation Serialized file
+ * Loads PhpPresentation Serialized file.
*
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
- * @throws \Exception
+ * @throws FileNotFoundException
+ * @throws InvalidFileFormatException
*/
- public function load($pFilename)
+ public function load(string $pFilename): PhpPresentation
{
// Check if file exists
if (!file_exists($pFilename)) {
- throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ throw new FileNotFoundException($pFilename);
}
// Unserialize... First make sure the file supports it!
if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
- throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\Serialized: " . $pFilename . ".");
+ throw new InvalidFileFormatException($pFilename, Serialized::class);
}
return $this->loadSerialized($pFilename);
}
/**
- * Load PhpPresentation Serialized file
+ * Load PhpPresentation Serialized file.
*
- * @param string $pFilename
- * @return \PhpOffice\PhpPresentation\PhpPresentation
+ * @throws InvalidFileFormatException
*/
- private function loadSerialized($pFilename)
+ private function loadSerialized(string $pFilename): PhpPresentation
{
- $oArchive = new \ZipArchive();
- if ($oArchive->open($pFilename) === true) {
- $xmlContent = $oArchive->getFromName('PhpPresentation.xml');
+ $oArchive = new ZipArchive();
+ if (true !== $oArchive->open($pFilename)) {
+ throw new InvalidFileFormatException($pFilename, Serialized::class);
+ }
- if (!empty($xmlContent)) {
- $xmlData = simplexml_load_string($xmlContent);
- $file = unserialize(base64_decode((string) $xmlData->data));
+ $xmlContent = $oArchive->getFromName('PhpPresentation.xml');
+ if (empty($xmlContent)) {
+ throw new InvalidFileFormatException($pFilename, Serialized::class, 'The file PhpPresentation.xml is malformed');
+ }
- // Update media links
- for ($i = 0; $i < $file->getSlideCount(); ++$i) {
- for ($j = 0; $j < $file->getSlide($i)->getShapeCollection()->count(); ++$j) {
- if ($file->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) {
- $imgTemp = $file->getSlide($i)->getShapeCollection()->offsetGet($j);
- $imgTemp->setPath('zip://' . $pFilename . '#media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME), false);
- }
+ $xmlData = simplexml_load_string($xmlContent);
+ $file = unserialize(base64_decode((string) $xmlData->data));
+
+ // Update media links
+ for ($i = 0; $i < $file->getSlideCount(); ++$i) {
+ for ($j = 0; $j < $file->getSlide($i)->getShapeCollection()->count(); ++$j) {
+ if ($file->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) {
+ $imgTemp = $file->getSlide($i)->getShapeCollection()->offsetGet($j);
+ $imgPath = 'zip://' . $pFilename . '#media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME);
+ if ($imgTemp instanceof DrawingFile) {
+ $imgTemp->setPath($imgPath, false);
+ } else {
+ $imgTemp->setPath($imgPath);
}
}
-
- $oArchive->close();
- return $file;
}
}
- return null;
+ $oArchive->close();
+
+ return $file;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/AbstractGraphic.php b/PhpOffice/PhpPresentation/Shape/AbstractGraphic.php
old mode 100755
new mode 100644
index 982a08b..e7a4e73
--- a/PhpOffice/PhpPresentation/Shape/AbstractGraphic.php
+++ b/PhpOffice/PhpPresentation/Shape/AbstractGraphic.php
@@ -10,91 +10,94 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\AbstractShape;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * Abstract drawing
+ * Abstract drawing.
*/
abstract class AbstractGraphic extends AbstractShape implements ComparableInterface
{
/**
- * Image counter
+ * Image counter.
*
* @var int
*/
private static $imageCounter = 0;
/**
- * Image index
+ * Image index.
*
* @var int
*/
private $imageIndex = 0;
/**
- * Name
+ * Name.
*
* @var string
*/
protected $name;
/**
- * Description
+ * Description.
*
* @var string
*/
protected $description;
/**
- * Proportional resize
+ * Proportional resize.
*
- * @var boolean
+ * @var bool
*/
protected $resizeProportional;
/**
- * Slide relation ID (should not be used by user code!)
+ * Slide relation ID (should not be used by user code!).
*
* @var string
*/
public $relationId = null;
/**
- * Create a new \PhpOffice\PhpPresentation\Slide\AbstractDrawing
+ * Create a new \PhpOffice\PhpPresentation\Slide\AbstractDrawing.
*/
public function __construct()
{
// Initialise values
- $this->name = '';
- $this->description = '';
+ $this->name = '';
+ $this->description = '';
$this->resizeProportional = true;
// Set image index
- self::$imageCounter++;
+ ++self::$imageCounter;
$this->imageIndex = self::$imageCounter;
// Initialize parent
parent::__construct();
}
-
+
public function __clone()
{
parent::__clone();
-
- self::$imageCounter++;
+
+ ++self::$imageCounter;
$this->imageIndex = self::$imageCounter;
}
/**
- * Get image index
+ * Get image index.
*
* @return int
*/
@@ -104,7 +107,7 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Get Name
+ * Get Name.
*
* @return string
*/
@@ -114,19 +117,21 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Set Name
+ * Set Name.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return $this
*/
public function setName($pValue = '')
{
$this->name = $pValue;
+
return $this;
}
/**
- * Get Description
+ * Get Description.
*
* @return string
*/
@@ -136,9 +141,10 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Set Description
+ * Set Description.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return $this
*/
public function setDescription($pValue = '')
@@ -149,16 +155,15 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Set Width
+ * Set Width.
*
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Shape\AbstractGraphic
+ * @return self
*/
- public function setWidth($pValue = 0)
+ public function setWidth(int $pValue = 0)
{
// Resize proportional?
- if ($this->resizeProportional && $pValue != 0 && $this->width != 0) {
- $ratio = $this->height / $this->width;
+ if ($this->resizeProportional && 0 != $pValue && 0 != $this->width) {
+ $ratio = $this->height / $this->width;
$this->height = (int) round($ratio * $pValue);
}
@@ -169,16 +174,15 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Set Height
+ * Set Height.
*
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Shape\AbstractGraphic
+ * @return self
*/
- public function setHeight($pValue = 0)
+ public function setHeight(int $pValue = 0)
{
// Resize proportional?
- if ($this->resizeProportional && $pValue != 0 && $this->height != 0) {
- $ratio = $this->width / $this->height;
+ if ($this->resizeProportional && 0 != $pValue && 0 != $this->height) {
+ $ratio = $this->width / $this->height;
$this->width = (int) round($ratio * $pValue);
}
@@ -189,22 +193,22 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Set width and height with proportional resize
+ * Set width and height with proportional resize.
+ *
* @author Vincent@luo MSN:kele_100@hotmail.com
- * @param int $width
- * @param int $height
- * @return \PhpOffice\PhpPresentation\Shape\AbstractGraphic
+ *
+ * @return self
*/
- public function setWidthAndHeight($width = 0, $height = 0)
+ public function setWidthAndHeight(int $width = 0, int $height = 0)
{
$xratio = $width / $this->width;
$yratio = $height / $this->height;
- if ($this->resizeProportional && !($width == 0 || $height == 0)) {
+ if ($this->resizeProportional && !(0 == $width || 0 == $height)) {
if (($xratio * $this->height) < $height) {
$this->height = (int) ceil($xratio * $this->height);
- $this->width = $width;
+ $this->width = $width;
} else {
- $this->width = (int) ceil($yratio * $this->width);
+ $this->width = (int) ceil($yratio * $this->width);
$this->height = $height;
}
}
@@ -213,9 +217,9 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Get ResizeProportional
+ * Get ResizeProportional.
*
- * @return boolean
+ * @return bool
*/
public function isResizeProportional()
{
@@ -223,12 +227,11 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Set ResizeProportional
+ * Set ResizeProportional.
*
- * @param boolean $pValue
- * @return \PhpOffice\PhpPresentation\Shape\AbstractGraphic
+ * @param bool $pValue
*/
- public function setResizeProportional($pValue = true)
+ public function setResizeProportional($pValue = true): self
{
$this->resizeProportional = $pValue;
@@ -236,11 +239,11 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->name . $this->description . parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/AutoShape.php b/PhpOffice/PhpPresentation/Shape/AutoShape.php
new file mode 100644
index 0000000..3d2c21c
--- /dev/null
+++ b/PhpOffice/PhpPresentation/Shape/AutoShape.php
@@ -0,0 +1,300 @@
+outline = new Outline();
+ }
+
+ /**
+ * @return string
+ */
+ public function getText(): string
+ {
+ return $this->text;
+ }
+
+ /**
+ * @param string $text
+ *
+ * @return self
+ */
+ public function setText(string $text): self
+ {
+ $this->text = $text;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param string $type
+ *
+ * @return self
+ */
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ /**
+ * @return Outline
+ */
+ public function getOutline(): Outline
+ {
+ return $this->outline;
+ }
+
+ /**
+ * @param Outline $outline
+ *
+ * @return self
+ */
+ public function setOutline(Outline $outline): self
+ {
+ $this->outline = $outline;
+
+ return $this;
+ }
+}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart.php b/PhpOffice/PhpPresentation/Shape/Chart.php
old mode 100755
new mode 100644
index a851801..3d56b73
--- a/PhpOffice/PhpPresentation/Shape/Chart.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -24,148 +27,187 @@ use PhpOffice\PhpPresentation\Shape\Chart\Title;
use PhpOffice\PhpPresentation\Shape\Chart\View3D;
/**
- * Chart element
+ * Chart element.
*/
class Chart extends AbstractGraphic implements ComparableInterface
{
+ public const BLANKAS_GAP = 'gap';
+ public const BLANKAS_ZERO = 'zero';
+ public const BLANKAS_SPAN = 'span';
+
/**
- * Title
+ * Title.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * @var Title
*/
private $title;
/**
- * Legend
+ * Legend.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * @var Legend
*/
private $legend;
/**
- * Plot area
+ * Plot area.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
+ * @var PlotArea
*/
private $plotArea;
/**
- * View 3D
+ * View 3D.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\View3D
+ * @var View3D
*/
private $view3D;
/**
- * Include spreadsheet for editing data? Requires PHPExcel in the same folder as PhpPresentation
+ * Is the spreadsheet included for editing data ?
*
* @var bool
*/
private $includeSpreadsheet = false;
/**
- * Create a new Chart
+ * How to display blank (missing) values? Not set by default.
+ *
+ * @var string
+ */
+ private $displayBlankAs = self::BLANKAS_ZERO;
+
+ /**
+ * Create a new Chart.
*/
public function __construct()
{
// Initialize
- $this->title = new Title();
- $this->legend = new Legend();
+ $this->title = new Title();
+ $this->legend = new Legend();
$this->plotArea = new PlotArea();
- $this->view3D = new View3D();
+ $this->view3D = new View3D();
// Initialize parent
parent::__construct();
}
-
+
public function __clone()
{
parent::__clone();
-
- $this->title = clone $this->title;
- $this->legend = clone $this->legend;
- $this->plotArea = clone $this->plotArea;
- $this->view3D = clone $this->view3D;
+
+ $this->title = clone $this->title;
+ $this->legend = clone $this->legend;
+ $this->plotArea = clone $this->plotArea;
+ $this->view3D = clone $this->view3D;
}
/**
- * Get Title
+ * How missing/blank values are displayed on chart (dispBlanksAs property)
*
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * @return string
*/
- public function getTitle()
+ public function getDisplayBlankAs(): string
+ {
+ return $this->displayBlankAs;
+ }
+
+ /**
+ * Get Title.
+ *
+ * @return Title
+ */
+ public function getTitle(): Title
{
return $this->title;
}
/**
- * Get Legend
+ * Get Legend.
*
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * @return Legend
*/
- public function getLegend()
+ public function getLegend(): Legend
{
return $this->legend;
}
/**
- * Get PlotArea
+ * Get PlotArea.
*
- * @return \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
+ * @return PlotArea
*/
- public function getPlotArea()
+ public function getPlotArea(): PlotArea
{
return $this->plotArea;
}
/**
- * Get View3D
+ * Get View3D.
*
- * @return \PhpOffice\PhpPresentation\Shape\Chart\View3D
+ * @return View3D
*/
- public function getView3D()
+ public function getView3D(): View3D
{
return $this->view3D;
}
/**
- * Include spreadsheet for editing data? Requires PHPExcel in the same folder as PhpPresentation
+ * Is the spreadsheet included for editing data ?
*
- * @return boolean
+ * @return bool
*/
- public function hasIncludedSpreadsheet()
+ public function hasIncludedSpreadsheet(): bool
{
return $this->includeSpreadsheet;
}
/**
- * Include spreadsheet for editing data? Requires PHPExcel in the same folder as PhpPresentation
+ * Define a way to display missing/blank values (dispBlanksAs property)
*
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart
+ * @param string $value
+ *
+ * @return self
*/
- public function setIncludeSpreadsheet($value = false)
+ public function setDisplayBlankAs(string $value): self
{
- $this->includeSpreadsheet = $value;
+ if (in_array($value, [self::BLANKAS_GAP, self::BLANKAS_SPAN, self::BLANKAS_ZERO])) {
+ $this->displayBlankAs = $value;
+ }
+
return $this;
}
/**
- * Get indexed filename (using image index)
+ * Is the spreadsheet included for editing data ?
+ *
+ * @param bool $value
+ *
+ * @return self
+ */
+ public function setIncludeSpreadsheet(bool $value = false): self
+ {
+ $this->includeSpreadsheet = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get indexed filename (using image index).
*
* @return string
*/
- public function getIndexedFilename()
+ public function getIndexedFilename(): string
{
return 'chart' . $this->getImageIndex() . '.xml';
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(parent::getHashCode() . $this->title->getHashCode() . $this->legend->getHashCode() . $this->plotArea->getHashCode() . $this->view3D->getHashCode() . ($this->includeSpreadsheet ? 1 : 0) . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Axis.php b/PhpOffice/PhpPresentation/Shape/Chart/Axis.php
old mode 100755
new mode 100644
index e9433da..86e1878
--- a/PhpOffice/PhpPresentation/Shape/Chart/Axis.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Axis.php
@@ -10,37 +10,50 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\ComparableInterface;
use PhpOffice\PhpPresentation\Style\Font;
use PhpOffice\PhpPresentation\Style\Outline;
-/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Axis
- */
class Axis implements ComparableInterface
{
- const AXIS_X = 'x';
- const AXIS_Y = 'y';
+ public const AXIS_X = 'x';
+ public const AXIS_Y = 'y';
- const TICK_MARK_NONE = 'none';
- const TICK_MARK_CROSS = 'cross';
- const TICK_MARK_INSIDE = 'in';
- const TICK_MARK_OUTSIDE = 'out';
+ public const TICK_MARK_NONE = 'none';
+ public const TICK_MARK_CROSS = 'cross';
+ public const TICK_MARK_INSIDE = 'in';
+ public const TICK_MARK_OUTSIDE = 'out';
+
+ public const TICK_LABEL_POSITION_NEXT_TO = 'nextTo';
+ public const TICK_LABEL_POSITION_HIGH = 'high';
+ public const TICK_LABEL_POSITION_LOW = 'low';
+
+ public const CROSSES_AUTO = 'autoZero';
+ public const CROSSES_MIN = 'min';
+ public const CROSSES_MAX = 'max';
/**
- * Title
+ * Title.
*
* @var string
*/
private $title = 'Axis Title';
+ /**
+ * @var int
+ */
+ private $titleRotation = 0;
+
/**
* Format code
*
@@ -49,19 +62,19 @@ class Axis implements ComparableInterface
private $formatCode = '';
/**
- * Font
+ * Font.
*
- * @var \PhpOffice\PhpPresentation\Style\Font
+ * @var Font
*/
private $font;
/**
- * @var Gridlines
+ * @var Gridlines|null
*/
protected $majorGridlines;
/**
- * @var Gridlines
+ * @var Gridlines|null
*/
protected $minorGridlines;
@@ -75,6 +88,16 @@ class Axis implements ComparableInterface
*/
protected $maxBounds;
+ /**
+ * @var string
+ */
+ protected $crossesAt = self::CROSSES_AUTO;
+
+ /**
+ * @var bool
+ */
+ protected $isReversedOrder = false;
+
/**
* @var string
*/
@@ -85,6 +108,11 @@ class Axis implements ComparableInterface
*/
protected $majorTickMark = self::TICK_MARK_NONE;
+ /**
+ * @var string
+ */
+ protected $tickLabelPosition = self::TICK_LABEL_POSITION_NEXT_TO;
+
/**
* @var float
*/
@@ -101,39 +129,40 @@ class Axis implements ComparableInterface
protected $outline;
/**
- * @var boolean
+ * @var bool
*/
protected $isVisible = true;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Axis instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Axis instance.
*
* @param string $title Title
*/
- public function __construct($title = 'Axis Title')
+ public function __construct(string $title = 'Axis Title')
{
$this->title = $title;
$this->outline = new Outline();
- $this->font = new Font();
+ $this->font = new Font();
}
/**
- * Get Title
+ * Get Title.
*
* @return string
*/
- public function getTitle()
+ public function getTitle(): string
{
return $this->title;
}
/**
- * Set Title
+ * Set Title.
*
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * @param string $value
+ *
+ * @return self
*/
- public function setTitle($value = 'Axis Title')
+ public function setTitle(string $value = 'Axis Title'): self
{
$this->title = $value;
@@ -141,45 +170,47 @@ class Axis implements ComparableInterface
}
/**
- * Get font
+ * Get font.
*
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * @return Font|null
*/
- public function getFont()
+ public function getFont(): ?Font
{
return $this->font;
}
/**
- * Set font
+ * Set font.
*
- * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * @param Font|null $font
+ *
+ * @return self
*/
- public function setFont(Font $pFont = null)
+ public function setFont(Font $font = null): self
{
- $this->font = $pFont;
+ $this->font = $font;
+
return $this;
}
/**
- * Get Format Code
+ * Get Format Code.
*
* @return string
*/
- public function getFormatCode()
+ public function getFormatCode(): string
{
return $this->formatCode;
}
/**
- * Set Format Code
+ * Set Format Code.
*
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * @param string $value
+ *
+ * @return self
*/
- public function setFormatCode($value = '')
+ public function setFormatCode(string $value = ''): self
{
$this->formatCode = $value;
@@ -189,162 +220,230 @@ class Axis implements ComparableInterface
/**
* @return int|null
*/
- public function getMinBounds()
+ public function getMinBounds(): ?int
{
return $this->minBounds;
}
/**
* @param int|null $minBounds
- * @return Axis
+ *
+ * @return self
*/
- public function setMinBounds($minBounds = null)
+ public function setMinBounds(int $minBounds = null): self
{
- $this->minBounds = is_null($minBounds) ? null : (int)$minBounds;
+ $this->minBounds = is_null($minBounds) ? null : $minBounds;
+
return $this;
}
/**
* @return int|null
*/
- public function getMaxBounds()
+ public function getMaxBounds(): ?int
{
return $this->maxBounds;
}
/**
* @param int|null $maxBounds
- * @return Axis
+ *
+ * @return self
*/
- public function setMaxBounds($maxBounds = null)
+ public function setMaxBounds(int $maxBounds = null): self
{
- $this->maxBounds = is_null($maxBounds) ? null : (int)$maxBounds;
- return $this;
- }
+ $this->maxBounds = is_null($maxBounds) ? null : $maxBounds;
- /**
- * @return Gridlines
- */
- public function getMajorGridlines()
- {
- return $this->majorGridlines;
- }
-
- /**
- * @param Gridlines $majorGridlines
- * @return Axis
- */
- public function setMajorGridlines(Gridlines $majorGridlines)
- {
- $this->majorGridlines = $majorGridlines;
- return $this;
- }
-
- /**
- * @return Gridlines
- */
- public function getMinorGridlines()
- {
- return $this->minorGridlines;
- }
-
- /**
- * @param Gridlines $minorGridlines
- * @return Axis
- */
- public function setMinorGridlines(Gridlines $minorGridlines)
- {
- $this->minorGridlines = $minorGridlines;
return $this;
}
/**
* @return string
*/
- public function getMinorTickMark()
+ public function getCrossesAt(): string
+ {
+ return $this->crossesAt;
+ }
+
+ /**
+ * @param string $value
+ *
+ * @return self
+ */
+ public function setCrossesAt(string $value = self::CROSSES_AUTO): self
+ {
+ $this->crossesAt = $value;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isReversedOrder(): bool
+ {
+ return $this->isReversedOrder;
+ }
+
+ /**
+ * @param bool $value
+ *
+ * @return self
+ */
+ public function setIsReversedOrder(bool $value = false): self
+ {
+ $this->isReversedOrder = $value;
+
+ return $this;
+ }
+
+ public function getMajorGridlines(): ?Gridlines
+ {
+ return $this->majorGridlines;
+ }
+
+ public function setMajorGridlines(Gridlines $majorGridlines): self
+ {
+ $this->majorGridlines = $majorGridlines;
+
+ return $this;
+ }
+
+ public function getMinorGridlines(): ?Gridlines
+ {
+ return $this->minorGridlines;
+ }
+
+ public function setMinorGridlines(Gridlines $minorGridlines): self
+ {
+ $this->minorGridlines = $minorGridlines;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMinorTickMark(): string
{
return $this->minorTickMark;
}
/**
- * @param string $pTickMark
- * @return Axis
+ * @param string $tickMark
+ *
+ * @return self
*/
- public function setMinorTickMark($pTickMark = self::TICK_MARK_NONE)
+ public function setMinorTickMark(string $tickMark = self::TICK_MARK_NONE): self
{
- $this->minorTickMark = $pTickMark;
+ $this->minorTickMark = $tickMark;
+
return $this;
}
/**
* @return string
*/
- public function getMajorTickMark()
+ public function getMajorTickMark(): string
{
return $this->majorTickMark;
}
/**
- * @param string $pTickMark
- * @return Axis
+ * @param string $tickMark
+ *
+ * @return self
*/
- public function setMajorTickMark($pTickMark = self::TICK_MARK_NONE)
+ public function setMajorTickMark(string $tickMark = self::TICK_MARK_NONE): self
{
- $this->majorTickMark = $pTickMark;
+ $this->majorTickMark = $tickMark;
+
return $this;
}
/**
- * @return float
+ * @return float|null
*/
- public function getMinorUnit()
+ public function getMinorUnit(): ?float
{
return $this->minorUnit;
}
/**
- * @param float $pUnit
- * @return Axis
+ * @param float|null $unit
+ *
+ * @return self
*/
- public function setMinorUnit($pUnit = null)
+ public function setMinorUnit($unit = null): self
{
- $this->minorUnit = $pUnit;
+ $this->minorUnit = $unit;
+
return $this;
}
/**
- * @return float
+ * @return float|null
*/
- public function getMajorUnit()
+ public function getMajorUnit(): ?float
{
return $this->majorUnit;
}
/**
- * @param float $pUnit
- * @return Axis
+ * @param float|null $unit
+ *
+ * @return self
*/
- public function setMajorUnit($pUnit = null)
+ public function setMajorUnit(float $unit = null): self
{
- $this->majorUnit = $pUnit;
+ $this->majorUnit = $unit;
+
return $this;
}
/**
* @return Outline
*/
- public function getOutline()
+ public function getOutline(): Outline
{
return $this->outline;
}
/**
* @param Outline $outline
- * @return Axis
+ *
+ * @return self
*/
- public function setOutline(Outline $outline)
+ public function setOutline(Outline $outline): self
{
$this->outline = $outline;
+
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTitleRotation(): int
+ {
+ return $this->titleRotation;
+ }
+
+ /**
+ * @param int $titleRotation
+ *
+ * @return self
+ */
+ public function setTitleRotation(int $titleRotation): self
+ {
+ if ($titleRotation < 0) {
+ $titleRotation = 0;
+ }
+ if ($titleRotation > 360) {
+ $titleRotation = 360;
+ }
+ $this->titleRotation = $titleRotation;
+
return $this;
}
@@ -353,64 +452,95 @@ class Axis implements ComparableInterface
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->title . $this->formatCode . __CLASS__);
}
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
- * @return $this
+ * @param int $value Hash index
+ *
+ * @return self
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
return $this;
}
/**
* Axis is hidden ?
- * @return boolean
+ *
+ * @return bool
*/
- public function isVisible()
+ public function isVisible(): bool
{
return $this->isVisible;
}
/**
- * Hide an axis
+ * Hide an axis.
*
- * @param boolean $value delete
- * @return $this
+ * @param bool $value delete
+ *
+ * @return self
*/
- public function setIsVisible($value)
+ public function setIsVisible(bool $value): self
{
- $this->isVisible = (bool)$value;
+ $this->isVisible = $value;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTickLabelPosition(): string
+ {
+ return $this->tickLabelPosition;
+ }
+
+ /**
+ * @param string $value
+ *
+ * @return self
+ */
+ public function setTickLabelPosition(string $value = self::TICK_LABEL_POSITION_NEXT_TO): self
+ {
+ if (in_array($value, [
+ self::TICK_LABEL_POSITION_HIGH,
+ self::TICK_LABEL_POSITION_LOW,
+ self::TICK_LABEL_POSITION_NEXT_TO,
+ ])) {
+ $this->tickLabelPosition = $value;
+ }
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Gridlines.php b/PhpOffice/PhpPresentation/Shape/Chart/Gridlines.php
old mode 100755
new mode 100644
index 4040a5a..83f1248
--- a/PhpOffice/PhpPresentation/Shape/Chart/Gridlines.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Gridlines.php
@@ -1,4 +1,22 @@
outline = $outline;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Legend.php b/PhpOffice/PhpPresentation/Shape/Chart/Legend.php
old mode 100755
new mode 100644
index 989d932..6878f62
--- a/PhpOffice/PhpPresentation/Shape/Chart/Legend.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Legend.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -24,102 +27,102 @@ use PhpOffice\PhpPresentation\Style\Fill;
use PhpOffice\PhpPresentation\Style\Font;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * \PhpOffice\PhpPresentation\Shape\Chart\Legend.
*/
class Legend implements ComparableInterface
{
/** Legend positions */
- const POSITION_BOTTOM = 'b';
- const POSITION_LEFT = 'l';
- const POSITION_RIGHT = 'r';
- const POSITION_TOP = 't';
- const POSITION_TOPRIGHT = 'tr';
+ public const POSITION_BOTTOM = 'b';
+ public const POSITION_LEFT = 'l';
+ public const POSITION_RIGHT = 'r';
+ public const POSITION_TOP = 't';
+ public const POSITION_TOPRIGHT = 'tr';
/**
- * Visible
+ * Visible.
*
- * @var boolean
+ * @var bool
*/
private $visible = true;
/**
- * Position
+ * Position.
*
* @var string
*/
private $position = self::POSITION_RIGHT;
/**
- * OffsetX (as a fraction of the chart)
+ * OffsetX (as a fraction of the chart).
*
* @var float
*/
private $offsetX = 0;
/**
- * OffsetY (as a fraction of the chart)
+ * OffsetY (as a fraction of the chart).
*
* @var float
*/
private $offsetY = 0;
/**
- * Width (as a fraction of the chart)
+ * Width (as a fraction of the chart).
*
* @var float
*/
private $width = 0;
/**
- * Height (as a fraction of the chart)
+ * Height (as a fraction of the chart).
*
* @var float
*/
private $height = 0;
/**
- * Font
+ * Font.
*
- * @var \PhpOffice\PhpPresentation\Style\Font
+ * @var Font|null
*/
private $font;
/**
- * Border
+ * Border.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $border;
/**
- * Fill
+ * Fill.
*
* @var \PhpOffice\PhpPresentation\Style\Fill
*/
private $fill;
/**
- * Alignment
+ * Alignment.
*
* @var \PhpOffice\PhpPresentation\Style\Alignment
*/
private $alignment;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Legend instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Legend instance.
*/
public function __construct()
{
- $this->font = new Font();
- $this->border = new Border();
- $this->fill = new Fill();
+ $this->font = new Font();
+ $this->border = new Border();
+ $this->fill = new Fill();
$this->alignment = new Alignment();
}
/**
- * Get Visible
+ * Get Visible.
*
- * @return boolean
+ * @return bool
*/
public function isVisible()
{
@@ -127,19 +130,21 @@ class Legend implements ComparableInterface
}
/**
- * Set Visible
+ * Set Visible.
+ *
+ * @param bool $value
*
- * @param boolean $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
*/
public function setVisible($value = true)
{
$this->visible = $value;
+
return $this;
}
/**
- * Get Position
+ * Get Position.
*
* @return string
*/
@@ -149,130 +154,113 @@ class Legend implements ComparableInterface
}
/**
- * Set Position
+ * Set Position.
+ *
+ * @param string $value
*
- * @param string $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
*/
public function setPosition($value = self::POSITION_RIGHT)
{
$this->position = $value;
+
return $this;
}
/**
- * Get OffsetX (as a fraction of the chart)
- *
- * @return float
+ * Get OffsetX (as a fraction of the chart).
*/
- public function getOffsetX()
+ public function getOffsetX(): float
{
return $this->offsetX;
}
/**
- * Set OffsetX (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * Set OffsetX (as a fraction of the chart).
*/
- public function setOffsetX($value = 0)
+ public function setOffsetX(float $pValue = 0): self
{
- $this->offsetX = (double)$value;
+ $this->offsetX = $pValue;
+
return $this;
}
/**
- * Get OffsetY (as a fraction of the chart)
- *
- * @return float
+ * Get OffsetY (as a fraction of the chart).
*/
- public function getOffsetY()
+ public function getOffsetY(): float
{
return $this->offsetY;
}
/**
- * Set OffsetY (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * Set OffsetY (as a fraction of the chart).
*/
- public function setOffsetY($value = 0)
+ public function setOffsetY(float $pValue = 0): self
{
- $this->offsetY = (double)$value;
+ $this->offsetY = $pValue;
+
return $this;
}
/**
- * Get Width (as a fraction of the chart)
- *
- * @return float
+ * Get Width (as a fraction of the chart).
*/
- public function getWidth()
+ public function getWidth(): float
{
return $this->width;
}
/**
- * Set Width (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * Set Width (as a fraction of the chart).
*/
- public function setWidth($value = 0)
+ public function setWidth(float $pValue = 0): self
{
- $this->width = (double)$value;
+ $this->width = $pValue;
+
return $this;
}
/**
- * Get Height (as a fraction of the chart)
- *
- * @return float
+ * Get Height (as a fraction of the chart).
*/
- public function getHeight()
+ public function getHeight(): float
{
return $this->height;
}
/**
- * Set Height (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * Set Height (as a fraction of the chart).
*/
- public function setHeight($value = 0)
+ public function setHeight(float $value = 0): self
{
- $this->height = (double)$value;
+ $this->height = $value;
+
return $this;
}
/**
- * Get font
- *
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Get font.
*/
- public function getFont()
+ public function getFont(): ?Font
{
return $this->font;
}
/**
- * Set font
+ * Set font.
*
- * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
+ * @param Font|null $pFont Font
*/
- public function setFont(Font $pFont = null)
+ public function setFont(Font $pFont = null): self
{
$this->font = $pFont;
+
return $this;
}
/**
- * Get Border
+ * Get Border.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -282,19 +270,19 @@ class Legend implements ComparableInterface
}
/**
- * Set Border
+ * Set Border.
*
- * @param \PhpOffice\PhpPresentation\Style\Border $border
* @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
*/
public function setBorder(Border $border)
{
$this->border = $border;
+
return $this;
}
/**
- * Get Fill
+ * Get Fill.
*
* @return \PhpOffice\PhpPresentation\Style\Fill
*/
@@ -304,19 +292,19 @@ class Legend implements ComparableInterface
}
/**
- * Set Fill
+ * Set Fill.
*
- * @param \PhpOffice\PhpPresentation\Style\Fill $fill
* @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
*/
public function setFill(Fill $fill)
{
$this->fill = $fill;
+
return $this;
}
/**
- * Get alignment
+ * Get alignment.
*
* @return \PhpOffice\PhpPresentation\Style\Alignment
*/
@@ -326,59 +314,61 @@ class Legend implements ComparableInterface
}
/**
- * Set alignment
+ * Set alignment.
*
- * @param \PhpOffice\PhpPresentation\Style\Alignment $alignment
* @return \PhpOffice\PhpPresentation\Shape\Chart\Legend
*/
public function setAlignment(Alignment $alignment)
{
$this->alignment = $alignment;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->position . $this->offsetX . $this->offsetY . $this->width . $this->height . $this->font->getHashCode() . $this->border->getHashCode() . $this->fill->getHashCode() . $this->alignment->getHashCode() . ($this->visible ? 't' : 'f') . __CLASS__);
}
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
* @return Legend
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Marker.php b/PhpOffice/PhpPresentation/Shape/Chart/Marker.php
old mode 100755
new mode 100644
index c1ca912..008f787
--- a/PhpOffice/PhpPresentation/Shape/Chart/Marker.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Marker.php
@@ -10,30 +10,36 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
-/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Axis
- */
+use PhpOffice\PhpPresentation\Style\Border;
+use PhpOffice\PhpPresentation\Style\Fill;
+
class Marker
{
- const SYMBOL_CIRCLE = 'circle';
- const SYMBOL_DASH = 'dash';
- const SYMBOL_DIAMOND = 'diamond';
- const SYMBOL_DOT = 'dot';
- const SYMBOL_NONE = 'none';
- const SYMBOL_PLUS = 'plus';
- const SYMBOL_SQUARE = 'square';
- const SYMBOL_STAR = 'star';
- const SYMBOL_TRIANGLE = 'triangle';
- const SYMBOL_X = 'x';
+ public const SYMBOL_CIRCLE = 'circle';
+ public const SYMBOL_DASH = 'dash';
+ public const SYMBOL_DIAMOND = 'diamond';
+ public const SYMBOL_DOT = 'dot';
+ public const SYMBOL_NONE = 'none';
+ public const SYMBOL_PLUS = 'plus';
+ public const SYMBOL_SQUARE = 'square';
+ public const SYMBOL_STAR = 'star';
+ public const SYMBOL_TRIANGLE = 'triangle';
+ public const SYMBOL_X = 'x';
- public static $arraySymbol = array(
+ /**
+ * @var array
+ */
+ public static $arraySymbol = [
self::SYMBOL_CIRCLE,
self::SYMBOL_DASH,
self::SYMBOL_DIAMOND,
@@ -43,8 +49,8 @@ class Marker
self::SYMBOL_SQUARE,
self::SYMBOL_STAR,
self::SYMBOL_TRIANGLE,
- self::SYMBOL_X
- );
+ self::SYMBOL_X,
+ ];
/**
* @var string
@@ -57,38 +63,82 @@ class Marker
protected $size = 5;
/**
- * @return string
+ * @var Fill
*/
- public function getSymbol()
+ protected $fill;
+
+ /**
+ * @var Border
+ */
+ protected $border;
+
+ public function __construct()
+ {
+ $this->fill = new Fill();
+ $this->border = new Border();
+ }
+
+ public function getSymbol(): string
{
return $this->symbol;
}
- /**
- * @param string $symbol
- * @return $this
- */
- public function setSymbol($symbol = self::SYMBOL_NONE)
+ public function setSymbol(string $symbol = self::SYMBOL_NONE): self
{
$this->symbol = $symbol;
+
return $this;
}
- /**
- * @return int
- */
- public function getSize()
+ public function getSize(): int
{
return $this->size;
}
- /**
- * @param int $size
- * @return $this
- */
- public function setSize($size = 5)
+ public function setSize(int $size = 5): self
{
$this->size = $size;
+
+ return $this;
+ }
+
+ /**
+ * @return Fill
+ */
+ public function getFill(): Fill
+ {
+ return $this->fill;
+ }
+
+ /**
+ * @param Fill $fill
+ *
+ * @return self
+ */
+ public function setFill(Fill $fill): self
+ {
+ $this->fill = $fill;
+
+ return $this;
+ }
+
+ /**
+ * @return Border
+ */
+ public function getBorder(): Border
+ {
+ return $this->border;
+ }
+
+ /**
+ * @param Border $border
+ *
+ * @return self
+ */
+ public function setBorder(Border $border): self
+ {
+ $this->border = $border;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/PlotArea.php b/PhpOffice/PhpPresentation/Shape/Chart/PlotArea.php
old mode 100755
new mode 100644
index a46a3b2..2eb9717
--- a/PhpOffice/PhpPresentation/Shape/Chart/PlotArea.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/PlotArea.php
@@ -10,108 +10,99 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\ComparableInterface;
+use PhpOffice\PhpPresentation\Exception\UndefinedChartTypeException;
use PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
+ * \PhpOffice\PhpPresentation\Shape\Chart\PlotArea.
*/
class PlotArea implements ComparableInterface
{
/**
- * Type
+ * Type.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType
+ * @var AbstractType|null
*/
private $type;
/**
- * Axis X
+ * Axis X.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * @var Axis
*/
private $axisX;
/**
- * Axis Y
+ * Axis Y.
*
- * @var \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * @var Axis
*/
private $axisY;
/**
- * OffsetX (as a fraction of the chart)
+ * OffsetX (as a fraction of the chart).
*
* @var float
*/
private $offsetX = 0;
/**
- * OffsetY (as a fraction of the chart)
+ * OffsetY (as a fraction of the chart).
*
* @var float
*/
private $offsetY = 0;
/**
- * Width (as a fraction of the chart)
+ * Width (as a fraction of the chart).
*
* @var float
*/
private $width = 0;
/**
- * Height (as a fraction of the chart)
+ * Height (as a fraction of the chart).
*
* @var float
*/
private $height = 0;
- /**
- * Create a new \PhpOffice\PhpPresentation\Shape\Chart\PlotArea instance
- */
public function __construct()
{
- $this->type = null;
$this->axisX = new Axis();
$this->axisY = new Axis();
}
-
+
public function __clone()
{
- $this->axisX = clone $this->axisX;
- $this->axisY = clone $this->axisY;
+ $this->axisX = clone $this->axisX;
+ $this->axisY = clone $this->axisY;
}
/**
- * Get type
- *
- * @return AbstractType
- * @throws \Exception
+ * @throws UndefinedChartTypeException
*/
- public function getType()
+ public function getType(): AbstractType
{
if (is_null($this->type)) {
- throw new \Exception('Chart type has not been set.');
+ throw new UndefinedChartTypeException();
}
return $this->type;
}
- /**
- * Set type
- *
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractType $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
- */
- public function setType(Type\AbstractType $value)
+ public function setType(AbstractType $value): self
{
$this->type = $value;
@@ -119,159 +110,141 @@ class PlotArea implements ComparableInterface
}
/**
- * Get Axis X
- *
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * Get Axis X.
*/
- public function getAxisX()
+ public function getAxisX(): Axis
{
return $this->axisX;
}
/**
- * Get Axis Y
- *
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Axis
+ * Get Axis Y.
*/
- public function getAxisY()
+ public function getAxisY(): Axis
{
return $this->axisY;
}
/**
- * Get OffsetX (as a fraction of the chart)
- *
- * @return float
+ * Get OffsetX (as a fraction of the chart).
*/
- public function getOffsetX()
+ public function getOffsetX(): float
{
return $this->offsetX;
}
/**
- * Set OffsetX (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
+ * Set OffsetX (as a fraction of the chart).
*/
- public function setOffsetX($value = 0)
+ public function setOffsetX(float $pValue = 0): self
{
- $this->offsetX = (double)$value;
+ $this->offsetX = $pValue;
return $this;
}
/**
- * Get OffsetY (as a fraction of the chart)
- *
- * @return float
+ * Get OffsetY (as a fraction of the chart).
*/
- public function getOffsetY()
+ public function getOffsetY(): float
{
return $this->offsetY;
}
/**
- * Set OffsetY (as a fraction of the chart)
+ * Set OffsetY (as a fraction of the chart).
*
- * @param float|int $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
*/
- public function setOffsetY($value = 0)
+ public function setOffsetY(float $pValue = 0): self
{
- $this->offsetY = (double)$value;
+ $this->offsetY = $pValue;
return $this;
}
/**
- * Get Width (as a fraction of the chart)
- *
- * @return float
+ * Get Width (as a fraction of the chart).
*/
- public function getWidth()
+ public function getWidth(): float
{
return $this->width;
}
/**
- * Set Width (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
+ * Set Width (as a fraction of the chart).
*/
- public function setWidth($value = 0)
+ public function setWidth(int $pValue = 0): self
{
- $this->width = (double)$value;
+ $this->width = $pValue;
return $this;
}
/**
- * Get Height (as a fraction of the chart)
- *
- * @return float
+ * Get Height (as a fraction of the chart).
*/
- public function getHeight()
+ public function getHeight(): float
{
return $this->height;
}
/**
- * Set Height (as a fraction of the chart)
+ * Set Height (as a fraction of the chart).
*
- * @param float|int $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\PlotArea
*/
- public function setHeight($value = 0)
+ public function setHeight(float $value = 0): self
{
- $this->height = (double)$value;
+ $this->height = $value;
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5((is_null($this->type) ? 'null' : $this->type->getHashCode()) . $this->axisX->getHashCode() . $this->axisY->getHashCode() . $this->offsetX . $this->offsetY . $this->width . $this->height . __CLASS__);
}
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
* @return PlotArea
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Series.php b/PhpOffice/PhpPresentation/Shape/Chart/Series.php
old mode 100755
new mode 100644
index 73cd03d..ea1bf49
--- a/PhpOffice/PhpPresentation/Shape/Chart/Series.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Series.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -22,54 +25,49 @@ use PhpOffice\PhpPresentation\Style\Fill;
use PhpOffice\PhpPresentation\Style\Font;
use PhpOffice\PhpPresentation\Style\Outline;
-/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Series
- */
class Series implements ComparableInterface
{
/* Label positions */
- const LABEL_BESTFIT = 'bestFit';
- const LABEL_BOTTOM = 'b';
- const LABEL_CENTER = 'ctr';
- const LABEL_INSIDEBASE = 'inBase';
- const LABEL_INSIDEEND = 'inEnd';
- const LABEL_LEFT = 'i';
- const LABEL_OUTSIDEEND = 'outEnd';
- const LABEL_RIGHT = 'r';
- const LABEL_TOP = 't';
+ public const LABEL_BESTFIT = 'bestFit';
+ public const LABEL_BOTTOM = 'b';
+ public const LABEL_CENTER = 'ctr';
+ public const LABEL_INSIDEBASE = 'inBase';
+ public const LABEL_INSIDEEND = 'inEnd';
+ public const LABEL_LEFT = 'i';
+ public const LABEL_OUTSIDEEND = 'outEnd';
+ public const LABEL_RIGHT = 'r';
+ public const LABEL_TOP = 't';
/**
- * DataPointFills (key/value)
- * @var array
+ * DataPointFills (key/value).
+ *
+ * @var array
*/
- protected $dataPointFills = array();
+ protected $dataPointFills = [];
/**
- * Data Label Number Format
+ * Data Label Number Format.
+ *
* @var string
*/
protected $DlblNumFormat = '';
/**
- * Separator
- * @var string
+ * @var string|null
*/
- protected $separator = null;
+ protected $separator;
/**
- * Fill
- * @var \PhpOffice\PhpPresentation\Style\Fill
+ * @var Fill|null
*/
protected $fill;
/**
- * Font
- * @var \PhpOffice\PhpPresentation\Style\Font
+ * @var Font|null
*/
protected $font;
/**
- * Label position
* @var string
*/
protected $labelPosition = 'ctr';
@@ -80,98 +78,101 @@ class Series implements ComparableInterface
protected $marker;
/**
- * @var Outline
+ * @var Outline|null
*/
protected $outline;
/**
- * Show Category Name
- * @var boolean
+ * Show Category Name.
+ *
+ * @var bool
*/
private $showCategoryName = false;
/**
- * Show Leader Lines
- * @var boolean
+ * Show Leader Lines.
+ *
+ * @var bool
*/
private $showLeaderLines = true;
/**
- * Show Legend Key
- * @var boolean
+ * Show Legend Key.
+ *
+ * @var bool
*/
private $showLegendKey = false;
/**
- * ShowPercentage
- * @var boolean
+ * ShowPercentage.
+ *
+ * @var bool
*/
private $showPercentage = false;
/**
- * ShowSeriesName
- * @var boolean
+ * ShowSeriesName.
+ *
+ * @var bool
*/
private $showSeriesName = false;
/**
- * ShowValue
- * @var boolean
+ * ShowValue.
+ *
+ * @var bool
*/
private $showValue = true;
/**
- * Title
+ * Title.
+ *
* @var string
*/
private $title = 'Series Title';
/**
- * Values (key/value)
- * @var array
+ * Values (key/value).
+ *
+ * @var array
*/
- private $values = array();
+ private $values = [];
/**
- * Hash index
- * @var string
+ * Hash index.
+ *
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Series instance
- *
- * @param string $title Title
- * @param array $values Values
+ * @param string $title
+ * @param array $values
*/
- public function __construct($title = 'Series Title', $values = array())
+ public function __construct(string $title = 'Series Title', array $values = [])
{
$this->fill = new Fill();
$this->font = new Font();
$this->font->setName('Calibri');
$this->font->setSize(9);
- $this->title = $title;
- $this->values = $values;
$this->marker = new Marker();
+
+ $this->title = $title;
+ $this->values = $values;
}
/**
- * Get Title
- *
- * @return string
+ * Get Title.
*/
- public function getTitle()
+ public function getTitle(): string
{
return $this->title;
}
/**
- * Set Title
- *
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set Title.
*/
- public function setTitle($value = 'Series Title')
+ public function setTitle(string $value = 'Series Title'): self
{
$this->title = $value;
@@ -179,66 +180,50 @@ class Series implements ComparableInterface
}
/**
- * Get Data Label NumFormat
- *
- * @return string
+ * Get Data Label NumFormat.
*/
- public function getDlblNumFormat()
+ public function getDlblNumFormat(): string
{
return $this->DlblNumFormat;
}
/**
- * Has Data Label NumFormat
- *
- * @return string
+ * Has Data Label NumFormat.
*/
- public function hasDlblNumFormat()
+ public function hasDlblNumFormat(): bool
{
return !empty($this->DlblNumFormat);
}
/**
- * Set Data Label NumFormat
- *
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set Data Label NumFormat.
*/
- public function setDlblNumFormat($value = '')
+ public function setDlblNumFormat(string $value = ''): self
{
$this->DlblNumFormat = $value;
+
return $this;
}
/**
- * Get Fill
- *
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @return Fill
*/
- public function getFill()
+ public function getFill(): ?Fill
{
return $this->fill;
}
- /**
- * Set Fill
- *
- * @param \PhpOffice\PhpPresentation\Style\Fill $fill
- * @return Series
- */
- public function setFill(Fill $fill = null)
+ public function setFill(Fill $fill = null): self
{
$this->fill = $fill;
+
return $this;
}
/**
- * Get DataPointFill
- *
- * @param int $dataPointIndex Data point index.
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @param int $dataPointIndex data point index
*/
- public function getDataPointFill($dataPointIndex)
+ public function getDataPointFill(int $dataPointIndex): Fill
{
if (!isset($this->dataPointFills[$dataPointIndex])) {
$this->dataPointFills[$dataPointIndex] = new Fill();
@@ -248,46 +233,44 @@ class Series implements ComparableInterface
}
/**
- * Get DataPointFills
- *
* @return Fill[]
*/
- public function getDataPointFills()
+ public function getDataPointFills(): array
{
return $this->dataPointFills;
}
/**
- * Get Values
+ * Get Values.
*
- * @return array
+ * @return array
*/
- public function getValues()
+ public function getValues(): array
{
return $this->values;
}
/**
- * Set Values
+ * Set Values.
*
- * @param array $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * @param array $values
*/
- public function setValues($value = array())
+ public function setValues(array $values = []): self
{
- $this->values = $value;
+ $this->values = $values;
return $this;
}
/**
- * Add Value
+ * Add Value.
*
- * @param mixed $key
- * @param mixed $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * @param string $key
+ * @param string|null $value
+ *
+ * @return self
*/
- public function addValue($key, $value)
+ public function addValue(string $key, ?string $value): self
{
$this->values[$key] = $value;
@@ -295,22 +278,17 @@ class Series implements ComparableInterface
}
/**
- * Get ShowSeriesName
- *
- * @return boolean
+ * Get ShowSeriesName.
*/
- public function hasShowSeriesName()
+ public function hasShowSeriesName(): bool
{
return $this->showSeriesName;
}
/**
- * Set ShowSeriesName
- *
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set ShowSeriesName.
*/
- public function setShowSeriesName($value)
+ public function setShowSeriesName(bool $value): self
{
$this->showSeriesName = $value;
@@ -318,22 +296,17 @@ class Series implements ComparableInterface
}
/**
- * Get ShowCategoryName
- *
- * @return boolean
+ * Get ShowCategoryName.
*/
- public function hasShowCategoryName()
+ public function hasShowCategoryName(): bool
{
return $this->showCategoryName;
}
/**
- * Set ShowCategoryName
- *
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set ShowCategoryName.
*/
- public function setShowCategoryName($value)
+ public function setShowCategoryName(bool $value): self
{
$this->showCategoryName = $value;
@@ -341,45 +314,35 @@ class Series implements ComparableInterface
}
/**
- * Get ShowValue
- *
- * @return boolean
+ * Get ShowValue.
*/
- public function hasShowLegendKey()
+ public function hasShowLegendKey(): bool
{
return $this->showLegendKey;
}
/**
- * Set ShowValue
- *
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set ShowValue.
*/
- public function setShowLegendKey($value)
+ public function setShowLegendKey(bool $value): self
{
- $this->showLegendKey = (bool)$value;
+ $this->showLegendKey = $value;
return $this;
}
/**
- * Get ShowValue
- *
- * @return boolean
+ * Get ShowValue.
*/
- public function hasShowValue()
+ public function hasShowValue(): bool
{
return $this->showValue;
}
/**
- * Set ShowValue
- *
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set ShowValue.
*/
- public function setShowValue($value)
+ public function setShowValue(bool $value): self
{
$this->showValue = $value;
@@ -387,73 +350,54 @@ class Series implements ComparableInterface
}
/**
- * Get ShowPercentage
- *
- * @return boolean
+ * Get ShowPercentage.
*/
- public function hasShowPercentage()
+ public function hasShowPercentage(): bool
{
return $this->showPercentage;
}
/**
- * Set ShowPercentage
- *
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set ShowPercentage.
*/
- public function setShowPercentage($value)
+ public function setShowPercentage(bool $value): self
{
$this->showPercentage = $value;
return $this;
}
- /**
- * Get ShowLeaderLines
- *
- * @return boolean
- */
- public function hasShowSeparator()
+ public function hasShowSeparator(): bool
{
return !is_null($this->separator);
}
- /**
- * Set Separator
- * @param string $pValue
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
- */
- public function setSeparator($pValue)
+ public function setSeparator(?string $pValue): self
{
$this->separator = $pValue;
+
return $this;
}
- /**
- * Get Separator
- * @return string
- */
- public function getSeparator()
+ public function getSeparator(): ?string
{
return $this->separator;
}
/**
- * Get ShowLeaderLines
- *
- * @return boolean
+ * Get ShowLeaderLines.
*/
- public function hasShowLeaderLines()
+ public function hasShowLeaderLines(): bool
{
return $this->showLeaderLines;
}
/**
- * Set ShowLeaderLines
+ * Set ShowLeaderLines.
*
- * @param boolean $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * @param bool $value
+ *
+ * @return self
*/
public function setShowLeaderLines($value)
{
@@ -463,23 +407,21 @@ class Series implements ComparableInterface
}
/**
- * Get font
+ * Get font.
*
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * @return Font
*/
- public function getFont()
+ public function getFont(): ?Font
{
return $this->font;
}
/**
- * Set font
+ * Set font.
*
- * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * @param Font|null $pFont Font
*/
- public function setFont(Font $pFont = null)
+ public function setFont(Font $pFont = null): self
{
$this->font = $pFont;
@@ -487,105 +429,87 @@ class Series implements ComparableInterface
}
/**
- * Get label position
- *
- * @return string
+ * Get label position.
*/
- public function getLabelPosition()
+ public function getLabelPosition(): string
{
return $this->labelPosition;
}
/**
- * Set label position
- *
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * Set label position.
*/
- public function setLabelPosition($value)
+ public function setLabelPosition(string $value): self
{
$this->labelPosition = $value;
return $this;
}
- /**
- * @return Marker
- */
- public function getMarker()
+ public function getMarker(): Marker
{
return $this->marker;
}
- /**
- * @param Marker $marker
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
- */
- public function setMarker(Marker $marker)
+ public function setMarker(Marker $marker): self
{
$this->marker = $marker;
+
return $this;
}
- /**
- * @return Outline
- */
- public function getOutline()
+ public function getOutline(): ?Outline
{
return $this->outline;
}
- /**
- * @param Outline $outline
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
- */
- public function setOutline(Outline $outline)
+ public function setOutline(?Outline $outline): self
{
$this->outline = $outline;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5((is_null($this->fill) ? 'null' : $this->fill->getHashCode()) . (is_null($this->font) ? 'null' : $this->font->getHashCode()) . var_export($this->values, true) . var_export($this, true) . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series
+ * @param int $value Hash index
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value): self
{
$this->hashIndex = $value;
+
return $this;
}
-
/**
- * @link http://php.net/manual/en/language.oop5.cloning.php
+ * @see http://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Title.php b/PhpOffice/PhpPresentation/Shape/Chart/Title.php
old mode 100755
new mode 100644
index 5643a6d..a031657
--- a/PhpOffice/PhpPresentation/Shape/Chart/Title.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Title.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -22,88 +25,88 @@ use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Style\Font;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * \PhpOffice\PhpPresentation\Shape\Chart\Title.
*/
class Title implements ComparableInterface
{
/**
- * Visible
+ * Visible.
*
- * @var boolean
+ * @var bool
*/
private $visible = true;
/**
- * Text
+ * Text.
*
* @var string
*/
private $text = 'Chart Title';
/**
- * OffsetX (as a fraction of the chart)
+ * OffsetX (as a fraction of the chart).
*
* @var float
*/
private $offsetX = 0.01;
/**
- * OffsetY (as a fraction of the chart)
+ * OffsetY (as a fraction of the chart).
*
* @var float
*/
private $offsetY = 0.01;
/**
- * Width (as a fraction of the chart)
+ * Width (as a fraction of the chart).
*
* @var float
*/
private $width = 0;
/**
- * Height (as a fraction of the chart)
+ * Height (as a fraction of the chart).
*
* @var float
*/
private $height = 0;
/**
- * Alignment
+ * Alignment.
*
* @var \PhpOffice\PhpPresentation\Style\Alignment
*/
private $alignment;
/**
- * Font
+ * Font.
*
* @var \PhpOffice\PhpPresentation\Style\Font
*/
private $font;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Title instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\Chart\Title instance.
*/
public function __construct()
{
$this->alignment = new Alignment();
- $this->font = new Font();
+ $this->font = new Font();
$this->font->setName('Calibri');
$this->font->setSize(18);
}
/**
- * Get Visible
+ * Get Visible.
*
- * @return boolean
+ * @return bool
*/
public function isVisible()
{
@@ -111,9 +114,10 @@ class Title implements ComparableInterface
}
/**
- * Set Visible
+ * Set Visible.
+ *
+ * @param bool $value
*
- * @param boolean $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\Title
*/
public function setVisible($value = true)
@@ -124,7 +128,7 @@ class Title implements ComparableInterface
}
/**
- * Get Text
+ * Get Text.
*
* @return string
*/
@@ -134,9 +138,10 @@ class Title implements ComparableInterface
}
/**
- * Set Text
+ * Set Text.
+ *
+ * @param string $value
*
- * @param string $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\Title
*/
public function setText($value = null)
@@ -147,22 +152,17 @@ class Title implements ComparableInterface
}
/**
- * Get OffsetX (as a fraction of the chart)
- *
- * @return float
+ * Get OffsetX (as a fraction of the chart).
*/
- public function getOffsetX()
+ public function getOffsetX(): float
{
return $this->offsetX;
}
/**
- * Set OffsetX (as a fraction of the chart)
- *
- * @param float $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * Set OffsetX (as a fraction of the chart).
*/
- public function setOffsetX($value = 0.01)
+ public function setOffsetX(float $value = 0.01): self
{
$this->offsetX = $value;
@@ -170,92 +170,73 @@ class Title implements ComparableInterface
}
/**
- * Get OffsetY (as a fraction of the chart)
- *
- * @return float
+ * Get OffsetY (as a fraction of the chart).
*/
- public function getOffsetY()
+ public function getOffsetY(): float
{
return $this->offsetY;
}
/**
- * Set OffsetY (as a fraction of the chart)
- *
- * @param float $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * Set OffsetY (as a fraction of the chart).
*/
- public function setOffsetY($value = 0.01)
+ public function setOffsetY(float $pValue = 0.01): self
{
- $this->offsetY = $value;
+ $this->offsetY = $pValue;
return $this;
}
/**
- * Get Width (as a fraction of the chart)
- *
- * @return float
+ * Get Width (as a fraction of the chart).
*/
- public function getWidth()
+ public function getWidth(): float
{
return $this->width;
}
/**
- * Set Width (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * Set Width (as a fraction of the chart).
*/
- public function setWidth($value = 0)
+ public function setWidth(float $pValue = 0): self
{
- $this->width = (double)$value;
+ $this->width = $pValue;
return $this;
}
/**
- * Get Height (as a fraction of the chart)
- *
- * @return float
+ * Get Height (as a fraction of the chart).
*/
- public function getHeight()
+ public function getHeight(): float
{
return $this->height;
}
/**
- * Set Height (as a fraction of the chart)
- *
- * @param float|int $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * Set Height (as a fraction of the chart).
*/
- public function setHeight($value = 0)
+ public function setHeight(float $value = 0): self
{
- $this->height = (double)$value;
+ $this->height = $value;
return $this;
}
/**
- * Get font
- *
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Get font.
*/
- public function getFont()
+ public function getFont(): ?Font
{
return $this->font;
}
/**
- * Set font
+ * Set font.
*
- * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Title
+ * @param Font|null $pFont Font
*/
- public function setFont(Font $pFont = null)
+ public function setFont(Font $pFont = null): self
{
$this->font = $pFont;
@@ -263,7 +244,7 @@ class Title implements ComparableInterface
}
/**
- * Get alignment
+ * Get alignment.
*
* @return \PhpOffice\PhpPresentation\Style\Alignment
*/
@@ -273,9 +254,8 @@ class Title implements ComparableInterface
}
/**
- * Set alignment
+ * Set alignment.
*
- * @param \PhpOffice\PhpPresentation\Style\Alignment $alignment
* @return \PhpOffice\PhpPresentation\Shape\Chart\Title
*/
public function setAlignment(Alignment $alignment)
@@ -286,40 +266,42 @@ class Title implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->text . $this->offsetX . $this->offsetY . $this->width . $this->height . $this->font->getHashCode() . $this->alignment->getHashCode() . ($this->visible ? 't' : 'f') . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
* @return Title
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractType.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractType.php
old mode 100755
new mode 100644
index 57c74db..1121231
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractType.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractType.php
@@ -10,162 +10,141 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
use PhpOffice\PhpPresentation\Shape\Chart\Series;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type
+ * \PhpOffice\PhpPresentation\Shape\Chart\Type.
*/
abstract class AbstractType implements ComparableInterface
{
/**
* Has Axis X?
*
- * @var boolean
+ * @var bool
*/
protected $hasAxisX = true;
/**
* Has Axis Y?
*
- * @var boolean
+ * @var bool
*/
protected $hasAxisY = true;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
-
+
/**
- * Data
- *
- * @var array
+ * @var array
*/
- private $data = array();
+ private $series = [];
/**
* Has Axis X?
- *
- * @return boolean
*/
- public function hasAxisX()
+ public function hasAxisX(): bool
{
return $this->hasAxisX;
}
/**
* Has Axis Y?
- *
- * @return boolean
*/
- public function hasAxisY()
+ public function hasAxisY(): bool
{
return $this->hasAxisY;
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
* @return AbstractType
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
return $this;
}
/**
- * Add Series
+ * Add Series.
*
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Series $value
* @return $this
*/
public function addSeries(Series $value)
{
- $this->data[] = $value;
+ $this->series[] = $value;
+
return $this;
}
/**
- * Get Series
+ * Get Series.
*
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Series[]
+ * @return array
*/
- public function getSeries()
+ public function getSeries(): array
{
- return $this->data;
+ return $this->series;
}
/**
- * Set Series
+ * Set Series.
+ *
+ * @param array $series
*
- * @param array $value Array of \PhpOffice\PhpPresentation\Shape\Chart\Series
* @return $this
*/
- public function setSeries($value = array())
+ public function setSeries(array $series = [])
{
- $this->data = $value;
+ $this->series = $series;
+
return $this;
}
/**
- * Get Data
- *
- * @deprecated getSeries
- */
- public function getData()
- {
- return $this->getSeries();
- }
-
- /**
- * Set Data
- *
- * @deprecated setSeries
- * @param array $value
- * @return AbstractType
- */
- public function setData($value = array())
- {
- return $this->setSeries($value);
- }
-
- /**
- * @link http://php.net/manual/en/language.oop5.cloning.php
+ * @see http://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
- $arrayClone = array();
- foreach ($this->data as $itemSeries) {
+ $arrayClone = [];
+ foreach ($this->series as $itemSeries) {
$arrayClone[] = clone $itemSeries;
}
- $this->data = $arrayClone;
+ $this->series = $arrayClone;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php
old mode 100755
new mode 100644
index f0a8c31..2347176
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeBar.php
@@ -10,66 +10,75 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar
+ * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar.
*/
class AbstractTypeBar extends AbstractType
{
/** Orientation of bars */
- const DIRECTION_VERTICAL = 'col';
- const DIRECTION_HORIZONTAL = 'bar';
+ public const DIRECTION_VERTICAL = 'col';
+ public const DIRECTION_HORIZONTAL = 'bar';
/** Grouping of bars */
- const GROUPING_CLUSTERED = 'clustered'; //Chart series are drawn next to each other along the category axis.
- const GROUPING_STACKED = 'stacked'; //Chart series are drawn next to each other on the value axis.
- const GROUPING_PERCENTSTACKED = 'percentStacked'; //Chart series are drawn next to each other along the value axis and scaled to total 100%
-
+ public const GROUPING_CLUSTERED = 'clustered'; //Chart series are drawn next to each other along the category axis.
+ public const GROUPING_STACKED = 'stacked'; //Chart series are drawn next to each other on the value axis.
+ public const GROUPING_PERCENTSTACKED = 'percentStacked'; //Chart series are drawn next to each other along the value axis and scaled to total 100%
/**
- * Orientation of bars
+ * Orientation of bars.
*
* @var string
*/
protected $barDirection = self::DIRECTION_VERTICAL;
-
/**
- * Grouping of bars
+ * Grouping of bars.
*
* @var string
*/
protected $barGrouping = self::GROUPING_CLUSTERED;
-
/**
- * Space between bar or columns clusters
+ * Space between bar or columns clusters.
*
* @var int
*/
protected $gapWidthPercent = 150;
+ /**
+ * Overlap within bar or columns clusters. Value between 100 and -100 percent.
+ * For stacked bar charts, the default overlap will be 100, for grouped bar charts 0.
+ *
+ * @var int
+ */
+ protected $overlapWidthPercent = 0;
/**
- * Set bar orientation
+ * Set bar orientation.
+ *
+ * @param string $value
*
- * @param string $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractTypeBar
*/
public function setBarDirection($value = self::DIRECTION_VERTICAL)
{
$this->barDirection = $value;
+
return $this;
}
/**
- * Get orientation
+ * Get orientation.
*
* @return string
*/
@@ -79,19 +88,26 @@ class AbstractTypeBar extends AbstractType
}
/**
- * Set bar grouping (stack or expanded style bar)
+ * Set bar grouping (stack or expanded style bar).
+ *
+ * @param string $value
*
- * @param string $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractTypeBar
*/
public function setBarGrouping($value = self::GROUPING_CLUSTERED)
{
$this->barGrouping = $value;
+ $this->overlapWidthPercent = 0;
+
+ if ($value === self::GROUPING_STACKED || $value === self::GROUPING_PERCENTSTACKED) {
+ $this->overlapWidthPercent = 100;
+ }
+
return $this;
}
/**
- * Get grouping (stack or expanded style bar)
+ * Get grouping (stack or expanded style bar).
*
* @return string
*/
@@ -110,6 +126,7 @@ class AbstractTypeBar extends AbstractType
/**
* @param int $gapWidthPercent
+ *
* @return $this
*/
public function setGapWidthPercent($gapWidthPercent)
@@ -121,20 +138,48 @@ class AbstractTypeBar extends AbstractType
$gapWidthPercent = 500;
}
$this->gapWidthPercent = $gapWidthPercent;
+
return $this;
}
-
+
/**
- * Get hash code
+ * @return int
+ */
+ public function getOverlapWidthPercent(): int
+ {
+ return $this->overlapWidthPercent;
+ }
+
+ /**
+ * @param int $value overlap width percentage
+ *
+ * @return self
+ */
+ public function setOverlapWidthPercent(int $value): self
+ {
+ if ($value < -100) {
+ $value = -100;
+ }
+ if ($value > 100) {
+ $value = 100;
+ }
+ $this->overlapWidthPercent = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hash = '';
foreach ($this->getSeries() as $series) {
$hash .= $series->getHashCode();
}
+
return $hash;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeLine.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeLine.php
new file mode 100644
index 0000000..3040f0b
--- /dev/null
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypeLine.php
@@ -0,0 +1,65 @@
+isSmooth;
+ }
+
+ /**
+ * Set Line Smoothness
+ *
+ * @param bool $value
+ *
+ * @return AbstractTypeLine
+ */
+ public function setIsSmooth(bool $value = true): AbstractTypeLine
+ {
+ $this->isSmooth = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ return md5($this->isSmooth() ? '1' : '0');
+ }
+}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php
old mode 100755
new mode 100644
index f055fe3..b538d9a
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/AbstractTypePie.php
@@ -10,67 +10,67 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar
+ * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar.
*/
class AbstractTypePie extends AbstractType
{
/**
- * Create a new self instance
+ * Create a new self instance.
*/
public function __construct()
{
$this->hasAxisX = false;
$this->hasAxisY = false;
}
-
+
/**
- * Explosion of the Pie
+ * Explosion of the Pie.
*
- * @var integer
+ * @var int
*/
protected $explosion = 0;
-
+
/**
- * Set explosion
- *
- * @param integer $value
- * @return \PhpOffice\PhpPresentation\Shape\Chart\Type\AbstractTypePie
+ * Set explosion.
*/
- public function setExplosion($value = 0)
+ public function setExplosion(int $value = 0): self
{
$this->explosion = $value;
+
return $this;
}
-
+
/**
- * Get orientation
- *
- * @return string
+ * Get orientation.
*/
- public function getExplosion()
+ public function getExplosion(): int
{
return $this->explosion;
}
-
+
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hash = '';
foreach ($this->getSeries() as $series) {
$hash .= $series->getHashCode();
}
+
return $hash;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Area.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Area.php
old mode 100755
new mode 100644
index fdfc5aa..49fc9bd
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Area.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Area.php
@@ -10,31 +10,35 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Area
+ * \PhpOffice\PhpPresentation\Shape\Chart\Type\Area.
*/
class Area extends AbstractType implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hash = '';
foreach ($this->getSeries() as $series) {
$hash .= $series->getHashCode();
}
+
return md5($hash . __CLASS__);
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar.php
old mode 100755
new mode 100644
index da44e04..9ea5b3d
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar.php
@@ -10,26 +10,29 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar
+ * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar.
*/
class Bar extends AbstractTypeBar implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar3D.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar3D.php
old mode 100755
new mode 100644
index 540a530..1011ad0
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar3D.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Bar3D.php
@@ -10,26 +10,29 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D
+ * \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D.
*/
class Bar3D extends AbstractTypeBar implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Doughnut.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Doughnut.php
old mode 100755
new mode 100644
index 5f30a51..292c823
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Doughnut.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Doughnut.php
@@ -10,22 +10,26 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * self
+ * self.
*/
class Doughnut extends AbstractTypePie implements ComparableInterface
{
/**
- * Hole Size
+ * Hole Size.
+ *
* @var int
*/
protected $holeSize = 50;
@@ -40,8 +44,10 @@ class Doughnut extends AbstractTypePie implements ComparableInterface
/**
* @param int $holeSize
+ *
* @return Doughnut
- * @link https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.charts.holesize(v=office.14).aspx
+ *
+ * @see https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.charts.holesize(v=office.14).aspx
*/
public function setHoleSize($holeSize = 50)
{
@@ -52,15 +58,16 @@ class Doughnut extends AbstractTypePie implements ComparableInterface
$holeSize = 90;
}
$this->holeSize = $holeSize;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Line.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Line.php
old mode 100755
new mode 100644
index 9be7a78..cefca9d
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Line.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Line.php
@@ -10,31 +10,32 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
-/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Line
- */
-class Line extends AbstractType implements ComparableInterface
+class Line extends AbstractTypeLine implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hash = '';
foreach ($this->getSeries() as $series) {
$hash .= $series->getHashCode();
}
- return md5($hash . __CLASS__);
+
+ return md5(parent::getHashCode() . $hash . __CLASS__);
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie.php
old mode 100755
new mode 100644
index a1c0d57..53cb6b1
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie.php
@@ -10,26 +10,29 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * self
+ * self.
*/
class Pie extends AbstractTypePie implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie3D.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie3D.php
old mode 100755
new mode 100644
index b7bc6bb..05689c6
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie3D.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Pie3D.php
@@ -10,26 +10,29 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * self
+ * self.
*/
class Pie3D extends AbstractTypePie implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Radar.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Radar.php
new file mode 100644
index 0000000..9f45b5d
--- /dev/null
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Radar.php
@@ -0,0 +1,41 @@
+getSeries() as $series) {
+ $hash .= $series->getHashCode();
+ }
+
+ return md5($hash . __CLASS__);
+ }
+}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/Type/Scatter.php b/PhpOffice/PhpPresentation/Shape/Chart/Type/Scatter.php
old mode 100755
new mode 100644
index ba1fa3b..58f2576
--- a/PhpOffice/PhpPresentation/Shape/Chart/Type/Scatter.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/Type/Scatter.php
@@ -10,31 +10,32 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart\Type;
use PhpOffice\PhpPresentation\ComparableInterface;
-/**
- * \PhpOffice\PhpPresentation\Shape\Chart\Type\Scatter
- */
-class Scatter extends AbstractType implements ComparableInterface
+class Scatter extends AbstractTypeLine implements ComparableInterface
{
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hash = '';
foreach ($this->getSeries() as $series) {
$hash .= $series->getHashCode();
}
- return md5($hash . __CLASS__);
+
+ return md5(parent::getHashCode() . $hash . __CLASS__);
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Chart/View3D.php b/PhpOffice/PhpPresentation/Shape/Chart/View3D.php
old mode 100755
new mode 100644
index 80ffea6..21bbaee
--- a/PhpOffice/PhpPresentation/Shape/Chart/View3D.php
+++ b/PhpOffice/PhpPresentation/Shape/Chart/View3D.php
@@ -10,78 +10,81 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Chart;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Shape\Chart\View3D
+ * \PhpOffice\PhpPresentation\Shape\Chart\View3D.
*/
class View3D implements ComparableInterface
{
/**
- * Rotation X
+ * Rotation X.
*
* @var int
*/
protected $rotationX = 0;
/**
- * Rotation Y
+ * Rotation Y.
*
* @var int
*/
protected $rotationY = 0;
/**
- * Right Angle Axes
+ * Right Angle Axes.
*
- * @var boolean
+ * @var bool
*/
private $rightAngleAxes = true;
/**
- * Perspective
+ * Perspective.
*
* @var int
*/
private $perspective = 30;
/**
- * Height Percent
+ * Height Percent.
*
- * @var int
+ * @var int|null
*/
private $heightPercent = 100;
/**
- * Depth Percent
+ * Depth Percent.
*
* @var int
*/
private $depthPercent = 100;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Chart\View3D instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\Chart\View3D instance.
*/
public function __construct()
{
}
/**
- * Get Rotation X
+ * Get Rotation X.
*
* @return int
*/
@@ -91,9 +94,10 @@ class View3D implements ComparableInterface
}
/**
- * Set Rotation X (-90 to 90)
+ * Set Rotation X (-90 to 90).
+ *
+ * @param int $pValue
*
- * @param int $pValue
* @return \PhpOffice\PhpPresentation\Shape\Chart\View3D
*/
public function setRotationX($pValue = 0)
@@ -104,7 +108,7 @@ class View3D implements ComparableInterface
}
/**
- * Get Rotation Y
+ * Get Rotation Y.
*
* @return int
*/
@@ -114,9 +118,10 @@ class View3D implements ComparableInterface
}
/**
- * Set Rotation Y (-90 to 90)
+ * Set Rotation Y (-90 to 90).
+ *
+ * @param int $pValue
*
- * @param int $pValue
* @return \PhpOffice\PhpPresentation\Shape\Chart\View3D
*/
public function setRotationY($pValue = 0)
@@ -127,9 +132,9 @@ class View3D implements ComparableInterface
}
/**
- * Get RightAngleAxes
+ * Get RightAngleAxes.
*
- * @return boolean
+ * @return bool
*/
public function hasRightAngleAxes()
{
@@ -137,9 +142,10 @@ class View3D implements ComparableInterface
}
/**
- * Set RightAngleAxes
+ * Set RightAngleAxes.
+ *
+ * @param bool $value
*
- * @param boolean $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\View3D
*/
public function setRightAngleAxes($value = true)
@@ -150,7 +156,7 @@ class View3D implements ComparableInterface
}
/**
- * Get Perspective
+ * Get Perspective.
*
* @return int
*/
@@ -160,9 +166,10 @@ class View3D implements ComparableInterface
}
/**
- * Set Perspective (0 to 100)
+ * Set Perspective (0 to 100).
+ *
+ * @param int $value
*
- * @param int $value
* @return \PhpOffice\PhpPresentation\Shape\Chart\View3D
*/
public function setPerspective($value = 30)
@@ -173,7 +180,7 @@ class View3D implements ComparableInterface
}
/**
- * Get HeightPercent
+ * Get HeightPercent.
*
* @return int
*/
@@ -183,12 +190,9 @@ class View3D implements ComparableInterface
}
/**
- * Set HeightPercent (5 to 500)
- *
- * @param int $value
- * @return $this
+ * Set HeightPercent (5 to 500).
*/
- public function setHeightPercent($value = 100)
+ public function setHeightPercent(?int $value = 100): self
{
$this->heightPercent = $value;
@@ -196,19 +200,18 @@ class View3D implements ComparableInterface
}
/**
- * Get DepthPercent
- *
- * @return int
+ * Get DepthPercent.
*/
- public function getDepthPercent()
+ public function getDepthPercent(): ?int
{
return $this->depthPercent;
}
/**
- * Set DepthPercent (20 to 2000)
+ * Set DepthPercent (20 to 2000).
+ *
+ * @param int $value
*
- * @param int $value
* @return $this
*/
public function setDepthPercent($value = 100)
@@ -219,40 +222,42 @@ class View3D implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->rotationX . $this->rotationY . ($this->rightAngleAxes ? 't' : 'f') . $this->perspective . $this->heightPercent . $this->depthPercent . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
* @return View3D
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Comment.php b/PhpOffice/PhpPresentation/Shape/Comment.php
old mode 100755
new mode 100644
index 9dfe73f..d909e05
--- a/PhpOffice/PhpPresentation/Shape/Comment.php
+++ b/PhpOffice/PhpPresentation/Shape/Comment.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\AbstractShape;
@@ -22,12 +25,12 @@ use PhpOffice\PhpPresentation\ComparableInterface;
use PhpOffice\PhpPresentation\Shape\Comment\Author;
/**
- * Comment shape
+ * Comment shape.
*/
class Comment extends AbstractShape implements ComparableInterface
{
/**
- * @var Author
+ * @var Author|null
*/
protected $author;
@@ -47,21 +50,15 @@ class Comment extends AbstractShape implements ComparableInterface
$this->setDate(time());
}
- /**
- * @return Author
- */
- public function getAuthor()
+ public function getAuthor(): ?Author
{
return $this->author;
}
- /**
- * @param Author $author
- * @return Comment
- */
- public function setAuthor(Author $author)
+ public function setAuthor(Author $author): self
{
$this->author = $author;
+
return $this;
}
@@ -75,11 +72,13 @@ class Comment extends AbstractShape implements ComparableInterface
/**
* @param int $dtComment timestamp of the comment
+ *
* @return Comment
*/
public function setDate($dtComment)
{
- $this->dtComment = (int)$dtComment;
+ $this->dtComment = (int) $dtComment;
+
return $this;
}
@@ -93,18 +92,20 @@ class Comment extends AbstractShape implements ComparableInterface
/**
* @param string $text
+ *
* @return Comment
*/
public function setText($text = '')
{
$this->text = $text;
+
return $this;
}
/**
- * Comment has not height
+ * Comment has not height.
*
- * @return null
+ * @return int|null
*/
public function getHeight()
{
@@ -112,20 +113,19 @@ class Comment extends AbstractShape implements ComparableInterface
}
/**
- * Set Height
+ * Set Height.
*
- * @param int $pValue
* @return $this
*/
- public function setHeight($pValue = 0)
+ public function setHeight(int $pValue = 0)
{
return $this;
}
/**
- * Comment has not width
+ * Comment has not width.
*
- * @return null
+ * @return int|null
*/
public function getWidth()
{
@@ -133,12 +133,11 @@ class Comment extends AbstractShape implements ComparableInterface
}
/**
- * Set Width
+ * Set Width.
*
- * @param int $pValue
- * @return $this
+ * @return self
*/
- public function setWidth($pValue = 0)
+ public function setWidth(int $pValue = 0)
{
return $this;
}
diff --git a/PhpOffice/PhpPresentation/Shape/Comment/Author.php b/PhpOffice/PhpPresentation/Shape/Comment/Author.php
old mode 100755
new mode 100644
index 3c0ba2a..59784f3
--- a/PhpOffice/PhpPresentation/Shape/Comment/Author.php
+++ b/PhpOffice/PhpPresentation/Shape/Comment/Author.php
@@ -1,4 +1,22 @@
idxAuthor = (int) $idxAuthor;
+
return $this;
}
@@ -47,11 +67,13 @@ class Author
/**
* @param mixed $initials
+ *
* @return Author
*/
public function setInitials($initials)
{
$this->initials = $initials;
+
return $this;
}
@@ -65,20 +87,22 @@ class Author
/**
* @param string $name
+ *
* @return Author
*/
public function setName($name)
{
$this->name = $name;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->getInitials() . $this->getName() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php b/PhpOffice/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php
old mode 100755
new mode 100644
index 1301e25..f361ba7
--- a/PhpOffice/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php
+++ b/PhpOffice/PhpPresentation/Shape/Drawing/AbstractDrawingAdapter.php
@@ -1,4 +1,22 @@
*/
- protected $arrayMimeExtension = array(
+ protected $arrayMimeExtension = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
- );
+ 'image/svg+xml' => 'svg',
+ ];
+
+ /**
+ * @var string
+ */
+ protected $path;
/**
* Base64 constructor.
@@ -32,65 +58,58 @@ class Base64 extends AbstractDrawingAdapter
{
parent::__construct();
$this->uniqueName = md5(rand(0, 9999) . time() . rand(0, 9999));
+ $this->data = '';
}
- /**
- * @return mixed
- */
- public function getData()
+ public function getData(): string
{
return $this->data;
}
- /**
- * @param mixed $data
- * @return Base64
- */
- public function setData($data)
+ public function setData(string $data): self
{
$this->data = $data;
+
return $this;
}
- /**
- * @return string
- */
- public function getContents()
+ public function getContents(): string
{
list(, $imageContents) = explode(';', $this->getData());
list(, $imageContents) = explode(',', $imageContents);
+
return base64_decode($imageContents);
}
/**
- * @return string
- * @throws \Exception
+ * @throws UnauthorizedMimetypeException
*/
- public function getExtension()
+ public function getExtension(): string
{
- list($data, ) = explode(';', $this->getData());
+ list($data) = explode(';', $this->getData());
list(, $mime) = explode(':', $data);
if (!array_key_exists($mime, $this->arrayMimeExtension)) {
- throw new \Exception('Type Mime not found : "'.$mime.'"');
+ throw new UnauthorizedMimetypeException($mime, $this->arrayMimeExtension);
}
+
return $this->arrayMimeExtension[$mime];
}
- /**
- * @return string
- * @throws \Exception
- */
- public function getIndexedFilename()
+ public function getIndexedFilename(): string
{
return $this->uniqueName . $this->getImageIndex() . '.' . $this->getExtension();
}
- /**
- * @return string
- */
- public function getMimeType()
+ public function getMimeType(): string
{
+ list($data) = explode(';', $this->getData());
+ list(, $mime) = explode(':', $data);
+
+ if (!empty($mime)) {
+ return $mime;
+ }
+
$sImage = $this->getContents();
if (!function_exists('getimagesizefromstring')) {
$uri = 'data://application/octet-stream;base64,' . base64_encode($sImage);
@@ -98,6 +117,22 @@ class Base64 extends AbstractDrawingAdapter
} else {
$image = getimagesizefromstring($sImage);
}
+
return image_type_to_mime_type($image[2]);
}
+
+ /**
+ * Get Path.
+ */
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+
+ public function setPath(string $path): self
+ {
+ $this->path = $path;
+
+ return $this;
+ }
}
diff --git a/PhpOffice/PhpPresentation/Shape/Drawing/File.php b/PhpOffice/PhpPresentation/Shape/Drawing/File.php
old mode 100755
new mode 100644
index e920988..754985a
--- a/PhpOffice/PhpPresentation/Shape/Drawing/File.php
+++ b/PhpOffice/PhpPresentation/Shape/Drawing/File.php
@@ -1,45 +1,64 @@
path;
}
/**
- * Set Path
+ * Set Path.
*
- * @param string $pValue File path
- * @param boolean $pVerifyFile Verify file
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Drawing\File
+ * @param string $pValue File path
+ * @param bool $pVerifyFile Verify file
+ *
+ * @throws FileNotFoundException
+ *
+ * @return self
*/
- public function setPath($pValue = '', $pVerifyFile = true)
+ public function setPath(string $pValue = '', bool $pVerifyFile = true): self
{
if ($pVerifyFile) {
if (!file_exists($pValue)) {
- throw new \Exception("File $pValue not found!");
+ throw new FileNotFoundException($pValue);
}
}
$this->path = $pValue;
if ($pVerifyFile) {
- if ($this->width == 0 && $this->height == 0) {
+ if (0 == $this->width && 0 == $this->height) {
list($this->width, $this->height) = getimagesize($this->getPath());
}
}
@@ -47,45 +66,40 @@ class File extends AbstractDrawingAdapter
return $this;
}
- /**
- * @return string
- */
- public function getContents()
+ public function getContents(): string
{
return CommonFile::fileGetContents($this->getPath());
}
-
- /**
- * @return string
- */
- public function getExtension()
+ public function getExtension(): string
{
return pathinfo($this->getPath(), PATHINFO_EXTENSION);
}
/**
- * @throws \Exception
- * @return string
+ * @throws FileNotFoundException
*/
- public function getMimeType()
+ public function getMimeType(): string
{
if (!CommonFile::fileExists($this->getPath())) {
- throw new \Exception('File '.$this->getPath().' does not exist');
+ throw new FileNotFoundException($this->getPath());
}
$image = getimagesizefromstring(CommonFile::fileGetContents($this->getPath()));
- return image_type_to_mime_type($image[2]);
+
+ if (is_array($image)) {
+ return image_type_to_mime_type($image[2]);
+ }
+
+ return mime_content_type($this->getPath());
}
- /**
- * @return string
- */
- public function getIndexedFilename()
+ public function getIndexedFilename(): string
{
$output = str_replace('.' . $this->getExtension(), '', pathinfo($this->getPath(), PATHINFO_FILENAME));
$output .= $this->getImageIndex();
- $output .= '.'.$this->getExtension();
+ $output .= '.' . $this->getExtension();
$output = str_replace(' ', '_', $output);
+
return $output;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Drawing/Gd.php b/PhpOffice/PhpPresentation/Shape/Drawing/Gd.php
old mode 100755
new mode 100644
index d4509d9..59bcf8d
--- a/PhpOffice/PhpPresentation/Shape/Drawing/Gd.php
+++ b/PhpOffice/PhpPresentation/Shape/Drawing/Gd.php
@@ -1,51 +1,69 @@
imageResource)) {
// Get width/height
- $this->width = imagesx($this->imageResource);
+ $this->width = imagesx($this->imageResource);
$this->height = imagesy($this->imageResource);
}
@@ -83,7 +102,7 @@ class Gd extends AbstractDrawingAdapter
}
/**
- * Get rendering function
+ * Get rendering function.
*
* @return string
*/
@@ -93,71 +112,86 @@ class Gd extends AbstractDrawingAdapter
}
/**
- * Set rendering function
+ * Set rendering function.
+ *
+ * @param string $value
*
- * @param string $value
* @return $this
*/
public function setRenderingFunction($value = self::RENDERING_DEFAULT)
{
$this->renderingFunction = $value;
+
return $this;
}
/**
- * Get mime type
- *
- * @return string
+ * Get mime type.
*/
- public function getMimeType()
+ public function getMimeType(): string
{
return $this->mimeType;
}
/**
- * Set mime type
+ * Set mime type.
+ *
+ * @param string $value
*
- * @param string $value
* @return $this
*/
public function setMimeType($value = self::MIMETYPE_DEFAULT)
{
$this->mimeType = $value;
+
return $this;
}
- /**
- * @return string
- */
- public function getContents()
+ public function getContents(): string
{
ob_start();
- if ($this->getMimeType() === self::MIMETYPE_DEFAULT) {
+ if (self::MIMETYPE_DEFAULT === $this->getMimeType()) {
imagealphablending($this->getImageResource(), false);
imagesavealpha($this->getImageResource(), true);
}
call_user_func($this->getRenderingFunction(), $this->getImageResource());
$imageContents = ob_get_contents();
ob_end_clean();
+
return $imageContents;
}
- /**
- * @return string
- */
- public function getExtension()
+ public function getExtension(): string
{
$extension = strtolower($this->getMimeType());
$extension = explode('/', $extension);
$extension = $extension[1];
+
return $extension;
}
- /**
- * @return string
- */
- public function getIndexedFilename()
+ public function getIndexedFilename(): string
{
return $this->uniqueName . $this->getImageIndex() . '.' . $this->getExtension();
}
+
+ /**
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * Get Path.
+ */
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+
+ public function setPath(string $path): self
+ {
+ $this->path = $path;
+
+ return $this;
+ }
}
diff --git a/PhpOffice/PhpPresentation/Shape/Drawing/ZipFile.php b/PhpOffice/PhpPresentation/Shape/Drawing/ZipFile.php
old mode 100755
new mode 100644
index 769f1d9..2a921e9
--- a/PhpOffice/PhpPresentation/Shape/Drawing/ZipFile.php
+++ b/PhpOffice/PhpPresentation/Shape/Drawing/ZipFile.php
@@ -1,8 +1,27 @@
path;
}
/**
- * Set Path
+ * Set Path.
+ *
+ * @param string $pValue File path
*
- * @param string $pValue File path
* @return \PhpOffice\PhpPresentation\Shape\Drawing\ZipFile
*/
- public function setPath($pValue = '')
+ public function setPath(string $pValue = ''): self
{
$this->path = $pValue;
+
return $this;
}
/**
- * @return string
- * @throws \Exception
+ * @throws FileNotFoundException
*/
- public function getContents()
+ public function getContents(): string
{
if (!CommonFile::fileExists($this->getZipFileOut())) {
- throw new \Exception('File '.$this->getZipFileOut().' does not exist');
+ throw new FileNotFoundException($this->getZipFileOut());
}
$imageZip = new \ZipArchive();
@@ -48,26 +66,22 @@ class ZipFile extends AbstractDrawingAdapter
$imageContents = $imageZip->getFromName($this->getZipFileIn());
$imageZip->close();
unset($imageZip);
+
return $imageContents;
}
-
- /**
- * @return string
- */
- public function getExtension()
+ public function getExtension(): string
{
return pathinfo($this->getZipFileIn(), PATHINFO_EXTENSION);
}
/**
- * @return string
- * @throws \Exception
+ * @throws FileNotFoundException
*/
- public function getMimeType()
+ public function getMimeType(): string
{
if (!CommonFile::fileExists($this->getZipFileOut())) {
- throw new \Exception('File '.$this->getZipFileOut().' does not exist');
+ throw new FileNotFoundException($this->getZipFileOut());
}
$oArchive = new \ZipArchive();
$oArchive->open($this->getZipFileOut());
@@ -77,33 +91,34 @@ class ZipFile extends AbstractDrawingAdapter
} else {
$image = getimagesizefromstring($oArchive->getFromName($this->getZipFileIn()));
}
+
return image_type_to_mime_type($image[2]);
}
- /**
- * @return string
- */
- public function getIndexedFilename()
+ public function getIndexedFilename(): string
{
$output = pathinfo($this->getZipFileIn(), PATHINFO_FILENAME);
$output = str_replace('.' . $this->getExtension(), '', $output);
$output .= $this->getImageIndex();
- $output .= '.'.$this->getExtension();
+ $output .= '.' . $this->getExtension();
$output = str_replace(' ', '_', $output);
+
return $output;
}
- protected function getZipFileOut()
+ protected function getZipFileOut(): string
{
$path = str_replace('zip://', '', $this->getPath());
$path = explode('#', $path);
+
return empty($path[0]) ? '' : $path[0];
}
- protected function getZipFileIn()
+ protected function getZipFileIn(): string
{
$path = str_replace('zip://', '', $this->getPath());
$path = explode('#', $path);
+
return empty($path[1]) ? '' : $path[1];
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Group.php b/PhpOffice/PhpPresentation/Shape/Group.php
old mode 100755
new mode 100644
index 5a4faf9..dcb29ac
--- a/PhpOffice/PhpPresentation/Shape/Group.php
+++ b/PhpOffice/PhpPresentation/Shape/Group.php
@@ -10,73 +10,68 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
+use ArrayObject;
use PhpOffice\PhpPresentation\AbstractShape;
use PhpOffice\PhpPresentation\GeometryCalculator;
-use PHPOffice\PhpPresentation\ShapeContainerInterface;
-use PhpOffice\PhpPresentation\Shape\Drawing;
-use PhpOffice\PhpPresentation\Shape\RichText;
-use PhpOffice\PhpPresentation\Shape\Table;
+use PhpOffice\PhpPresentation\ShapeContainerInterface;
class Group extends AbstractShape implements ShapeContainerInterface
{
/**
- * Collection of shapes
- *
- * @var \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[]
- */
- private $shapeCollection = null;
+ * Collection of shapes.
+ *
+ * @var array|ArrayObject
+ */
+ private $shapeCollection;
/**
- * Extent X
- *
- * @var int
- */
+ * Extent X.
+ *
+ * @var int
+ */
protected $extentX;
/**
- * Extent Y
- *
- * @var int
- */
+ * Extent Y.
+ *
+ * @var int
+ */
protected $extentY;
public function __construct()
{
parent::__construct();
- // For logic purposes.
- $this->offsetX = null;
- $this->offsetY = null;
-
// Shape collection
- $this->shapeCollection = new \ArrayObject();
+ $this->shapeCollection = new ArrayObject();
}
/**
- * Get collection of shapes
- *
- * @return \ArrayObject|AbstractShape[]
- */
+ * Get collection of shapes.
+ *
+ * @return array|ArrayObject
+ */
public function getShapeCollection()
{
return $this->shapeCollection;
}
/**
- * Add shape to slide
+ * Add shape to slide.
*
- * @param \PhpOffice\PhpPresentation\AbstractShape $shape
- * @return \PhpOffice\PhpPresentation\AbstractShape
- * @throws \Exception
+ * @return AbstractShape
*/
- public function addShape(AbstractShape $shape)
+ public function addShape(AbstractShape $shape): AbstractShape
{
$shape->setContainer($this);
@@ -84,13 +79,11 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Get X Offset
- *
- * @return int
- */
- public function getOffsetX()
+ * Get X Offset.
+ */
+ public function getOffsetX(): int
{
- if ($this->offsetX === null) {
+ if (empty($this->offsetX)) {
$offsets = GeometryCalculator::calculateOffsets($this);
$this->offsetX = $offsets[GeometryCalculator::X];
$this->offsetY = $offsets[GeometryCalculator::Y];
@@ -100,24 +93,21 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Ignores setting the X Offset, preserving the default behavior.
- *
- * @param int $pValue
- * @return $this
- */
- public function setOffsetX($pValue = 0)
+ * Ignores setting the X Offset, preserving the default behavior.
+ *
+ * @return $this
+ */
+ public function setOffsetX(int $pValue = 0)
{
return $this;
}
/**
- * Get Y Offset
- *
- * @return int
- */
- public function getOffsetY()
+ * Get Y Offset.
+ */
+ public function getOffsetY(): int
{
- if ($this->offsetY === null) {
+ if (empty($this->offsetY)) {
$offsets = GeometryCalculator::calculateOffsets($this);
$this->offsetX = $offsets[GeometryCalculator::X];
$this->offsetY = $offsets[GeometryCalculator::Y];
@@ -127,24 +117,21 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Ignores setting the Y Offset, preserving the default behavior.
- *
- * @param int $pValue
- * @return $this
- */
- public function setOffsetY($pValue = 0)
+ * Ignores setting the Y Offset, preserving the default behavior.
+ *
+ * @return $this
+ */
+ public function setOffsetY(int $pValue = 0)
{
return $this;
}
/**
- * Get X Extent
- *
- * @return int
- */
- public function getExtentX()
+ * Get X Extent.
+ */
+ public function getExtentX(): int
{
- if ($this->extentX === null) {
+ if (null === $this->extentX) {
$extents = GeometryCalculator::calculateExtents($this);
$this->extentX = $extents[GeometryCalculator::X] - $this->getOffsetX();
$this->extentY = $extents[GeometryCalculator::Y] - $this->getOffsetY();
@@ -154,13 +141,11 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Get Y Extent
- *
- * @return int
- */
- public function getExtentY()
+ * Get Y Extent.
+ */
+ public function getExtentY(): int
{
- if ($this->extentY === null) {
+ if (null === $this->extentY) {
$extents = GeometryCalculator::calculateExtents($this);
$this->extentX = $extents[GeometryCalculator::X] - $this->getOffsetX();
$this->extentY = $extents[GeometryCalculator::Y] - $this->getOffsetY();
@@ -170,34 +155,31 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Ignores setting the width, preserving the default behavior.
- *
- * @param int $pValue
- * @return $this
- */
- public function setWidth($pValue = 0)
- {
- return $this;
- }
-
- /**
- * Ignores setting the height, preserving the default behavior.
- *
- * @param int $pValue
- * @return $this
- */
- public function setHeight($pValue = 0)
- {
- return $this;
- }
-
- /**
- * Create rich text shape
+ * Ignores setting the width, preserving the default behavior.
*
- * @return \PhpOffice\PhpPresentation\Shape\RichText
- * @throws \Exception
+ * @return self
*/
- public function createRichTextShape()
+ public function setWidth(int $pValue = 0)
+ {
+ return $this;
+ }
+
+ /**
+ * Ignores setting the height, preserving the default behavior.
+ *
+ * @return $this
+ */
+ public function setHeight(int $pValue = 0)
+ {
+ return $this;
+ }
+
+ /**
+ * Create rich text shape.
+ *
+ * @return RichText
+ */
+ public function createRichTextShape(): RichText
{
$shape = new RichText();
$this->addShape($shape);
@@ -206,16 +188,16 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Create line shape
+ * Create line shape.
*
- * @param int $fromX Starting point x offset
- * @param int $fromY Starting point y offset
- * @param int $toX Ending point x offset
- * @param int $toY Ending point y offset
- * @return \PhpOffice\PhpPresentation\Shape\Line
- * @throws \Exception
+ * @param int $fromX Starting point x offset
+ * @param int $fromY Starting point y offset
+ * @param int $toX Ending point x offset
+ * @param int $toY Ending point y offset
+ *
+ * @return Line
*/
- public function createLineShape($fromX, $fromY, $toX, $toY)
+ public function createLineShape(int $fromX, int $fromY, int $toX, int $toY): Line
{
$shape = new Line($fromX, $fromY, $toX, $toY);
$this->addShape($shape);
@@ -224,12 +206,11 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Create chart shape
+ * Create chart shape.
*
- * @return \PhpOffice\PhpPresentation\Shape\Chart
- * @throws \Exception
+ * @return Chart
*/
- public function createChartShape()
+ public function createChartShape(): Chart
{
$shape = new Chart();
$this->addShape($shape);
@@ -238,12 +219,11 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Create drawing shape
+ * Create drawing shape.
*
- * @return \PhpOffice\PhpPresentation\Shape\Drawing\File
- * @throws \Exception
+ * @return Drawing\File
*/
- public function createDrawingShape()
+ public function createDrawingShape(): Drawing\File
{
$shape = new Drawing\File();
$this->addShape($shape);
@@ -252,13 +232,13 @@ class Group extends AbstractShape implements ShapeContainerInterface
}
/**
- * Create table shape
+ * Create table shape.
*
- * @param int $columns Number of columns
- * @return \PhpOffice\PhpPresentation\Shape\Table
- * @throws \Exception
+ * @param int $columns Number of columns
+ *
+ * @return Table
*/
- public function createTableShape($columns = 1)
+ public function createTableShape(int $columns = 1): Table
{
$shape = new Table($columns);
$this->addShape($shape);
diff --git a/PhpOffice/PhpPresentation/Shape/Hyperlink.php b/PhpOffice/PhpPresentation/Shape/Hyperlink.php
old mode 100755
new mode 100644
index 0a95507..06e0be9
--- a/PhpOffice/PhpPresentation/Shape/Hyperlink.php
+++ b/PhpOffice/PhpPresentation/Shape/Hyperlink.php
@@ -10,84 +10,93 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
/**
- * Hyperlink element
+ * Hyperlink element.
*/
class Hyperlink
{
/**
- * URL to link the shape to
+ * URL to link the shape to.
*
* @var string
*/
private $url;
/**
- * Tooltip to display on the hyperlink
+ * Tooltip to display on the hyperlink.
*
* @var string
*/
private $tooltip;
/**
- * Slide number to link to
+ * Slide number to link to.
*
* @var int
*/
private $slideNumber = null;
/**
- * Slide relation ID (should not be used by user code!)
+ * Slide relation ID (should not be used by user code!).
*
* @var string
*/
public $relationId = null;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Hyperlink
+ * If true, uses the text color, instead of theme color
*
- * @param string $pUrl Url to link the shape to
- * @param string $pTooltip Tooltip to display on the hyperlink
- * @throws \Exception
+ * @var bool
*/
- public function __construct($pUrl = '', $pTooltip = '')
+ private $isTextColorUsed = false;
+
+ /**
+ * Create a new \PhpOffice\PhpPresentation\Shape\Hyperlink.
+ *
+ * @param string $pUrl Url to link the shape to
+ * @param string $pTooltip Tooltip to display on the hyperlink
+ */
+ public function __construct(string $pUrl = '', string $pTooltip = '')
{
- // Initialise member variables
$this->setUrl($pUrl);
$this->setTooltip($pTooltip);
}
/**
- * Get URL
+ * Get URL.
*
* @return string
*/
- public function getUrl()
+ public function getUrl(): string
{
return $this->url;
}
/**
- * Set URL
+ * Set URL.
*
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Hyperlink
+ * @param string $value
+ *
+ * @return self
*/
- public function setUrl($value = '')
+ public function setUrl(string $value = ''): self
{
$this->url = $value;
@@ -95,22 +104,23 @@ class Hyperlink
}
/**
- * Get tooltip
+ * Get tooltip.
*
* @return string
*/
- public function getTooltip()
+ public function getTooltip(): string
{
return $this->tooltip;
}
/**
- * Set tooltip
+ * Set tooltip.
*
- * @param string $value
- * @return \PhpOffice\PhpPresentation\Shape\Hyperlink
+ * @param string $value
+ *
+ * @return self
*/
- public function setTooltip($value = '')
+ public function setTooltip(string $value = ''): self
{
$this->tooltip = $value;
@@ -118,72 +128,105 @@ class Hyperlink
}
/**
- * Get slide number
+ * Get slide number.
*
* @return int
*/
- public function getSlideNumber()
+ public function getSlideNumber(): int
{
return $this->slideNumber;
}
/**
- * Set slide number
+ * Set slide number.
*
- * @param int $value
- * @return \PhpOffice\PhpPresentation\Shape\Hyperlink
+ * @param int $value
+ *
+ * @return self
*/
- public function setSlideNumber($value = 1)
+ public function setSlideNumber(int $value = 1): self
{
- $this->url = 'ppaction://hlinksldjump';
+ $this->url = 'ppaction://hlinksldjump';
$this->slideNumber = $value;
return $this;
}
/**
- * Is this hyperlink internal? (to another slide)
+ * Is this hyperlink internal? (to another slide).
*
- * @return boolean
+ * @return bool
*/
- public function isInternal()
+ public function isInternal(): bool
{
- return strpos($this->url, 'ppaction://') !== false;
+ return false !== strpos($this->url, 'ppaction://');
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->url . $this->tooltip . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get whether or not to use text color for a hyperlink, instead of theme color.
+ *
+ * @see https://docs.microsoft.com/en-us/openspecs/office_standards/ms-odrawxml/014fbc20-3705-4812-b8cd-93f5af05b504
+ *
+ * @return bool whether or not to use text color for a hyperlink, instead of theme color
+ */
+ public function isTextColorUsed(): bool
+ {
+ return $this->isTextColorUsed;
+ }
+
+ /**
+ * Set whether or not to use text color for a hyperlink, instead of theme color.
+ *
+ * @see https://docs.microsoft.com/en-us/openspecs/office_standards/ms-odrawxml/014fbc20-3705-4812-b8cd-93f5af05b504
+ *
+ * @param bool $isTextColorUsed
+ *
+ * @return self
+ */
+ public function setIsTextColorUsed(bool $isTextColorUsed): self
+ {
+ $this->isTextColorUsed = $isTextColorUsed;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Line.php b/PhpOffice/PhpPresentation/Shape/Line.php
old mode 100755
new mode 100644
index e9a7a07..be7574d
--- a/PhpOffice/PhpPresentation/Shape/Line.php
+++ b/PhpOffice/PhpPresentation/Shape/Line.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\AbstractShape;
@@ -22,12 +25,12 @@ use PhpOffice\PhpPresentation\ComparableInterface;
use PhpOffice\PhpPresentation\Style\Border;
/**
- * Line shape
+ * Line shape.
*/
class Line extends AbstractShape implements ComparableInterface
{
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Line instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\Line instance.
*
* @param int $fromX
* @param int $fromY
@@ -46,11 +49,11 @@ class Line extends AbstractShape implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->getBorder()->getLineStyle() . parent::getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/Media.php b/PhpOffice/PhpPresentation/Shape/Media.php
old mode 100755
new mode 100644
index 64a98b7..1207195
--- a/PhpOffice/PhpPresentation/Shape/Media.php
+++ b/PhpOffice/PhpPresentation/Shape/Media.php
@@ -10,26 +10,25 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\ComparableInterface;
use PhpOffice\PhpPresentation\Shape\Drawing\File;
/**
- * Media element
+ * Media element.
*/
class Media extends File implements ComparableInterface
{
-
- /**
- * @return string
- */
- public function getMimeType()
+ public function getMimeType(): string
{
switch (strtolower($this->getExtension())) {
case 'mp4':
@@ -44,6 +43,7 @@ class Media extends File implements ComparableInterface
default:
$mimetype = 'application/octet-stream';
}
+
return $mimetype;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Placeholder.php b/PhpOffice/PhpPresentation/Shape/Placeholder.php
old mode 100755
new mode 100644
index a334104..45f6ac8
--- a/PhpOffice/PhpPresentation/Shape/Placeholder.php
+++ b/PhpOffice/PhpPresentation/Shape/Placeholder.php
@@ -10,86 +10,75 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
class Placeholder
{
/** Placeholder Type constants */
- const PH_TYPE_BODY = 'body';
- const PH_TYPE_CHART = 'chart';
- const PH_TYPE_SUBTITLE = 'subTitle';
- const PH_TYPE_TITLE = 'title';
- const PH_TYPE_FOOTER = 'ftr';
- const PH_TYPE_DATETIME = 'dt';
- const PH_TYPE_SLIDENUM = 'sldNum';
+ public const PH_TYPE_BODY = 'body';
+ public const PH_TYPE_CHART = 'chart';
+ public const PH_TYPE_SUBTITLE = 'subTitle';
+ public const PH_TYPE_TITLE = 'title';
+ public const PH_TYPE_FOOTER = 'ftr';
+ public const PH_TYPE_DATETIME = 'dt';
+ public const PH_TYPE_SLIDENUM = 'sldNum';
+
/**
- * hasCustomPrompt
* Indicates whether the placeholder should have a customer prompt.
*
* @var bool
*/
protected $hasCustomPrompt;
+
/**
- * idx
* Specifies the index of the placeholder. This is used when applying templates or changing layouts to
* match a placeholder on one template or master to another.
*
- * @var int
+ * @var int|null
*/
protected $idx;
+
/**
- * type
- * Specifies what content type the placeholder is to contains
+ * Specifies what content type the placeholder is to contains.
+ *
+ * @var string
*/
protected $type;
- /**
- * Placeholder constructor.
- * @param $type
- */
- public function __construct($type)
+ public function __construct(string $type)
{
$this->type = $type;
}
- /**
- * @return mixed
- */
- public function getType()
+ public function getType(): string
{
return $this->type;
}
- /**
- * @param mixed $type
- * @return Placeholder
- */
- public function setType($type)
+ public function setType(string $type): self
{
$this->type = $type;
+
return $this;
}
- /**
- * @return int
- */
- public function getIdx()
+ public function getIdx(): ?int
{
return $this->idx;
}
- /**
- * @param int $idx
- * @return Placeholder
- */
- public function setIdx($idx)
+ public function setIdx(int $idx): self
{
$this->idx = $idx;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/RichText.php b/PhpOffice/PhpPresentation/Shape/RichText.php
old mode 100755
new mode 100644
index d7cc341..b908277
--- a/PhpOffice/PhpPresentation/Shape/RichText.php
+++ b/PhpOffice/PhpPresentation/Shape/RichText.php
@@ -10,74 +10,78 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\AbstractShape;
use PhpOffice\PhpPresentation\ComparableInterface;
+use PhpOffice\PhpPresentation\Exception\OutOfBoundsException;
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
use PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface;
/**
- * \PhpOffice\PhpPresentation\Shape\RichText
+ * \PhpOffice\PhpPresentation\Shape\RichText.
*/
class RichText extends AbstractShape implements ComparableInterface
{
/** Wrapping */
- const WRAP_NONE = 'none';
- const WRAP_SQUARE = 'square';
+ public const WRAP_NONE = 'none';
+ public const WRAP_SQUARE = 'square';
/** Autofit */
- const AUTOFIT_DEFAULT = 'spAutoFit';
- const AUTOFIT_SHAPE = 'spAutoFit';
- const AUTOFIT_NOAUTOFIT = 'noAutofit';
- const AUTOFIT_NORMAL = 'normAutofit';
+ public const AUTOFIT_DEFAULT = 'spAutoFit';
+ public const AUTOFIT_SHAPE = 'spAutoFit';
+ public const AUTOFIT_NOAUTOFIT = 'noAutofit';
+ public const AUTOFIT_NORMAL = 'normAutofit';
/** Overflow */
- const OVERFLOW_CLIP = 'clip';
- const OVERFLOW_OVERFLOW = 'overflow';
+ public const OVERFLOW_CLIP = 'clip';
+ public const OVERFLOW_OVERFLOW = 'overflow';
/**
- * Rich text paragraphs
+ * Rich text paragraphs.
*
- * @var \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[]
+ * @var array
*/
private $richTextParagraphs;
/**
- * Active paragraph
+ * Active paragraph.
*
* @var int
*/
private $activeParagraph = 0;
/**
- * Text wrapping
+ * Text wrapping.
*
* @var string
*/
private $wrap = self::WRAP_SQUARE;
/**
- * Autofit
+ * Autofit.
*
* @var string
*/
private $autoFit = self::AUTOFIT_DEFAULT;
/**
- * Horizontal overflow
+ * Horizontal overflow.
*
* @var string
*/
private $horizontalOverflow = self::OVERFLOW_OVERFLOW;
/**
- * Vertical overflow
+ * Vertical overflow.
*
* @var string
*/
@@ -86,122 +90,125 @@ class RichText extends AbstractShape implements ComparableInterface
/**
* Text upright?
*
- * @var boolean
+ * @var bool
*/
private $upright = false;
/**
* Vertical text?
*
- * @var boolean
+ * @var bool
*/
private $vertical = false;
/**
- * Number of columns (1 - 16)
+ * Number of columns (1 - 16).
*
* @var int
*/
private $columns = 1;
/**
- * Bottom inset (in pixels)
+ * The spacing between columns
+ *
+ * @var int
+ */
+ private $columnSpacing = 0;
+
+ /**
+ * Bottom inset (in pixels).
*
* @var float
*/
private $bottomInset = 4.8;
/**
- * Left inset (in pixels)
+ * Left inset (in pixels).
*
* @var float
*/
private $leftInset = 9.6;
/**
- * Right inset (in pixels)
+ * Right inset (in pixels).
*
* @var float
*/
private $rightInset = 9.6;
/**
- * Top inset (in pixels)
+ * Top inset (in pixels).
*
* @var float
*/
private $topInset = 4.8;
/**
- * Horizontal Auto Shrink
- * @var boolean
+ * Horizontal Auto Shrink.
+ *
+ * @var bool|null
*/
private $autoShrinkHorizontal;
/**
- * Vertical Auto Shrink
- * @var boolean
+ * Vertical Auto Shrink.
+ *
+ * @var bool|null
*/
private $autoShrinkVertical;
-
+
/**
- * The percentage of the original font size to which the text is scaled
- * @var float
+ * The percentage of the original font size to which the text is scaled.
+ *
+ * @var float|null
*/
private $fontScale;
-
+
/**
- * The percentage of the reduction of the line spacing
- * @var float
+ * The percentage of the reduction of the line spacing.
+ *
+ * @var float|null
*/
private $lnSpcReduction;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\RichText instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\RichText instance.
*/
public function __construct()
{
// Initialise variables
- $this->richTextParagraphs = array(
- new Paragraph()
- );
- $this->activeParagraph = 0;
+ $this->richTextParagraphs = [
+ new Paragraph(),
+ ];
// Initialize parent
parent::__construct();
}
/**
- * Get active paragraph index
+ * Get active paragraph index.
*
* @return int
*/
- public function getActiveParagraphIndex()
+ public function getActiveParagraphIndex(): int
{
return $this->activeParagraph;
}
- /**
- * Get active paragraph
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
- */
- public function getActiveParagraph()
+ public function getActiveParagraph(): Paragraph
{
return $this->richTextParagraphs[$this->activeParagraph];
}
/**
- * Set active paragraph
+ * Set active paragraph.
*
- * @param int $index
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @throws OutOfBoundsException
*/
- public function setActiveParagraph($index = 0)
+ public function setActiveParagraph(int $index = 0): Paragraph
{
if ($index >= count($this->richTextParagraphs)) {
- throw new \Exception("Invalid paragraph count.");
+ throw new OutOfBoundsException(0, count($this->richTextParagraphs), $index);
}
$this->activeParagraph = $index;
@@ -210,38 +217,33 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get paragraph
+ * Get paragraph.
*
- * @param int $index
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @throws OutOfBoundsException
*/
- public function getParagraph($index = 0)
+ public function getParagraph(int $index = 0): Paragraph
{
if ($index >= count($this->richTextParagraphs)) {
- throw new \Exception("Invalid paragraph count.");
+ throw new OutOfBoundsException(0, count($this->richTextParagraphs), $index);
}
return $this->richTextParagraphs[$index];
}
/**
- * Create paragraph
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
- * @throws \Exception
+ * Create paragraph.
*/
- public function createParagraph()
+ public function createParagraph(): Paragraph
{
$numParagraphs = count($this->richTextParagraphs);
if ($numParagraphs > 0) {
- $alignment = clone $this->getActiveParagraph()->getAlignment();
- $font = clone $this->getActiveParagraph()->getFont();
+ $alignment = clone $this->getActiveParagraph()->getAlignment();
+ $font = clone $this->getActiveParagraph()->getFont();
$bulletStyle = clone $this->getActiveParagraph()->getBulletStyle();
}
$this->richTextParagraphs[] = new Paragraph();
- $this->activeParagraph = count($this->richTextParagraphs) - 1;
+ $this->activeParagraph = count($this->richTextParagraphs) - 1;
if (isset($alignment)) {
$this->getActiveParagraph()->setAlignment($alignment);
@@ -252,17 +254,18 @@ class RichText extends AbstractShape implements ComparableInterface
if (isset($bulletStyle)) {
$this->getActiveParagraph()->setBulletStyle($bulletStyle);
}
+
return $this->getActiveParagraph();
}
/**
- * Add text
+ * Add text.
*
- * @param \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface $pText Rich text element
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param TextElementInterface|null $pText Rich text element
+ *
+ * @return self
*/
- public function addText(TextElementInterface $pText = null)
+ public function addText(TextElementInterface $pText = null): self
{
$this->richTextParagraphs[$this->activeParagraph]->addText($pText);
@@ -270,51 +273,50 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Create text (can not be formatted !)
+ * Create text (can not be formatted !).
*
- * @param string $pText Text
- * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElement
- * @throws \Exception
+ * @param string $pText Text
+ *
+ * @return RichText\TextElement
*/
- public function createText($pText = '')
+ public function createText(string $pText = ''): RichText\TextElement
{
return $this->richTextParagraphs[$this->activeParagraph]->createText($pText);
}
/**
- * Create break
+ * Create break.
*
- * @return \PhpOffice\PhpPresentation\Shape\RichText\BreakElement
- * @throws \Exception
+ * @return RichText\BreakElement
*/
- public function createBreak()
+ public function createBreak(): RichText\BreakElement
{
return $this->richTextParagraphs[$this->activeParagraph]->createBreak();
}
/**
- * Create text run (can be formatted)
+ * Create text run (can be formatted).
*
- * @param string $pText Text
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Run
- * @throws \Exception
+ * @param string $pText Text
+ *
+ * @return RichText\Run
*/
- public function createTextRun($pText = '')
+ public function createTextRun(string $pText = ''): RichText\Run
{
return $this->richTextParagraphs[$this->activeParagraph]->createTextRun($pText);
}
/**
- * Get plain text
+ * Get plain text.
*
* @return string
*/
- public function getPlainText()
+ public function getPlainText(): string
{
// Return value
$returnValue = '';
- // Loop trough all \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ // Loop trough all Paragraph
foreach ($this->richTextParagraphs as $p) {
$returnValue .= $p->getPlainText();
}
@@ -324,7 +326,7 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Convert to string
+ * Convert to string.
*
* @return string
*/
@@ -334,50 +336,44 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get paragraphs
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[]
+ * @return array
*/
- public function getParagraphs()
+ public function getParagraphs(): array
{
return $this->richTextParagraphs;
}
/**
- * Set paragraphs
+ * Set paragraphs.
*
- * @param \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] $paragraphs Array of paragraphs
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param array $paragraphs Array of paragraphs
*/
- public function setParagraphs($paragraphs = null)
+ public function setParagraphs(array $paragraphs = []): self
{
- if (!is_array($paragraphs)) {
- throw new \Exception("Invalid \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] array passed.");
- }
-
$this->richTextParagraphs = $paragraphs;
- $this->activeParagraph = count($this->richTextParagraphs) - 1;
+ $this->activeParagraph = count($this->richTextParagraphs) - 1;
+
return $this;
}
/**
- * Get text wrapping
+ * Get text wrapping.
*
* @return string
*/
- public function getWrap()
+ public function getWrap(): string
{
return $this->wrap;
}
/**
- * Set text wrapping
+ * Set text wrapping.
*
- * @param $value string
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param string $value
+ *
+ * @return self
*/
- public function setWrap($value = self::WRAP_SQUARE)
+ public function setWrap(string $value = self::WRAP_SQUARE): self
{
$this->wrap = $value;
@@ -385,51 +381,48 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get autofit
+ * Get autofit.
*
* @return string
*/
- public function getAutoFit()
+ public function getAutoFit(): string
{
return $this->autoFit;
}
/**
- * Get pourcentage of fontScale
- *
- * @return float
+ * Get pourcentage of fontScale.
*/
- public function getFontScale()
+ public function getFontScale(): ?float
{
return $this->fontScale;
}
/**
- * Get pourcentage of the line space reduction
- *
- * @return float
+ * Get pourcentage of the line space reduction.
*/
- public function getLineSpaceReduction()
+ public function getLineSpaceReduction(): ?float
{
return $this->lnSpcReduction;
}
/**
- * Set autofit
+ * Set autofit.
*
- * @param $value string
- * @param $fontScale float
- * @param $lnSpcReduction float
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param string $value
+ * @param float|null $fontScale
+ * @param float|null $lnSpcReduction
+ *
+ * @return self
*/
- public function setAutoFit($value = self::AUTOFIT_DEFAULT, $fontScale = null, $lnSpcReduction = null)
+ public function setAutoFit(string $value = self::AUTOFIT_DEFAULT, float $fontScale = null, float $lnSpcReduction = null): self
{
$this->autoFit = $value;
-
+
if (!is_null($fontScale)) {
$this->fontScale = $fontScale;
}
-
+
if (!is_null($lnSpcReduction)) {
$this->lnSpcReduction = $lnSpcReduction;
}
@@ -438,22 +431,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get horizontal overflow
+ * Get horizontal overflow.
*
* @return string
*/
- public function getHorizontalOverflow()
+ public function getHorizontalOverflow(): string
{
return $this->horizontalOverflow;
}
/**
- * Set horizontal overflow
+ * Set horizontal overflow.
*
- * @param $value string
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param string $value
+ *
+ * @return self
*/
- public function setHorizontalOverflow($value = self::OVERFLOW_OVERFLOW)
+ public function setHorizontalOverflow(string $value = self::OVERFLOW_OVERFLOW): self
{
$this->horizontalOverflow = $value;
@@ -461,22 +455,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get vertical overflow
+ * Get vertical overflow.
*
* @return string
*/
- public function getVerticalOverflow()
+ public function getVerticalOverflow(): string
{
return $this->verticalOverflow;
}
/**
- * Set vertical overflow
+ * Set vertical overflow.
*
- * @param $value string
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param string $value
+ *
+ * @return self
*/
- public function setVerticalOverflow($value = self::OVERFLOW_OVERFLOW)
+ public function setVerticalOverflow(string $value = self::OVERFLOW_OVERFLOW): self
{
$this->verticalOverflow = $value;
@@ -484,22 +479,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get upright
+ * Get upright.
*
- * @return boolean
+ * @return bool
*/
- public function isUpright()
+ public function isUpright(): bool
{
return $this->upright;
}
/**
- * Set vertical
+ * Set vertical.
*
- * @param $value boolean
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param bool $value
+ *
+ * @return self
*/
- public function setUpright($value = false)
+ public function setUpright(bool $value = false): self
{
$this->upright = $value;
@@ -507,22 +503,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get vertical
+ * Get vertical.
*
- * @return boolean
+ * @return bool
*/
- public function isVertical()
+ public function isVertical(): bool
{
return $this->vertical;
}
/**
- * Set vertical
+ * Set vertical.
*
- * @param $value boolean
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param bool $value
+ *
+ * @return self
*/
- public function setVertical($value = false)
+ public function setVertical(bool $value = false): self
{
$this->vertical = $value;
@@ -530,26 +527,28 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get columns
+ * Get columns.
*
* @return int
*/
- public function getColumns()
+ public function getColumns(): int
{
return $this->columns;
}
/**
- * Set columns
+ * Set columns.
*
- * @param $value int
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param int $value
+ *
+ * @return self
+ *
+ * @throws OutOfBoundsException
*/
- public function setColumns($value = 1)
+ public function setColumns(int $value = 1): self
{
if ($value > 16 || $value < 1) {
- throw new \Exception('Number of columns should be 1-16');
+ throw new OutOfBoundsException(1, 16, $value);
}
$this->columns = $value;
@@ -558,22 +557,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get bottom inset
+ * Get bottom inset.
*
* @return float
*/
- public function getInsetBottom()
+ public function getInsetBottom(): float
{
return $this->bottomInset;
}
/**
- * Set bottom inset
+ * Set bottom inset.
*
- * @param $value float
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param float $value
+ *
+ * @return self
*/
- public function setInsetBottom($value = 4.8)
+ public function setInsetBottom(float $value = 4.8): self
{
$this->bottomInset = $value;
@@ -581,22 +581,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get left inset
+ * Get left inset.
*
* @return float
*/
- public function getInsetLeft()
+ public function getInsetLeft(): float
{
return $this->leftInset;
}
/**
- * Set left inset
+ * Set left inset.
*
- * @param $value float
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param float $value
+ *
+ * @return self
*/
- public function setInsetLeft($value = 9.6)
+ public function setInsetLeft(float $value = 9.6): self
{
$this->leftInset = $value;
@@ -604,22 +605,23 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get right inset
+ * Get right inset.
*
* @return float
*/
- public function getInsetRight()
+ public function getInsetRight(): float
{
return $this->rightInset;
}
/**
- * Set left inset
+ * Set left inset.
*
- * @param $value float
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param float $value
+ *
+ * @return self
*/
- public function setInsetRight($value = 9.6)
+ public function setInsetRight(float $value = 9.6): self
{
$this->rightInset = $value;
@@ -627,84 +629,115 @@ class RichText extends AbstractShape implements ComparableInterface
}
/**
- * Get top inset
+ * Get top inset.
*
* @return float
*/
- public function getInsetTop()
+ public function getInsetTop(): float
{
return $this->topInset;
}
/**
- * Set top inset
+ * Set top inset.
*
- * @param $value float
- * @return \PhpOffice\PhpPresentation\Shape\RichText
+ * @param float $value
+ *
+ * @return self
*/
- public function setInsetTop($value = 4.8)
+ public function setInsetTop(float $value = 4.8): self
{
$this->topInset = $value;
return $this;
}
- /**
- * Set horizontal auto shrink
- * @param bool $value
- * @return RichText
- */
- public function setAutoShrinkHorizontal($value = null)
+ public function setAutoShrinkHorizontal(bool $value = null): self
{
- if (is_bool($value)) {
- $this->autoShrinkHorizontal = $value;
- }
+ $this->autoShrinkHorizontal = $value;
+
return $this;
}
-
- /**
- * Get horizontal auto shrink
- * @return bool
- */
- public function hasAutoShrinkHorizontal()
+
+ public function hasAutoShrinkHorizontal(): ?bool
{
return $this->autoShrinkHorizontal;
}
/**
- * Set vertical auto shrink
- * @param bool $value
+ * Set vertical auto shrink.
+ *
* @return RichText
*/
- public function setAutoShrinkVertical($value = null)
+ public function setAutoShrinkVertical(bool $value = null): self
{
- if (is_bool($value)) {
- $this->autoShrinkVertical = $value;
- }
+ $this->autoShrinkVertical = $value;
+
return $this;
}
-
+
/**
- * Set vertical auto shrink
- * @return bool
+ * Set vertical auto shrink.
*/
- public function hasAutoShrinkVertical()
+ public function hasAutoShrinkVertical(): ?bool
{
return $this->autoShrinkVertical;
}
-
+
/**
- * Get hash code
+ * Get spacing between columns
+ *
+ * @return int
+ */
+ public function getColumnSpacing(): int
+ {
+ return $this->columnSpacing;
+ }
+
+ /**
+ * Set spacing between columns
+ *
+ * @param int $value
+ *
+ * @return self
+ */
+ public function setColumnSpacing(int $value = 0): self
+ {
+ if ($value >= 0) {
+ $this->columnSpacing = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hashElements = '';
foreach ($this->richTextParagraphs as $element) {
$hashElements .= $element->getHashCode();
}
- return md5($hashElements . $this->wrap . $this->autoFit . $this->horizontalOverflow . $this->verticalOverflow . ($this->upright ? '1' : '0') . ($this->vertical ? '1' : '0') . $this->columns . $this->bottomInset . $this->leftInset . $this->rightInset . $this->topInset . parent::getHashCode() . __CLASS__);
+ return md5(
+ $hashElements
+ . $this->wrap
+ . $this->autoFit
+ . $this->horizontalOverflow
+ . $this->verticalOverflow
+ . ($this->upright ? '1' : '0')
+ . ($this->vertical ? '1' : '0')
+ . $this->columns
+ . $this->columnSpacing
+ . $this->bottomInset
+ . $this->leftInset
+ . $this->rightInset
+ . $this->topInset
+ . parent::getHashCode()
+ . __CLASS__
+ );
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/RichText/BreakElement.php b/PhpOffice/PhpPresentation/Shape/RichText/BreakElement.php
old mode 100755
new mode 100644
index dbd5bb7..5df5f06
--- a/PhpOffice/PhpPresentation/Shape/RichText/BreakElement.php
+++ b/PhpOffice/PhpPresentation/Shape/RichText/BreakElement.php
@@ -10,27 +10,32 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\RichText;
+use PhpOffice\PhpPresentation\Style\Font;
+
/**
- * Rich text break
+ * Rich text break.
*/
class BreakElement implements TextElementInterface
{
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Break instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Break instance.
*/
public function __construct()
{
}
/**
- * Get text
+ * Get text.
*
* @return string Text
*/
@@ -40,53 +45,47 @@ class BreakElement implements TextElementInterface
}
/**
- * Set text
+ * Set text.
*
- * @param $pText string Text
- * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
+ * @param string $pText Text value
*/
- public function setText($pText = '')
+ public function setText($pText = ''): self
{
return $this;
}
/**
- * Get font
- *
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Get font.
*/
- public function getFont()
+ public function getFont(): ?Font
{
return null;
}
/**
- * Set language
+ * Set language.
*
- * @param $lang
- * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
+ * @param string $lang
*/
- public function setLanguage($lang)
+ public function setLanguage($lang): self
{
return $this;
}
/**
- * Get language
- *
- * @return string Language
+ * Get language.
*/
- public function getLanguage()
+ public function getLanguage(): ?string
{
return null;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(__CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/RichText/Paragraph.php b/PhpOffice/PhpPresentation/Shape/RichText/Paragraph.php
old mode 100755
new mode 100644
index ab64b70..5fa2692
--- a/PhpOffice/PhpPresentation/Shape/RichText/Paragraph.php
+++ b/PhpOffice/PhpPresentation/Shape/RichText/Paragraph.php
@@ -10,11 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -23,79 +26,90 @@ use PhpOffice\PhpPresentation\Style\Bullet;
use PhpOffice\PhpPresentation\Style\Font;
/**
- * \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * \PhpOffice\PhpPresentation\Shape\RichText\Paragraph.
*/
class Paragraph implements ComparableInterface
{
- /**
- * Rich text elements
- *
- * @var \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface[]
- */
- private $richTextElements;
+ public const LINE_SPACING_MODE_PERCENT = 'percent';
+ public const LINE_SPACING_MODE_POINT = 'point';
/**
- * Alignment
+ * Rich text elements.
*
- * @var \PhpOffice\PhpPresentation\Style\Alignment
+ * @var array
+ */
+ private $richTextElements = [];
+
+ /**
+ * Alignment.
+ *
+ * @var Alignment
*/
private $alignment;
/**
- * Font
+ * Font.
*
- * @var \PhpOffice\PhpPresentation\Style\Font
+ * @var Font|null
*/
private $font;
/**
- * Bullet style
+ * Bullet style.
*
- * @var \PhpOffice\PhpPresentation\Style\Bullet
+ * @var Bullet
*/
private $bulletStyle;
/**
- * @var integer
+ * @var int
*/
private $lineSpacing = 100;
/**
- * Hash index
- *
* @var string
*/
+ private $lineSpacingMode = self::LINE_SPACING_MODE_PERCENT;
+
+ /**
+ * @var int
+ */
+ private $spacingBefore = 0;
+
+ /**
+ * @var int
+ */
+ private $spacingAfter = 0;
+
+ /**
+ * Hash index.
+ *
+ * @var int
+ */
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Paragraph instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Paragraph instance.
*/
public function __construct()
{
- // Initialise variables
- $this->richTextElements = array();
$this->alignment = new Alignment();
$this->font = new Font();
$this->bulletStyle = new Bullet();
}
/**
- * Get alignment
- *
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Get alignment.
*/
- public function getAlignment()
+ public function getAlignment(): Alignment
{
return $this->alignment;
}
/**
- * Set alignment
- *
- * @param \PhpOffice\PhpPresentation\Style\Alignment $alignment
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * Set alignment.
*/
- public function setAlignment(Alignment $alignment)
+ public function setAlignment(Alignment $alignment): self
{
$this->alignment = $alignment;
@@ -103,23 +117,19 @@ class Paragraph implements ComparableInterface
}
/**
- * Get font
- *
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Get font.
*/
- public function getFont()
+ public function getFont(): ?Font
{
return $this->font;
}
/**
- * Set font
+ * Set font.
*
- * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @param Font|null $pFont Font
*/
- public function setFont(Font $pFont = null)
+ public function setFont(Font $pFont = null): self
{
$this->font = $pFont;
@@ -127,23 +137,17 @@ class Paragraph implements ComparableInterface
}
/**
- * Get bullet style
- *
- * @return \PhpOffice\PhpPresentation\Style\Bullet
+ * Get bullet style.
*/
- public function getBulletStyle()
+ public function getBulletStyle(): ?Bullet
{
return $this->bulletStyle;
}
/**
* Set bullet style
- *
- * @param \PhpOffice\PhpPresentation\Style\Bullet $style
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
*/
- public function setBulletStyle(Bullet $style = null)
+ public function setBulletStyle(Bullet $style = null): self
{
$this->bulletStyle = $style;
@@ -151,13 +155,11 @@ class Paragraph implements ComparableInterface
}
/**
- * Create text (can not be formatted !)
+ * Create text (can not be formatted !).
*
- * @param string $pText Text
- * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElement
- * @throws \Exception
+ * @param string $pText Text
*/
- public function createText($pText = '')
+ public function createText(string $pText = ''): TextElement
{
$objText = new TextElement($pText);
$this->addText($objText);
@@ -166,13 +168,11 @@ class Paragraph implements ComparableInterface
}
/**
- * Add text
+ * Add text.
*
- * @param \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface $pText Rich text element
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @param TextElementInterface|null $pText Rich text element
*/
- public function addText(TextElementInterface $pText = null)
+ public function addText(TextElementInterface $pText = null): self
{
$this->richTextElements[] = $pText;
@@ -180,12 +180,9 @@ class Paragraph implements ComparableInterface
}
/**
- * Create break
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText\BreakElement
- * @throws \Exception
+ * Create break.
*/
- public function createBreak()
+ public function createBreak(): BreakElement
{
$objText = new BreakElement();
$this->addText($objText);
@@ -194,13 +191,11 @@ class Paragraph implements ComparableInterface
}
/**
- * Create text run (can be formatted)
+ * Create text run (can be formatted).
*
- * @param string $pText Text
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Run
- * @throws \Exception
+ * @param string $pText Text
*/
- public function createTextRun($pText = '')
+ public function createTextRun(string $pText = ''): Run
{
$objText = new Run($pText);
$objText->setFont(clone $this->font);
@@ -210,7 +205,7 @@ class Paragraph implements ComparableInterface
}
/**
- * Convert to string
+ * Convert to string.
*
* @return string
*/
@@ -220,16 +215,14 @@ class Paragraph implements ComparableInterface
}
/**
- * Get plain text
- *
- * @return string
+ * Get plain text.
*/
- public function getPlainText()
+ public function getPlainText(): string
{
// Return value
$returnValue = '';
- // Loop trough all \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
+ // Loop trough all TextElementInterface
foreach ($this->richTextElements as $text) {
if ($text instanceof TextElementInterface) {
$returnValue .= $text->getText();
@@ -241,37 +234,33 @@ class Paragraph implements ComparableInterface
}
/**
- * Get Rich Text elements
+ * Get Rich Text elements.
*
- * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface[]
+ * @return array
*/
- public function getRichTextElements()
+ public function getRichTextElements(): array
{
return $this->richTextElements;
}
/**
- * Set Rich Text elements
+ * Set Rich Text elements.
*
- * @param \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface[] $pElements Array of elements
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @param array $pElements Array of elements
*/
- public function setRichTextElements($pElements = null)
+ public function setRichTextElements(array $pElements = []): self
{
- if (!is_array($pElements)) {
- throw new \Exception("Invalid \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface[] array passed.");
- }
$this->richTextElements = $pElements;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hashElements = '';
foreach ($this->richTextElements as $element) {
@@ -282,46 +271,127 @@ class Paragraph implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
/**
* @return int
*/
- public function getLineSpacing()
+ public function getLineSpacing(): int
{
return $this->lineSpacing;
}
/**
+ * Value in points
+ *
* @param int $lineSpacing
- * @return Paragraph
+ *
+ * @return self
*/
- public function setLineSpacing($lineSpacing)
+ public function setLineSpacing($lineSpacing): self
{
$this->lineSpacing = $lineSpacing;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLineSpacingMode(): string
+ {
+ return $this->lineSpacingMode;
+ }
+
+ /**
+ * @param string $lineSpacingMode
+ *
+ * @return self
+ */
+ public function setLineSpacingMode(string $lineSpacingMode): self
+ {
+ if (in_array($lineSpacingMode, [
+ self::LINE_SPACING_MODE_PERCENT,
+ self::LINE_SPACING_MODE_POINT,
+ ])) {
+ $this->lineSpacingMode = $lineSpacingMode;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Value in points
+ *
+ * @return int
+ */
+ public function getSpacingBefore(): int
+ {
+ return $this->spacingBefore;
+ }
+
+ /**
+ * Value in points
+ *
+ * @param int $spacingBefore
+ *
+ * @return self
+ */
+ public function setSpacingBefore(int $spacingBefore): self
+ {
+ $this->spacingBefore = $spacingBefore;
+
+ return $this;
+ }
+
+ /**
+ * Value in points
+ *
+ * @return int
+ */
+ public function getSpacingAfter(): int
+ {
+ return $this->spacingAfter;
+ }
+
+ /**
+ * Value in points
+ *
+ * @param int $spacingAfter
+ *
+ * @return self
+ */
+ public function setSpacingAfter(int $spacingAfter): self
+ {
+ $this->spacingAfter = $spacingAfter;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/RichText/Run.php b/PhpOffice/PhpPresentation/Shape/RichText/Run.php
old mode 100755
new mode 100644
index 97455c0..ba4f532
--- a/PhpOffice/PhpPresentation/Shape/RichText/Run.php
+++ b/PhpOffice/PhpPresentation/Shape/RichText/Run.php
@@ -10,29 +10,32 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Style\Font;
/**
- * Rich text run
+ * Rich text run.
*/
class Run extends TextElement implements TextElementInterface
{
/**
- * Font
+ * Font.
*
* @var \PhpOffice\PhpPresentation\Style\Font
*/
private $font;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Run instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Run instance.
*
* @param string $pText Text
*/
@@ -44,20 +47,18 @@ class Run extends TextElement implements TextElementInterface
}
/**
- * Get font
- *
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Get font.
*/
- public function getFont()
+ public function getFont(): Font
{
return $this->font;
}
/**
- * Set font
+ * Set font.
+ *
+ * @param Font|null $pFont Font
*
- * @param \PhpOffice\PhpPresentation\Style\Font $pFont Font
- * @throws \Exception
* @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
*/
public function setFont(Font $pFont = null)
@@ -68,11 +69,11 @@ class Run extends TextElement implements TextElementInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->getText() . $this->font->getHashCode() . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/RichText/TextElement.php b/PhpOffice/PhpPresentation/Shape/RichText/TextElement.php
old mode 100755
new mode 100644
index e19b9b8..3c74d02
--- a/PhpOffice/PhpPresentation/Shape/RichText/TextElement.php
+++ b/PhpOffice/PhpPresentation/Shape/RichText/TextElement.php
@@ -10,22 +10,26 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Shape\Hyperlink;
+use PhpOffice\PhpPresentation\Style\Font;
/**
- * Rich text text element
+ * Rich text text element.
*/
class TextElement implements TextElementInterface
{
/**
- * Text
+ * Text.
*
* @var string
*/
@@ -37,14 +41,14 @@ class TextElement implements TextElementInterface
protected $language;
/**
- * Hyperlink
+ * Hyperlink.
*
- * @var \PhpOffice\PhpPresentation\Shape\Hyperlink
+ * @var Hyperlink|null
*/
protected $hyperlink;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\RichText\TextElement instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\RichText\TextElement instance.
*
* @param string $pText Text
*/
@@ -55,7 +59,7 @@ class TextElement implements TextElementInterface
}
/**
- * Get text
+ * Get text.
*
* @return string Text
*/
@@ -65,9 +69,10 @@ class TextElement implements TextElementInterface
}
/**
- * Set text
+ * Set text.
+ *
+ * @param string $pText Text value
*
- * @param $pText string Text
* @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
*/
public function setText($pText = '')
@@ -78,32 +83,19 @@ class TextElement implements TextElementInterface
}
/**
- * Get font
- *
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Get font.
*/
- public function getFont()
+ public function getFont(): ?Font
{
return null;
}
- /**
- * Has Hyperlink?
- *
- * @return boolean
- */
- public function hasHyperlink()
+ public function hasHyperlink(): bool
{
return !is_null($this->hyperlink);
}
- /**
- * Get Hyperlink
- *
- * @return \PhpOffice\PhpPresentation\Shape\Hyperlink
- * @throws \Exception
- */
- public function getHyperlink()
+ public function getHyperlink(): Hyperlink
{
if (is_null($this->hyperlink)) {
$this->hyperlink = new Hyperlink();
@@ -113,10 +105,8 @@ class TextElement implements TextElementInterface
}
/**
- * Set Hyperlink
+ * Set Hyperlink.
*
- * @param \PhpOffice\PhpPresentation\Shape\Hyperlink $pHyperlink
- * @throws \Exception
* @return \PhpOffice\PhpPresentation\Shape\RichText\TextElement
*/
public function setHyperlink(Hyperlink $pHyperlink = null)
@@ -127,7 +117,8 @@ class TextElement implements TextElementInterface
}
/**
- * Get language
+ * Get language.
+ *
* @return string
*/
public function getLanguage()
@@ -136,22 +127,25 @@ class TextElement implements TextElementInterface
}
/**
- * Set language
+ * Set language.
+ *
* @param string $language
+ *
* @return TextElement
*/
public function setLanguage($language)
{
$this->language = $language;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->text . (is_null($this->hyperlink) ? '' : $this->hyperlink->getHashCode()) . __CLASS__);
}
diff --git a/PhpOffice/PhpPresentation/Shape/RichText/TextElementInterface.php b/PhpOffice/PhpPresentation/Shape/RichText/TextElementInterface.php
old mode 100755
new mode 100644
index 9830244..4c70443
--- a/PhpOffice/PhpPresentation/Shape/RichText/TextElementInterface.php
+++ b/PhpOffice/PhpPresentation/Shape/RichText/TextElementInterface.php
@@ -10,35 +10,39 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\RichText;
/**
- * Rich text element interface
+ * Rich text element interface.
*/
interface TextElementInterface
{
/**
- * Get text
+ * Get text.
*
* @return string Text
*/
public function getText();
/**
- * Set text
+ * Set text.
+ *
+ * @param string $pText Text value
*
- * @param $pText string Text
* @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
*/
public function setText($pText = '');
/**
- * Get font
+ * Get font.
*
* @return \PhpOffice\PhpPresentation\Style\Font
*/
@@ -51,14 +55,15 @@ interface TextElementInterface
/**
* @param string $lang
+ *
* @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface
*/
public function setLanguage($lang);
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode();
+ public function getHashCode(): string;
}
diff --git a/PhpOffice/PhpPresentation/Shape/Table.php b/PhpOffice/PhpPresentation/Shape/Table.php
old mode 100755
new mode 100644
index 21da321..d6313be
--- a/PhpOffice/PhpPresentation/Shape/Table.php
+++ b/PhpOffice/PhpPresentation/Shape/Table.php
@@ -10,44 +10,46 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\ComparableInterface;
+use PhpOffice\PhpPresentation\Exception\OutOfBoundsException;
use PhpOffice\PhpPresentation\Shape\Table\Row;
/**
- * Table shape
+ * Table shape.
*/
class Table extends AbstractGraphic implements ComparableInterface
{
/**
- * Rows
+ * Rows.
*
- * @var \PhpOffice\PhpPresentation\Shape\Table\Row[]
+ * @var array
*/
- private $rows;
+ private $rows = [];
/**
- * Number of columns
+ * Number of columns.
*
* @var int
*/
private $columnCount = 1;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Table instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\Table instance.
*
* @param int $columns Number of columns
*/
public function __construct($columns = 1)
{
- // Initialise variables
- $this->rows = array();
$this->columnCount = $columns;
// Initialize parent
@@ -58,43 +60,53 @@ class Table extends AbstractGraphic implements ComparableInterface
}
/**
- * Get row
+ * Get row.
*
- * @param int $row Row number
- * @param boolean $exceptionAsNull Return a null value instead of an exception?
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Table\Row
+ * @param int $row Row number
+ *
+ * @throws OutOfBoundsException
*/
- public function getRow($row = 0, $exceptionAsNull = false)
+ public function getRow(int $row = 0): Row
{
if (!isset($this->rows[$row])) {
- if ($exceptionAsNull) {
- return null;
- }
- throw new \Exception('Row number out of bounds.');
+ throw new OutOfBoundsException(
+ 0,
+ (count($this->rows) - 1) < 0 ? 0 : count($this->rows) - 1,
+ $row
+ );
}
return $this->rows[$row];
}
/**
- * Get rows
+ * @param int $row
*
- * @return \PhpOffice\PhpPresentation\Shape\Table\Row[]
+ * @return bool
*/
- public function getRows()
+ public function hasRow(int $row): bool
+ {
+ return isset($this->rows[$row]);
+ }
+
+ /**
+ * Get rows.
+ *
+ * @return Row[]
+ */
+ public function getRows(): array
{
return $this->rows;
}
/**
- * Create row
+ * Create row.
*
- * @return \PhpOffice\PhpPresentation\Shape\Table\Row
+ * @return Row
*/
- public function createRow()
+ public function createRow(): Row
{
- $row = new Row($this->columnCount);
+ $row = new Row($this->columnCount);
$this->rows[] = $row;
return $row;
@@ -103,27 +115,29 @@ class Table extends AbstractGraphic implements ComparableInterface
/**
* @return int
*/
- public function getNumColumns()
+ public function getNumColumns(): int
{
return $this->columnCount;
}
/**
* @param int $numColumn
- * @return Table
+ *
+ * @return self
*/
- public function setNumColumns($numColumn)
+ public function setNumColumns(int $numColumn): self
{
$this->columnCount = $numColumn;
+
return $this;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hashElements = '';
foreach ($this->rows as $row) {
diff --git a/PhpOffice/PhpPresentation/Shape/Table/Cell.php b/PhpOffice/PhpPresentation/Shape/Table/Cell.php
old mode 100755
new mode 100644
index 0a184d2..c8e8a94
--- a/PhpOffice/PhpPresentation/Shape/Table/Cell.php
+++ b/PhpOffice/PhpPresentation/Shape/Table/Cell.php
@@ -10,90 +10,94 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Table;
use PhpOffice\PhpPresentation\ComparableInterface;
+use PhpOffice\PhpPresentation\Exception\OutOfBoundsException;
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
use PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface;
use PhpOffice\PhpPresentation\Style\Borders;
use PhpOffice\PhpPresentation\Style\Fill;
/**
- * Table cell
+ * Table cell.
*/
class Cell implements ComparableInterface
{
/**
- * Rich text paragraphs
+ * Rich text paragraphs.
*
- * @var \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[]
+ * @var array
*/
private $richTextParagraphs;
/**
- * Active paragraph
+ * Active paragraph.
*
* @var int
*/
private $activeParagraph = 0;
/**
- * Fill
+ * Fill.
*
* @var \PhpOffice\PhpPresentation\Style\Fill
*/
private $fill;
/**
- * Borders
+ * Borders.
*
* @var \PhpOffice\PhpPresentation\Style\Borders
*/
private $borders;
/**
- * Width (in pixels)
+ * Width (in pixels).
*
* @var int
*/
private $width = 0;
/**
- * Colspan
+ * Colspan.
*
* @var int
*/
private $colSpan = 0;
/**
- * Rowspan
+ * Rowspan.
*
* @var int
*/
private $rowSpan = 0;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\RichText instance
+ * Create a new \PhpOffice\PhpPresentation\Shape\RichText instance.
*/
public function __construct()
{
// Initialise variables
- $this->richTextParagraphs = array(
- new Paragraph()
- );
- $this->activeParagraph = 0;
+ $this->richTextParagraphs = [
+ new Paragraph(),
+ ];
+ $this->activeParagraph = 0;
// Set fill
$this->fill = new Fill();
@@ -103,7 +107,7 @@ class Cell implements ComparableInterface
}
/**
- * Get active paragraph index
+ * Get active paragraph index.
*
* @return int
*/
@@ -113,26 +117,24 @@ class Cell implements ComparableInterface
}
/**
- * Get active paragraph
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * Get active paragraph.
*/
- public function getActiveParagraph()
+ public function getActiveParagraph(): Paragraph
{
return $this->richTextParagraphs[$this->activeParagraph];
}
/**
- * Set active paragraph
+ * Set active paragraph.
*
- * @param int $index
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @param int $index
+ *
+ * @throws OutOfBoundsException
*/
- public function setActiveParagraph($index = 0)
+ public function setActiveParagraph($index = 0): Paragraph
{
if ($index >= count($this->richTextParagraphs)) {
- throw new \Exception("Invalid paragraph count.");
+ throw new OutOfBoundsException(0, count($this->richTextParagraphs), $index);
}
$this->activeParagraph = $index;
@@ -141,28 +143,25 @@ class Cell implements ComparableInterface
}
/**
- * Get paragraph
+ * Get paragraph.
*
- * @param int $index
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ * @param int $index
+ *
+ * @throws OutOfBoundsException
*/
- public function getParagraph($index = 0)
+ public function getParagraph(int $index = 0): Paragraph
{
if ($index >= count($this->richTextParagraphs)) {
- throw new \Exception("Invalid paragraph count.");
+ throw new OutOfBoundsException(0, count($this->richTextParagraphs), $index);
}
return $this->richTextParagraphs[$index];
}
/**
- * Create paragraph
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
- * @throws \Exception
+ * Create paragraph.
*/
- public function createParagraph()
+ public function createParagraph(): Paragraph
{
$this->richTextParagraphs[] = new Paragraph();
$totalRichTextParagraphs = count($this->richTextParagraphs);
@@ -177,14 +176,15 @@ class Cell implements ComparableInterface
$this->getActiveParagraph()->setFont($font);
$this->getActiveParagraph()->setBulletStyle($bulletStyle);
}
+
return $this->getActiveParagraph();
}
/**
- * Add text
+ * Add text.
+ *
+ * @param TextElementInterface $pText Rich text element
*
- * @param \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface $pText Rich text element
- * @throws \Exception
* @return \PhpOffice\PhpPresentation\Shape\Table\Cell
*/
public function addText(TextElementInterface $pText = null)
@@ -195,11 +195,11 @@ class Cell implements ComparableInterface
}
/**
- * Create text (can not be formatted !)
+ * Create text (can not be formatted !).
+ *
+ * @param string $pText Text
*
- * @param string $pText Text
* @return \PhpOffice\PhpPresentation\Shape\RichText\TextElement
- * @throws \Exception
*/
public function createText($pText = '')
{
@@ -207,10 +207,9 @@ class Cell implements ComparableInterface
}
/**
- * Create break
+ * Create break.
*
* @return \PhpOffice\PhpPresentation\Shape\RichText\BreakElement
- * @throws \Exception
*/
public function createBreak()
{
@@ -218,19 +217,19 @@ class Cell implements ComparableInterface
}
/**
- * Create text run (can be formatted)
+ * Create text run (can be formatted).
+ *
+ * @param string $pText Text
*
- * @param string $pText Text
* @return \PhpOffice\PhpPresentation\Shape\RichText\Run
- * @throws \Exception
*/
- public function createTextRun($pText = '')
+ public function createTextRun(string $pText = '')
{
return $this->richTextParagraphs[$this->activeParagraph]->createTextRun($pText);
}
/**
- * Get plain text
+ * Get plain text.
*
* @return string
*/
@@ -239,7 +238,7 @@ class Cell implements ComparableInterface
// Return value
$returnValue = '';
- // Loop trough all \PhpOffice\PhpPresentation\Shape\RichText\Paragraph
+ // Loop trough all Paragraph
foreach ($this->richTextParagraphs as $p) {
$returnValue .= $p->getPlainText();
}
@@ -249,7 +248,7 @@ class Cell implements ComparableInterface
}
/**
- * Convert to string
+ * Convert to string.
*
* @return string
*/
@@ -259,9 +258,9 @@ class Cell implements ComparableInterface
}
/**
- * Get paragraphs
+ * Get paragraphs.
*
- * @return \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[]
+ * @return array
*/
public function getParagraphs()
{
@@ -269,24 +268,22 @@ class Cell implements ComparableInterface
}
/**
- * Set paragraphs
+ * Set paragraphs.
+ *
+ * @param array $paragraphs Array of paragraphs
*
- * @param \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] $paragraphs Array of paragraphs
- * @throws \Exception
* @return \PhpOffice\PhpPresentation\Shape\Table\Cell
*/
- public function setParagraphs($paragraphs = null)
+ public function setParagraphs(array $paragraphs = []): self
{
- if (!is_array($paragraphs)) {
- throw new \Exception("Invalid \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] array passed.");
- }
$this->richTextParagraphs = $paragraphs;
- $this->activeParagraph = count($this->richTextParagraphs) - 1;
+ $this->activeParagraph = count($this->richTextParagraphs) - 1;
+
return $this;
}
/**
- * Get fill
+ * Get fill.
*
* @return \PhpOffice\PhpPresentation\Style\Fill
*/
@@ -296,9 +293,8 @@ class Cell implements ComparableInterface
}
/**
- * Set fill
+ * Set fill.
*
- * @param \PhpOffice\PhpPresentation\Style\Fill $fill
* @return \PhpOffice\PhpPresentation\Shape\Table\Cell
*/
public function setFill(Fill $fill)
@@ -309,7 +305,7 @@ class Cell implements ComparableInterface
}
/**
- * Get borders
+ * Get borders.
*
* @return \PhpOffice\PhpPresentation\Style\Borders
*/
@@ -319,9 +315,8 @@ class Cell implements ComparableInterface
}
/**
- * Set borders
+ * Set borders.
*
- * @param \PhpOffice\PhpPresentation\Style\Borders $borders
* @return \PhpOffice\PhpPresentation\Shape\Table\Cell
*/
public function setBorders(Borders $borders)
@@ -332,7 +327,7 @@ class Cell implements ComparableInterface
}
/**
- * Get width
+ * Get width.
*
* @return int
*/
@@ -342,58 +337,35 @@ class Cell implements ComparableInterface
}
/**
- * Set width
+ * Set width.
*
- * @param int $value
- * @return \PhpOffice\PhpPresentation\Shape\Table\Cell
+ * @return self
*/
- public function setWidth($value = 0)
+ public function setWidth(int $pValue = 0)
{
- $this->width = $value;
+ $this->width = $pValue;
return $this;
}
- /**
- * Get colSpan
- *
- * @return int
- */
- public function getColSpan()
+ public function getColSpan(): int
{
return $this->colSpan;
}
- /**
- * Set colSpan
- *
- * @param int $value
- * @return \PhpOffice\PhpPresentation\Shape\Table\Cell
- */
- public function setColSpan($value = 0)
+ public function setColSpan(int $value = 0): self
{
$this->colSpan = $value;
return $this;
}
- /**
- * Get rowSpan
- *
- * @return int
- */
- public function getRowSpan()
+ public function getRowSpan(): int
{
return $this->rowSpan;
}
- /**
- * Set rowSpan
- *
- * @param int $value
- * @return \PhpOffice\PhpPresentation\Shape\Table\Cell
- */
- public function setRowSpan($value = 0)
+ public function setRowSpan(int $value = 0): self
{
$this->rowSpan = $value;
@@ -401,11 +373,11 @@ class Cell implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hashElements = '';
foreach ($this->richTextParagraphs as $element) {
@@ -416,28 +388,32 @@ class Cell implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Shape/Table/Row.php b/PhpOffice/PhpPresentation/Shape/Table/Row.php
old mode 100755
new mode 100644
index 18d4475..039bbf1
--- a/PhpOffice/PhpPresentation/Shape/Table/Row.php
+++ b/PhpOffice/PhpPresentation/Shape/Table/Row.php
@@ -10,136 +10,154 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Shape\Table;
use PhpOffice\PhpPresentation\ComparableInterface;
+use PhpOffice\PhpPresentation\Exception\OutOfBoundsException;
use PhpOffice\PhpPresentation\Style\Fill;
/**
- * Table row
+ * Table row.
*/
class Row implements ComparableInterface
{
/**
- * Cells
+ * Cells.
*
- * @var \PhpOffice\PhpPresentation\Shape\Table\Cell[]
+ * @var Cell[]
*/
- private $cells;
+ private $cells = [];
/**
- * Fill
+ * Fill.
*
- * @var \PhpOffice\PhpPresentation\Style\Fill
+ * @var Fill
*/
private $fill;
/**
- * Height (in pixels)
+ * Height (in pixels).
*
* @var int
*/
private $height = 38;
/**
- * Active cell index
+ * Active cell index.
*
* @var int
*/
private $activeCellIndex = -1;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Shape\Table\Row instance
- *
* @param int $columns Number of columns
*/
- public function __construct($columns = 1)
+ public function __construct(int $columns = 1)
{
- // Initialise variables
- $this->cells = array();
- for ($i = 0; $i < $columns; $i++) {
+ // Fill
+ $this->fill = new Fill();
+ // Cells
+ for ($inc = 0; $inc < $columns; ++$inc) {
$this->cells[] = new Cell();
}
-
- // Set fill
- $this->fill = new Fill();
}
/**
- * Get cell
+ * Get cell.
*
- * @param int $cell Cell number
- * @param boolean $exceptionAsNull Return a null value instead of an exception?
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Shape\Table\Cell
+ * @param int $cell Cell number
+ *
+ * @throws OutOfBoundsException
*/
- public function getCell($cell = 0, $exceptionAsNull = false)
+ public function getCell(int $cell = 0): Cell
{
if (!isset($this->cells[$cell])) {
- if ($exceptionAsNull) {
- return null;
- }
- throw new \Exception('Cell number out of bounds.');
+ throw new OutOfBoundsException(
+ 0,
+ (count($this->cells) - 1) < 0 ? count($this->cells) - 1 : 0,
+ $cell
+ );
}
return $this->cells[$cell];
}
/**
- * Get cells
+ * Get cell.
*
- * @return \PhpOffice\PhpPresentation\Shape\Table\Cell[]
+ * @param int $cell Cell number
+ *
+ * @return bool
*/
- public function getCells()
+ public function hasCell(int $cell): bool
+ {
+ return isset($this->cells[$cell]);
+ }
+
+ /**
+ * Get cells.
+ *
+ * @return array
+ */
+ public function getCells(): array
{
return $this->cells;
}
/**
- * Next cell (moves one cell to the right)
+ * Next cell (moves one cell to the right).
*
- * @return \PhpOffice\PhpPresentation\Shape\Table\Cell
- * @throws \Exception
+ * @return Cell
+ *
+ * @throws OutOfBoundsException
*/
- public function nextCell()
+ public function nextCell(): Cell
{
- $this->activeCellIndex++;
+ ++$this->activeCellIndex;
if (isset($this->cells[$this->activeCellIndex])) {
$this->cells[$this->activeCellIndex]->setFill(clone $this->getFill());
+
return $this->cells[$this->activeCellIndex];
}
- throw new \Exception("Cell count out of bounds.");
+
+ throw new OutOfBoundsException(
+ 0,
+ (count($this->cells) - 1) < 0 ? count($this->cells) - 1 : 0,
+ $this->activeCellIndex
+ );
}
/**
- * Get fill
+ * Get fill.
*
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @return Fill
*/
- public function getFill()
+ public function getFill(): Fill
{
return $this->fill;
}
/**
- * Set fill
+ * Set fill.
*
- * @param \PhpOffice\PhpPresentation\Style\Fill $fill
- * @return \PhpOffice\PhpPresentation\Shape\Table\Row
+ * @return self
*/
- public function setFill(Fill $fill)
+ public function setFill(Fill $fill): self
{
$this->fill = $fill;
@@ -147,22 +165,23 @@ class Row implements ComparableInterface
}
/**
- * Get height
+ * Get height.
*
* @return int
*/
- public function getHeight()
+ public function getHeight(): int
{
return $this->height;
}
/**
- * Set height
+ * Set height.
*
- * @param int $value
- * @return \PhpOffice\PhpPresentation\Shape\Table\Row
+ * @param int $value
+ *
+ * @return self
*/
- public function setHeight($value = 0)
+ public function setHeight(int $value = 0): self
{
$this->height = $value;
@@ -170,11 +189,11 @@ class Row implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
$hashElements = '';
foreach ($this->cells as $cell) {
@@ -185,28 +204,32 @@ class Row implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/ShapeContainerInterface.php b/PhpOffice/PhpPresentation/ShapeContainerInterface.php
old mode 100755
new mode 100644
index 4575c74..48e24f1
--- a/PhpOffice/PhpPresentation/ShapeContainerInterface.php
+++ b/PhpOffice/PhpPresentation/ShapeContainerInterface.php
@@ -10,58 +10,56 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
+use ArrayObject;
+
/**
- * PhpOffice\PhpPresentation\ShapeContainerInterface
+ * PhpOffice\PhpPresentation\ShapeContainerInterface.
*/
interface ShapeContainerInterface
{
/**
- * Get collection of shapes
- *
- * @return \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[]
- */
+ * Get collection of shapes.
+ *
+ * @return array|ArrayObject
+ */
public function getShapeCollection();
/**
- * Add shape to slide
- *
- * @param \PhpOffice\PhpPresentation\AbstractShape $shape
- * @return \PhpOffice\PhpPresentation\AbstractShape
- */
+ * Add shape to slide.
+ *
+ * @return AbstractShape
+ */
public function addShape(AbstractShape $shape);
/**
- * Get X Offset
- *
- * @return int
- */
- public function getOffsetX();
+ * Get X Offset.
+ */
+ public function getOffsetX(): int;
/**
- * Get Y Offset
- *
- * @return int
- */
- public function getOffsetY();
+ * Get Y Offset.
+ */
+ public function getOffsetY(): int;
/**
- * Get X Extent
- *
- * @return int
- */
- public function getExtentX();
+ * Get X Extent.
+ */
+ public function getExtentX(): int;
/**
- * Get Y Extent
- *
- * @return int
- */
- public function getExtentY();
+ * Get Y Extent.
+ */
+ public function getExtentY(): int;
+
+ public function getHashCode(): string;
}
diff --git a/PhpOffice/PhpPresentation/Slide.php b/PhpOffice/PhpPresentation/Slide.php
old mode 100755
new mode 100644
index 473d7b7..61c0d57
--- a/PhpOffice/PhpPresentation/Slide.php
+++ b/PhpOffice/PhpPresentation/Slide.php
@@ -10,66 +10,65 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation;
-use PhpOffice\PhpPresentation\Shape\Chart;
-use PhpOffice\PhpPresentation\Shape\RichText;
-use PhpOffice\PhpPresentation\Shape\Table;
use PhpOffice\PhpPresentation\Slide\AbstractSlide;
use PhpOffice\PhpPresentation\Slide\Note;
use PhpOffice\PhpPresentation\Slide\SlideLayout;
/**
- * Slide class
+ * Slide class.
*/
class Slide extends AbstractSlide implements ComparableInterface, ShapeContainerInterface
{
/**
- * The slide is shown in presentation
+ * The slide is shown in presentation.
+ *
* @var bool
*/
protected $isVisible = true;
/**
- * Slide layout
+ * Slide layout.
*
- * @var SlideLayout
+ * @var SlideLayout|null
*/
private $slideLayout;
/**
- * Slide master id
+ * Slide master id.
*
- * @var integer
+ * @var int
*/
private $slideMasterId = 1;
/**
- *
- * @var \PhpOffice\PhpPresentation\Slide\Note
+ * @var Note
*/
private $slideNote;
/**
- *
* @var \PhpOffice\PhpPresentation\Slide\Animation[]
*/
- protected $animations = array();
+ protected $animations = [];
/**
- * Name of the title
+ * Name of the title.
*
- * @var string
+ * @var string|null
*/
protected $name;
/**
- * Create a new slide
+ * Create a new slide.
*
* @param PhpPresentation $pParent
*/
@@ -89,32 +88,30 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
$oSlideLayout = reset($arraySlideLayouts);
$this->setSlideLayout($oSlideLayout);
}
+ // Set note
+ $this->setNote(new Note());
}
/**
- * Get slide layout
- *
- * @return SlideLayout
+ * Get slide layout.
*/
- public function getSlideLayout()
+ public function getSlideLayout(): ?SlideLayout
{
return $this->slideLayout;
}
/**
- * Set slide layout
- *
- * @param SlideLayout $layout
- * @return \PhpOffice\PhpPresentation\Slide
+ * Set slide layout.
*/
- public function setSlideLayout(SlideLayout $layout)
+ public function setSlideLayout(SlideLayout $layout): self
{
$this->slideLayout = $layout;
+
return $this;
}
/**
- * Get slide master id
+ * Get slide master id.
*
* @return int
*/
@@ -124,9 +121,10 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
}
/**
- * Set slide master id
+ * Set slide master id.
+ *
+ * @param int $masterId
*
- * @param int $masterId
* @return \PhpOffice\PhpPresentation\Slide
*/
public function setSlideMasterId($masterId = 1)
@@ -137,7 +135,7 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
}
/**
- * Copy slide (!= clone!)
+ * Copy slide (!= clone!).
*
* @return \PhpOffice\PhpPresentation\Slide
*/
@@ -148,24 +146,12 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
return $copied;
}
- /**
- *
- * @return \PhpOffice\PhpPresentation\Slide\Note
- */
- public function getNote()
+ public function getNote(): Note
{
- if (is_null($this->slideNote)) {
- $this->setNote();
- }
return $this->slideNote;
}
- /**
- *
- * @param \PhpOffice\PhpPresentation\Slide\Note $note
- * @return \PhpOffice\PhpPresentation\Slide
- */
- public function setNote(Note $note = null)
+ public function setNote(Note $note = null): self
{
$this->slideNote = (is_null($note) ? new Note() : $note);
$this->slideNote->setParent($this);
@@ -174,27 +160,27 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
}
/**
- * Get the name of the slide
+ * Get the name of the slide.
+ *
* @return string
*/
- public function getName()
+ public function getName(): ?string
{
return $this->name;
}
/**
- * Set the name of the slide
- * @param string $name
- * @return $this
+ * Set the name of the slide.
*/
- public function setName($name = null)
+ public function setName(?string $name = null): self
{
$this->name = $name;
+
return $this;
}
/**
- * @return boolean
+ * @return bool
*/
public function isVisible()
{
@@ -202,29 +188,33 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
}
/**
- * @param boolean $value
+ * @param bool $value
+ *
* @return Slide
*/
public function setIsVisible($value = true)
{
- $this->isVisible = (bool)$value;
+ $this->isVisible = (bool) $value;
+
return $this;
}
/**
- * Add an animation to the slide
+ * Add an animation to the slide.
+ *
+ * @param \PhpOffice\PhpPresentation\Slide\Animation $animation
*
- * @param \PhpOffice\PhpPresentation\Slide\Animation
* @return Slide
*/
public function addAnimation($animation)
{
$this->animations[] = $animation;
+
return $this;
}
/**
- * Get collection of animations
+ * Get collection of animations.
*
* @return \PhpOffice\PhpPresentation\Slide\Animation[]
*/
@@ -234,13 +224,16 @@ class Slide extends AbstractSlide implements ComparableInterface, ShapeContainer
}
/**
- * Set collection of animations
+ * Set collection of animations.
+ *
* @param \PhpOffice\PhpPresentation\Slide\Animation[] $array
+ *
* @return Slide
*/
- public function setAnimations(array $array = array())
+ public function setAnimations(array $array = [])
{
$this->animations = $array;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Slide/AbstractBackground.php b/PhpOffice/PhpPresentation/Slide/AbstractBackground.php
old mode 100755
new mode 100644
index 72edbdf..42aa4d5
--- a/PhpOffice/PhpPresentation/Slide/AbstractBackground.php
+++ b/PhpOffice/PhpPresentation/Slide/AbstractBackground.php
@@ -1,8 +1,25 @@
|ArrayObject
*/
- protected $shapeCollection = null;
+ protected $shapeCollection = [];
/**
- * Extent Y
+ * Extent Y.
*
* @var int
*/
protected $extentY;
/**
- * Extent X
+ * Extent X.
*
* @var int
*/
protected $extentX;
/**
- * Offset X
+ * Offset X.
*
* @var int
*/
protected $offsetX;
/**
- * Offset Y
+ * Offset Y.
*
* @var int
*/
protected $offsetY;
/**
- * Slide identifier
+ * Slide identifier.
*
* @var string
*/
protected $identifier;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
protected $hashIndex;
/**
- * Parent presentation
+ * Parent presentation.
*
- * @var PhpPresentation
+ * @var PhpPresentation|null
*/
protected $parent;
/**
- * Background of the slide
+ * Background of the slide.
*
* @var AbstractBackground
*/
protected $background;
/**
- * Get collection of shapes
+ * Get collection of shapes.
*
- * @return \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[]
+ * @return array|ArrayObject
*/
public function getShapeCollection()
{
@@ -107,284 +110,252 @@ abstract class AbstractSlide implements ComparableInterface, ShapeContainerInter
}
/**
- * Get collection of shapes
+ * Get collection of shapes.
+ *
+ * @param array|ArrayObject $shapeCollection
*
- * @param array $shapeCollection
* @return AbstractSlide
*/
- public function setShapeCollection($shapeCollection = array())
+ public function setShapeCollection($shapeCollection = [])
{
$this->shapeCollection = $shapeCollection;
+
return $this;
}
/**
- * Add shape to slide
+ * Add shape to slide.
*
- * @param \PhpOffice\PhpPresentation\AbstractShape $shape
- * @return \PhpOffice\PhpPresentation\AbstractShape
- * @throws \Exception
+ * @return AbstractShape
*/
- public function addShape(AbstractShape $shape)
+ public function addShape(AbstractShape $shape): AbstractShape
{
$shape->setContainer($this);
+
return $shape;
}
/**
- * Get X Offset
- *
- * @return int
+ * Get X Offset.
*/
- public function getOffsetX()
+ public function getOffsetX(): int
{
- if ($this->offsetX === null) {
+ if (null === $this->offsetX) {
$offsets = GeometryCalculator::calculateOffsets($this);
$this->offsetX = $offsets[GeometryCalculator::X];
$this->offsetY = $offsets[GeometryCalculator::Y];
}
+
return $this->offsetX;
}
/**
- * Get Y Offset
- *
- * @return int
+ * Get Y Offset.
*/
- public function getOffsetY()
+ public function getOffsetY(): int
{
- if ($this->offsetY === null) {
+ if (null === $this->offsetY) {
$offsets = GeometryCalculator::calculateOffsets($this);
$this->offsetX = $offsets[GeometryCalculator::X];
$this->offsetY = $offsets[GeometryCalculator::Y];
}
+
return $this->offsetY;
}
/**
- * Get X Extent
- *
- * @return int
+ * Get X Extent.
*/
- public function getExtentX()
+ public function getExtentX(): int
{
- if ($this->extentX === null) {
+ if (null === $this->extentX) {
$extents = GeometryCalculator::calculateExtents($this);
$this->extentX = $extents[GeometryCalculator::X];
$this->extentY = $extents[GeometryCalculator::Y];
}
+
return $this->extentX;
}
/**
- * Get Y Extent
- *
- * @return int
+ * Get Y Extent.
*/
- public function getExtentY()
+ public function getExtentY(): int
{
- if ($this->extentY === null) {
+ if (null === $this->extentY) {
$extents = GeometryCalculator::calculateExtents($this);
$this->extentX = $extents[GeometryCalculator::X];
$this->extentY = $extents[GeometryCalculator::Y];
}
+
return $this->extentY;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->identifier . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
/**
- * Create rich text shape
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText
- * @throws \Exception
+ * Create rich text shape.
*/
- public function createRichTextShape()
+ public function createRichTextShape(): RichText
{
$shape = new RichText();
$this->addShape($shape);
+
return $shape;
}
/**
- * Create line shape
+ * Create line shape.
*
- * @param int $fromX Starting point x offset
- * @param int $fromY Starting point y offset
- * @param int $toX Ending point x offset
- * @param int $toY Ending point y offset
- * @return \PhpOffice\PhpPresentation\Shape\Line
- * @throws \Exception
+ * @param int $fromX Starting point x offset
+ * @param int $fromY Starting point y offset
+ * @param int $toX Ending point x offset
+ * @param int $toY Ending point y offset
*/
- public function createLineShape($fromX, $fromY, $toX, $toY)
+ public function createLineShape(int $fromX, int $fromY, int $toX, int $toY): Line
{
$shape = new Line($fromX, $fromY, $toX, $toY);
$this->addShape($shape);
+
return $shape;
}
/**
- * Create chart shape
- *
- * @return \PhpOffice\PhpPresentation\Shape\Chart
- * @throws \Exception
+ * Create chart shape.
*/
- public function createChartShape()
+ public function createChartShape(): Chart
{
$shape = new Chart();
$this->addShape($shape);
+
return $shape;
}
/**
- * Create drawing shape
- *
- * @return \PhpOffice\PhpPresentation\Shape\Drawing\File
- * @throws \Exception
+ * Create drawing shape.
*/
- public function createDrawingShape()
+ public function createDrawingShape(): File
{
$shape = new File();
$this->addShape($shape);
+
return $shape;
}
/**
- * Create table shape
+ * Create table shape.
*
- * @param int $columns Number of columns
- * @return \PhpOffice\PhpPresentation\Shape\Table
- * @throws \Exception
+ * @param int $columns Number of columns
*/
- public function createTableShape($columns = 1)
+ public function createTableShape(int $columns = 1): Table
{
$shape = new Table($columns);
$this->addShape($shape);
+
return $shape;
}
/**
- * Creates a group within this slide
- *
- * @return \PhpOffice\PhpPresentation\Shape\Group
- * @throws \Exception
+ * Creates a group within this slide.
*/
- public function createGroup()
+ public function createGroup(): Group
{
$shape = new Group();
$this->addShape($shape);
+
return $shape;
}
/**
- * Get parent
- *
- * @return PhpPresentation
+ * Get parent.
*/
- public function getParent()
+ public function getParent(): ?PhpPresentation
{
return $this->parent;
}
/**
- * Re-bind parent
- *
- * @param \PhpOffice\PhpPresentation\PhpPresentation $parent
- * @return \PhpOffice\PhpPresentation\Slide\AbstractSlide
- * @throws \Exception
+ * Re-bind parent.
*/
- public function rebindParent(PhpPresentation $parent)
+ public function rebindParent(PhpPresentation $parent): AbstractSlide
{
$this->parent->removeSlideByIndex($this->parent->getIndex($this));
$this->parent = $parent;
+
return $this;
}
- /**
- * @return AbstractBackground
- */
- public function getBackground()
+ public function getBackground(): ?AbstractBackground
{
return $this->background;
}
- /**
- * @param AbstractBackground $background
- * @return \PhpOffice\PhpPresentation\Slide\AbstractSlide
- */
- public function setBackground(AbstractBackground $background = null)
+ public function setBackground(AbstractBackground $background = null): AbstractSlide
{
$this->background = $background;
+
return $this;
}
- /**
- *
- * @return \PhpOffice\PhpPresentation\Slide\Transition
- */
- public function getTransition()
+ public function getTransition(): ?Transition
{
return $this->slideTransition;
}
- /**
- *
- * @param \PhpOffice\PhpPresentation\Slide\Transition $transition
- * @return \PhpOffice\PhpPresentation\Slide\AbstractSlide
- */
- public function setTransition(Transition $transition = null)
+ public function setTransition(Transition $transition = null): self
{
$this->slideTransition = $transition;
+
return $this;
}
- /**
- * @return string
- */
- public function getRelsIndex()
+ public function getRelsIndex(): string
{
return $this->relsIndex;
}
- /**
- * @param string $indexName
- */
- public function setRelsIndex($indexName)
+ public function setRelsIndex(string $indexName): self
{
$this->relsIndex = $indexName;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Slide/Animation.php b/PhpOffice/PhpPresentation/Slide/Animation.php
old mode 100755
new mode 100644
index 54f77da..2e6d1dc
--- a/PhpOffice/PhpPresentation/Slide/Animation.php
+++ b/PhpOffice/PhpPresentation/Slide/Animation.php
@@ -1,4 +1,23 @@
*/
- protected $shapeCollection = array();
+ protected $shapeCollection = [];
/**
- * @param AbstractShape $shape
* @return Animation
*/
public function addShape(AbstractShape $shape)
{
$this->shapeCollection[] = $shape;
+
return $this;
}
/**
- * @return array
+ * @return array
*/
- public function getShapeCollection()
+ public function getShapeCollection(): array
{
return $this->shapeCollection;
}
/**
- * @param array $array
+ * @param array $array
+ *
* @return Animation
*/
- public function setShapeCollection(array $array = array())
+ public function setShapeCollection(array $array = [])
{
$this->shapeCollection = $array;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Slide/Background/Color.php b/PhpOffice/PhpPresentation/Slide/Background/Color.php
old mode 100755
new mode 100644
index fb975f6..24507d8
--- a/PhpOffice/PhpPresentation/Slide/Background/Color.php
+++ b/PhpOffice/PhpPresentation/Slide/Background/Color.php
@@ -1,4 +1,22 @@
color = $color;
+
return $this;
}
- /**
- * @return StyleColor
- */
- public function getColor()
+ public function getColor(): ?StyleColor
{
return $this->color;
}
diff --git a/PhpOffice/PhpPresentation/Slide/Background/Image.php b/PhpOffice/PhpPresentation/Slide/Background/Image.php
old mode 100755
new mode 100644
index ca6eb54..39c998e
--- a/PhpOffice/PhpPresentation/Slide/Background/Image.php
+++ b/PhpOffice/PhpPresentation/Slide/Background/Image.php
@@ -1,7 +1,26 @@
path;
}
/**
- * Set Path
+ * Set Path.
*
- * @param string $pValue File path
- * @param boolean $pVerifyFile Verify file
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Slide\Background\Image
+ * @param string $pValue File path
+ * @param bool $pVerifyFile Verify file
+ *
+ * @throws FileNotFoundException
+ *
+ * @return self
*/
- public function setPath($pValue = '', $pVerifyFile = true)
+ public function setPath(string $pValue = '', bool $pVerifyFile = true)
{
if ($pVerifyFile) {
if (!file_exists($pValue)) {
- throw new \Exception("File not found : $pValue");
+ throw new FileNotFoundException($pValue);
}
- if ($this->width == 0 && $this->height == 0) {
+ if (0 == $this->width && 0 == $this->height) {
// Get width/height
list($this->width, $this->height) = getimagesize($pValue);
}
}
$this->path = $pValue;
+
return $this;
}
/**
- * Get Filename
+ * Get Filename.
*
* @return string
*/
- public function getFilename()
+ public function getFilename(): string
{
- return basename($this->path);
+ return $this->path ? basename($this->path) : '';
}
/**
- * Get Extension
+ * Get Extension.
*
* @return string
*/
- public function getExtension()
+ public function getExtension(): string
{
- $exploded = explode('.', basename($this->path));
+ $exploded = explode('.', $this->getFilename());
return $exploded[count($exploded) - 1];
}
/**
- * Get indexed filename (using image index)
+ * Get indexed filename (using image index).
+ *
+ * @param string $numSlide
*
- * @param integer $numSlide
* @return string
*/
public function getIndexedFilename($numSlide)
diff --git a/PhpOffice/PhpPresentation/Slide/Background/SchemeColor.php b/PhpOffice/PhpPresentation/Slide/Background/SchemeColor.php
old mode 100755
new mode 100644
index 51bba87..3cb785d
--- a/PhpOffice/PhpPresentation/Slide/Background/SchemeColor.php
+++ b/PhpOffice/PhpPresentation/Slide/Background/SchemeColor.php
@@ -1,4 +1,22 @@
schemeColor = $color;
+
return $this;
}
- /**
- * @return StyleSchemeColor
- */
- public function getSchemeColor()
+ public function getSchemeColor(): ?StyleSchemeColor
{
return $this->schemeColor;
}
diff --git a/PhpOffice/PhpPresentation/Slide/Iterator.php b/PhpOffice/PhpPresentation/Slide/Iterator.php
old mode 100755
new mode 100644
index 66d159d..d761be9
--- a/PhpOffice/PhpPresentation/Slide/Iterator.php
+++ b/PhpOffice/PhpPresentation/Slide/Iterator.php
@@ -10,49 +10,46 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Slide;
+use IteratorIterator;
use PhpOffice\PhpPresentation\PhpPresentation;
-/**
- * \PhpOffice\PhpPresentation\Slide\Iterator
- *
- * Used to iterate slides in PhpPresentation
- */
-class Iterator extends \IteratorIterator
+// @phpstan-ignore-next-line
+class Iterator extends IteratorIterator
{
/**
- * Presentation to iterate
+ * Presentation to iterate.
*
- * @var \PhpOffice\PhpPresentation\PhpPresentation
+ * @var PhpPresentation
*/
private $subject;
/**
- * Current iterator position
+ * Current iterator position.
*
* @var int
*/
private $position = 0;
/**
- * Create a new slide iterator
- *
- * @param PhpPresentation $subject
+ * Create a new slide iterator.
*/
- public function __construct(PhpPresentation $subject = null)
+ public function __construct(PhpPresentation $subject)
{
- // Set subject
$this->subject = $subject;
}
/**
- * Destructor
+ * Destructor.
*/
public function __destruct()
{
@@ -60,18 +57,18 @@ class Iterator extends \IteratorIterator
}
/**
- * Rewind iterator
+ * Rewind iterator.
*/
+ #[\ReturnTypeWillChange]
public function rewind()
{
$this->position = 0;
}
/**
- * Current \PhpOffice\PhpPresentation\Slide
+ * Current \PhpOffice\PhpPresentation\Slide.
*
* @return \PhpOffice\PhpPresentation\Slide
- * @throws \Exception
*/
public function current()
{
@@ -79,7 +76,7 @@ class Iterator extends \IteratorIterator
}
/**
- * Current key
+ * Current key.
*
* @return int
*/
@@ -89,8 +86,9 @@ class Iterator extends \IteratorIterator
}
/**
- * Next value
+ * Next value.
*/
+ #[\ReturnTypeWillChange]
public function next()
{
++$this->position;
@@ -99,8 +97,9 @@ class Iterator extends \IteratorIterator
/**
* More \PhpOffice\PhpPresentation\Slide instances available?
*
- * @return boolean
+ * @return bool
*/
+ #[\ReturnTypeWillChange]
public function valid()
{
return $this->position < $this->subject->getSlideCount();
diff --git a/PhpOffice/PhpPresentation/Slide/Layout.php b/PhpOffice/PhpPresentation/Slide/Layout.php
old mode 100755
new mode 100644
index b4fc027..1c1ec93
--- a/PhpOffice/PhpPresentation/Slide/Layout.php
+++ b/PhpOffice/PhpPresentation/Slide/Layout.php
@@ -10,28 +10,31 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Slide;
/**
- * \PhpOffice\PhpPresentation\Slide\Layout
+ * \PhpOffice\PhpPresentation\Slide\Layout.
*/
class Layout
{
/** Layout constants */
- const TITLE_SLIDE = 'Title Slide';
- const TITLE_AND_CONTENT = 'Title and Content';
- const SECTION_HEADER = 'Section Header';
- const TWO_CONTENT = 'Two Content';
- const COMPARISON = 'Comparison';
- const TITLE_ONLY = 'Title Only';
- const BLANK = 'Blank';
- const CONTENT_WITH_CAPTION = 'Content with Caption';
- const PICTURE_WITH_CAPTION = 'Picture with Caption';
- const TITLE_AND_VERTICAL_TEXT = 'Title and Vertical Text';
- const VERTICAL_TITLE_AND_TEXT = 'Vertical Title and Text';
+ public const TITLE_SLIDE = 'Title Slide';
+ public const TITLE_AND_CONTENT = 'Title and Content';
+ public const SECTION_HEADER = 'Section Header';
+ public const TWO_CONTENT = 'Two Content';
+ public const COMPARISON = 'Comparison';
+ public const TITLE_ONLY = 'Title Only';
+ public const BLANK = 'Blank';
+ public const CONTENT_WITH_CAPTION = 'Content with Caption';
+ public const PICTURE_WITH_CAPTION = 'Picture with Caption';
+ public const TITLE_AND_VERTICAL_TEXT = 'Title and Vertical Text';
+ public const VERTICAL_TITLE_AND_TEXT = 'Vertical Title and Text';
}
diff --git a/PhpOffice/PhpPresentation/Slide/Note.php b/PhpOffice/PhpPresentation/Slide/Note.php
old mode 100755
new mode 100644
index f4af120..d1423a0
--- a/PhpOffice/PhpPresentation/Slide/Note.php
+++ b/PhpOffice/PhpPresentation/Slide/Note.php
@@ -10,83 +10,84 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Slide;
+use ArrayObject;
use PhpOffice\PhpPresentation\AbstractShape;
use PhpOffice\PhpPresentation\ComparableInterface;
use PhpOffice\PhpPresentation\GeometryCalculator;
+use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\ShapeContainerInterface;
use PhpOffice\PhpPresentation\Slide;
-use PhpOffice\PhpPresentation\Shape\RichText;
-/**
- * Note class
- */
class Note implements ComparableInterface, ShapeContainerInterface
{
/**
- * Parent slide
+ * Parent slide.
*
* @var Slide
*/
private $parent;
/**
- * Collection of shapes
+ * Collection of shapes.
*
- * @var \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[]
+ * @var array|ArrayObject
*/
- private $shapeCollection = null;
+ private $shapeCollection;
/**
- * Note identifier
+ * Note identifier.
*
* @var string
*/
private $identifier;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Offset X
+ * Offset X.
*
* @var int
*/
protected $offsetX;
/**
- * Offset Y
+ * Offset Y.
*
* @var int
*/
protected $offsetY;
/**
- * Extent X
+ * Extent X.
*
* @var int
*/
protected $extentX;
/**
- * Extent Y
+ * Extent Y.
*
* @var int
*/
protected $extentY;
/**
- * Create a new note
+ * Create a new note.
*
* @param Slide $pParent
*/
@@ -96,16 +97,16 @@ class Note implements ComparableInterface, ShapeContainerInterface
$this->parent = $pParent;
// Shape collection
- $this->shapeCollection = new \ArrayObject();
+ $this->shapeCollection = new ArrayObject();
// Set identifier
$this->identifier = md5(rand(0, 9999) . time());
}
/**
- * Get collection of shapes
+ * Get collection of shapes.
*
- * @return \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[]
+ * @return array|ArrayObject
*/
public function getShapeCollection()
{
@@ -113,13 +114,11 @@ class Note implements ComparableInterface, ShapeContainerInterface
}
/**
- * Add shape to slide
+ * Add shape to slide.
*
- * @param \PhpOffice\PhpPresentation\AbstractShape $shape
- * @return \PhpOffice\PhpPresentation\AbstractShape
- * @throws \Exception
+ * @return AbstractShape
*/
- public function addShape(AbstractShape $shape)
+ public function addShape(AbstractShape $shape): AbstractShape
{
$shape->setContainer($this);
@@ -127,12 +126,9 @@ class Note implements ComparableInterface, ShapeContainerInterface
}
/**
- * Create rich text shape
- *
- * @return \PhpOffice\PhpPresentation\Shape\RichText
- * @throws \Exception
+ * Create rich text shape.
*/
- public function createRichTextShape()
+ public function createRichTextShape(): RichText
{
$shape = new RichText();
$this->addShape($shape);
@@ -141,7 +137,7 @@ class Note implements ComparableInterface, ShapeContainerInterface
}
/**
- * Get parent
+ * Get parent.
*
* @return Slide
*/
@@ -151,111 +147,110 @@ class Note implements ComparableInterface, ShapeContainerInterface
}
/**
- * Set parent
+ * Set parent.
*
- * @param Slide $parent
* @return Note
*/
public function setParent(Slide $parent)
{
$this->parent = $parent;
+
return $this;
}
-
/**
- * Get X Offset
- *
- * @return int
+ * Get X Offset.
*/
- public function getOffsetX()
+ public function getOffsetX(): int
{
- if ($this->offsetX === null) {
+ if (null === $this->offsetX) {
$offsets = GeometryCalculator::calculateOffsets($this);
$this->offsetX = $offsets[GeometryCalculator::X];
$this->offsetY = $offsets[GeometryCalculator::Y];
}
+
return $this->offsetX;
}
-
+
/**
- * Get Y Offset
- *
- * @return int
+ * Get Y Offset.
*/
- public function getOffsetY()
+ public function getOffsetY(): int
{
- if ($this->offsetY === null) {
+ if (null === $this->offsetY) {
$offsets = GeometryCalculator::calculateOffsets($this);
$this->offsetX = $offsets[GeometryCalculator::X];
$this->offsetY = $offsets[GeometryCalculator::Y];
}
+
return $this->offsetY;
}
-
+
/**
- * Get X Extent
- *
- * @return int
+ * Get X Extent.
*/
- public function getExtentX()
+ public function getExtentX(): int
{
- if ($this->extentX === null) {
+ if (null === $this->extentX) {
$extents = GeometryCalculator::calculateExtents($this);
$this->extentX = $extents[GeometryCalculator::X];
$this->extentY = $extents[GeometryCalculator::Y];
}
+
return $this->extentX;
}
-
+
/**
- * Get Y Extent
- *
- * @return int
+ * Get Y Extent.
*/
- public function getExtentY()
+ public function getExtentY(): int
{
- if ($this->extentY === null) {
+ if (null === $this->extentY) {
$extents = GeometryCalculator::calculateExtents($this);
$this->extentX = $extents[GeometryCalculator::X];
$this->extentY = $extents[GeometryCalculator::Y];
}
+
return $this->extentY;
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5($this->identifier . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Slide/SlideLayout.php b/PhpOffice/PhpPresentation/Slide/SlideLayout.php
old mode 100755
new mode 100644
index 72f42b8..9c30db2
--- a/PhpOffice/PhpPresentation/Slide/SlideLayout.php
+++ b/PhpOffice/PhpPresentation/Slide/SlideLayout.php
@@ -10,10 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Slide;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -22,42 +26,43 @@ use PhpOffice\PhpPresentation\Style\ColorMap;
class SlideLayout extends AbstractSlide implements ComparableInterface, ShapeContainerInterface
{
+ /**
+ * @var SlideMaster
+ */
protected $slideMaster;
/**
- * Slide relation ID (should not be used by user code!)
+ * Slide relation ID (should not be used by user code!).
*
* @var string
*/
public $relationId;
/**
- * Slide layout NR (should not be used by user code!)
+ * Slide layout NR (should not be used by user code!).
*
* @var int
*/
public $layoutNr;
/**
- * Slide layout ID (should not be used by user code!)
+ * Slide layout ID (should not be used by user code!).
*
* @var int
*/
public $layoutId;
/**
- * Slide layout ID (should not be used by user code!)
+ * Slide layout ID (should not be used by user code!).
*
- * @var int
+ * @var string|null
*/
protected $layoutName;
/**
- * Mapping of colors to the theme
+ * Mapping of colors to the theme.
*
* @var \PhpOffice\PhpPresentation\Style\ColorMap
*/
public $colorMap;
/**
- * Create a new slideLayout
- *
- * @param SlideMaster $pSlideMaster
+ * Create a new slideLayout.
*/
public function __construct(SlideMaster $pSlideMaster)
{
@@ -71,28 +76,19 @@ class SlideLayout extends AbstractSlide implements ComparableInterface, ShapeCon
$this->colorMap = new ColorMap();
}
- /**
- * @return int
- */
- public function getLayoutName()
+ public function getLayoutName(): ?string
{
return $this->layoutName;
}
- /**
- * @param int $layoutName
- * @return SlideLayout
- */
- public function setLayoutName($layoutName)
+ public function setLayoutName(string $layoutName): self
{
$this->layoutName = $layoutName;
+
return $this;
}
- /**
- * @return SlideMaster
- */
- public function getSlideMaster()
+ public function getSlideMaster(): SlideMaster
{
return $this->slideMaster;
}
diff --git a/PhpOffice/PhpPresentation/Slide/SlideMaster.php b/PhpOffice/PhpPresentation/Slide/SlideMaster.php
old mode 100755
new mode 100644
index f06f740..3966f00
--- a/PhpOffice/PhpPresentation/Slide/SlideMaster.php
+++ b/PhpOffice/PhpPresentation/Slide/SlideMaster.php
@@ -10,10 +10,14 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Slide;
use PhpOffice\PhpPresentation\ComparableInterface;
@@ -25,35 +29,32 @@ use PhpOffice\PhpPresentation\Style\ColorMap;
use PhpOffice\PhpPresentation\Style\SchemeColor;
use PhpOffice\PhpPresentation\Style\TextStyle;
-/**
- * Class SlideMaster
- */
class SlideMaster extends AbstractSlide implements ComparableInterface, ShapeContainerInterface
{
/**
- * Collection of Slide objects
+ * Collection of Slide objects.
*
- * @var \PhpOffice\PhpPresentation\Slide\SlideLayout[]
+ * @var array
*/
- protected $slideLayouts = array();
+ protected $slideLayouts = [];
/**
- * Mapping of colors to the theme
+ * Mapping of colors to the theme.
*
- * @var \PhpOffice\PhpPresentation\Style\ColorMap
+ * @var ColorMap
*/
public $colorMap;
/**
- * @var \PhpOffice\PhpPresentation\Style\TextStyle
+ * @var TextStyle
*/
protected $textStyles;
/**
- * @var \PhpOffice\PhpPresentation\Style\SchemeColor[]
+ * @var array
*/
- protected $arraySchemeColor = array();
+ protected $arraySchemeColor = [];
/**
- * @var array
+ * @var array
*/
- protected $defaultSchemeColor = array(
+ protected $defaultSchemeColor = [
'dk1' => '000000',
'lt1' => 'FFFFFF',
'dk2' => '1F497D',
@@ -66,13 +67,10 @@ class SlideMaster extends AbstractSlide implements ComparableInterface, ShapeCon
'accent6' => 'F79646',
'hlink' => '0000FF',
'folHlink' => '800080',
- );
+ ];
/**
- * Create a new slideMaster
- *
- * @param PhpPresentation $pParent
- * @throws \Exception
+ * Create a new slideMaster.
*/
public function __construct(PhpPresentation $pParent = null)
{
@@ -99,35 +97,36 @@ class SlideMaster extends AbstractSlide implements ComparableInterface, ShapeCon
}
/**
- * Create a slideLayout and add it to this presentation
+ * Create a slideLayout and add it to this presentation.
*
- * @return \PhpOffice\PhpPresentation\Slide\SlideLayout
- * @throws \Exception
+ * @return SlideLayout
*/
- public function createSlideLayout()
+ public function createSlideLayout(): SlideLayout
{
$newSlideLayout = new SlideLayout($this);
$this->addSlideLayout($newSlideLayout);
+
return $newSlideLayout;
}
/**
- * Add slideLayout
+ * Add slideLayout.
*
- * @param \PhpOffice\PhpPresentation\Slide\SlideLayout $slideLayout
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Slide\SlideLayout
+ * @param SlideLayout|null $slideLayout
+ *
+ * @return SlideLayout
*/
- public function addSlideLayout(SlideLayout $slideLayout = null)
+ public function addSlideLayout(SlideLayout $slideLayout = null): SlideLayout
{
$this->slideLayouts[] = $slideLayout;
+
return $slideLayout;
}
/**
- * @return SlideLayout[]
+ * @return array
*/
- public function getAllSlideLayouts()
+ public function getAllSlideLayouts(): array
{
return $this->slideLayouts;
}
@@ -135,35 +134,35 @@ class SlideMaster extends AbstractSlide implements ComparableInterface, ShapeCon
/**
* @return TextStyle
*/
- public function getTextStyles()
+ public function getTextStyles(): TextStyle
{
return $this->textStyles;
}
/**
- * @param TextStyle $textStyle
- * @return $this
+ * @return self
*/
- public function setTextStyles(TextStyle $textStyle)
+ public function setTextStyles(TextStyle $textStyle): self
{
$this->textStyles = $textStyle;
+
return $this;
}
/**
- * @param SchemeColor $schemeColor
- * @return $this
+ * @return self
*/
- public function addSchemeColor(SchemeColor $schemeColor)
+ public function addSchemeColor(SchemeColor $schemeColor): self
{
$this->arraySchemeColor[$schemeColor->getValue()] = $schemeColor;
+
return $this;
}
/**
- * @return \PhpOffice\PhpPresentation\Style\SchemeColor[]
+ * @return array
*/
- public function getAllSchemeColors()
+ public function getAllSchemeColors(): array
{
return $this->arraySchemeColor;
}
diff --git a/PhpOffice/PhpPresentation/Slide/Transition.php b/PhpOffice/PhpPresentation/Slide/Transition.php
old mode 100755
new mode 100644
index b2e6a29..9836f7c
--- a/PhpOffice/PhpPresentation/Slide/Transition.php
+++ b/PhpOffice/PhpPresentation/Slide/Transition.php
@@ -10,72 +10,72 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Slide;
-use PhpOffice\PhpPresentation\Slide;
-use PhpOffice\PhpPresentation\Shape\RichText;
-
/**
- * Transition class
+ * Transition class.
*/
class Transition
{
- const SPEED_FAST = 'fast';
- const SPEED_MEDIUM = 'med';
- const SPEED_SLOW = 'slow';
+ public const SPEED_FAST = 'fast';
+ public const SPEED_MEDIUM = 'med';
+ public const SPEED_SLOW = 'slow';
- const TRANSITION_BLINDS_HORIZONTAL = 'blinds_horz';
- const TRANSITION_BLINDS_VERTICAL = 'blinds_vert';
- const TRANSITION_CHECKER_HORIZONTAL = 'checker_horz';
- const TRANSITION_CHECKER_VERTICAL = 'checker_vert';
- const TRANSITION_CIRCLE = 'circle';
- const TRANSITION_COMB_HORIZONTAL = 'comb_horz';
- const TRANSITION_COMB_VERTICAL = 'comb_vert';
- const TRANSITION_COVER_DOWN = 'cover_d';
- const TRANSITION_COVER_LEFT = 'cover_l';
- const TRANSITION_COVER_LEFT_DOWN = 'cover_ld';
- const TRANSITION_COVER_LEFT_UP = 'cover_lu';
- const TRANSITION_COVER_RIGHT = 'cover_r';
- const TRANSITION_COVER_RIGHT_DOWN = 'cover_rd';
- const TRANSITION_COVER_RIGHT_UP = 'cover_ru';
- const TRANSITION_COVER_UP = 'cover_u';
- const TRANSITION_CUT = 'cut';
- const TRANSITION_DIAMOND = 'diamond';
- const TRANSITION_DISSOLVE = 'dissolve';
- const TRANSITION_FADE = 'fade';
- const TRANSITION_NEWSFLASH = 'newsflash';
- const TRANSITION_PLUS = 'plus';
- const TRANSITION_PULL_DOWN = 'pull_d';
- const TRANSITION_PULL_LEFT = 'pull_l';
- const TRANSITION_PULL_RIGHT = 'pull_r';
- const TRANSITION_PULL_UP = 'pull_u';
- const TRANSITION_PUSH_DOWN = 'push_d';
- const TRANSITION_PUSH_LEFT = 'push_l';
- const TRANSITION_PUSH_RIGHT = 'push_r';
- const TRANSITION_PUSH_UP = 'push_u';
- const TRANSITION_RANDOM = 'random';
- const TRANSITION_RANDOMBAR_HORIZONTAL = 'randomBar_horz';
- const TRANSITION_RANDOMBAR_VERTICAL = 'randomBar_vert';
- const TRANSITION_SPLIT_IN_HORIZONTAL = 'split_in_horz';
- const TRANSITION_SPLIT_OUT_HORIZONTAL = 'split_out_horz';
- const TRANSITION_SPLIT_IN_VERTICAL = 'split_in_vert';
- const TRANSITION_SPLIT_OUT_VERTICAL = 'split_out_vert';
- const TRANSITION_STRIPS_LEFT_DOWN = 'strips_ld';
- const TRANSITION_STRIPS_LEFT_UP = 'strips_lu';
- const TRANSITION_STRIPS_RIGHT_DOWN = 'strips_rd';
- const TRANSITION_STRIPS_RIGHT_UP = 'strips_ru';
- const TRANSITION_WEDGE = 'wedge';
- const TRANSITION_WIPE_DOWN = 'wipe_d';
- const TRANSITION_WIPE_LEFT = 'wipe_l';
- const TRANSITION_WIPE_RIGHT = 'wipe_r';
- const TRANSITION_WIPE_UP = 'wipe_u';
- const TRANSITION_ZOOM_IN = 'zoom_in';
- const TRANSITION_ZOOM_OUT = 'zoom_out';
+ public const TRANSITION_BLINDS_HORIZONTAL = 'blinds_horz';
+ public const TRANSITION_BLINDS_VERTICAL = 'blinds_vert';
+ public const TRANSITION_CHECKER_HORIZONTAL = 'checker_horz';
+ public const TRANSITION_CHECKER_VERTICAL = 'checker_vert';
+ public const TRANSITION_CIRCLE = 'circle';
+ public const TRANSITION_COMB_HORIZONTAL = 'comb_horz';
+ public const TRANSITION_COMB_VERTICAL = 'comb_vert';
+ public const TRANSITION_COVER_DOWN = 'cover_d';
+ public const TRANSITION_COVER_LEFT = 'cover_l';
+ public const TRANSITION_COVER_LEFT_DOWN = 'cover_ld';
+ public const TRANSITION_COVER_LEFT_UP = 'cover_lu';
+ public const TRANSITION_COVER_RIGHT = 'cover_r';
+ public const TRANSITION_COVER_RIGHT_DOWN = 'cover_rd';
+ public const TRANSITION_COVER_RIGHT_UP = 'cover_ru';
+ public const TRANSITION_COVER_UP = 'cover_u';
+ public const TRANSITION_CUT = 'cut';
+ public const TRANSITION_DIAMOND = 'diamond';
+ public const TRANSITION_DISSOLVE = 'dissolve';
+ public const TRANSITION_FADE = 'fade';
+ public const TRANSITION_NEWSFLASH = 'newsflash';
+ public const TRANSITION_PLUS = 'plus';
+ public const TRANSITION_PULL_DOWN = 'pull_d';
+ public const TRANSITION_PULL_LEFT = 'pull_l';
+ public const TRANSITION_PULL_RIGHT = 'pull_r';
+ public const TRANSITION_PULL_UP = 'pull_u';
+ public const TRANSITION_PUSH_DOWN = 'push_d';
+ public const TRANSITION_PUSH_LEFT = 'push_l';
+ public const TRANSITION_PUSH_RIGHT = 'push_r';
+ public const TRANSITION_PUSH_UP = 'push_u';
+ public const TRANSITION_RANDOM = 'random';
+ public const TRANSITION_RANDOMBAR_HORIZONTAL = 'randomBar_horz';
+ public const TRANSITION_RANDOMBAR_VERTICAL = 'randomBar_vert';
+ public const TRANSITION_SPLIT_IN_HORIZONTAL = 'split_in_horz';
+ public const TRANSITION_SPLIT_OUT_HORIZONTAL = 'split_out_horz';
+ public const TRANSITION_SPLIT_IN_VERTICAL = 'split_in_vert';
+ public const TRANSITION_SPLIT_OUT_VERTICAL = 'split_out_vert';
+ public const TRANSITION_STRIPS_LEFT_DOWN = 'strips_ld';
+ public const TRANSITION_STRIPS_LEFT_UP = 'strips_lu';
+ public const TRANSITION_STRIPS_RIGHT_DOWN = 'strips_rd';
+ public const TRANSITION_STRIPS_RIGHT_UP = 'strips_ru';
+ public const TRANSITION_WEDGE = 'wedge';
+ public const TRANSITION_WIPE_DOWN = 'wipe_d';
+ public const TRANSITION_WIPE_LEFT = 'wipe_l';
+ public const TRANSITION_WIPE_RIGHT = 'wipe_r';
+ public const TRANSITION_WIPE_UP = 'wipe_u';
+ public const TRANSITION_ZOOM_IN = 'zoom_in';
+ public const TRANSITION_ZOOM_OUT = 'zoom_out';
/**
* @var bool
@@ -86,25 +86,21 @@ class Transition
*/
protected $hasTimeTrigger = false;
/**
- * @var int
+ * @var int|null
*/
protected $advanceTimeTrigger = null;
/**
- * @var null|self::SPEED_SLOW|self::SPEED_MEDIUM|self::SPEED_FAST
+ * @var string|null
*/
protected $speed = null;
/**
- * @var null|self::TRANSITION_*
+ * @var string|null
*/
protected $transitionType = null;
- /**
- * @var array
- */
- protected $transitionOptions = array();
- public function setSpeed($speed = self::SPEED_MEDIUM)
+ public function setSpeed(?string $speed = self::SPEED_MEDIUM): self
{
- if (in_array($speed, array(self::SPEED_FAST, self::SPEED_MEDIUM, self::SPEED_SLOW))) {
+ if (in_array($speed, [self::SPEED_FAST, self::SPEED_MEDIUM, self::SPEED_SLOW])) {
$this->speed = $speed;
} else {
$this->speed = null;
@@ -113,53 +109,49 @@ class Transition
return $this;
}
- public function getSpeed()
+ public function getSpeed(): ?string
{
return $this->speed;
}
- public function setManualTrigger($value = false)
+ public function setManualTrigger(bool $value = false): self
{
- if (is_bool($value)) {
- $this->hasManualTrigger = $value;
- }
+ $this->hasManualTrigger = $value;
+
return $this;
}
- public function hasManualTrigger()
+ public function hasManualTrigger(): bool
{
return $this->hasManualTrigger;
}
- public function setTimeTrigger($value = false, $advanceTime = 1000)
+ public function setTimeTrigger(bool $value = false, int $advanceTime = 1000): self
{
- if (is_bool($value)) {
- $this->hasTimeTrigger = $value;
- }
- $this->advanceTimeTrigger = null;
- if ($this->hasTimeTrigger === true) {
- $this->advanceTimeTrigger = (int) $advanceTime;
- }
+ $this->hasTimeTrigger = $value;
+ $this->advanceTimeTrigger = true === $value ? $advanceTime : null;
+
return $this;
}
- public function hasTimeTrigger()
+ public function hasTimeTrigger(): bool
{
return $this->hasTimeTrigger;
}
- public function getAdvanceTimeTrigger()
+ public function getAdvanceTimeTrigger(): ?int
{
return $this->advanceTimeTrigger;
}
- public function setTransitionType($type = null)
+ public function setTransitionType(string $type = null): self
{
$this->transitionType = $type;
+
return $this;
}
- public function getTransitionType()
+ public function getTransitionType(): ?string
{
return $this->transitionType;
}
diff --git a/PhpOffice/PhpPresentation/Style/Alignment.php b/PhpOffice/PhpPresentation/Style/Alignment.php
old mode 100755
new mode 100644
index 89cde6a..fae963e
--- a/PhpOffice/PhpPresentation/Style/Alignment.php
+++ b/PhpOffice/PhpPresentation/Style/Alignment.php
@@ -10,136 +10,142 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
+use PhpOffice\PhpPresentation\Exception\OutOfBoundsException;
-/**
- * \PhpOffice\PhpPresentation\Style\Alignment
- */
class Alignment implements ComparableInterface
{
/* Horizontal alignment */
- const HORIZONTAL_GENERAL = 'l';
- const HORIZONTAL_LEFT = 'l';
- const HORIZONTAL_RIGHT = 'r';
- const HORIZONTAL_CENTER = 'ctr';
- const HORIZONTAL_JUSTIFY = 'just';
- const HORIZONTAL_DISTRIBUTED = 'dist';
+ public const HORIZONTAL_GENERAL = 'l';
+ public const HORIZONTAL_LEFT = 'l';
+ public const HORIZONTAL_RIGHT = 'r';
+ public const HORIZONTAL_CENTER = 'ctr';
+ public const HORIZONTAL_JUSTIFY = 'just';
+ public const HORIZONTAL_DISTRIBUTED = 'dist';
/* Vertical alignment */
- const VERTICAL_BASE = 'base';
- const VERTICAL_AUTO = 'auto';
- const VERTICAL_BOTTOM = 'b';
- const VERTICAL_TOP = 't';
- const VERTICAL_CENTER = 'ctr';
+ public const VERTICAL_BASE = 'base';
+ public const VERTICAL_AUTO = 'auto';
+ public const VERTICAL_BOTTOM = 'b';
+ public const VERTICAL_TOP = 't';
+ public const VERTICAL_CENTER = 'ctr';
/* Text direction */
- const TEXT_DIRECTION_HORIZONTAL = 'horz';
- const TEXT_DIRECTION_VERTICAL_90 = 'vert';
- const TEXT_DIRECTION_VERTICAL_270 = 'vert270';
- const TEXT_DIRECTION_STACKED = 'wordArtVert';
+ public const TEXT_DIRECTION_HORIZONTAL = 'horz';
+ public const TEXT_DIRECTION_VERTICAL_90 = 'vert';
+ public const TEXT_DIRECTION_VERTICAL_270 = 'vert270';
+ public const TEXT_DIRECTION_STACKED = 'wordArtVert';
- private $supportedStyles = array(
+ /**
+ * @var array
+ */
+ private $supportedStyles = [
self::HORIZONTAL_GENERAL,
self::HORIZONTAL_LEFT,
self::HORIZONTAL_RIGHT,
- );
+ ];
/**
- * Horizontal
+ * Horizontal.
+ *
* @var string
*/
- private $horizontal;
+ private $horizontal = self::HORIZONTAL_LEFT;
/**
- * Vertical
+ * Vertical.
+ *
* @var string
*/
- private $vertical;
+ private $vertical = self::VERTICAL_BASE;
/**
- * Text Direction
+ * Text Direction.
+ *
* @var string
*/
private $textDirection = self::TEXT_DIRECTION_HORIZONTAL;
/**
- * Level
+ * Level.
+ *
* @var int
*/
private $level = 0;
/**
- * Indent - only possible with horizontal alignment left and right
- * @var int
+ * Indent - only possible with horizontal alignment left and right.
+ *
+ * @var float
*/
private $indent = 0;
/**
- * Margin left - only possible with horizontal alignment left and right
- * @var int
+ * Margin left - only possible with horizontal alignment left and right.
+ *
+ * @var float
*/
private $marginLeft = 0;
/**
- * Margin right - only possible with horizontal alignment left and right
- * @var int
+ * Margin right - only possible with horizontal alignment left and right.
+ *
+ * @var float
*/
private $marginRight = 0;
/**
- * Margin top
- * @var int
+ * Margin top.
+ *
+ * @var float
*/
private $marginTop = 0;
/**
- * Margin bottom
- * @var int
+ * Margin bottom.
+ *
+ * @var float
*/
private $marginBottom = 0;
/**
- * Hash index
- * @var string
+ * RTL Direction Support
+ *
+ * @var bool
+ */
+ private $isRTL = false;
+
+ /**
+ * Hash index.
+ *
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Style\Alignment
+ * Get Horizontal.
*/
- public function __construct()
- {
- // Initialise values
- $this->horizontal = self::HORIZONTAL_LEFT;
- $this->vertical = self::VERTICAL_BASE;
- }
-
- /**
- * Get Horizontal
- *
- * @return string
- */
- public function getHorizontal()
+ public function getHorizontal(): string
{
return $this->horizontal;
}
/**
- * Set Horizontal
- *
- * @param string $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set Horizontal.
*/
- public function setHorizontal($pValue = self::HORIZONTAL_LEFT)
+ public function setHorizontal(string $pValue = self::HORIZONTAL_LEFT): self
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = self::HORIZONTAL_LEFT;
}
$this->horizontal = $pValue;
@@ -148,24 +154,19 @@ class Alignment implements ComparableInterface
}
/**
- * Get Vertical
- *
- * @return string
+ * Get Vertical.
*/
- public function getVertical()
+ public function getVertical(): string
{
return $this->vertical;
}
/**
- * Set Vertical
- *
- * @param string $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set Vertical.
*/
- public function setVertical($pValue = self::VERTICAL_BASE)
+ public function setVertical(string $pValue = self::VERTICAL_BASE): self
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = self::VERTICAL_BASE;
}
$this->vertical = $pValue;
@@ -174,26 +175,24 @@ class Alignment implements ComparableInterface
}
/**
- * Get Level
- *
- * @return int
+ * Get Level.
*/
- public function getLevel()
+ public function getLevel(): int
{
return $this->level;
}
/**
- * Set Level
+ * Set Level.
*
- * @param int $pValue Ranging 0 - 8
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * @param int $pValue Ranging 0 - 8
+ *
+ * @throws OutOfBoundsException
*/
- public function setLevel($pValue = 0)
+ public function setLevel(int $pValue = 0): self
{
if ($pValue < 0) {
- throw new \Exception("Invalid value should be more than 0.");
+ throw new OutOfBoundsException(0, null, $pValue);
}
$this->level = $pValue;
@@ -201,22 +200,17 @@ class Alignment implements ComparableInterface
}
/**
- * Get indent
- *
- * @return int
+ * Get indent.
*/
- public function getIndent()
+ public function getIndent(): float
{
return $this->indent;
}
/**
- * Set indent
- *
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set indent.
*/
- public function setIndent($pValue = 0)
+ public function setIndent(float $pValue = 0): self
{
if ($pValue > 0 && !in_array($this->getHorizontal(), $this->supportedStyles)) {
$pValue = 0; // indent not supported
@@ -228,22 +222,17 @@ class Alignment implements ComparableInterface
}
/**
- * Get margin left
- *
- * @return int
+ * Get margin left.
*/
- public function getMarginLeft()
+ public function getMarginLeft(): float
{
return $this->marginLeft;
}
/**
- * Set margin left
- *
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set margin left.
*/
- public function setMarginLeft($pValue = 0)
+ public function setMarginLeft(float $pValue = 0): self
{
if ($pValue > 0 && !in_array($this->getHorizontal(), $this->supportedStyles)) {
$pValue = 0; // margin left not supported
@@ -255,22 +244,17 @@ class Alignment implements ComparableInterface
}
/**
- * Get margin right
- *
- * @return int
+ * Get margin right.
*/
- public function getMarginRight()
+ public function getMarginRight(): float
{
return $this->marginRight;
}
/**
- * Set margin ight
- *
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set margin ight.
*/
- public function setMarginRight($pValue = 0)
+ public function setMarginRight(float $pValue = 0): self
{
if ($pValue > 0 && !in_array($this->getHorizontal(), $this->supportedStyles)) {
$pValue = 0; // margin right not supported
@@ -282,22 +266,17 @@ class Alignment implements ComparableInterface
}
/**
- * Get margin top
- *
- * @return int
+ * Get margin top.
*/
- public function getMarginTop()
+ public function getMarginTop(): float
{
return $this->marginTop;
}
/**
- * Set margin top
- *
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set margin top.
*/
- public function setMarginTop($pValue = 0)
+ public function setMarginTop(float $pValue = 0): self
{
$this->marginTop = $pValue;
@@ -305,55 +284,64 @@ class Alignment implements ComparableInterface
}
/**
- * Get margin bottom
- *
- * @return int
+ * Get margin bottom.
*/
- public function getMarginBottom()
+ public function getMarginBottom(): float
{
return $this->marginBottom;
}
/**
- * Set margin bottom
- *
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Alignment
+ * Set margin bottom.
*/
- public function setMarginBottom($pValue = 0)
+ public function setMarginBottom(float $pValue = 0): self
{
$this->marginBottom = $pValue;
return $this;
}
- /**
- * @return string
- */
- public function getTextDirection()
+ public function getTextDirection(): string
{
return $this->textDirection;
}
- /**
- * @param string $pValue
- * @return Alignment
- */
- public function setTextDirection($pValue = self::TEXT_DIRECTION_HORIZONTAL)
+ public function setTextDirection(string $pValue = self::TEXT_DIRECTION_HORIZONTAL): self
{
if (empty($pValue)) {
$pValue = self::TEXT_DIRECTION_HORIZONTAL;
}
$this->textDirection = $pValue;
+
return $this;
}
/**
- * Get hash code
+ * @return bool
+ */
+ public function isRTL(): bool
+ {
+ return $this->isRTL;
+ }
+
+ /**
+ * @param bool $value
+ *
+ * @return self
+ */
+ public function setIsRTL(bool $value = false): self
+ {
+ $this->isRTL = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(
$this->horizontal
@@ -362,33 +350,38 @@ class Alignment implements ComparableInterface
. $this->indent
. $this->marginLeft
. $this->marginRight
+ . ($this->isRTL ? '1' : '0')
. __CLASS__
);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Border.php b/PhpOffice/PhpPresentation/Style/Border.php
old mode 100755
new mode 100644
index f8dddf8..9842432
--- a/PhpOffice/PhpPresentation/Style/Border.php
+++ b/PhpOffice/PhpPresentation/Style/Border.php
@@ -10,130 +10,125 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
-/**
- * \PhpOffice\PhpPresentation\Style\Border
- */
class Border implements ComparableInterface
{
/* Line style */
- const LINE_NONE = 'none';
- const LINE_SINGLE = 'sng';
- const LINE_DOUBLE = 'dbl';
- const LINE_THICKTHIN = 'thickThin';
- const LINE_THINTHICK = 'thinThick';
- const LINE_TRI = 'tri';
+ public const LINE_NONE = 'none';
+ public const LINE_SINGLE = 'sng';
+ public const LINE_DOUBLE = 'dbl';
+ public const LINE_THICKTHIN = 'thickThin';
+ public const LINE_THINTHICK = 'thinThick';
+ public const LINE_TRI = 'tri';
/* Dash style */
- const DASH_DASH = 'dash';
- const DASH_DASHDOT = 'dashDot';
- const DASH_DOT = 'dot';
- const DASH_LARGEDASH = 'lgDash';
- const DASH_LARGEDASHDOT = 'lgDashDot';
- const DASH_LARGEDASHDOTDOT = 'lgDashDotDot';
- const DASH_SOLID = 'solid';
- const DASH_SYSDASH = 'sysDash';
- const DASH_SYSDASHDOT = 'sysDashDot';
- const DASH_SYSDASHDOTDOT = 'sysDashDotDot';
- const DASH_SYSDOT = 'sysDot';
+ public const DASH_DASH = 'dash';
+ public const DASH_DASHDOT = 'dashDot';
+ public const DASH_DOT = 'dot';
+ public const DASH_LARGEDASH = 'lgDash';
+ public const DASH_LARGEDASHDOT = 'lgDashDot';
+ public const DASH_LARGEDASHDOTDOT = 'lgDashDotDot';
+ public const DASH_SOLID = 'solid';
+ public const DASH_SYSDASH = 'sysDash';
+ public const DASH_SYSDASHDOT = 'sysDashDot';
+ public const DASH_SYSDASHDOTDOT = 'sysDashDotDot';
+ public const DASH_SYSDOT = 'sysDot';
/**
- * Line width
+ * Line width.
*
* @var int
*/
private $lineWidth = 1;
/**
- * Line style
+ * Line style.
*
* @var string
*/
- private $lineStyle;
+ private $lineStyle = self::LINE_SINGLE;
/**
- * Dash style
+ * Dash style.
*
* @var string
*/
- private $dashStyle;
+ private $dashStyle = self::DASH_SOLID;
/**
- * Border color
+ * Border color.
*
- * @var \PhpOffice\PhpPresentation\Style\Color
+ * @var Color
*/
private $color;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
- /**
- * Create a new \PhpOffice\PhpPresentation\Style\Border
- */
public function __construct()
{
- // Initialise values
- $this->lineWidth = 1;
- $this->lineStyle = self::LINE_SINGLE;
- $this->dashStyle = self::DASH_SOLID;
- $this->color = new Color(Color::COLOR_BLACK);
+ $this->color = new Color(Color::COLOR_BLACK);
}
/**
- * Get line width (in points)
+ * Get line width (in points).
*
* @return int
*/
- public function getLineWidth()
+ public function getLineWidth(): int
{
return $this->lineWidth;
}
/**
- * Set line width (in points)
+ * Set line width (in points).
*
- * @param int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Border
+ * @param int $pValue
+ *
+ * @return self
*/
- public function setLineWidth($pValue = 1)
+ public function setLineWidth($pValue = 1): self
{
- $this->lineWidth = $pValue;
+ $this->lineWidth = (int) $pValue;
return $this;
}
/**
- * Get line style
+ * Get line style.
*
* @return string
*/
- public function getLineStyle()
+ public function getLineStyle(): string
{
return $this->lineStyle;
}
/**
- * Set line style
+ * Set line style.
*
- * @param string $pValue
- * @return \PhpOffice\PhpPresentation\Style\Border
+ * @param string $pValue
+ *
+ * @return self
*/
- public function setLineStyle($pValue = self::LINE_SINGLE)
+ public function setLineStyle(string $pValue = self::LINE_SINGLE): self
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = self::LINE_SINGLE;
}
$this->lineStyle = $pValue;
@@ -142,24 +137,25 @@ class Border implements ComparableInterface
}
/**
- * Get dash style
+ * Get dash style.
*
* @return string
*/
- public function getDashStyle()
+ public function getDashStyle(): string
{
return $this->dashStyle;
}
/**
- * Set dash style
+ * Set dash style.
*
- * @param string $pValue
- * @return \PhpOffice\PhpPresentation\Style\Border
+ * @param string $pValue
+ *
+ * @return self
*/
- public function setDashStyle($pValue = self::DASH_SOLID)
+ public function setDashStyle(string $pValue = self::DASH_SOLID): self
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = self::DASH_SOLID;
}
$this->dashStyle = $pValue;
@@ -168,23 +164,23 @@ class Border implements ComparableInterface
}
/**
- * Get Border Color
+ * Get Border Color.
*
- * @return \PhpOffice\PhpPresentation\Style\Color
+ * @return Color
*/
- public function getColor()
+ public function getColor(): ?Color
{
return $this->color;
}
/**
- * Set Border Color
+ * Set Border Color.
*
- * @param \PhpOffice\PhpPresentation\Style\Color $color
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Style\Border
+ * @param Color|null $color
+ *
+ * @return self
*/
- public function setColor(Color $color = null)
+ public function setColor(Color $color = null): self
{
$this->color = $color;
@@ -192,11 +188,11 @@ class Border implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(
$this->lineStyle
@@ -208,28 +204,32 @@ class Border implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Borders.php b/PhpOffice/PhpPresentation/Style/Borders.php
old mode 100755
new mode 100644
index 3d01568..01fa011
--- a/PhpOffice/PhpPresentation/Style/Borders.php
+++ b/PhpOffice/PhpPresentation/Style/Borders.php
@@ -10,87 +10,90 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Style\Borders
+ * \PhpOffice\PhpPresentation\Style\Borders.
*/
class Borders implements ComparableInterface
{
/**
- * Left
+ * Left.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $left;
/**
- * Right
+ * Right.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $right;
/**
- * Top
+ * Top.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $top;
/**
- * Bottom
+ * Bottom.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $bottom;
/**
- * Diagonal up
+ * Diagonal up.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $diagonalUp;
/**
- * Diagonal down
+ * Diagonal down.
*
* @var \PhpOffice\PhpPresentation\Style\Border
*/
private $diagonalDown;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Style\Borders
+ * Create a new \PhpOffice\PhpPresentation\Style\Borders.
*/
public function __construct()
{
// Initialise values
- $this->left = new Border();
- $this->right = new Border();
- $this->top = new Border();
- $this->bottom = new Border();
- $this->diagonalUp = new Border();
+ $this->left = new Border();
+ $this->right = new Border();
+ $this->top = new Border();
+ $this->bottom = new Border();
+ $this->diagonalUp = new Border();
$this->diagonalUp->setLineStyle(Border::LINE_NONE);
- $this->diagonalDown = new Border();
+ $this->diagonalDown = new Border();
$this->diagonalDown->setLineStyle(Border::LINE_NONE);
}
/**
- * Get Left
+ * Get Left.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -100,7 +103,7 @@ class Borders implements ComparableInterface
}
/**
- * Get Right
+ * Get Right.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -110,7 +113,7 @@ class Borders implements ComparableInterface
}
/**
- * Get Top
+ * Get Top.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -120,7 +123,7 @@ class Borders implements ComparableInterface
}
/**
- * Get Bottom
+ * Get Bottom.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -130,7 +133,7 @@ class Borders implements ComparableInterface
}
/**
- * Get Diagonal Up
+ * Get Diagonal Up.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -140,7 +143,7 @@ class Borders implements ComparableInterface
}
/**
- * Get Diagonal Down
+ * Get Diagonal Down.
*
* @return \PhpOffice\PhpPresentation\Style\Border
*/
@@ -150,11 +153,11 @@ class Borders implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(
$this->getLeft()->getHashCode()
@@ -168,28 +171,32 @@ class Borders implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Bullet.php b/PhpOffice/PhpPresentation/Style/Bullet.php
old mode 100755
new mode 100644
index de078a1..dd3729d
--- a/PhpOffice/PhpPresentation/Style/Bullet.php
+++ b/PhpOffice/PhpPresentation/Style/Bullet.php
@@ -10,134 +10,133 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Style\Bullet
+ * \PhpOffice\PhpPresentation\Style\Bullet.
*/
class Bullet implements ComparableInterface
{
/* Bullet types */
- const TYPE_NONE = 'none';
- const TYPE_BULLET = 'bullet';
- const TYPE_NUMERIC = 'numeric';
+ public const TYPE_NONE = 'none';
+ public const TYPE_BULLET = 'bullet';
+ public const TYPE_NUMERIC = 'numeric';
/* Numeric bullet styles */
- const NUMERIC_DEFAULT = 'arabicPeriod';
- const NUMERIC_ALPHALCPARENBOTH = 'alphaLcParenBoth';
- const NUMERIC_ALPHAUCPARENBOTH = 'alphaUcParenBoth';
- const NUMERIC_ALPHALCPARENR = 'alphaLcParenR';
- const NUMERIC_ALPHAUCPARENR = 'alphaUcParenR';
- const NUMERIC_ALPHALCPERIOD = 'alphaLcPeriod';
- const NUMERIC_ALPHAUCPERIOD = 'alphaUcPeriod';
- const NUMERIC_ARABICPARENBOTH = 'arabicParenBoth';
- const NUMERIC_ARABICPARENR = 'arabicParenR';
- const NUMERIC_ARABICPERIOD = 'arabicPeriod';
- const NUMERIC_ARABICPLAIN = 'arabicPlain';
- const NUMERIC_ROMANLCPARENBOTH = 'romanLcParenBoth';
- const NUMERIC_ROMANUCPARENBOTH = 'romanUcParenBoth';
- const NUMERIC_ROMANLCPARENR = 'romanLcParenR';
- const NUMERIC_ROMANUCPARENR = 'romanUcParenR';
- const NUMERIC_ROMANLCPERIOD = 'romanLcPeriod';
- const NUMERIC_ROMANUCPERIOD = 'romanUcPeriod';
- const NUMERIC_CIRCLENUMDBPLAIN = 'circleNumDbPlain';
- const NUMERIC_CIRCLENUMWDBLACKPLAIN = 'circleNumWdBlackPlain';
- const NUMERIC_CIRCLENUMWDWHITEPLAIN = 'circleNumWdWhitePlain';
- const NUMERIC_ARABICDBPERIOD = 'arabicDbPeriod';
- const NUMERIC_ARABICDBPLAIN = 'arabicDbPlain';
- const NUMERIC_EA1CHSPERIOD = 'ea1ChsPeriod';
- const NUMERIC_EA1CHSPLAIN = 'ea1ChsPlain';
- const NUMERIC_EA1CHTPERIOD = 'ea1ChtPeriod';
- const NUMERIC_EA1CHTPLAIN = 'ea1ChtPlain';
- const NUMERIC_EA1JPNCHSDBPERIOD = 'ea1JpnChsDbPeriod';
- const NUMERIC_EA1JPNKORPLAIN = 'ea1JpnKorPlain';
- const NUMERIC_EA1JPNKORPERIOD = 'ea1JpnKorPeriod';
- const NUMERIC_ARABIC1MINUS = 'arabic1Minus';
- const NUMERIC_ARABIC2MINUS = 'arabic2Minus';
- const NUMERIC_HEBREW2MINUS = 'hebrew2Minus';
- const NUMERIC_THAIALPHAPERIOD = 'thaiAlphaPeriod';
- const NUMERIC_THAIALPHAPARENR = 'thaiAlphaParenR';
- const NUMERIC_THAIALPHAPARENBOTH = 'thaiAlphaParenBoth';
- const NUMERIC_THAINUMPERIOD = 'thaiNumPeriod';
- const NUMERIC_THAINUMPARENR = 'thaiNumParenR';
- const NUMERIC_THAINUMPARENBOTH = 'thaiNumParenBoth';
- const NUMERIC_HINDIALPHAPERIOD = 'hindiAlphaPeriod';
- const NUMERIC_HINDINUMPERIOD = 'hindiNumPeriod';
- const NUMERIC_HINDINUMPARENR = 'hindiNumParenR';
- const NUMERIC_HINDIALPHA1PERIOD = 'hindiAlpha1Period';
+ public const NUMERIC_DEFAULT = 'arabicPeriod';
+ public const NUMERIC_ALPHALCPARENBOTH = 'alphaLcParenBoth';
+ public const NUMERIC_ALPHAUCPARENBOTH = 'alphaUcParenBoth';
+ public const NUMERIC_ALPHALCPARENR = 'alphaLcParenR';
+ public const NUMERIC_ALPHAUCPARENR = 'alphaUcParenR';
+ public const NUMERIC_ALPHALCPERIOD = 'alphaLcPeriod';
+ public const NUMERIC_ALPHAUCPERIOD = 'alphaUcPeriod';
+ public const NUMERIC_ARABICPARENBOTH = 'arabicParenBoth';
+ public const NUMERIC_ARABICPARENR = 'arabicParenR';
+ public const NUMERIC_ARABICPERIOD = 'arabicPeriod';
+ public const NUMERIC_ARABICPLAIN = 'arabicPlain';
+ public const NUMERIC_ROMANLCPARENBOTH = 'romanLcParenBoth';
+ public const NUMERIC_ROMANUCPARENBOTH = 'romanUcParenBoth';
+ public const NUMERIC_ROMANLCPARENR = 'romanLcParenR';
+ public const NUMERIC_ROMANUCPARENR = 'romanUcParenR';
+ public const NUMERIC_ROMANLCPERIOD = 'romanLcPeriod';
+ public const NUMERIC_ROMANUCPERIOD = 'romanUcPeriod';
+ public const NUMERIC_CIRCLENUMDBPLAIN = 'circleNumDbPlain';
+ public const NUMERIC_CIRCLENUMWDBLACKPLAIN = 'circleNumWdBlackPlain';
+ public const NUMERIC_CIRCLENUMWDWHITEPLAIN = 'circleNumWdWhitePlain';
+ public const NUMERIC_ARABICDBPERIOD = 'arabicDbPeriod';
+ public const NUMERIC_ARABICDBPLAIN = 'arabicDbPlain';
+ public const NUMERIC_EA1CHSPERIOD = 'ea1ChsPeriod';
+ public const NUMERIC_EA1CHSPLAIN = 'ea1ChsPlain';
+ public const NUMERIC_EA1CHTPERIOD = 'ea1ChtPeriod';
+ public const NUMERIC_EA1CHTPLAIN = 'ea1ChtPlain';
+ public const NUMERIC_EA1JPNCHSDBPERIOD = 'ea1JpnChsDbPeriod';
+ public const NUMERIC_EA1JPNKORPLAIN = 'ea1JpnKorPlain';
+ public const NUMERIC_EA1JPNKORPERIOD = 'ea1JpnKorPeriod';
+ public const NUMERIC_ARABIC1MINUS = 'arabic1Minus';
+ public const NUMERIC_ARABIC2MINUS = 'arabic2Minus';
+ public const NUMERIC_HEBREW2MINUS = 'hebrew2Minus';
+ public const NUMERIC_THAIALPHAPERIOD = 'thaiAlphaPeriod';
+ public const NUMERIC_THAIALPHAPARENR = 'thaiAlphaParenR';
+ public const NUMERIC_THAIALPHAPARENBOTH = 'thaiAlphaParenBoth';
+ public const NUMERIC_THAINUMPERIOD = 'thaiNumPeriod';
+ public const NUMERIC_THAINUMPARENR = 'thaiNumParenR';
+ public const NUMERIC_THAINUMPARENBOTH = 'thaiNumParenBoth';
+ public const NUMERIC_HINDIALPHAPERIOD = 'hindiAlphaPeriod';
+ public const NUMERIC_HINDINUMPERIOD = 'hindiNumPeriod';
+ public const NUMERIC_HINDINUMPARENR = 'hindiNumParenR';
+ public const NUMERIC_HINDIALPHA1PERIOD = 'hindiAlpha1Period';
/**
- * Bullet type
+ * Bullet type.
*
* @var string
*/
private $bulletType = self::TYPE_NONE;
/**
- * Bullet font
+ * Bullet font.
*
* @var string
*/
private $bulletFont;
/**
- * Bullet char
+ * Bullet char.
*
* @var string
*/
private $bulletChar = '-';
/**
- * Bullet char
+ * Bullet char.
*
* @var Color
*/
private $bulletColor;
/**
- * Bullet numeric style
+ * Bullet numeric style.
*
* @var string
*/
private $bulletNumericStyle = self::NUMERIC_DEFAULT;
/**
- * Bullet numeric start at
+ * Bullet numeric start at.
+ *
+ * @var int|string
+ */
+ private $bulletNumericStartAt;
+
+ /**
+ * Hash index.
*
* @var int
*/
- private $bulletNumericStartAt = 1;
-
- /**
- * Hash index
- *
- * @var string
- */
private $hashIndex;
- /**
- * Create a new \PhpOffice\PhpPresentation\Style\Bullet
- */
public function __construct()
{
- // Initialise values
- $this->bulletType = self::TYPE_NONE;
- $this->bulletFont = 'Calibri';
- $this->bulletChar = '-';
- $this->bulletColor = new Color();
- $this->bulletNumericStyle = self::NUMERIC_DEFAULT;
- $this->bulletNumericStartAt = 1;
+ $this->bulletType = self::TYPE_NONE;
+ $this->bulletFont = 'Calibri';
+ $this->bulletChar = '-';
+ $this->bulletColor = new Color();
+ $this->bulletNumericStyle = self::NUMERIC_DEFAULT;
+ $this->bulletNumericStartAt = 1;
}
/**
- * Get bullet type
+ * Get bullet type.
*
* @return string
*/
@@ -147,9 +146,10 @@ class Bullet implements ComparableInterface
}
/**
- * Set bullet type
+ * Set bullet type.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\Style\Bullet
*/
public function setBulletType($pValue = self::TYPE_NONE)
@@ -160,7 +160,7 @@ class Bullet implements ComparableInterface
}
/**
- * Get bullet font
+ * Get bullet font.
*
* @return string
*/
@@ -170,14 +170,15 @@ class Bullet implements ComparableInterface
}
/**
- * Set bullet font
+ * Set bullet font.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\Style\Bullet
*/
public function setBulletFont($pValue = 'Calibri')
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = 'Calibri';
}
$this->bulletFont = $pValue;
@@ -186,7 +187,7 @@ class Bullet implements ComparableInterface
}
/**
- * Get bullet char
+ * Get bullet char.
*
* @return string
*/
@@ -196,9 +197,10 @@ class Bullet implements ComparableInterface
}
/**
- * Set bullet char
+ * Set bullet char.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\Style\Bullet
*/
public function setBulletChar($pValue = '-')
@@ -209,7 +211,7 @@ class Bullet implements ComparableInterface
}
/**
- * Get bullet numeric style
+ * Get bullet numeric style.
*
* @return string
*/
@@ -219,9 +221,10 @@ class Bullet implements ComparableInterface
}
/**
- * Set bullet numeric style
+ * Set bullet numeric style.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\Style\Bullet
*/
public function setBulletNumericStyle($pValue = self::NUMERIC_DEFAULT)
@@ -232,9 +235,9 @@ class Bullet implements ComparableInterface
}
/**
- * Get bullet numeric start at
+ * Get bullet numeric start at.
*
- * @return string
+ * @return int|string
*/
public function getBulletNumericStartAt()
{
@@ -242,9 +245,10 @@ class Bullet implements ComparableInterface
}
/**
- * Set bullet numeric start at
+ * Set bullet numeric start at.
*
* @param int|string $pValue
+ *
* @return \PhpOffice\PhpPresentation\Style\Bullet
*/
public function setBulletNumericStartAt($pValue = 1)
@@ -255,11 +259,11 @@ class Bullet implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(
$this->bulletType
@@ -272,29 +276,33 @@ class Bullet implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
/**
@@ -306,12 +314,12 @@ class Bullet implements ComparableInterface
}
/**
- * @param Color $bulletColor
* @return Bullet
*/
public function setBulletColor(Color $bulletColor)
{
$this->bulletColor = $bulletColor;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Color.php b/PhpOffice/PhpPresentation/Style/Color.php
old mode 100755
new mode 100644
index 76fddd8..56e5102
--- a/PhpOffice/PhpPresentation/Style/Color.php
+++ b/PhpOffice/PhpPresentation/Style/Color.php
@@ -10,59 +10,62 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Style\Color
+ * \PhpOffice\PhpPresentation\Style\Color.
*/
class Color implements ComparableInterface
{
/* Colors */
- const COLOR_BLACK = 'FF000000';
- const COLOR_WHITE = 'FFFFFFFF';
- const COLOR_RED = 'FFFF0000';
- const COLOR_DARKRED = 'FF800000';
- const COLOR_BLUE = 'FF0000FF';
- const COLOR_DARKBLUE = 'FF000080';
- const COLOR_GREEN = 'FF00FF00';
- const COLOR_DARKGREEN = 'FF008000';
- const COLOR_YELLOW = 'FFFFFF00';
- const COLOR_DARKYELLOW = 'FF808000';
+ public const COLOR_BLACK = 'FF000000';
+ public const COLOR_WHITE = 'FFFFFFFF';
+ public const COLOR_RED = 'FFFF0000';
+ public const COLOR_DARKRED = 'FF800000';
+ public const COLOR_BLUE = 'FF0000FF';
+ public const COLOR_DARKBLUE = 'FF000080';
+ public const COLOR_GREEN = 'FF00FF00';
+ public const COLOR_DARKGREEN = 'FF008000';
+ public const COLOR_YELLOW = 'FFFFFF00';
+ public const COLOR_DARKYELLOW = 'FF808000';
/**
- * ARGB - Alpha RGB
+ * ARGB - Alpha RGB.
*
* @var string
*/
private $argb;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Style\Color
+ * Create a new \PhpOffice\PhpPresentation\Style\Color.
*
* @param string $pARGB
*/
public function __construct($pARGB = self::COLOR_BLACK)
{
// Initialise values
- $this->argb = $pARGB;
+ $this->argb = $pARGB;
}
/**
- * Get ARGB
+ * Get ARGB.
*
* @return string
*/
@@ -72,14 +75,15 @@ class Color implements ComparableInterface
}
/**
- * Set ARGB
+ * Set ARGB.
+ *
+ * @param string $pValue
*
- * @param string $pValue
* @return \PhpOffice\PhpPresentation\Style\Color
*/
public function setARGB($pValue = self::COLOR_BLACK)
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = self::COLOR_BLACK;
}
$this->argb = $pValue;
@@ -89,25 +93,29 @@ class Color implements ComparableInterface
/**
* Get the alpha % of the ARGB
- * Will return 100 if no ARGB
- * @return integer
+ * Will return 100 if no ARGB.
+ *
+ * @return int
*/
- public function getAlpha()
+ public function getAlpha(): int
{
$alpha = 100;
if (strlen($this->argb) >= 6) {
$dec = hexdec(substr($this->argb, 0, 2));
- $alpha = number_format(($dec/255) * 100, 2);
+ $alpha = (int) number_format(($dec / 255) * 100, 0);
}
+
return $alpha;
}
/**
- * Set the alpha % of the ARGB
+ * Set the alpha % of the ARGB.
+ *
* @param int $alpha
+ *
* @return $this
*/
- public function setAlpha($alpha = 100)
+ public function setAlpha(int $alpha = 100): self
{
if ($alpha < 0) {
$alpha = 0;
@@ -116,20 +124,21 @@ class Color implements ComparableInterface
$alpha = 100;
}
$alpha = round(($alpha / 100) * 255);
- $alpha = dechex($alpha);
+ $alpha = dechex((int) $alpha);
$alpha = str_pad($alpha, 2, '0', STR_PAD_LEFT);
$this->argb = $alpha . substr($this->argb, 2);
+
return $this;
}
/**
- * Get RGB
+ * Get RGB.
*
* @return string
*/
public function getRGB()
{
- if (strlen($this->argb) == 6) {
+ if (6 == strlen($this->argb)) {
return $this->argb;
} else {
return substr($this->argb, 2);
@@ -137,18 +146,19 @@ class Color implements ComparableInterface
}
/**
- * Set RGB
+ * Set RGB.
+ *
+ * @param string $pValue
+ * @param string $pAlpha
*
- * @param string $pValue
- * @param string $pAlpha
* @return \PhpOffice\PhpPresentation\Style\Color
*/
public function setRGB($pValue = '000000', $pAlpha = 'FF')
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = '000000';
}
- if ($pAlpha == '') {
+ if ('' == $pAlpha) {
$pAlpha = 'FF';
}
$this->argb = $pAlpha . $pValue;
@@ -157,11 +167,11 @@ class Color implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(
$this->argb
@@ -170,28 +180,32 @@ class Color implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/ColorMap.php b/PhpOffice/PhpPresentation/Style/ColorMap.php
old mode 100755
new mode 100644
index 77f66b9..f95a3bc
--- a/PhpOffice/PhpPresentation/Style/ColorMap.php
+++ b/PhpOffice/PhpPresentation/Style/ColorMap.php
@@ -10,39 +10,45 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
/**
- * PhpOffice\PhpPresentation\Style\ColorMap
+ * PhpOffice\PhpPresentation\Style\ColorMap.
*/
class ColorMap
{
- const COLOR_BG1 = 'bg1';
- const COLOR_BG2 = 'bg2';
- const COLOR_TX1 = 'tx1';
- const COLOR_TX2 = 'tx2';
- const COLOR_ACCENT1 = 'accent1';
- const COLOR_ACCENT2 = 'accent2';
- const COLOR_ACCENT3 = 'accent3';
- const COLOR_ACCENT4 = 'accent4';
- const COLOR_ACCENT5 = 'accent5';
- const COLOR_ACCENT6 = 'accent6';
- const COLOR_HLINK = 'hlink';
- const COLOR_FOLHLINK = 'folHlink';
+ public const COLOR_BG1 = 'bg1';
+ public const COLOR_BG2 = 'bg2';
+ public const COLOR_TX1 = 'tx1';
+ public const COLOR_TX2 = 'tx2';
+ public const COLOR_ACCENT1 = 'accent1';
+ public const COLOR_ACCENT2 = 'accent2';
+ public const COLOR_ACCENT3 = 'accent3';
+ public const COLOR_ACCENT4 = 'accent4';
+ public const COLOR_ACCENT5 = 'accent5';
+ public const COLOR_ACCENT6 = 'accent6';
+ public const COLOR_HLINK = 'hlink';
+ public const COLOR_FOLHLINK = 'folHlink';
/**
- * Mapping - Stores the mapping betweenSlide and theme
+ * Mapping - Stores the mapping betweenSlide and theme.
*
- * @var array
+ * @var array
*/
- protected $mapping = array();
+ protected $mapping = [];
- public static $mappingDefault = array(
+ /**
+ * @var array
+ */
+ public static $mappingDefault = [
self::COLOR_BG1 => 'lt1',
self::COLOR_TX1 => 'dk1',
self::COLOR_BG2 => 'lt2',
@@ -54,12 +60,12 @@ class ColorMap
self::COLOR_ACCENT5 => 'accent5',
self::COLOR_ACCENT6 => 'accent6',
self::COLOR_HLINK => 'hlink',
- self::COLOR_FOLHLINK => 'folHlink'
- );
+ self::COLOR_FOLHLINK => 'folHlink',
+ ];
/**
* ColorMap constructor.
- * Create a new ColorMap with standard values
+ * Create a new ColorMap with standard values.
*/
public function __construct()
{
@@ -67,36 +73,33 @@ class ColorMap
}
/**
- * Change the color of one of the elements in the map
- *
- * @param string $item
- * @param string $newThemeColor
- * @return ColorMap
+ * Change the color of one of the elements in the map.
*/
- public function changeColor($item, $newThemeColor)
+ public function changeColor(string $item, string $newThemeColor): self
{
$this->mapping[$item] = $newThemeColor;
+
return $this;
}
/**
- * Store a new map. For use with the reader
+ * Store a new map. For use with the reader.
*
- * @param array $arrayMapping
- * @return ColorMap
+ * @param array $arrayMapping
*/
- public function setMapping(array $arrayMapping = array())
+ public function setMapping(array $arrayMapping = []): self
{
$this->mapping = $arrayMapping;
+
return $this;
}
/**
- * Get the whole mapping as an array
+ * Get the whole mapping as an array.
*
- * @return array
+ * @return array
*/
- public function getMapping()
+ public function getMapping(): array
{
return $this->mapping;
}
diff --git a/PhpOffice/PhpPresentation/Style/Fill.php b/PhpOffice/PhpPresentation/Style/Fill.php
old mode 100755
new mode 100644
index b252762..19b92db
--- a/PhpOffice/PhpPresentation/Style/Fill.php
+++ b/PhpOffice/PhpPresentation/Style/Fill.php
@@ -10,107 +10,105 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
-/**
- * \PhpOffice\PhpPresentation\Style\Fill
- */
class Fill implements ComparableInterface
{
/* Fill types */
- const FILL_NONE = 'none';
- const FILL_SOLID = 'solid';
- const FILL_GRADIENT_LINEAR = 'linear';
- const FILL_GRADIENT_PATH = 'path';
- const FILL_PATTERN_DARKDOWN = 'darkDown';
- const FILL_PATTERN_DARKGRAY = 'darkGray';
- const FILL_PATTERN_DARKGRID = 'darkGrid';
- const FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal';
- const FILL_PATTERN_DARKTRELLIS = 'darkTrellis';
- const FILL_PATTERN_DARKUP = 'darkUp';
- const FILL_PATTERN_DARKVERTICAL = 'darkVertical';
- const FILL_PATTERN_GRAY0625 = 'gray0625';
- const FILL_PATTERN_GRAY125 = 'gray125';
- const FILL_PATTERN_LIGHTDOWN = 'lightDown';
- const FILL_PATTERN_LIGHTGRAY = 'lightGray';
- const FILL_PATTERN_LIGHTGRID = 'lightGrid';
- const FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal';
- const FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis';
- const FILL_PATTERN_LIGHTUP = 'lightUp';
- const FILL_PATTERN_LIGHTVERTICAL = 'lightVertical';
- const FILL_PATTERN_MEDIUMGRAY = 'mediumGray';
+ public const FILL_NONE = 'none';
+ public const FILL_SOLID = 'solid';
+ public const FILL_GRADIENT_LINEAR = 'linear';
+ public const FILL_GRADIENT_PATH = 'path';
+ public const FILL_PATTERN_DARKDOWN = 'darkDown';
+ public const FILL_PATTERN_DARKGRAY = 'darkGray';
+ public const FILL_PATTERN_DARKGRID = 'darkGrid';
+ public const FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal';
+ public const FILL_PATTERN_DARKTRELLIS = 'darkTrellis';
+ public const FILL_PATTERN_DARKUP = 'darkUp';
+ public const FILL_PATTERN_DARKVERTICAL = 'darkVertical';
+ public const FILL_PATTERN_GRAY0625 = 'gray0625';
+ public const FILL_PATTERN_GRAY125 = 'gray125';
+ public const FILL_PATTERN_LIGHTDOWN = 'lightDown';
+ public const FILL_PATTERN_LIGHTGRAY = 'lightGray';
+ public const FILL_PATTERN_LIGHTGRID = 'lightGrid';
+ public const FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal';
+ public const FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis';
+ public const FILL_PATTERN_LIGHTUP = 'lightUp';
+ public const FILL_PATTERN_LIGHTVERTICAL = 'lightVertical';
+ public const FILL_PATTERN_MEDIUMGRAY = 'mediumGray';
/**
- * Fill type
+ * Fill type.
*
* @var string
*/
- private $fillType;
+ private $fillType = self::FILL_NONE;
/**
- * Rotation
+ * Rotation.
*
- * @var double
+ * @var float
*/
- private $rotation;
+ private $rotation = 0.0;
/**
- * Start color
+ * Start color.
*
- * @var \PhpOffice\PhpPresentation\Style\Color
+ * @var Color
*/
private $startColor;
/**
- * End color
+ * End color.
*
- * @var \PhpOffice\PhpPresentation\Style\Color
+ * @var Color
*/
private $endColor;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Style\Fill
+ * Create a new \PhpOffice\PhpPresentation\Style\Fill.
*/
public function __construct()
{
- // Initialise values
- $this->fillType = self::FILL_NONE;
- $this->rotation = (double)0;
- $this->startColor = new Color(Color::COLOR_WHITE);
- $this->endColor = new Color(Color::COLOR_BLACK);
+ $this->startColor = new Color(Color::COLOR_BLACK);
+ $this->endColor = new Color(Color::COLOR_WHITE);
}
/**
- * Get Fill Type
+ * Get Fill Type.
*
* @return string
*/
- public function getFillType()
+ public function getFillType(): string
{
return $this->fillType;
}
/**
- * Set Fill Type
+ * Set Fill Type.
*
- * @param string $pValue \PhpOffice\PhpPresentation\Style\Fill fill type
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @param string $pValue Fill type
+ *
+ * @return self
*/
- public function setFillType($pValue = self::FILL_NONE)
+ public function setFillType(string $pValue = self::FILL_NONE): self
{
$this->fillType = $pValue;
@@ -118,34 +116,35 @@ class Fill implements ComparableInterface
}
/**
- * Get Rotation
+ * Get Rotation.
*
- * @return double
+ * @return float
*/
- public function getRotation()
+ public function getRotation(): float
{
return $this->rotation;
}
/**
- * Set Rotation
+ * Set Rotation.
*
- * @param float|int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @param float $pValue
+ *
+ * @return self
*/
- public function setRotation($pValue = 0)
+ public function setRotation(float $pValue = 0): self
{
- $this->rotation = (double)$pValue;
+ $this->rotation = $pValue;
return $this;
}
/**
- * Get Start Color
+ * Get Start Color.
*
- * @return \PhpOffice\PhpPresentation\Style\Color
+ * @return Color
*/
- public function getStartColor()
+ public function getStartColor(): Color
{
// It's a get but it may lead to a modified color which we won't detect but in which case we must bind.
// So bind as an assurance.
@@ -153,13 +152,13 @@ class Fill implements ComparableInterface
}
/**
- * Set Start Color
+ * Set Start Color.
*
- * @param \PhpOffice\PhpPresentation\Style\Color $pValue
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @param Color $pValue
+ *
+ * @return self
*/
- public function setStartColor(Color $pValue = null)
+ public function setStartColor(Color $pValue): self
{
$this->startColor = $pValue;
@@ -167,11 +166,11 @@ class Fill implements ComparableInterface
}
/**
- * Get End Color
+ * Get End Color.
*
- * @return \PhpOffice\PhpPresentation\Style\Color
+ * @return Color
*/
- public function getEndColor()
+ public function getEndColor(): Color
{
// It's a get but it may lead to a modified color which we won't detect but in which case we must bind.
// So bind as an assurance.
@@ -179,13 +178,13 @@ class Fill implements ComparableInterface
}
/**
- * Set End Color
+ * Set End Color.
*
- * @param \PhpOffice\PhpPresentation\Style\Color $pValue
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Style\Fill
+ * @param Color $pValue
+ *
+ * @return self
*/
- public function setEndColor(Color $pValue = null)
+ public function setEndColor(Color $pValue): self
{
$this->endColor = $pValue;
@@ -193,11 +192,11 @@ class Fill implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(
$this->getFillType()
@@ -209,28 +208,32 @@ class Fill implements ComparableInterface
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Font.php b/PhpOffice/PhpPresentation/Style/Font.php
old mode 100755
new mode 100644
index e4b49d0..2e25b25
--- a/PhpOffice/PhpPresentation/Style/Font.php
+++ b/PhpOffice/PhpPresentation/Style/Font.php
@@ -10,133 +10,134 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Style\Font
+ * \PhpOffice\PhpPresentation\Style\Font.
*/
class Font implements ComparableInterface
{
/* Underline types */
- const UNDERLINE_NONE = 'none';
- const UNDERLINE_DASH = 'dash';
- const UNDERLINE_DASHHEAVY = 'dashHeavy';
- const UNDERLINE_DASHLONG = 'dashLong';
- const UNDERLINE_DASHLONGHEAVY = 'dashLongHeavy';
- const UNDERLINE_DOUBLE = 'dbl';
- const UNDERLINE_DOTHASH = 'dotDash';
- const UNDERLINE_DOTHASHHEAVY = 'dotDashHeavy';
- const UNDERLINE_DOTDOTDASH = 'dotDotDash';
- const UNDERLINE_DOTDOTDASHHEAVY = 'dotDotDashHeavy';
- const UNDERLINE_DOTTED = 'dotted';
- const UNDERLINE_DOTTEDHEAVY = 'dottedHeavy';
- const UNDERLINE_HEAVY = 'heavy';
- const UNDERLINE_SINGLE = 'sng';
- const UNDERLINE_WAVY = 'wavy';
- const UNDERLINE_WAVYDOUBLE = 'wavyDbl';
- const UNDERLINE_WAVYHEAVY = 'wavyHeavy';
- const UNDERLINE_WORDS = 'words';
+ public const UNDERLINE_NONE = 'none';
+ public const UNDERLINE_DASH = 'dash';
+ public const UNDERLINE_DASHHEAVY = 'dashHeavy';
+ public const UNDERLINE_DASHLONG = 'dashLong';
+ public const UNDERLINE_DASHLONGHEAVY = 'dashLongHeavy';
+ public const UNDERLINE_DOUBLE = 'dbl';
+ public const UNDERLINE_DOTHASH = 'dotDash';
+ public const UNDERLINE_DOTHASHHEAVY = 'dotDashHeavy';
+ public const UNDERLINE_DOTDOTDASH = 'dotDotDash';
+ public const UNDERLINE_DOTDOTDASHHEAVY = 'dotDotDashHeavy';
+ public const UNDERLINE_DOTTED = 'dotted';
+ public const UNDERLINE_DOTTEDHEAVY = 'dottedHeavy';
+ public const UNDERLINE_HEAVY = 'heavy';
+ public const UNDERLINE_SINGLE = 'sng';
+ public const UNDERLINE_WAVY = 'wavy';
+ public const UNDERLINE_WAVYDOUBLE = 'wavyDbl';
+ public const UNDERLINE_WAVYHEAVY = 'wavyHeavy';
+ public const UNDERLINE_WORDS = 'words';
+
+ public const FORMAT_LATIN = 'latin';
+ public const FORMAT_EAST_ASIAN = 'ea';
+ public const FORMAT_COMPLEX_SCRIPT = 'cs';
/**
- * Name
+ * Name.
*
* @var string
*/
- private $name;
-
- /**
- * Font Size
- *
- * @var float|int
- */
- private $size;
-
- /**
- * Bold
- *
- * @var boolean
- */
- private $bold;
+ private $name = 'Calibri';
/**
- * Italic
+ * Font Size.
*
- * @var boolean
+ * @var int
*/
- private $italic;
+ private $size = 10;
/**
- * Superscript
+ * Bold.
*
- * @var boolean
+ * @var bool
*/
- private $superScript;
+ private $bold = false;
/**
- * Subscript
+ * Italic.
*
- * @var boolean
+ * @var bool
*/
- private $subScript;
+ private $italic = false;
/**
- * Underline
+ * Superscript.
+ *
+ * @var bool
+ */
+ private $superScript = false;
+
+ /**
+ * Subscript.
+ *
+ * @var bool
+ */
+ private $subScript = false;
+
+ /**
+ * Underline.
*
* @var string
*/
- private $underline;
+ private $underline = self::UNDERLINE_NONE;
/**
- * Strikethrough
+ * Strikethrough.
*
- * @var boolean
+ * @var bool
*/
- private $strikethrough;
+ private $strikethrough = false;
/**
- * Foreground color
+ * Foreground color.
*
- * @var \PhpOffice\PhpPresentation\Style\Color
+ * @var Color
*/
private $color;
/**
- * Character Spacing
+ * Character Spacing.
*
- * @var int
+ * @var float
*/
- private $characterSpacing;
+ private $characterSpacing = 0;
/**
- * Hash index
+ * Format
*
* @var string
*/
- private $hashIndex;
+ private $format = self::FORMAT_LATIN;
/**
- * Create a new \PhpOffice\PhpPresentation\Style\Font
+ * Hash index.
+ *
+ * @var int
*/
+ private $hashIndex;
+
public function __construct()
{
- // Initialise values
- $this->name = 'Calibri';
- $this->size = 10;
- $this->characterSpacing = 0;
- $this->bold = false;
- $this->italic = false;
- $this->superScript = false;
- $this->subScript = false;
- $this->underline = self::UNDERLINE_NONE;
- $this->strikethrough = false;
- $this->color = new Color(Color::COLOR_BLACK);
+ $this->color = new Color(Color::COLOR_BLACK);
}
/**
@@ -144,7 +145,7 @@ class Font implements ComparableInterface
*
* @return string
*/
- public function getName()
+ public function getName(): string
{
return $this->name;
}
@@ -152,181 +153,139 @@ class Font implements ComparableInterface
/**
* Set Name
*
- * @param string $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * @param string $pValue
+ *
+ * @return self
*/
- public function setName($pValue = 'Calibri')
+ public function setName(string $pValue = 'Calibri'): self
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = 'Calibri';
}
$this->name = $pValue;
return $this;
}
-
+
/**
- * Get Character Spacing
+ * Get Character Spacing.
*
- * @return double
+ * @return float
*/
- public function getCharacterSpacing()
+ public function getCharacterSpacing(): float
{
return $this->characterSpacing;
}
-
+
/**
* Set Character Spacing
- * Value in pt
- * @param float|int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Value in pt.
+ *
+ * @param float $pValue
+ *
+ * @return self
*/
- public function setCharacterSpacing($pValue = 0)
+ public function setCharacterSpacing(float $pValue = 0): self
{
- if ($pValue == '') {
- $pValue = 0;
- }
$this->characterSpacing = $pValue * 100;
-
+
return $this;
}
/**
- * Get Size
- *
- * @return double
+ * Get Size.
*/
- public function getSize()
+ public function getSize(): int
{
return $this->size;
}
/**
- * Set Size
- *
- * @param float|int $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Set Size.
*/
- public function setSize($pValue = 10)
+ public function setSize(int $pValue = 10): self
{
- if ($pValue == '') {
- $pValue = 10;
- }
$this->size = $pValue;
return $this;
}
/**
- * Get Bold
+ * Get Bold.
*
- * @return boolean
+ * @return bool
*/
- public function isBold()
+ public function isBold(): bool
{
return $this->bold;
}
/**
- * Set Bold
- *
- * @param boolean $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Set Bold.
*/
- public function setBold($pValue = false)
+ public function setBold(bool $pValue = false): self
{
- if ($pValue == '') {
- $pValue = false;
- }
$this->bold = $pValue;
return $this;
}
/**
- * Get Italic
+ * Get Italic.
*
- * @return boolean
+ * @return bool
*/
- public function isItalic()
+ public function isItalic(): bool
{
return $this->italic;
}
/**
- * Set Italic
- *
- * @param boolean $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Set Italic.
*/
- public function setItalic($pValue = false)
+ public function setItalic(bool $pValue = false): self
{
- if ($pValue == '') {
- $pValue = false;
- }
$this->italic = $pValue;
return $this;
}
/**
- * Get SuperScript
+ * Get SuperScript.
*
- * @return boolean
+ * @return bool
*/
- public function isSuperScript()
+ public function isSuperScript(): bool
{
return $this->superScript;
}
/**
- * Set SuperScript
- *
- * @param boolean $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Set SuperScript.
*/
- public function setSuperScript($pValue = false)
+ public function setSuperScript(bool $pValue = false): self
{
- if ($pValue == '') {
- $pValue = false;
- }
-
$this->superScript = $pValue;
// Set SubScript at false only if SuperScript is true
- if ($pValue === true) {
+ if (true === $pValue) {
$this->subScript = false;
}
return $this;
}
- /**
- * Get SubScript
- *
- * @return boolean
- */
- public function isSubScript()
+ public function isSubScript(): bool
{
return $this->subScript;
}
- /**
- * Set SubScript
- *
- * @param boolean $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
- */
- public function setSubScript($pValue = false)
+ public function setSubScript(bool $pValue = false): self
{
- if ($pValue == '') {
- $pValue = false;
- }
-
$this->subScript = $pValue;
// Set SuperScript at false only if SubScript is true
- if ($pValue === true) {
+ if (true === $pValue) {
$this->superScript = false;
}
@@ -334,24 +293,25 @@ class Font implements ComparableInterface
}
/**
- * Get Underline
+ * Get Underline.
*
* @return string
*/
- public function getUnderline()
+ public function getUnderline(): string
{
return $this->underline;
}
/**
- * Set Underline
+ * Set Underline.
*
- * @param string $pValue \PhpOffice\PhpPresentation\Style\Font underline type
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * @param string $pValue Underline type
+ *
+ * @return self
*/
- public function setUnderline($pValue = self::UNDERLINE_NONE)
+ public function setUnderline(string $pValue = self::UNDERLINE_NONE): self
{
- if ($pValue == '') {
+ if ('' == $pValue) {
$pValue = self::UNDERLINE_NONE;
}
$this->underline = $pValue;
@@ -360,91 +320,122 @@ class Font implements ComparableInterface
}
/**
- * Get Strikethrough
+ * Get Strikethrough.
*
- * @return boolean
+ * @return bool
*/
- public function isStrikethrough()
+ public function isStrikethrough(): bool
{
return $this->strikethrough;
}
/**
- * Set Strikethrough
- *
- * @param boolean $pValue
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Set Strikethrough.
*/
- public function setStrikethrough($pValue = false)
+ public function setStrikethrough(bool $pValue = false): self
{
- if ($pValue == '') {
- $pValue = false;
- }
$this->strikethrough = $pValue;
return $this;
}
/**
- * Get Color
- *
- * @return \PhpOffice\PhpPresentation\Style\Color|\PhpOffice\PhpPresentation\Style\SchemeColor
+ * Get Color.
*/
- public function getColor()
+ public function getColor(): Color
{
return $this->color;
}
/**
- * Set Color
- *
- * @param \PhpOffice\PhpPresentation\Style\Color|\PhpOffice\PhpPresentation\Style\SchemeColor $pValue
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Style\Font
+ * Set Color.
*/
- public function setColor($pValue = null)
+ public function setColor(Color $pValue): self
{
- if (!$pValue instanceof Color) {
- throw new \Exception('$pValue must be an instance of \PhpOffice\PhpPresentation\Style\Color');
- }
$this->color = $pValue;
return $this;
}
/**
- * Get hash code
+ * Get format
*
- * @return string Hash code
+ * @return string
*/
- public function getHashCode()
+ public function getFormat(): string
{
- return md5($this->name . $this->size . ($this->bold ? 't' : 'f') . ($this->italic ? 't' : 'f') . ($this->superScript ? 't' : 'f') . ($this->subScript ? 't' : 'f') . $this->underline . ($this->strikethrough ? 't' : 'f') . $this->color->getHashCode() . __CLASS__);
+ return $this->format;
}
/**
- * Get hash index
+ * Set format
+ *
+ * @param string $value
+ *
+ * @return self
+ */
+ public function setFormat(string $value = self::FORMAT_LATIN): self
+ {
+ if (in_array($value, [
+ self::FORMAT_COMPLEX_SCRIPT,
+ self::FORMAT_EAST_ASIAN,
+ self::FORMAT_LATIN,
+ ])) {
+ $this->format = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ return md5(
+ $this->name
+ . $this->size
+ . ($this->bold ? 't' : 'f')
+ . ($this->italic ? 't' : 'f')
+ . ($this->superScript ? 't' : 'f')
+ . ($this->subScript ? 't' : 'f')
+ . $this->underline
+ . ($this->strikethrough ? 't' : 'f')
+ . $this->format
+ . $this->color->getHashCode()
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Outline.php b/PhpOffice/PhpPresentation/Style/Outline.php
old mode 100755
new mode 100644
index 03c7d21..99714e2
--- a/PhpOffice/PhpPresentation/Style/Outline.php
+++ b/PhpOffice/PhpPresentation/Style/Outline.php
@@ -10,15 +10,18 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
/**
- * \PhpOffice\PhpPresentation\Style\Outline
+ * \PhpOffice\PhpPresentation\Style\Outline.
*/
class Outline
{
@@ -26,54 +29,41 @@ class Outline
* @var Fill
*/
protected $fill;
- /**
- * @var int
- */
- protected $width;
-
/**
- * Outline constructor.
+ * @var float
*/
+ protected $width = 1;
+
public function __construct()
{
$this->fill = new Fill();
}
- /**
- * @return Fill
- */
- public function getFill()
+ public function getFill(): Fill
{
return $this->fill;
}
- /**
- * @param Fill $fill
- * @return Outline
- */
- public function setFill(Fill $fill)
+ public function setFill(Fill $fill): self
{
$this->fill = $fill;
+
return $this;
}
- /**
- * @return int
- */
- public function getWidth()
+ public function getWidth(): float
{
return $this->width;
}
/**
- * Value in points
- * @param int $width
- * @return Outline
+ * Value in points.
*/
- public function setWidth($width)
+ public function setWidth(float $pValue = 1): self
{
- $this->width = intval($width);
+ $this->width = $pValue;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/SchemeColor.php b/PhpOffice/PhpPresentation/Style/SchemeColor.php
old mode 100755
new mode 100644
index 7b8c7fb..040e103
--- a/PhpOffice/PhpPresentation/Style/SchemeColor.php
+++ b/PhpOffice/PhpPresentation/Style/SchemeColor.php
@@ -10,30 +10,32 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
class SchemeColor extends Color
{
+ /**
+ * @var string
+ */
protected $value;
- /**
- * @return string
- */
- public function getValue()
+ public function getValue(): string
{
return $this->value;
}
- /**
- * @param string $value
- */
- public function setValue($value)
+ public function setValue(string $value): self
{
$this->value = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/Shadow.php b/PhpOffice/PhpPresentation/Style/Shadow.php
old mode 100755
new mode 100644
index acf672e..826dd47
--- a/PhpOffice/PhpPresentation/Style/Shadow.php
+++ b/PhpOffice/PhpPresentation/Style/Shadow.php
@@ -10,122 +10,105 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\ComparableInterface;
/**
- * \PhpOffice\PhpPresentation\Style\Shadow
+ * \PhpOffice\PhpPresentation\Style\Shadow.
*/
class Shadow implements ComparableInterface
{
/* Shadow alignment */
- const SHADOW_BOTTOM = 'b';
- const SHADOW_BOTTOM_LEFT = 'bl';
- const SHADOW_BOTTOM_RIGHT = 'br';
- const SHADOW_CENTER = 'ctr';
- const SHADOW_LEFT = 'l';
- const SHADOW_TOP = 't';
- const SHADOW_TOP_LEFT = 'tl';
- const SHADOW_TOP_RIGHT = 'tr';
+ public const SHADOW_BOTTOM = 'b';
+ public const SHADOW_BOTTOM_LEFT = 'bl';
+ public const SHADOW_BOTTOM_RIGHT = 'br';
+ public const SHADOW_CENTER = 'ctr';
+ public const SHADOW_LEFT = 'l';
+ public const SHADOW_TOP = 't';
+ public const SHADOW_TOP_LEFT = 'tl';
+ public const SHADOW_TOP_RIGHT = 'tr';
/**
- * Visible
+ * Visible.
*
- * @var boolean
+ * @var bool
*/
- private $visible;
+ private $visible = false;
/**
- * Blur radius
- *
- * Defaults to 6
+ * Blur radius.
*
* @var int
*/
- private $blurRadius;
+ private $blurRadius = 6;
/**
- * Shadow distance
- *
- * Defaults to 2
+ * Shadow distance.
*
* @var int
*/
- private $distance;
+ private $distance = 2;
/**
- * Shadow direction (in degrees)
+ * Shadow direction (in degrees).
*
* @var int
*/
- private $direction;
+ private $direction = 0;
/**
- * Shadow alignment
+ * Shadow alignment.
*
* @var string
*/
- private $alignment;
+ private $alignment = self::SHADOW_BOTTOM_RIGHT;
/**
- * Color
- *
- * @var \PhpOffice\PhpPresentation\Style\Color
+ * @var Color|null
*/
private $color;
/**
- * Alpha
- *
* @var int
*/
- private $alpha;
+ private $alpha = 50;
/**
- * Hash index
+ * Hash index.
*
- * @var string
+ * @var int
*/
private $hashIndex;
/**
- * Create a new \PhpOffice\PhpPresentation\Style\Shadow
+ * Create a new \PhpOffice\PhpPresentation\Style\Shadow.
*/
public function __construct()
{
- // Initialise values
- $this->visible = false;
- $this->blurRadius = 6;
- $this->distance = 2;
- $this->direction = 0;
- $this->alignment = self::SHADOW_BOTTOM_RIGHT;
- $this->color = new Color(Color::COLOR_BLACK);
- $this->alpha = 50;
+ $this->color = new Color(Color::COLOR_BLACK);
}
/**
- * Get Visible
- *
- * @return boolean
+ * Get Visible.
*/
- public function isVisible()
+ public function isVisible(): bool
{
return $this->visible;
}
/**
- * Set Visible
- *
- * @param boolean $pValue
- * @return $this
+ * Set Visible.
*/
- public function setVisible($pValue = false)
+ public function setVisible(bool $pValue = false): self
{
$this->visible = $pValue;
@@ -133,22 +116,17 @@ class Shadow implements ComparableInterface
}
/**
- * Get Blur radius
- *
- * @return int
+ * Get Blur radius.
*/
- public function getBlurRadius()
+ public function getBlurRadius(): int
{
return $this->blurRadius;
}
/**
- * Set Blur radius
- *
- * @param int $pValue
- * @return $this
+ * Set Blur radius.
*/
- public function setBlurRadius($pValue = 6)
+ public function setBlurRadius(int $pValue = 6): self
{
$this->blurRadius = $pValue;
@@ -156,22 +134,19 @@ class Shadow implements ComparableInterface
}
/**
- * Get Shadow distance
- *
- * @return int
+ * Get Shadow distance.
*/
- public function getDistance()
+ public function getDistance(): int
{
return $this->distance;
}
/**
- * Set Shadow distance
+ * Set Shadow distance.
*
- * @param int $pValue
* @return $this
*/
- public function setDistance($pValue = 2)
+ public function setDistance(int $pValue = 2): self
{
$this->distance = $pValue;
@@ -179,22 +154,17 @@ class Shadow implements ComparableInterface
}
/**
- * Get Shadow direction (in degrees)
- *
- * @return int
+ * Get Shadow direction (in degrees).
*/
- public function getDirection()
+ public function getDirection(): int
{
return $this->direction;
}
/**
- * Set Shadow direction (in degrees)
- *
- * @param int $pValue
- * @return $this
+ * Set Shadow direction (in degrees).
*/
- public function setDirection($pValue = 0)
+ public function setDirection(int $pValue = 0): self
{
$this->direction = $pValue;
@@ -202,22 +172,17 @@ class Shadow implements ComparableInterface
}
/**
- * Get Shadow alignment
- *
- * @return int
+ * Get Shadow alignment.
*/
- public function getAlignment()
+ public function getAlignment(): string
{
return $this->alignment;
}
/**
- * Set Shadow alignment
- *
- * @param string $pValue
- * @return $this
+ * Set Shadow alignment.
*/
- public function setAlignment($pValue = self::SHADOW_BOTTOM_RIGHT)
+ public function setAlignment(string $pValue = self::SHADOW_BOTTOM_RIGHT): self
{
$this->alignment = $pValue;
@@ -225,23 +190,17 @@ class Shadow implements ComparableInterface
}
/**
- * Get Color
- *
- * @return \PhpOffice\PhpPresentation\Style\Color
+ * Get Color.
*/
- public function getColor()
+ public function getColor(): ?Color
{
return $this->color;
}
/**
- * Set Color
- *
- * @param \PhpOffice\PhpPresentation\Style\Color $pValue
- * @throws \Exception
- * @return $this
+ * Set Color.
*/
- public function setColor(Color $pValue = null)
+ public function setColor(Color $pValue = null): self
{
$this->color = $pValue;
@@ -249,22 +208,17 @@ class Shadow implements ComparableInterface
}
/**
- * Get Alpha
- *
- * @return int
+ * Get Alpha.
*/
- public function getAlpha()
+ public function getAlpha(): int
{
return $this->alpha;
}
/**
- * Set Alpha
- *
- * @param int $pValue
- * @return $this
+ * Set Alpha.
*/
- public function setAlpha($pValue = 0)
+ public function setAlpha(int $pValue = 0): self
{
$this->alpha = $pValue;
@@ -272,38 +226,42 @@ class Shadow implements ComparableInterface
}
/**
- * Get hash code
+ * Get hash code.
*
* @return string Hash code
*/
- public function getHashCode()
+ public function getHashCode(): string
{
return md5(($this->visible ? 't' : 'f') . $this->blurRadius . $this->distance . $this->direction . $this->alignment . $this->color->getHashCode() . $this->alpha . __CLASS__);
}
/**
- * Get hash index
+ * Get hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @return string Hash index
+ * @return int|null Hash index
*/
- public function getHashIndex()
+ public function getHashIndex(): ?int
{
return $this->hashIndex;
}
/**
- * Set hash index
+ * Set hash index.
*
* Note that this index may vary during script execution! Only reliable moment is
* while doing a write of a workbook and when changes are not allowed.
*
- * @param string $value Hash index
+ * @param int $value Hash index
+ *
+ * @return $this
*/
- public function setHashIndex($value)
+ public function setHashIndex(int $value)
{
$this->hashIndex = $value;
+
+ return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Style/TextStyle.php b/PhpOffice/PhpPresentation/Style/TextStyle.php
old mode 100755
new mode 100644
index 2f41c7a..64d7c5a
--- a/PhpOffice/PhpPresentation/Style/TextStyle.php
+++ b/PhpOffice/PhpPresentation/Style/TextStyle.php
@@ -10,39 +10,39 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Style;
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph as RichTextParagraph;
-/**
- * Class TextStyle
- */
class TextStyle
{
/**
- * @var array
+ * @var array
*/
- protected $bodyStyle = array();
+ protected $bodyStyle = [];
/**
- * @var array
+ * @var array
*/
- protected $titleStyle = array();
+ protected $titleStyle = [];
/**
- * @var array
+ * @var array
*/
- protected $otherStyle = array();
+ protected $otherStyle = [];
/**
* TextStyle constructor.
+ *
* @param bool $default
- * @throws \Exception
*/
- public function __construct($default = true)
+ public function __construct(bool $default = true)
{
if ($default) {
$oColorLT1 = new SchemeColor();
@@ -70,116 +70,92 @@ class TextStyle
}
}
- /**
- * @param $lvl
- * @return bool
- */
- private function checkLvl($lvl)
+ private function checkLvl(?int $lvl): bool
{
- if (!is_int($lvl)) {
- return false;
- }
- if ($lvl > 9) {
+ if (is_null($lvl) || $lvl > 9) {
return false;
}
+
return true;
}
- /**
- * @param RichTextParagraph $style
- * @param $lvl
- * @return TextStyle
- */
- public function setBodyStyleAtLvl(RichTextParagraph $style, $lvl)
+ public function setBodyStyleAtLvl(RichTextParagraph $style, ?int $lvl): self
{
if ($this->checkLvl($lvl)) {
$this->bodyStyle[$lvl] = $style;
}
+
return $this;
}
- /**
- * @param RichTextParagraph $style
- * @param $lvl
- * @return TextStyle
- */
- public function setTitleStyleAtLvl(RichTextParagraph $style, $lvl)
+ public function setTitleStyleAtLvl(RichTextParagraph $style, ?int $lvl): self
{
if ($this->checkLvl($lvl)) {
$this->titleStyle[$lvl] = $style;
}
+
return $this;
}
/**
- * @param RichTextParagraph $style
- * @param $lvl
* @return TextStyle
*/
- public function setOtherStyleAtLvl(RichTextParagraph $style, $lvl)
+ public function setOtherStyleAtLvl(RichTextParagraph $style, ?int $lvl): self
{
if ($this->checkLvl($lvl)) {
$this->otherStyle[$lvl] = $style;
}
+
return $this;
}
- /**
- * @param $lvl
- * @return mixed
- */
- public function getBodyStyleAtLvl($lvl)
+ public function getBodyStyleAtLvl(?int $lvl): ?RichTextParagraph
{
if ($this->checkLvl($lvl) && !empty($this->bodyStyle[$lvl])) {
return $this->bodyStyle[$lvl];
}
+
return null;
}
- /**
- * @param $lvl
- * @return mixed
- */
- public function getTitleStyleAtLvl($lvl)
+ public function getTitleStyleAtLvl(?int $lvl): ?RichTextParagraph
{
if ($this->checkLvl($lvl) && !empty($this->titleStyle[$lvl])) {
return $this->titleStyle[$lvl];
}
+
return null;
}
- /**
- * @param $lvl
- * @return mixed
- */
- public function getOtherStyleAtLvl($lvl)
+ public function getOtherStyleAtLvl(?int $lvl): ?RichTextParagraph
{
if ($this->checkLvl($lvl) && !empty($this->otherStyle[$lvl])) {
return $this->otherStyle[$lvl];
}
+
return null;
}
/**
- * @return array
+ * @return array
*/
- public function getBodyStyle()
+ public function getBodyStyle(): array
{
return $this->bodyStyle;
}
/**
- * @return array
+ * @return array
*/
- public function getTitleStyle()
+ public function getTitleStyle(): array
{
return $this->titleStyle;
}
/**
- * @return array
+ * @return array
*/
- public function getOtherStyle()
+ public function getOtherStyle(): array
{
return $this->otherStyle;
}
diff --git a/PhpOffice/PhpPresentation/Writer/AbstractDecoratorWriter.php b/PhpOffice/PhpPresentation/Writer/AbstractDecoratorWriter.php
old mode 100755
new mode 100644
index 1d17404..9b12bf9
--- a/PhpOffice/PhpPresentation/Writer/AbstractDecoratorWriter.php
+++ b/PhpOffice/PhpPresentation/Writer/AbstractDecoratorWriter.php
@@ -1,4 +1,22 @@
oHashTable = $hashTable;
+
return $this;
}
@@ -47,12 +65,12 @@ abstract class AbstractDecoratorWriter
}
/**
- * @param PhpPresentation $oPresentation
* @return $this
*/
public function setPresentation(PhpPresentation $oPresentation)
{
$this->oPresentation = $oPresentation;
+
return $this;
}
@@ -65,12 +83,12 @@ abstract class AbstractDecoratorWriter
}
/**
- * @param ZipInterface $oZip
* @return $this
*/
public function setZip(ZipInterface $oZip)
{
$this->oZip = $oZip;
+
return $this;
}
diff --git a/PhpOffice/PhpPresentation/Writer/AbstractWriter.php b/PhpOffice/PhpPresentation/Writer/AbstractWriter.php
old mode 100755
new mode 100644
index b17dcc1..14179ef
--- a/PhpOffice/PhpPresentation/Writer/AbstractWriter.php
+++ b/PhpOffice/PhpPresentation/Writer/AbstractWriter.php
@@ -1,8 +1,29 @@
oDrawingHashTable;
}
/**
- * Get PhpPresentation object
- *
- * @return PhpPresentation
- * @throws \Exception
+ * Get PhpPresentation object.
*/
- public function getPhpPresentation()
+ public function getPhpPresentation(): ?PhpPresentation
{
- if (empty($this->oPresentation)) {
- throw new \Exception("No PhpPresentation assigned.");
- }
return $this->oPresentation;
}
/**
- * Get PhpPresentation object
+ * Get PhpPresentation object.
*
- * @param PhpPresentation $pPhpPresentation PhpPresentation object
- * @throws \Exception
- * @return \PhpOffice\PhpPresentation\Writer\AbstractWriter
+ * @param PhpPresentation|null $pPhpPresentation PhpPresentation object
+ *
+ * @return self
*/
public function setPhpPresentation(PhpPresentation $pPhpPresentation = null)
{
$this->oPresentation = $pPhpPresentation;
+
return $this;
}
-
- /**
- * @param ZipInterface $oZipAdapter
- * @return $this
- */
- public function setZipAdapter(ZipInterface $oZipAdapter)
+ public function setZipAdapter(ZipInterface $oZipAdapter): self
{
$this->oZipAdapter = $oZipAdapter;
+
return $this;
}
- /**
- * @return ZipInterface
- */
- public function getZipAdapter()
+ public function getZipAdapter(): ?ZipInterface
{
return $this->oZipAdapter;
}
/**
- * Get an array of all drawings
+ * Get an array of all drawings.
*
- * @return \PhpOffice\PhpPresentation\Shape\AbstractDrawing[] All drawings in PhpPresentation
- * @throws \Exception
+ * @return array
*/
- protected function allDrawings()
+ protected function allDrawings(): array
{
// Get an array of all drawings
- $aDrawings = array();
+ $aDrawings = [];
// Get an array of all master slides
$aSlideMasters = $this->getPhpPresentation()->getAllMasterSlides();
@@ -104,7 +110,7 @@ abstract class AbstractWriter
}, $aSlideMasters);
// Get an array of all slide layouts
- $aSlideLayouts = array();
+ $aSlideLayouts = [];
array_walk_recursive($aSlideMasterLayouts, function ($oSlideLayout) use (&$aSlideLayouts) {
$aSlideLayouts[] = $oSlideLayout;
});
@@ -118,9 +124,14 @@ abstract class AbstractWriter
return $aDrawings;
}
- private function iterateCollection(\ArrayIterator $oIterator)
+ /**
+ * @param ArrayIterator $oIterator
+ *
+ * @return array
+ */
+ private function iterateCollection(ArrayIterator $oIterator): array
{
- $arrayReturn = array();
+ $arrayReturn = [];
if ($oIterator->count() <= 0) {
return $arrayReturn;
}
@@ -137,6 +148,7 @@ abstract class AbstractWriter
}
$oIterator->next();
}
+
return $arrayReturn;
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation.php b/PhpOffice/PhpPresentation/Writer/ODPresentation.php
old mode 100755
new mode 100644
index 027afdf..a963259
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation.php
@@ -10,54 +10,58 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Writer;
+use DirectoryIterator;
use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter;
+use PhpOffice\PhpPresentation\Exception\DirectoryNotFoundException;
+use PhpOffice\PhpPresentation\Exception\FileCopyException;
+use PhpOffice\PhpPresentation\Exception\FileRemoveException;
+use PhpOffice\PhpPresentation\Exception\InvalidParameterException;
use PhpOffice\PhpPresentation\HashTable;
use PhpOffice\PhpPresentation\PhpPresentation;
-use PhpOffice\PhpPresentation\Shape\AbstractDrawing;
-use PhpOffice\PhpPresentation\Shape\Table;
-use DirectoryIterator;
/**
- * ODPresentation writer
+ * ODPresentation writer.
*/
class ODPresentation extends AbstractWriter implements WriterInterface
{
/**
* @var \PhpOffice\PhpPresentation\Shape\Chart[]
*/
- public $chartArray = array();
+ public $chartArray = [];
/**
- * Use disk caching where possible?
- *
- * @var boolean
- */
+ * Use disk caching where possible?
+ *
+ * @var bool
+ */
private $useDiskCaching = false;
/**
- * Disk caching directory
+ * Disk caching directory.
*
* @var string
*/
private $diskCachingDirectory;
/**
- * Create a new \PhpOffice\PhpPresentation\Writer\ODPresentation
+ * Create a new \PhpOffice\PhpPresentation\Writer\ODPresentation.
*
* @param PhpPresentation $pPhpPresentation
- * @throws \Exception
*/
public function __construct(PhpPresentation $pPhpPresentation = null)
{
// Assign PhpPresentation
- $this->setPhpPresentation($pPhpPresentation);
+ $this->setPhpPresentation($pPhpPresentation ?? new PhpPresentation());
// Set up disk caching location
$this->diskCachingDirectory = './';
@@ -69,21 +73,22 @@ class ODPresentation extends AbstractWriter implements WriterInterface
}
/**
- * Save PhpPresentation to file
+ * Save PhpPresentation to file.
*
- * @param string $pFilename
- * @throws \Exception
+ * @throws FileCopyException
+ * @throws FileRemoveException
+ * @throws InvalidParameterException
*/
- public function save($pFilename)
+ public function save(string $pFilename): void
{
if (empty($pFilename)) {
- throw new \Exception("Filename is empty");
+ throw new InvalidParameterException('pFilename', '');
}
// If $pFilename is php://output or php://stdout, make it a temporary file...
$originalFilename = $pFilename;
- if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
+ if ('php://output' == strtolower($pFilename) || 'php://stdout' == strtolower($pFilename)) {
$pFilename = @tempnam('./', 'phppttmp');
- if ($pFilename == '') {
+ if ('' == $pFilename) {
$pFilename = $originalFilename;
}
}
@@ -97,22 +102,22 @@ class ODPresentation extends AbstractWriter implements WriterInterface
// Variables
$oPresentation = $this->getPhpPresentation();
- $arrayChart = array();
+ $arrayChart = [];
- $arrayFiles = array();
- $oDir = new DirectoryIterator(dirname(__FILE__).DIRECTORY_SEPARATOR.'ODPresentation');
+ $arrayFiles = [];
+ $oDir = new DirectoryIterator(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ODPresentation');
foreach ($oDir as $oFile) {
if (!$oFile->isFile()) {
continue;
}
$class = __NAMESPACE__ . '\\ODPresentation\\' . $oFile->getBasename('.php');
- $o = new \ReflectionClass($class);
+ $class = new \ReflectionClass($class);
- if ($o->isAbstract() || !$o->isSubclassOf('PhpOffice\PhpPresentation\Writer\ODPresentation\AbstractDecoratorWriter')) {
+ if ($class->isAbstract() || !$class->isSubclassOf('PhpOffice\PhpPresentation\Writer\ODPresentation\AbstractDecoratorWriter')) {
continue;
}
- $arrayFiles[$oFile->getBasename('.php')] = $o;
+ $arrayFiles[$oFile->getBasename('.php')] = $class;
}
ksort($arrayFiles);
@@ -133,11 +138,11 @@ class ODPresentation extends AbstractWriter implements WriterInterface
// If a temporary file was used, copy it to the correct file stream
if ($originalFilename != $pFilename) {
- if (copy($pFilename, $originalFilename) === false) {
- throw new \Exception("Could not copy temporary zip file $pFilename to $originalFilename.");
+ if (false === copy($pFilename, $originalFilename)) {
+ throw new FileCopyException($pFilename, $originalFilename);
}
- if (@unlink($pFilename) === false) {
- throw new \Exception('The file ' . $pFilename . ' could not be removed.');
+ if (false === @unlink($pFilename)) {
+ throw new FileRemoveException($pFilename);
}
}
}
@@ -145,7 +150,7 @@ class ODPresentation extends AbstractWriter implements WriterInterface
/**
* Get use disk caching where possible?
*
- * @return boolean
+ * @return bool
*/
public function hasDiskCaching()
{
@@ -155,27 +160,29 @@ class ODPresentation extends AbstractWriter implements WriterInterface
/**
* Set use disk caching where possible?
*
- * @param boolean $pValue
- * @param string $pDirectory Disk caching directory
- * @throws \Exception
+ * @param bool $pValue
+ * @param string $directory Disk caching directory
+ *
+ * @throws DirectoryNotFoundException
+ *
* @return \PhpOffice\PhpPresentation\Writer\ODPresentation
*/
- public function setUseDiskCaching($pValue = false, $pDirectory = null)
+ public function setUseDiskCaching(bool $pValue = false, string $directory = null)
{
$this->useDiskCaching = $pValue;
- if (!is_null($pDirectory)) {
- if (!is_dir($pDirectory)) {
- throw new \Exception("Directory does not exist: $pDirectory");
+ if (!is_null($directory)) {
+ if (!is_dir($directory)) {
+ throw new DirectoryNotFoundException($directory);
}
- $this->diskCachingDirectory = $pDirectory;
+ $this->diskCachingDirectory = $directory;
}
return $this;
}
/**
- * Get disk caching directory
+ * Get disk caching directory.
*
* @return string
*/
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php
old mode 100755
new mode 100644
index c5e390e..7683106
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/AbstractDecoratorWriter.php
@@ -1,4 +1,22 @@
arrayChart = $arrayChart;
+
return $this;
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/Content.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/Content.php
old mode 100755
new mode 100644
index cb399ab..bf9fc5b
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/Content.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/Content.php
@@ -1,4 +1,22 @@
>
*/
- protected $arrStyleBullet = array();
+ protected $arrStyleBullet = [];
/**
* Stores paragraph information for text shapes.
*
- * @var array
+ * @var array
*/
- protected $arrStyleParagraph = array();
+ protected $arrStyleParagraph = [];
/**
* Stores font styles for text shapes that include lists.
*
- * @var Run[]
+ * @var array
*/
- protected $arrStyleTextFont = array();
+ protected $arrStyleTextFont = [];
/**
* Used to track the current shape ID.
*
- * @var integer
+ * @var int
*/
protected $shapeId;
- /**
- * @return ZipInterface
- * @throws \Exception
- */
- public function render()
+ public function render(): ZipInterface
{
$this->getZip()->addFromString('content.xml', $this->writeContent());
+
return $this->getZip();
}
-
-
/**
- * Write content file to XML format
+ * Write content file to XML format.
*
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- public function writeContent()
+ protected function writeContent(): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -113,12 +128,13 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('xmlns:rdfa', 'http://docs.oasis-open.org/opendocument/meta/rdfa#');
$objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0');
$objWriter->writeAttribute('xmlns:officeooo', 'http://openoffice.org/2009/office');
+ $objWriter->writeAttribute('xmlns:loext', 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0');
$objWriter->writeAttribute('office:version', '1.2');
// office:automatic-styles
$objWriter->startElement('office:automatic-styles');
- $this->shapeId = 0;
+ $this->shapeId = 0;
$incSlide = 0;
foreach ($this->getPresentation()->getAllSlides() as $pSlide) {
// Slides
@@ -148,31 +164,31 @@ class Content extends AbstractDecoratorWriter
}
}
- $incSlide++;
+ ++$incSlide;
}
// Style : Bullet
if (!empty($this->arrStyleBullet)) {
foreach ($this->arrStyleBullet as $key => $item) {
- $oStyle = $item['oStyle'];
+ $oStyle = $item['oStyle'];
$arrLevel = explode(';', $item['level']);
// style:style
$objWriter->startElement('text:list-style');
$objWriter->writeAttribute('style:name', 'L_' . $key);
foreach ($arrLevel as $level) {
- if ($level != '') {
+ if ('' != $level) {
$oAlign = $item['oAlign_' . $level];
// text:list-level-style-bullet
$objWriter->startElement('text:list-level-style-bullet');
- $objWriter->writeAttribute('text:level', $level + 1);
+ $objWriter->writeAttribute('text:level', intval($level) + 1);
$objWriter->writeAttribute('text:bullet-char', $oStyle->getBulletChar());
// style:list-level-properties
$objWriter->startElement('style:list-level-properties');
if ($oAlign->getIndent() < 0) {
- $objWriter->writeAttribute('text:space-before', CommonDrawing::pixelsToCentimeters($oAlign->getMarginLeft() - (-1 * $oAlign->getIndent())) . 'cm');
- $objWriter->writeAttribute('text:min-label-width', CommonDrawing::pixelsToCentimeters(-1 * $oAlign->getIndent()) . 'cm');
+ $objWriter->writeAttribute('text:space-before', CommonDrawing::pixelsToCentimeters((int) ($oAlign->getMarginLeft() - (-1 * $oAlign->getIndent()))) . 'cm');
+ $objWriter->writeAttribute('text:min-label-width', CommonDrawing::pixelsToCentimeters((int) (-1 * $oAlign->getIndent())) . 'cm');
} else {
- $objWriter->writeAttribute('text:space-before', (CommonDrawing::pixelsToCentimeters($oAlign->getMarginLeft() - $oAlign->getIndent())) . 'cm');
- $objWriter->writeAttribute('text:min-label-width', CommonDrawing::pixelsToCentimeters($oAlign->getIndent()) . 'cm');
+ $objWriter->writeAttribute('text:space-before', (CommonDrawing::pixelsToCentimeters((int) ($oAlign->getMarginLeft() - $oAlign->getIndent()))) . 'cm');
+ $objWriter->writeAttribute('text:min-label-width', CommonDrawing::pixelsToCentimeters((int) $oAlign->getIndent()) . 'cm');
}
$objWriter->endElement();
@@ -198,6 +214,24 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('style:family', 'paragraph');
// style:paragraph-properties
$objWriter->startElement('style:paragraph-properties');
+ $objWriter->writeAttributeIf(
+ $item->getLineSpacingMode() === Paragraph::LINE_SPACING_MODE_PERCENT,
+ 'fo:line-height',
+ $item->getLineSpacing() . '%'
+ );
+ $objWriter->writeAttributeIf(
+ $item->getLineSpacingMode() === Paragraph::LINE_SPACING_MODE_POINT,
+ 'fo:line-height',
+ $item->getLineSpacing() . 'pt'
+ );
+ $objWriter->writeAttribute(
+ 'fo:margin-top',
+ Text::numberFormat(CommonDrawing::pointstoCentimeters($item->getSpacingBefore()), 3) . 'cm'
+ );
+ $objWriter->writeAttribute(
+ 'fo:margin-bottom',
+ Text::numberFormat(CommonDrawing::pointstoCentimeters($item->getSpacingAfter()), 3) . 'cm'
+ );
switch ($item->getAlignment()->getHorizontal()) {
case Alignment::HORIZONTAL_LEFT:
$objWriter->writeAttribute('fo:text-align', 'left');
@@ -218,6 +252,10 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('fo:text-align', 'left');
break;
}
+ $objWriter->writeAttribute(
+ 'style:writing-mode',
+ $item->getAlignment()->isRTL() ? 'rl-tb' : 'lr-tb'
+ );
$objWriter->endElement();
$objWriter->endElement();
}
@@ -229,19 +267,37 @@ class Content extends AbstractDecoratorWriter
$objWriter->startElement('style:style');
$objWriter->writeAttribute('style:name', 'T_' . $key);
$objWriter->writeAttribute('style:family', 'text');
- // style:text-properties
+
+ // style:style > style:text-properties
$objWriter->startElement('style:text-properties');
$objWriter->writeAttribute('fo:color', '#' . $item->getFont()->getColor()->getRGB());
- $objWriter->writeAttribute('fo:font-family', $item->getFont()->getName());
- $objWriter->writeAttribute('fo:font-size', $item->getFont()->getSize() . 'pt');
- // @todo : fo:font-style
- if ($item->getFont()->isBold()) {
- $objWriter->writeAttribute('fo:font-weight', 'bold');
+ switch ($item->getFont()->getFormat()) {
+ case Font::FORMAT_LATIN:
+ $objWriter->writeAttribute('fo:font-family', $item->getFont()->getName());
+ $objWriter->writeAttribute('fo:font-size', $item->getFont()->getSize() . 'pt');
+ $objWriter->writeAttributeIf($item->getFont()->isBold(), 'fo:font-weight', 'bold');
+ $objWriter->writeAttribute('fo:language', ($item->getLanguage() ? $item->getLanguage() : 'en'));
+ $objWriter->writeAttribute('style:script-type', 'latin');
+ break;
+ case Font::FORMAT_EAST_ASIAN:
+ $objWriter->writeAttribute('style:font-family-asian', $item->getFont()->getName());
+ $objWriter->writeAttribute('style:font-size-asian', $item->getFont()->getSize() . 'pt');
+ $objWriter->writeAttributeIf($item->getFont()->isBold(), 'style:font-weight-asian', 'bold');
+ $objWriter->writeAttribute('style:language-asian', ($item->getLanguage() ? $item->getLanguage() : 'en'));
+ $objWriter->writeAttribute('style:script-type', 'asian');
+ break;
+ case Font::FORMAT_COMPLEX_SCRIPT:
+ $objWriter->writeAttribute('style:font-family-complex', $item->getFont()->getName());
+ $objWriter->writeAttribute('style:font-size-complex', $item->getFont()->getSize() . 'pt');
+ $objWriter->writeAttributeIf($item->getFont()->isBold(), 'style:font-weight-complex', 'bold');
+ $objWriter->writeAttribute('style:language-complex', ($item->getLanguage() ? $item->getLanguage() : 'en'));
+ $objWriter->writeAttribute('style:script-type', 'complex');
+ break;
}
- $objWriter->writeAttribute('fo:language', ($item->getLanguage() ? $item->getLanguage() : 'en'));
- // @todo : style:text-underline-style
+ // > style:style > style:text-properties
$objWriter->endElement();
+ // > style:style
$objWriter->endElement();
}
}
@@ -252,12 +308,12 @@ class Content extends AbstractDecoratorWriter
//===============================================
// office:body
$objWriter->startElement('office:body');
- // office:presentation
+ // office:body > office:presentation
$objWriter->startElement('office:presentation');
// Write slides
$slideCount = $this->getPresentation()->getSlideCount();
- $this->shapeId = 0;
+ $this->shapeId = 0;
for ($i = 0; $i < $slideCount; ++$i) {
$pSlide = $this->getPresentation()->getSlide($i);
$objWriter->startElement('draw:page');
@@ -300,13 +356,18 @@ class Content extends AbstractDecoratorWriter
$objWriter->endElement();
}
+ // office:document-content > office:body > office:presentation > presentation:settings
+ $objWriter->startElement('presentation:settings');
if ($this->getPresentation()->getPresentationProperties()->isLoopContinuouslyUntilEsc()) {
- $objWriter->startElement('presentation:settings');
$objWriter->writeAttribute('presentation:endless', 'true');
- $objWriter->writeAttribute('presentation:pause', 'P0s');
+ $objWriter->writeAttribute('presentation:pause', 'PT0S');
$objWriter->writeAttribute('presentation:mouse-visible', 'false');
- $objWriter->endElement();
}
+ if ($this->getPresentation()->getPresentationProperties()->getSlideshowType() === PresentationProperties::SLIDESHOW_TYPE_BROWSE) {
+ $objWriter->writeAttribute('presentation:full-screen', 'false');
+ }
+ $objWriter->endElement();
+
// > office:presentation
$objWriter->endElement();
// > office:body
@@ -319,20 +380,17 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write picture
- *
- * @param \PhpOffice\Common\XMLWriter $objWriter
- * @param \PhpOffice\PhpPresentation\Shape\Media $shape
+ * Write picture.
*/
- public function writeShapeMedia(XMLWriter $objWriter, Media $shape)
+ protected function writeShapeMedia(XMLWriter $objWriter, Media $shape): void
{
// draw:frame
$objWriter->startElement('draw:frame');
$objWriter->writeAttribute('draw:name', $shape->getName());
- $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm');
- $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm');
- $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getWidth()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getHeight()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY()), 3) . 'cm');
$objWriter->writeAttribute('draw:style-name', 'gr' . $this->shapeId);
// draw:frame > draw:plugin
$objWriter->startElement('draw:plugin');
@@ -367,21 +425,19 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write picture
+ * Write picture.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter
* @param AbstractDrawingAdapter $shape
- * @throws \Exception
*/
- public function writeShapeDrawing(XMLWriter $objWriter, ShapeDrawing\AbstractDrawingAdapter $shape)
+ protected function writeShapeDrawing(XMLWriter $objWriter, ShapeDrawing\AbstractDrawingAdapter $shape): void
{
// draw:frame
$objWriter->startElement('draw:frame');
$objWriter->writeAttribute('draw:name', $shape->getName());
- $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm');
- $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm');
- $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getWidth()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getHeight()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY()), 3) . 'cm');
$objWriter->writeAttribute('draw:style-name', 'gr' . $this->shapeId);
// draw:image
$objWriter->startElement('draw:image');
@@ -391,6 +447,7 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('xlink:type', 'simple');
$objWriter->writeAttribute('xlink:show', 'embed');
$objWriter->writeAttribute('xlink:actuate', 'onLoad');
+ $objWriter->writeAttribute('loext:mime-type', $shape->getMimeType());
$objWriter->writeElement('text:p');
$objWriter->endElement();
@@ -415,34 +472,30 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write text
- *
- * @param \PhpOffice\Common\XMLWriter $objWriter
- * @param \PhpOffice\PhpPresentation\Shape\RichText $shape
- * @throws \Exception
+ * Write text.
*/
- public function writeShapeTxt(XMLWriter $objWriter, RichText $shape)
+ protected function writeShapeTxt(XMLWriter $objWriter, RichText $shape): void
{
// draw:frame
$objWriter->startElement('draw:frame');
$objWriter->writeAttribute('draw:style-name', 'gr' . $this->shapeId);
- $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm');
- $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm');
- $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getWidth()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getHeight()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY()), 3) . 'cm');
// draw:text-box
$objWriter->startElement('draw:text-box');
- $paragraphs = $shape->getParagraphs();
- $paragraphId = 0;
- $sCstShpLastBullet = '';
- $iCstShpLastBulletLvl = 0;
- $bCstShpHasBullet = false;
+ $paragraphs = $shape->getParagraphs();
+ $paragraphId = 0;
+ $sCstShpLastBullet = '';
+ $iCstShpLastBulletLvl = 0;
+ $bCstShpHasBullet = false;
foreach ($paragraphs as $paragraph) {
// Close the bullet list
- if ($sCstShpLastBullet != 'bullet' && $bCstShpHasBullet === true) {
- for ($iInc = $iCstShpLastBulletLvl; $iInc >= 0; $iInc--) {
+ if ('bullet' != $sCstShpLastBullet && true === $bCstShpHasBullet) {
+ for ($iInc = $iCstShpLastBulletLvl; $iInc >= 0; --$iInc) {
// text:list-item
$objWriter->endElement();
// text:list
@@ -452,24 +505,24 @@ class Content extends AbstractDecoratorWriter
//===============================================
// Paragraph
//===============================================
- if ($paragraph->getBulletStyle()->getBulletType() == 'none') {
+ if ('none' == $paragraph->getBulletStyle()->getBulletType()) {
++$paragraphId;
// text:p
$objWriter->startElement('text:p');
$objWriter->writeAttribute('text:style-name', 'P_' . $paragraph->getHashCode());
// Loop trough rich text elements
- $richtexts = $paragraph->getRichTextElements();
+ $richtexts = $paragraph->getRichTextElements();
$richtextId = 0;
foreach ($richtexts as $richtext) {
++$richtextId;
- if ($richtext instanceof TextElement || $richtext instanceof Run) {
+ if ($richtext instanceof TextElement) {
// text:span
$objWriter->startElement('text:span');
if ($richtext instanceof Run) {
$objWriter->writeAttribute('text:style-name', 'T_' . $richtext->getHashCode());
}
- if ($richtext->hasHyperlink() === true && $richtext->getHyperlink()->getUrl() != '') {
+ if (true === $richtext->hasHyperlink() && '' != $richtext->getHyperlink()->getUrl()) {
// text:a
$objWriter->startElement('text:a');
$objWriter->writeAttribute('xlink:type', 'simple');
@@ -490,18 +543,18 @@ class Content extends AbstractDecoratorWriter
}
}
$objWriter->endElement();
- //===============================================
- // Bullet list
- //===============================================
- } elseif ($paragraph->getBulletStyle()->getBulletType() == 'bullet') {
+ //===============================================
+ // Bullet list
+ //===============================================
+ } elseif ('bullet' == $paragraph->getBulletStyle()->getBulletType()) {
$bCstShpHasBullet = true;
// Open the bullet list
- if ($sCstShpLastBullet != 'bullet' || ($sCstShpLastBullet == $paragraph->getBulletStyle()->getBulletType() && $iCstShpLastBulletLvl < $paragraph->getAlignment()->getLevel())) {
+ if ('bullet' != $sCstShpLastBullet || ($sCstShpLastBullet == $paragraph->getBulletStyle()->getBulletType() && $iCstShpLastBulletLvl < $paragraph->getAlignment()->getLevel())) {
// text:list
$objWriter->startElement('text:list');
$objWriter->writeAttribute('text:style-name', 'L_' . $paragraph->getBulletStyle()->getHashCode());
}
- if ($sCstShpLastBullet == 'bullet') {
+ if ('bullet' == $sCstShpLastBullet) {
if ($iCstShpLastBulletLvl == $paragraph->getAlignment()->getLevel()) {
// text:list-item
$objWriter->endElement();
@@ -523,17 +576,17 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('text:style-name', 'P_' . $paragraph->getHashCode());
// Loop trough rich text elements
- $richtexts = $paragraph->getRichTextElements();
+ $richtexts = $paragraph->getRichTextElements();
$richtextId = 0;
foreach ($richtexts as $richtext) {
++$richtextId;
- if ($richtext instanceof TextElement || $richtext instanceof Run) {
+ if ($richtext instanceof TextElement) {
// text:span
$objWriter->startElement('text:span');
if ($richtext instanceof Run) {
$objWriter->writeAttribute('text:style-name', 'T_' . $richtext->getHashCode());
}
- if ($richtext->hasHyperlink() === true && $richtext->getHyperlink()->getUrl() != '') {
+ if (true === $richtext->hasHyperlink() && '' != $richtext->getHyperlink()->getUrl()) {
// text:a
$objWriter->startElement('text:a');
$objWriter->writeAttribute('xlink:type', 'simple');
@@ -555,13 +608,13 @@ class Content extends AbstractDecoratorWriter
}
$objWriter->endElement();
}
- $sCstShpLastBullet = $paragraph->getBulletStyle()->getBulletType();
+ $sCstShpLastBullet = $paragraph->getBulletStyle()->getBulletType();
$iCstShpLastBulletLvl = $paragraph->getAlignment()->getLevel();
}
// Close the bullet list
- if ($sCstShpLastBullet == 'bullet' && $bCstShpHasBullet === true) {
- for ($iInc = $iCstShpLastBulletLvl; $iInc >= 0; $iInc--) {
+ if ('bullet' == $sCstShpLastBullet && true === $bCstShpHasBullet) {
+ for ($iInc = $iCstShpLastBulletLvl; $iInc >= 0; --$iInc) {
// text:list-item
$objWriter->endElement();
// text:list
@@ -574,20 +627,19 @@ class Content extends AbstractDecoratorWriter
// > draw:frame
$objWriter->endElement();
}
+
/**
- * Write Comment
- * @param XMLWriter $objWriter
- * @param Comment $oShape
+ * Write Comment.
*/
- public function writeShapeComment(XMLWriter $objWriter, Comment $oShape)
+ protected function writeShapeComment(XMLWriter $objWriter, Comment $oShape): void
{
- /**
+ /*
* Note : This element is not valid in the Schema 1.2
*/
// officeooo:annotation
$objWriter->startElement('officeooo:annotation');
- $objWriter->writeAttribute('svg:x', number_format(CommonDrawing::pixelsToCentimeters($oShape->getOffsetX()), 2, '.', '').'cm');
- $objWriter->writeAttribute('svg:y', number_format(CommonDrawing::pixelsToCentimeters($oShape->getOffsetY()), 2, '.', '').'cm');
+ $objWriter->writeAttribute('svg:x', number_format(CommonDrawing::pixelsToCentimeters((int) $oShape->getOffsetX()), 2, '.', '') . 'cm');
+ $objWriter->writeAttribute('svg:y', number_format(CommonDrawing::pixelsToCentimeters((int) $oShape->getOffsetY()), 2, '.', '') . 'cm');
if ($oShape->getAuthor() instanceof Comment\Author) {
$objWriter->writeElement('dc:creator', $oShape->getAuthor()->getName());
@@ -599,19 +651,15 @@ class Content extends AbstractDecoratorWriter
$objWriter->endElement();
}
- /**
- * @param XMLWriter $objWriter
- * @param Line $shape
- */
- public function writeShapeLine(XMLWriter $objWriter, Line $shape)
+ protected function writeShapeLine(XMLWriter $objWriter, Line $shape): void
{
// draw:line
$objWriter->startElement('draw:line');
$objWriter->writeAttribute('draw:style-name', 'gr' . $this->shapeId);
- $objWriter->writeAttribute('svg:x1', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y1', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm');
- $objWriter->writeAttribute('svg:x2', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()+$shape->getWidth()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y2', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()+$shape->getHeight()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x1', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y1', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x2', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX() + $shape->getWidth()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y2', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY() + $shape->getHeight()), 3) . 'cm');
// text:p
$objWriter->writeElement('text:p');
@@ -620,19 +668,16 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write table Shape
- * @param XMLWriter $objWriter
- * @param Table $shape
- * @throws \Exception
+ * Write table Shape.
*/
- public function writeShapeTable(XMLWriter $objWriter, Table $shape)
+ protected function writeShapeTable(XMLWriter $objWriter, Table $shape): void
{
// draw:frame
$objWriter->startElement('draw:frame');
- $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm');
- $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm');
- $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getHeight()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getWidth()), 3) . 'cm');
$arrayRows = $shape->getRows();
if (!empty($arrayRows)) {
@@ -647,15 +692,15 @@ class Content extends AbstractDecoratorWriter
foreach ($arrayRows as $keyRow => $shapeRow) {
// table:table-row
$objWriter->startElement('table:table-row');
- $objWriter->writeAttribute('table:style-name', 'gr'.$this->shapeId.'r'.$keyRow);
+ $objWriter->writeAttribute('table:style-name', 'gr' . $this->shapeId . 'r' . $keyRow);
//@todo getFill
$numColspan = 0;
foreach ($shapeRow->getCells() as $keyCell => $shapeCell) {
- if ($numColspan == 0) {
+ if (0 == $numColspan) {
// table:table-cell
$objWriter->startElement('table:table-cell');
- $objWriter->writeAttribute('table:style-name', 'gr' . $this->shapeId.'r'.$keyRow.'c'.$keyCell);
+ $objWriter->writeAttribute('table:style-name', 'gr' . $this->shapeId . 'r' . $keyRow . 'c' . $keyCell);
if ($shapeCell->getColspan() > 1) {
$objWriter->writeAttribute('table:number-columns-spanned', $shapeCell->getColspan());
$numColspan = $shapeCell->getColspan() - 1;
@@ -673,7 +718,7 @@ class Content extends AbstractDecoratorWriter
if ($shapeRichText instanceof Run) {
$objWriter->writeAttribute('text:style-name', 'T_' . $shapeRichText->getHashCode());
}
- if ($shapeRichText->hasHyperlink() === true && $shapeRichText->getHyperlink()->getUrl() !== '') {
+ if (true === $shapeRichText->hasHyperlink() && '' !== $shapeRichText->getHyperlink()->getUrl()) {
// text:a
$objWriter->startElement('text:a');
$objWriter->writeAttribute('xlink:type', 'simple');
@@ -703,7 +748,7 @@ class Content extends AbstractDecoratorWriter
} else {
// table:covered-table-cell
$objWriter->writeElement('table:covered-table-cell');
- $numColspan--;
+ --$numColspan;
}
}
// > table:table-row
@@ -717,12 +762,9 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write table Chart
- * @param XMLWriter $objWriter
- * @param Chart $shape
- * @throws \Exception
+ * Write table Chart.
*/
- public function writeShapeChart(XMLWriter $objWriter, Chart $shape)
+ protected function writeShapeChart(XMLWriter $objWriter, Chart $shape): void
{
$arrayChart = $this->getArrayChart();
$arrayChart[$this->shapeId] = $shape;
@@ -731,14 +773,14 @@ class Content extends AbstractDecoratorWriter
// draw:frame
$objWriter->startElement('draw:frame');
$objWriter->writeAttribute('draw:name', $shape->getTitle()->getText());
- $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetX()), 3) . 'cm');
- $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getOffsetY()), 3) . 'cm');
- $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getHeight()), 3) . 'cm');
- $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($shape->getWidth()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetX()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getOffsetY()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getHeight()), 3) . 'cm');
+ $objWriter->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $shape->getWidth()), 3) . 'cm');
// draw:object
$objWriter->startElement('draw:object');
- $objWriter->writeAttribute('xlink:href', './Object '.$this->shapeId);
+ $objWriter->writeAttribute('xlink:href', './Object ' . $this->shapeId);
$objWriter->writeAttribute('xlink:type', 'simple');
$objWriter->writeAttribute('xlink:show', 'embed');
@@ -749,13 +791,9 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Writes a group of shapes
- *
- * @param XMLWriter $objWriter
- * @param Group $group
- * @throws \Exception
+ * Writes a group of shapes.
*/
- public function writeShapeGroup(XMLWriter $objWriter, Group $group)
+ protected function writeShapeGroup(XMLWriter $objWriter, Group $group): void
{
// draw:g
$objWriter->startElement('draw:g');
@@ -785,12 +823,9 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Writes the style information for a group of shapes
- *
- * @param XMLWriter $objWriter
- * @param Group $group
+ * Writes the style information for a group of shapes.
*/
- public function writeGroupStyle(XMLWriter $objWriter, Group $group)
+ protected function writeGroupStyle(XMLWriter $objWriter, Group $group): void
{
$shapes = $group->getShapeCollection();
foreach ($shapes as $shape) {
@@ -814,12 +849,9 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write the default style information for a RichText shape
- *
- * @param \PhpOffice\Common\XMLWriter $objWriter
- * @param \PhpOffice\PhpPresentation\Shape\RichText $shape
+ * Write the default style information for a RichText shape.
*/
- public function writeTxtStyle(XMLWriter $objWriter, RichText $shape)
+ protected function writeTxtStyle(XMLWriter $objWriter, RichText $shape): void
{
// style:style
$objWriter->startElement('style:style');
@@ -841,24 +873,24 @@ class Content extends AbstractDecoratorWriter
case Fill::FILL_GRADIENT_LINEAR:
case Fill::FILL_GRADIENT_PATH:
$objWriter->writeAttribute('draw:fill', 'gradient');
- $objWriter->writeAttribute('draw:fill-gradient-name', 'gradient_'.$shape->getFill()->getHashCode());
+ $objWriter->writeAttribute('draw:fill-gradient-name', 'gradient_' . $shape->getFill()->getHashCode());
break;
case Fill::FILL_SOLID:
$objWriter->writeAttribute('draw:fill', 'solid');
- $objWriter->writeAttribute('draw:fill-color', '#'.$shape->getFill()->getStartColor()->getRGB());
+ $objWriter->writeAttribute('draw:fill-color', '#' . $shape->getFill()->getStartColor()->getRGB());
break;
case Fill::FILL_NONE:
default:
$objWriter->writeAttribute('draw:fill', 'none');
- $objWriter->writeAttribute('draw:fill-color', '#'.$shape->getFill()->getStartColor()->getRGB());
+ $objWriter->writeAttribute('draw:fill-color', '#' . $shape->getFill()->getStartColor()->getRGB());
break;
}
// Border
- if ($shape->getBorder()->getLineStyle() == Border::LINE_NONE) {
+ if (Border::LINE_NONE == $shape->getBorder()->getLineStyle()) {
$objWriter->writeAttribute('draw:stroke', 'none');
} else {
- $objWriter->writeAttribute('svg:stroke-color', '#'.$shape->getBorder()->getColor()->getRGB());
- $objWriter->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($shape->getBorder()->getLineWidth()), 3, '.', '').'cm');
+ $objWriter->writeAttribute('svg:stroke-color', '#' . $shape->getBorder()->getColor()->getRGB());
+ $objWriter->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($shape->getBorder()->getLineWidth()), 3, '.', '') . 'cm');
switch ($shape->getBorder()->getDashStyle()) {
case Border::DASH_SOLID:
$objWriter->writeAttribute('draw:stroke', 'solid');
@@ -874,7 +906,7 @@ class Content extends AbstractDecoratorWriter
case Border::DASH_SYSDASHDOTDOT:
case Border::DASH_SYSDOT:
$objWriter->writeAttribute('draw:stroke', 'dash');
- $objWriter->writeAttribute('draw:stroke-dash', 'strokeDash_'.$shape->getBorder()->getDashStyle());
+ $objWriter->writeAttribute('draw:stroke-dash', 'strokeDash_' . $shape->getBorder()->getDashStyle());
break;
default:
$objWriter->writeAttribute('draw:stroke', 'none');
@@ -888,7 +920,7 @@ class Content extends AbstractDecoratorWriter
// > style:style
$objWriter->endElement();
- $paragraphs = $shape->getParagraphs();
+ $paragraphs = $shape->getParagraphs();
$paragraphId = 0;
foreach ($paragraphs as $paragraph) {
++$paragraphId;
@@ -902,14 +934,14 @@ class Content extends AbstractDecoratorWriter
$bulletStyleHashCode = $paragraph->getBulletStyle()->getHashCode();
if (!isset($this->arrStyleBullet[$bulletStyleHashCode])) {
$this->arrStyleBullet[$bulletStyleHashCode]['oStyle'] = $paragraph->getBulletStyle();
- $this->arrStyleBullet[$bulletStyleHashCode]['level'] = '';
+ $this->arrStyleBullet[$bulletStyleHashCode]['level'] = '';
}
- if (strpos($this->arrStyleBullet[$bulletStyleHashCode]['level'], ';' . $paragraph->getAlignment()->getLevel()) === false) {
+ if (false === strpos($this->arrStyleBullet[$bulletStyleHashCode]['level'], ';' . $paragraph->getAlignment()->getLevel())) {
$this->arrStyleBullet[$bulletStyleHashCode]['level'] .= ';' . $paragraph->getAlignment()->getLevel();
$this->arrStyleBullet[$bulletStyleHashCode]['oAlign_' . $paragraph->getAlignment()->getLevel()] = $paragraph->getAlignment();
}
- $richtexts = $paragraph->getRichTextElements();
+ $richtexts = $paragraph->getRichTextElements();
$richtextId = 0;
foreach ($richtexts as $richtext) {
++$richtextId;
@@ -925,12 +957,9 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write the default style information for an AbstractDrawingAdapter
- *
- * @param \PhpOffice\Common\XMLWriter $objWriter
- * @param AbstractDrawingAdapter $shape
+ * Write the default style information for an AbstractDrawingAdapter.
*/
- public function writeDrawingStyle(XMLWriter $objWriter, AbstractDrawingAdapter $shape)
+ protected function writeDrawingStyle(XMLWriter $objWriter, AbstractDrawingAdapter $shape): void
{
// style:style
$objWriter->startElement('style:style');
@@ -951,11 +980,8 @@ class Content extends AbstractDecoratorWriter
/**
* Write the default style information for a Line shape.
- *
- * @param XMLWriter $objWriter
- * @param Line $shape
*/
- public function writeLineStyle(XMLWriter $objWriter, Line $shape)
+ protected function writeLineStyle(XMLWriter $objWriter, Line $shape): void
{
// style:style
$objWriter->startElement('style:style');
@@ -977,30 +1003,27 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('draw:stroke', 'none');
break;
}
- $objWriter->writeAttribute('svg:stroke-color', '#'.$shape->getBorder()->getColor()->getRGB());
- $objWriter->writeAttribute('svg:stroke-width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((CommonDrawing::pointsToPixels($shape->getBorder()->getLineWidth()))), 3).'cm');
+ $objWriter->writeAttribute('svg:stroke-color', '#' . $shape->getBorder()->getColor()->getRGB());
+ $objWriter->writeAttribute('svg:stroke-width', Text::numberFormat(CommonDrawing::pointsToCentimeters($shape->getBorder()->getLineWidth()), 3) . 'cm');
$objWriter->endElement();
$objWriter->endElement();
}
/**
- * Write the default style information for a Table shape
- *
- * @param XMLWriter $objWriter
- * @param Table $shape
+ * Write the default style information for a Table shape.
*/
- public function writeTableStyle(XMLWriter $objWriter, Table $shape)
+ protected function writeTableStyle(XMLWriter $objWriter, Table $shape): void
{
foreach ($shape->getRows() as $keyRow => $shapeRow) {
// style:style
$objWriter->startElement('style:style');
- $objWriter->writeAttribute('style:name', 'gr' . $this->shapeId.'r'.$keyRow);
+ $objWriter->writeAttribute('style:name', 'gr' . $this->shapeId . 'r' . $keyRow);
$objWriter->writeAttribute('style:family', 'table-row');
// style:table-row-properties
$objWriter->startElement('style:table-row-properties');
- $objWriter->writeAttribute('style:row-height', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::pointsToPixels($shapeRow->getHeight())), 3).'cm');
+ $objWriter->writeAttribute('style:row-height', Text::numberFormat(CommonDrawing::pointsToCentimeters($shapeRow->getHeight()), 3) . 'cm');
$objWriter->endElement();
$objWriter->endElement();
@@ -1008,22 +1031,22 @@ class Content extends AbstractDecoratorWriter
foreach ($shapeRow->getCells() as $keyCell => $shapeCell) {
// style:style
$objWriter->startElement('style:style');
- $objWriter->writeAttribute('style:name', 'gr' . $this->shapeId.'r'.$keyRow.'c'.$keyCell);
+ $objWriter->writeAttribute('style:name', 'gr' . $this->shapeId . 'r' . $keyRow . 'c' . $keyCell);
$objWriter->writeAttribute('style:family', 'table-cell');
- /**
+ /*
* Note : This element is not valid in the Schema 1.2
*/
// style:graphic-properties
- if ($shapeCell->getFill()->getFillType() != Fill::FILL_NONE) {
+ if (Fill::FILL_NONE != $shapeCell->getFill()->getFillType()) {
$objWriter->startElement('style:graphic-properties');
- if ($shapeCell->getFill()->getFillType() == Fill::FILL_SOLID) {
+ if (Fill::FILL_SOLID == $shapeCell->getFill()->getFillType()) {
$objWriter->writeAttribute('draw:fill', 'solid');
- $objWriter->writeAttribute('draw:fill-color', '#'.$shapeCell->getFill()->getStartColor()->getRGB());
+ $objWriter->writeAttribute('draw:fill-color', '#' . $shapeCell->getFill()->getStartColor()->getRGB());
}
- if ($shapeCell->getFill()->getFillType() == Fill::FILL_GRADIENT_LINEAR) {
+ if (Fill::FILL_GRADIENT_LINEAR == $shapeCell->getFill()->getFillType()) {
$objWriter->writeAttribute('draw:fill', 'gradient');
- $objWriter->writeAttribute('draw:fill-gradient-name', 'gradient_'.$shapeCell->getFill()->getHashCode());
+ $objWriter->writeAttribute('draw:fill-gradient-name', 'gradient_' . $shapeCell->getFill()->getHashCode());
}
$objWriter->endElement();
}
@@ -1043,7 +1066,7 @@ class Content extends AbstractDecoratorWriter
case Border::LINE_SINGLE:
$lineStyle = 'solid';
}
- $objWriter->writeAttribute('fo:border', $lineWidth.'pt '.$lineStyle.' #'.$lineColor);
+ $objWriter->writeAttribute('fo:border', $lineWidth . 'pt ' . $lineStyle . ' #' . $lineColor);
} else {
$lineStyle = 'none';
$lineWidth = Text::numberFormat($cellBorders->getBottom()->getLineWidth() / 1.75, 2);
@@ -1052,7 +1075,7 @@ class Content extends AbstractDecoratorWriter
case Border::LINE_SINGLE:
$lineStyle = 'solid';
}
- $objWriter->writeAttribute('fo:border-bottom', $lineWidth.'pt '.$lineStyle.' #'.$lineColor);
+ $objWriter->writeAttribute('fo:border-bottom', $lineWidth . 'pt ' . $lineStyle . ' #' . $lineColor);
// TOP
$lineStyle = 'none';
$lineWidth = Text::numberFormat($cellBorders->getTop()->getLineWidth() / 1.75, 2);
@@ -1061,7 +1084,7 @@ class Content extends AbstractDecoratorWriter
case Border::LINE_SINGLE:
$lineStyle = 'solid';
}
- $objWriter->writeAttribute('fo:border-top', $lineWidth.'pt '.$lineStyle.' #'.$lineColor);
+ $objWriter->writeAttribute('fo:border-top', $lineWidth . 'pt ' . $lineStyle . ' #' . $lineColor);
// RIGHT
$lineStyle = 'none';
$lineWidth = Text::numberFormat($cellBorders->getRight()->getLineWidth() / 1.75, 2);
@@ -1070,7 +1093,7 @@ class Content extends AbstractDecoratorWriter
case Border::LINE_SINGLE:
$lineStyle = 'solid';
}
- $objWriter->writeAttribute('fo:border-right', $lineWidth.'pt '.$lineStyle.' #'.$lineColor);
+ $objWriter->writeAttribute('fo:border-right', $lineWidth . 'pt ' . $lineStyle . ' #' . $lineColor);
// LEFT
$lineStyle = 'none';
$lineWidth = Text::numberFormat($cellBorders->getLeft()->getLineWidth() / 1.75, 2);
@@ -1079,7 +1102,7 @@ class Content extends AbstractDecoratorWriter
case Border::LINE_SINGLE:
$lineStyle = 'solid';
}
- $objWriter->writeAttribute('fo:border-left', $lineWidth.'pt '.$lineStyle.' #'.$lineColor);
+ $objWriter->writeAttribute('fo:border-left', $lineWidth . 'pt ' . $lineStyle . ' #' . $lineColor);
}
// >style:paragraph-properties
$objWriter->endElement();
@@ -1101,12 +1124,9 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write the slide note
- * @param XMLWriter $objWriter
- * @param \PhpOffice\PhpPresentation\Slide\Note $note
- * @throws \Exception
+ * Write the slide note.
*/
- public function writeSlideNote(XMLWriter $objWriter, Note $note)
+ protected function writeSlideNote(XMLWriter $objWriter, Note $note): void
{
$shapesNote = $note->getShapeCollection();
if (count($shapesNote) > 0) {
@@ -1126,22 +1146,19 @@ class Content extends AbstractDecoratorWriter
}
/**
- * Write style of a slide
- * @param XMLWriter $objWriter
- * @param Slide $slide
- * @param int $incPage
+ * Write style of a slide.
*/
- public function writeStyleSlide(XMLWriter $objWriter, Slide $slide, $incPage)
+ protected function writeStyleSlide(XMLWriter $objWriter, Slide $slide, int $incPage): void
{
// style:style
$objWriter->startElement('style:style');
$objWriter->writeAttribute('style:family', 'drawing-page');
- $objWriter->writeAttribute('style:name', 'stylePage'.$incPage);
+ $objWriter->writeAttribute('style:name', 'stylePage' . $incPage);
// style:style/style:drawing-page-properties
$objWriter->startElement('style:drawing-page-properties');
$objWriter->writeAttributeIf(!$slide->isVisible(), 'presentation:visibility', 'hidden');
if (!is_null($oTransition = $slide->getTransition())) {
- $objWriter->writeAttribute('presentation:duration', 'PT'.number_format($oTransition->getAdvanceTimeTrigger() / 1000, 6, '.', '').'S');
+ $objWriter->writeAttribute('presentation:duration', 'PT' . number_format($oTransition->getAdvanceTimeTrigger() / 1000, 6, '.', '') . 'S');
$objWriter->writeAttributeIf($oTransition->hasManualTrigger(), 'presentation:transition-type', 'manual');
$objWriter->writeAttributeIf($oTransition->hasTimeTrigger(), 'presentation:transition-type', 'automatic');
switch ($oTransition->getSpeed()) {
@@ -1156,7 +1173,7 @@ class Content extends AbstractDecoratorWriter
break;
}
- /**
+ /*
* http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#property-presentation_transition-style
*/
switch ($oTransition->getTransitionType()) {
@@ -1312,7 +1329,7 @@ class Content extends AbstractDecoratorWriter
}
if ($oBackground instanceof Slide\Background\Image) {
$objWriter->writeAttribute('draw:fill', 'bitmap');
- $objWriter->writeAttribute('draw:fill-image-name', 'background_'.$incPage);
+ $objWriter->writeAttribute('draw:fill-image-name', 'background_' . $incPage);
$objWriter->writeAttribute('style:repeat', 'stretch');
}
}
@@ -1321,14 +1338,9 @@ class Content extends AbstractDecoratorWriter
$objWriter->endElement();
}
-
- /**
- * @param XMLWriter $objWriter
- * @param Fill $oFill
- */
- protected function writeStylePartFill(XMLWriter $objWriter, $oFill)
+ protected function writeStylePartFill(XMLWriter $objWriter, ?Fill $oFill): void
{
- if (!($oFill instanceof Fill)) {
+ if (!$oFill) {
return;
}
switch ($oFill->getFillType()) {
@@ -1343,13 +1355,10 @@ class Content extends AbstractDecoratorWriter
}
}
-
/**
- * @param XMLWriter $objWriter
- * @param Shadow $oShadow
* @todo Improve for supporting any direction (https://sinepost.wordpress.com/2012/02/16/theyve-got-atan-you-want-atan2/)
*/
- protected function writeStylePartShadow(XMLWriter $objWriter, Shadow $oShadow)
+ protected function writeStylePartShadow(XMLWriter $objWriter, Shadow $oShadow): void
{
if (!$oShadow->isVisible()) {
return;
@@ -1357,29 +1366,29 @@ class Content extends AbstractDecoratorWriter
$objWriter->writeAttribute('draw:shadow', 'visible');
$objWriter->writeAttribute('draw:shadow-color', '#' . $oShadow->getColor()->getRGB());
- $distanceCms = CommonDrawing::pixelsToCentimeters($oShadow->getDistance());
- if ($oShadow->getDirection() == 0 || $oShadow->getDirection() == 360) {
+ $distanceCms = CommonDrawing::pixelsToCentimeters((int) $oShadow->getDistance());
+ if (0 == $oShadow->getDirection() || 360 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', $distanceCms . 'cm');
$objWriter->writeAttribute('draw:shadow-offset-y', '0cm');
- } elseif ($oShadow->getDirection() == 45) {
+ } elseif (45 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', $distanceCms . 'cm');
$objWriter->writeAttribute('draw:shadow-offset-y', $distanceCms . 'cm');
- } elseif ($oShadow->getDirection() == 90) {
+ } elseif (90 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', '0cm');
$objWriter->writeAttribute('draw:shadow-offset-y', $distanceCms . 'cm');
- } elseif ($oShadow->getDirection() == 135) {
+ } elseif (135 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', '-' . $distanceCms . 'cm');
$objWriter->writeAttribute('draw:shadow-offset-y', $distanceCms . 'cm');
- } elseif ($oShadow->getDirection() == 180) {
+ } elseif (180 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', '-' . $distanceCms . 'cm');
$objWriter->writeAttribute('draw:shadow-offset-y', '0cm');
- } elseif ($oShadow->getDirection() == 225) {
+ } elseif (225 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', '-' . $distanceCms . 'cm');
$objWriter->writeAttribute('draw:shadow-offset-y', '-' . $distanceCms . 'cm');
- } elseif ($oShadow->getDirection() == 270) {
+ } elseif (270 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', '0cm');
$objWriter->writeAttribute('draw:shadow-offset-y', '-' . $distanceCms . 'cm');
- } elseif ($oShadow->getDirection() == 315) {
+ } elseif (315 == $oShadow->getDirection()) {
$objWriter->writeAttribute('draw:shadow-offset-x', $distanceCms . 'cm');
$objWriter->writeAttribute('draw:shadow-offset-y', '-' . $distanceCms . 'cm');
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/Meta.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/Meta.php
old mode 100755
new mode 100644
index 751a420..d976503
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/Meta.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/Meta.php
@@ -1,16 +1,35 @@
writeElement('meta:keyword', $this->getPresentation()->getDocumentProperties()->getKeywords());
+ // meta:user-defined
+ $oDocumentProperties = $this->oPresentation->getDocumentProperties();
+ foreach ($oDocumentProperties->getCustomProperties() as $customProperty) {
+ $propertyValue = $oDocumentProperties->getCustomPropertyValue($customProperty);
+ $propertyType = $oDocumentProperties->getCustomPropertyType($customProperty);
+
+ $objWriter->startElement('meta:user-defined');
+ $objWriter->writeAttribute('meta:name', $customProperty);
+ switch ($propertyType) {
+ case DocumentProperties::PROPERTY_TYPE_INTEGER:
+ case DocumentProperties::PROPERTY_TYPE_FLOAT:
+ $objWriter->writeAttribute('meta:value-type', 'float');
+ $objWriter->writeRaw((string) $propertyValue);
+ break;
+ case DocumentProperties::PROPERTY_TYPE_BOOLEAN:
+ $objWriter->writeAttribute('meta:value-type', 'boolean');
+ $objWriter->writeRaw($propertyValue ? 'true' : 'false');
+ break;
+ case DocumentProperties::PROPERTY_TYPE_DATE:
+ $objWriter->writeAttribute('meta:value-type', 'date');
+ $objWriter->writeRaw(date(DATE_W3C, (int) $propertyValue));
+ break;
+ case DocumentProperties::PROPERTY_TYPE_STRING:
+ case DocumentProperties::PROPERTY_TYPE_UNKNOWN:
+ default:
+ $objWriter->writeAttribute('meta:value-type', 'string');
+ $objWriter->writeRaw((string) $propertyValue);
+ break;
+ }
+ $objWriter->endElement();
+ }
+
// @todo : Where these properties are written ?
// $this->getPresentation()->getDocumentProperties()->getCategory()
// $this->getPresentation()->getDocumentProperties()->getCompany()
@@ -58,8 +109,9 @@ class Meta extends AbstractDecoratorWriter
$objWriter->endElement();
$objWriter->endElement();
-
+
$this->getZip()->addFromString('meta.xml', $objWriter->getData());
+
return $this->getZip();
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php
old mode 100755
new mode 100644
index 9a263db..49f023e
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/MetaInfManifest.php
@@ -1,4 +1,22 @@
getArrayChart() as $key => $shape) {
$objWriter->startElement('manifest:file-entry');
$objWriter->writeAttribute('manifest:media-type', 'application/vnd.oasis.opendocument.chart');
- $objWriter->writeAttribute('manifest:full-path', 'Object '.$key.'/');
+ $objWriter->writeAttribute('manifest:full-path', 'Object ' . $key . '/');
$objWriter->endElement();
$objWriter->startElement('manifest:file-entry');
$objWriter->writeAttribute('manifest:media-type', 'text/xml');
- $objWriter->writeAttribute('manifest:full-path', 'Object '.$key.'/content.xml');
+ $objWriter->writeAttribute('manifest:full-path', 'Object ' . $key . '/content.xml');
$objWriter->endElement();
}
- $arrMedia = array();
+ $arrMedia = [];
for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
$shape = $this->getDrawingHashTable()->getByIndex($i);
- if (! ($shape instanceof ShapeDrawing\AbstractDrawingAdapter)) {
+ if (!($shape instanceof ShapeDrawing\AbstractDrawingAdapter)) {
continue;
}
$arrMedia[] = $shape->getIndexedFilename();
@@ -78,11 +94,11 @@ class MetaInfManifest extends AbstractDecoratorWriter
$oBkgImage = $oSlide->getBackground();
if ($oBkgImage instanceof Image) {
$arrayImage = getimagesize($oBkgImage->getPath());
- $mimeType = image_type_to_mime_type($arrayImage[2]);
+ $mimeType = image_type_to_mime_type($arrayImage[2]);
$objWriter->startElement('manifest:file-entry');
$objWriter->writeAttribute('manifest:media-type', $mimeType);
- $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . str_replace(' ', '_', $oBkgImage->getIndexedFilename($numSlide)));
+ $objWriter->writeAttribute('manifest:full-path', 'Pictures/' . str_replace(' ', '_', $oBkgImage->getIndexedFilename((string) $numSlide)));
$objWriter->endElement();
}
}
@@ -104,6 +120,7 @@ class MetaInfManifest extends AbstractDecoratorWriter
$objWriter->endElement();
$this->getZip()->addFromString('META-INF/manifest.xml', $objWriter->getData());
+
return $this->getZip();
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/Mimetype.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/Mimetype.php
old mode 100755
new mode 100644
index 1efd13e..8010124
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/Mimetype.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/Mimetype.php
@@ -1,4 +1,22 @@
getZip()->addFromString('mimetype', 'application/vnd.oasis.opendocument.presentation');
+
return $this->getZip();
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/ObjectsChart.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/ObjectsChart.php
old mode 100755
new mode 100644
index 2b827ba..12ded07
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/ObjectsChart.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/ObjectsChart.php
@@ -1,4 +1,22 @@
getArrayChart() as $keyChart => $shapeChart) {
$content = $this->writeContentPart($shapeChart);
if (!empty($content)) {
- $this->getZip()->addFromString('Object '.$keyChart.'/content.xml', $content);
+ $this->getZip()->addFromString('Object ' . $keyChart . '/content.xml', $content);
}
}
return $this->getZip();
}
- /**
- * @param Chart $chart
- * @return string
- * @throws \Exception
- */
- protected function writeContentPart(Chart $chart)
+ protected function writeContentPart(Chart $chart): string
{
$this->xmlContent = new XMLWriter(XMLWriter::STORAGE_MEMORY);
$chartType = $chart->getPlotArea()->getType();
// Data
- $this->arrayData = array();
- $this->arrayTitle = array();
+ $this->arrayData = [];
+ $this->arrayTitle = [];
$this->numData = 0;
foreach ($chartType->getSeries() as $series) {
$inc = 0;
$this->arrayTitle[] = $series->getTitle();
foreach ($series->getValues() as $key => $value) {
if (!isset($this->arrayData[$inc])) {
- $this->arrayData[$inc] = array();
+ $this->arrayData[$inc] = [];
}
if (empty($this->arrayData[$inc])) {
$this->arrayData[$inc][] = $key;
}
$this->arrayData[$inc][] = $value;
- $inc++;
+ ++$inc;
}
if ($inc > $this->numData) {
$this->numData = $inc;
@@ -145,7 +158,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->numSeries = 0;
foreach ($chartType->getSeries() as $series) {
$this->writeSeriesStyle($chart, $series);
- $this->numSeries++;
+ ++$this->numSeries;
}
$this->writeFloorStyle();
$this->writeLegendStyle($chart);
@@ -162,8 +175,8 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->startElement('office:chart');
// office:chart
$this->xmlContent->startElement('chart:chart');
- $this->xmlContent->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters($chart->getWidth()), 3) . 'cm');
- $this->xmlContent->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters($chart->getHeight()), 3) . 'cm');
+ $this->xmlContent->writeAttribute('svg:width', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getWidth()), 3) . 'cm');
+ $this->xmlContent->writeAttribute('svg:height', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getHeight()), 3) . 'cm');
$this->xmlContent->writeAttribute('xlink:href', '.');
$this->xmlContent->writeAttribute('xlink:type', 'simple');
$this->xmlContent->writeAttribute('chart:style-name', 'styleChart');
@@ -174,6 +187,7 @@ class ObjectsChart extends AbstractDecoratorWriter
}
$this->xmlContent->writeAttributeIf($chartType instanceof Doughnut, 'chart:class', 'chart:ring');
$this->xmlContent->writeAttributeIf($chartType instanceof Line, 'chart:class', 'chart:line');
+ $this->xmlContent->writeAttributeIf($chartType instanceof Radar, 'chart:class', 'chart:radar');
$this->xmlContent->writeAttributeIf($chartType instanceof Scatter, 'chart:class', 'chart:scatter');
$this->writeTitle($chart->getTitle());
@@ -193,11 +207,7 @@ class ObjectsChart extends AbstractDecoratorWriter
return $this->xmlContent->getData();
}
- /**
- * @param Chart $chart
- * @throws \Exception
- */
- private function writeAxis(Chart $chart)
+ protected function writeAxis(Chart $chart): void
{
$chartType = $chart->getPlotArea()->getType();
@@ -206,9 +216,16 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->writeAttribute('chart:dimension', 'x');
$this->xmlContent->writeAttribute('chart:name', 'primary-x');
$this->xmlContent->writeAttribute('chart:style-name', 'styleAxisX');
+ // chart:axis > chart:title
+ if ($chart->getPlotArea()->getAxisX()->isVisible()) {
+ $this->xmlContent->startElement('chart:title');
+ $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisXTitle');
+ $this->xmlContent->writeElement('text:p', $chart->getPlotArea()->getAxisX()->getTitle());
+ $this->xmlContent->endElement();
+ }
// chart:axis > chart:categories
$this->xmlContent->startElement('chart:categories');
- $this->xmlContent->writeAttribute('table:cell-range-address', 'table-local.$A$2:.$A$'.($this->numData+1));
+ $this->xmlContent->writeAttribute('table:cell-range-address', 'table-local.$A$2:.$A$' . ($this->numData + 1));
$this->xmlContent->endElement();
// chart:axis > chart:grid
$this->writeGridline($chart->getPlotArea()->getAxisX()->getMajorGridlines(), 'styleAxisXGridlinesMajor', 'major');
@@ -222,6 +239,13 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->writeAttribute('chart:dimension', 'y');
$this->xmlContent->writeAttribute('chart:name', 'primary-y');
$this->xmlContent->writeAttribute('chart:style-name', 'styleAxisY');
+ // chart:axis > chart:title
+ if ($chart->getPlotArea()->getAxisY()->isVisible()) {
+ $this->xmlContent->startElement('chart:title');
+ $this->xmlContent->writeAttribute('chart:style-name', 'styleAxisYTitle');
+ $this->xmlContent->writeElement('text:p', $chart->getPlotArea()->getAxisY()->getTitle());
+ $this->xmlContent->endElement();
+ }
// chart:axis > chart:grid
$this->writeGridline($chart->getPlotArea()->getAxisY()->getMajorGridlines(), 'styleAxisYGridlinesMajor', 'major');
// chart:axis > chart:grid
@@ -239,10 +263,10 @@ class ObjectsChart extends AbstractDecoratorWriter
}
}
- protected function writeGridline($oGridlines, $styleName, $chartClass)
+ protected function writeGridline(?Chart\Gridlines $oGridlines, string $styleName, string $chartClass): void
{
- if (!($oGridlines instanceof Chart\Gridlines)) {
- return ;
+ if (!$oGridlines) {
+ return;
}
$this->xmlContent->startElement('chart:grid');
@@ -252,50 +276,17 @@ class ObjectsChart extends AbstractDecoratorWriter
}
/**
- * @param Chart $chart
- * @throws \Exception
* @todo Set function in \PhpPresentation\Shape\Chart\Axis for defining width and color of the axis
*/
- protected function writeAxisStyle(Chart $chart)
+ protected function writeAxisStyle(Chart $chart): void
{
$chartType = $chart->getPlotArea()->getType();
// AxisX
- // style:style
- $this->xmlContent->startElement('style:style');
- $this->xmlContent->writeAttribute('style:name', 'styleAxisX');
- $this->xmlContent->writeAttribute('style:family', 'chart');
- // style:style > style:chart-properties
- $this->xmlContent->startElement('style:chart-properties');
- $this->xmlContent->writeAttribute('chart:display-label', 'true');
- $this->xmlContent->writeAttribute('chart:tick-marks-major-inner', 'false');
- $this->xmlContent->writeAttribute('chart:tick-marks-major-outer', 'false');
- if ($chartType instanceof AbstractTypePie) {
- $this->xmlContent->writeAttribute('chart:reverse-direction', 'true');
- }
- if ($chart->getPlotArea()->getAxisX()->getMinBounds() != null) {
- $this->xmlContent->writeAttribute('chart:minimum', $chart->getPlotArea()->getAxisX()->getMinBounds());
- }
- if ($chart->getPlotArea()->getAxisX()->getMaxBounds() != null) {
- $this->xmlContent->writeAttribute('chart:maximum', $chart->getPlotArea()->getAxisX()->getMaxBounds());
- }
- $this->xmlContent->endElement();
- // style:style > style:graphic-properties
- $this->xmlContent->startElement('style:graphic-properties');
- $this->xmlContent->writeAttribute('draw:stroke', 'solid');
- $this->xmlContent->writeAttribute('svg:stroke-width', '0.026cm');
- $this->xmlContent->writeAttribute('svg:stroke-color', '#878787');
- $this->xmlContent->endElement();
- // style:style > style:text-properties
- $oFont = $chart->getPlotArea()->getAxisX()->getFont();
- $this->xmlContent->startElement('style:text-properties');
- $this->xmlContent->writeAttribute('fo:color', '#'.$oFont->getColor()->getRGB());
- $this->xmlContent->writeAttribute('fo:font-family', $oFont->getName());
- $this->xmlContent->writeAttribute('fo:font-size', $oFont->getSize().'pt');
- $this->xmlContent->writeAttribute('fo:font-style', $oFont->isItalic() ? 'italic' : 'normal');
- $this->xmlContent->endElement();
- // ##style:style
- $this->xmlContent->endElement();
+ $this->writeAxisMainStyle($chart->getPlotArea()->getAxisX(), 'styleAxisX', $chartType);
+
+ // AxisX Title
+ $this->writeAxisTitleStyle($chart->getPlotArea()->getAxisX(), 'styleAxisXTitle');
// AxisX GridLines Major
$this->writeGridlineStyle($chart->getPlotArea()->getAxisX()->getMajorGridlines(), 'styleAxisXGridlinesMajor');
@@ -304,41 +295,10 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->writeGridlineStyle($chart->getPlotArea()->getAxisX()->getMinorGridlines(), 'styleAxisXGridlinesMinor');
// AxisY
- // style:style
- $this->xmlContent->startElement('style:style');
- $this->xmlContent->writeAttribute('style:name', 'styleAxisY');
- $this->xmlContent->writeAttribute('style:family', 'chart');
- // style:style > style:chart-properties
- $this->xmlContent->startElement('style:chart-properties');
- $this->xmlContent->writeAttribute('chart:display-label', 'true');
- $this->xmlContent->writeAttribute('chart:tick-marks-major-inner', 'false');
- $this->xmlContent->writeAttribute('chart:tick-marks-major-outer', 'false');
- if ($chartType instanceof AbstractTypePie) {
- $this->xmlContent->writeAttribute('chart:reverse-direction', 'true');
- }
- if ($chart->getPlotArea()->getAxisY()->getMinBounds() !== null) {
- $this->xmlContent->writeAttribute('chart:minimum', $chart->getPlotArea()->getAxisY()->getMinBounds());
- }
- if ($chart->getPlotArea()->getAxisY()->getMaxBounds() !== null) {
- $this->xmlContent->writeAttribute('chart:maximum', $chart->getPlotArea()->getAxisY()->getMaxBounds());
- }
- $this->xmlContent->endElement();
- // style:graphic-properties
- $this->xmlContent->startElement('style:graphic-properties');
- $this->xmlContent->writeAttribute('draw:stroke', 'solid');
- $this->xmlContent->writeAttribute('svg:stroke-width', '0.026cm');
- $this->xmlContent->writeAttribute('svg:stroke-color', '#878787');
- $this->xmlContent->endElement();
- // style:style > style:text-properties
- $oFont = $chart->getPlotArea()->getAxisY()->getFont();
- $this->xmlContent->startElement('style:text-properties');
- $this->xmlContent->writeAttribute('fo:color', '#'.$oFont->getColor()->getRGB());
- $this->xmlContent->writeAttribute('fo:font-family', $oFont->getName());
- $this->xmlContent->writeAttribute('fo:font-size', $oFont->getSize().'pt');
- $this->xmlContent->writeAttribute('fo:font-style', $oFont->isItalic() ? 'italic' : 'normal');
- $this->xmlContent->endElement();
- // ## style:style
- $this->xmlContent->endElement();
+ $this->writeAxisMainStyle($chart->getPlotArea()->getAxisY(), 'styleAxisY', $chartType);
+
+ // AxisY Title
+ $this->writeAxisTitleStyle($chart->getPlotArea()->getAxisY(), 'styleAxisYTitle');
// AxisY GridLines Major
$this->writeGridlineStyle($chart->getPlotArea()->getAxisY()->getMajorGridlines(), 'styleAxisYGridlinesMajor');
@@ -347,13 +307,82 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->writeGridlineStyle($chart->getPlotArea()->getAxisY()->getMinorGridlines(), 'styleAxisYGridlinesMinor');
}
- /**
- * @param Chart\Gridlines $oGridlines
- * @param string $styleName
- */
- protected function writeGridlineStyle($oGridlines, $styleName)
+ protected function writeAxisMainStyle(Chart\Axis $axis, string $styleName, AbstractType $chartType): void
{
- if (!($oGridlines instanceof Chart\Gridlines)) {
+ // style:style
+ $this->xmlContent->startElement('style:style');
+ $this->xmlContent->writeAttribute('style:name', $styleName);
+ $this->xmlContent->writeAttribute('style:family', 'chart');
+ // style:style > style:chart-properties
+ $this->xmlContent->startElement('style:chart-properties');
+ $this->xmlContent->writeAttribute('chart:display-label', 'true');
+ $this->xmlContent->writeAttribute('chart:tick-marks-major-inner', 'false');
+ $this->xmlContent->writeAttribute('chart:tick-marks-major-outer', 'false');
+ $this->xmlContent->writeAttributeIf($chartType instanceof AbstractTypePie, 'chart:reverse-direction', 'true');
+ $this->xmlContent->writeAttributeIf(null !== $axis->getMinBounds(), 'chart:minimum', $axis->getMinBounds());
+ $this->xmlContent->writeAttributeIf(null !== $axis->getMaxBounds(), 'chart:maximum', $axis->getMaxBounds());
+ $this->xmlContent->writeAttributeIf(null !== $axis->getMajorUnit(), 'chart:interval-major', $axis->getMajorUnit());
+ $this->xmlContent->writeAttributeIf(null !== $axis->getMinorUnit(), 'chart:interval-minor-divisor', $axis->getMinorUnit());
+ switch ($axis->getTickLabelPosition()) {
+ case Axis::TICK_LABEL_POSITION_NEXT_TO:
+ $this->xmlContent->writeAttribute('chart:axis-label-position', 'near-axis');
+ break;
+ case Axis::TICK_LABEL_POSITION_HIGH:
+ $this->xmlContent->writeAttribute('chart:axis-position', '0');
+ $this->xmlContent->writeAttribute('chart:axis-label-position', 'outside-end');
+ break;
+ case Axis::TICK_LABEL_POSITION_LOW:
+ $this->xmlContent->writeAttribute('chart:axis-position', '0');
+ $this->xmlContent->writeAttribute('chart:axis-label-position', 'outside-start');
+ $this->xmlContent->writeAttribute('chart:tick-mark-position', 'at-axis');
+ break;
+ }
+ $this->xmlContent->writeAttributeIf($chartType instanceof Radar && $styleName == 'styleAxisX', 'chart:reverse-direction', 'true');
+ $this->xmlContent->endElement();
+ // style:graphic-properties
+ $this->xmlContent->startElement('style:graphic-properties');
+ $this->xmlContent->writeAttribute('draw:stroke', $axis->getOutline()->getFill()->getFillType());
+ $this->xmlContent->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($axis->getOutline()->getWidth()), 3, '.', '') . 'cm');
+ $this->xmlContent->writeAttribute('svg:stroke-color', '#' . $axis->getOutline()->getFill()->getStartColor()->getRGB());
+ $this->xmlContent->endElement();
+ // style:style > style:text-properties
+ $this->xmlContent->startElement('style:text-properties');
+ $this->xmlContent->writeAttribute('fo:color', '#' . $axis->getFont()->getColor()->getRGB());
+ $this->xmlContent->writeAttribute('fo:font-family', $axis->getFont()->getName());
+ $this->xmlContent->writeAttribute('fo:font-size', $axis->getFont()->getSize() . 'pt');
+ $this->xmlContent->writeAttribute('fo:font-style', $axis->getFont()->isItalic() ? 'italic' : 'normal');
+ $this->xmlContent->endElement();
+ // ## style:style
+ $this->xmlContent->endElement();
+ }
+
+ protected function writeAxisTitleStyle(Chart\Axis $axis, string $styleName): void
+ {
+ // style:style
+ $this->xmlContent->startElement('style:style');
+ $this->xmlContent->writeAttribute('style:name', $styleName);
+ $this->xmlContent->writeAttribute('style:family', 'chart');
+ // style:chart-properties
+ $this->xmlContent->startElement('style:chart-properties');
+ $this->xmlContent->writeAttribute('chart:auto-position', 'true');
+ $this->xmlContent->writeAttributeIf($axis->getTitleRotation() != 0, 'style:rotation-angle', '-' . $axis->getTitleRotation());
+ // > style:chart-properties
+ $this->xmlContent->endElement();
+ // style:text-properties
+ $this->xmlContent->startElement('style:text-properties');
+ $this->xmlContent->writeAttribute('fo:color', '#' . $axis->getFont()->getColor()->getRGB());
+ $this->xmlContent->writeAttribute('fo:font-family', $axis->getFont()->getName());
+ $this->xmlContent->writeAttribute('fo:font-size', $axis->getFont()->getSize() . 'pt');
+ $this->xmlContent->writeAttribute('fo:font-style', $axis->getFont()->isItalic() ? 'italic' : 'normal');
+ // > style:text-properties
+ $this->xmlContent->endElement();
+ // > style:style
+ $this->xmlContent->endElement();
+ }
+
+ protected function writeGridlineStyle(?Chart\Gridlines $oGridlines, string $styleName): void
+ {
+ if (!$oGridlines) {
return;
}
// style:style
@@ -362,17 +391,14 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->writeAttribute('style:family', 'chart');
// style:style > style:graphic-properties
$this->xmlContent->startElement('style:graphic-properties');
- $this->xmlContent->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($oGridlines->getOutline()->getWidth()), 2, '.', '').'cm');
- $this->xmlContent->writeAttribute('svg:stroke-color', '#'.$oGridlines->getOutline()->getFill()->getStartColor()->getRGB());
+ $this->xmlContent->writeAttribute('svg:stroke-width', number_format(CommonDrawing::pointsToCentimeters($oGridlines->getOutline()->getWidth()), 2, '.', '') . 'cm');
+ $this->xmlContent->writeAttribute('svg:stroke-color', '#' . $oGridlines->getOutline()->getFill()->getStartColor()->getRGB());
$this->xmlContent->endElement();
// ##style:style
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- */
- private function writeChartStyle(Chart $chart)
+ protected function writeChartStyle(Chart $chart): void
{
// style:style
$this->xmlContent->startElement('style:style');
@@ -381,14 +407,14 @@ class ObjectsChart extends AbstractDecoratorWriter
// style:graphic-properties
$this->xmlContent->startElement('style:graphic-properties');
$this->xmlContent->writeAttribute('draw:stroke', $chart->getFill()->getFillType());
- $this->xmlContent->writeAttribute('draw:fill-color', '#'.$chart->getFill()->getStartColor()->getRGB());
+ $this->xmlContent->writeAttribute('draw:fill-color', '#' . $chart->getFill()->getStartColor()->getRGB());
// > style:graphic-properties
$this->xmlContent->endElement();
// > style:style
$this->xmlContent->endElement();
}
- private function writeFloor()
+ protected function writeFloor(): void
{
// chart:floor
$this->xmlContent->startElement('chart:floor');
@@ -397,7 +423,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- private function writeFloorStyle()
+ protected function writeFloorStyle(): void
{
// style:style
$this->xmlContent->startElement('style:style');
@@ -416,10 +442,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- */
- private function writeLegend(Chart $chart)
+ protected function writeLegend(Chart $chart): void
{
// chart:legend
$this->xmlContent->startElement('chart:legend');
@@ -442,18 +465,15 @@ class ObjectsChart extends AbstractDecoratorWriter
break;
}
$this->xmlContent->writeAttribute('chart:legend-position', $position);
- $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($chart->getLegend()->getOffsetX()), 3) . 'cm');
- $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($chart->getLegend()->getOffsetY()), 3) . 'cm');
+ $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getLegend()->getOffsetX()), 3) . 'cm');
+ $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $chart->getLegend()->getOffsetY()), 3) . 'cm');
$this->xmlContent->writeAttribute('style:legend-expansion', 'high');
$this->xmlContent->writeAttribute('chart:style-name', 'styleLegend');
// > chart:legend
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- */
- private function writeLegendStyle(Chart $chart)
+ protected function writeLegendStyle(Chart $chart): void
{
// style:style
$this->xmlContent->startElement('style:style');
@@ -466,9 +486,9 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
// style:text-properties
$this->xmlContent->startElement('style:text-properties');
- $this->xmlContent->writeAttribute('fo:color', '#'.$chart->getLegend()->getFont()->getColor()->getRGB());
+ $this->xmlContent->writeAttribute('fo:color', '#' . $chart->getLegend()->getFont()->getColor()->getRGB());
$this->xmlContent->writeAttribute('fo:font-family', $chart->getLegend()->getFont()->getName());
- $this->xmlContent->writeAttribute('fo:font-size', $chart->getLegend()->getFont()->getSize().'pt');
+ $this->xmlContent->writeAttribute('fo:font-size', $chart->getLegend()->getFont()->getSize() . 'pt');
$this->xmlContent->writeAttribute('fo:font-style', $chart->getLegend()->getFont()->isItalic() ? 'italic' : 'normal');
// > style:text-properties
$this->xmlContent->endElement();
@@ -476,11 +496,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- * @throws \Exception
- */
- private function writePlotArea(Chart $chart)
+ protected function writePlotArea(Chart $chart): void
{
$chartType = $chart->getPlotArea()->getType();
@@ -493,15 +509,15 @@ class ObjectsChart extends AbstractDecoratorWriter
}
if ($chartType instanceof Bar3D || $chartType instanceof Pie3D) {
// dr3d:light
- $arrayLight = array(
- array('#808080', '(0 0 1)', 'false', 'true'),
- array('#666666', '(0.2 0.4 1)', 'true', 'false'),
- array('#808080', '(0 0 1)', 'false', 'false'),
- array('#808080', '(0 0 1)', 'false', 'false'),
- array('#808080', '(0 0 1)', 'false', 'false'),
- array('#808080', '(0 0 1)', 'false', 'false'),
- array('#808080', '(0 0 1)', 'false', 'false'),
- );
+ $arrayLight = [
+ ['#808080', '(0 0 1)', 'false', 'true'],
+ ['#666666', '(0.2 0.4 1)', 'true', 'false'],
+ ['#808080', '(0 0 1)', 'false', 'false'],
+ ['#808080', '(0 0 1)', 'false', 'false'],
+ ['#808080', '(0 0 1)', 'false', 'false'],
+ ['#808080', '(0 0 1)', 'false', 'false'],
+ ['#808080', '(0 0 1)', 'false', 'false'],
+ ];
foreach ($arrayLight as $light) {
$this->xmlContent->startElement('dr3d:light');
$this->xmlContent->writeAttribute('dr3d:diffuse-color', $light[0]);
@@ -520,8 +536,8 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->numSeries = 0;
foreach ($chartType->getSeries() as $series) {
$this->writeSeries($chart, $series);
- $this->rangeCol++;
- $this->numSeries++;
+ ++$this->rangeCol;
+ ++$this->numSeries;
}
//**** Wall ****
@@ -533,11 +549,9 @@ class ObjectsChart extends AbstractDecoratorWriter
}
/**
- * @param Chart $chart
- * @throws \Exception
- * @link : http://books.evc-cit.info/odbook/ch08.html#chart-plot-area-section
+ * @see : http://books.evc-cit.info/odbook/ch08.html#chart-plot-area-section
*/
- private function writePlotAreaStyle(Chart $chart)
+ protected function writePlotAreaStyle(Chart $chart): void
{
$chartType = $chart->getPlotArea()->getType();
@@ -553,20 +567,33 @@ class ObjectsChart extends AbstractDecoratorWriter
} elseif ($chartType instanceof Pie3D) {
$this->xmlContent->writeAttribute('chart:three-dimensional', 'true');
$this->xmlContent->writeAttribute('chart:right-angled-axes', 'true');
+ } elseif ($chartType instanceof AbstractTypeLine) {
+ $this->xmlContent->writeAttributeIf($chartType->isSmooth(), 'chart:interpolation', 'cubic-spline');
+ }
+ switch ($chart->getDisplayBlankAs()) {
+ case Chart::BLANKAS_ZERO:
+ $this->xmlContent->writeAttribute('chart:treat-empty-cells', 'use-zero');
+ break;
+ case Chart::BLANKAS_GAP:
+ $this->xmlContent->writeAttribute('chart:treat-empty-cells', 'leave-gap');
+ break;
+ case Chart::BLANKAS_SPAN:
+ $this->xmlContent->writeAttribute('chart:treat-empty-cells', 'ignore');
+ break;
}
if ($chartType instanceof AbstractTypeBar) {
$chartVertical = 'false';
- if ($chartType->getBarDirection() == AbstractTypeBar::DIRECTION_HORIZONTAL) {
+ if (AbstractTypeBar::DIRECTION_HORIZONTAL == $chartType->getBarDirection()) {
$chartVertical = 'true';
}
$this->xmlContent->writeAttribute('chart:vertical', $chartVertical);
- if ($chartType->getBarGrouping() == Bar::GROUPING_CLUSTERED) {
+ if (Bar::GROUPING_CLUSTERED == $chartType->getBarGrouping()) {
$this->xmlContent->writeAttribute('chart:stacked', 'false');
$this->xmlContent->writeAttribute('chart:overlap', '0');
- } elseif ($chartType->getBarGrouping() == Bar::GROUPING_STACKED) {
+ } elseif (Bar::GROUPING_STACKED == $chartType->getBarGrouping()) {
$this->xmlContent->writeAttribute('chart:stacked', 'true');
$this->xmlContent->writeAttribute('chart:overlap', '100');
- } elseif ($chartType->getBarGrouping() == Bar::GROUPING_PERCENTSTACKED) {
+ } elseif (Bar::GROUPING_PERCENTSTACKED == $chartType->getBarGrouping()) {
$this->xmlContent->writeAttribute('chart:stacked', 'true');
$this->xmlContent->writeAttribute('chart:overlap', '100');
$this->xmlContent->writeAttribute('chart:percentage', 'true');
@@ -574,7 +601,7 @@ class ObjectsChart extends AbstractDecoratorWriter
}
$labelFormat = 'value';
if ($chartType instanceof AbstractTypeBar) {
- if ($chartType->getBarGrouping() == Bar::GROUPING_PERCENTSTACKED) {
+ if (Bar::GROUPING_PERCENTSTACKED == $chartType->getBarGrouping()) {
$labelFormat = 'percentage';
}
}
@@ -586,33 +613,22 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- * @param Chart\Series $series
- * @throws \Exception
- */
- private function writeSeries(Chart $chart, Chart\Series $series)
+ protected function writeSeries(Chart $chart, Chart\Series $series): void
{
$chartType = $chart->getPlotArea()->getType();
$numRange = count($series->getValues());
// chart:series
$this->xmlContent->startElement('chart:series');
- $this->xmlContent->writeAttribute('chart:values-cell-range-address', 'table-local.$'.$this->rangeCol.'$2:.$'.$this->rangeCol.'$'.($numRange+1));
- $this->xmlContent->writeAttribute('chart:label-cell-address', 'table-local.$'.$this->rangeCol.'$1');
- // if ($chartType instanceof Area) {
- // $this->xmlContent->writeAttribute('chart:class', 'chart:area');
- // } elseif ($chartType instanceof AbstractTypeBar) {
- // $this->xmlContent->writeAttribute('chart:class', 'chart:bar');
- // } elseif ($chartType instanceof Line) {
- // $this->xmlContent->writeAttribute('chart:class', 'chart:line');
- // } elseif ($chartType instanceof AbstractTypePie) {
- // $this->xmlContent->writeAttribute('chart:class', 'chart:circle');
- // } elseif ($chartType instanceof Scatter) {
- // $this->xmlContent->writeAttribute('chart:class', 'chart:scatter');
- // }
- $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries'.$this->numSeries);
- if ($chartType instanceof Area || $chartType instanceof AbstractTypeBar || $chartType instanceof Line || $chartType instanceof Scatter) {
+ $this->xmlContent->writeAttribute('chart:values-cell-range-address', 'table-local.$' . $this->rangeCol . '$2:.$' . $this->rangeCol . '$' . ($numRange + 1));
+ $this->xmlContent->writeAttribute('chart:label-cell-address', 'table-local.$' . $this->rangeCol . '$1');
+ $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries' . $this->numSeries);
+ if ($chartType instanceof Area
+ || $chartType instanceof AbstractTypeBar
+ || $chartType instanceof Line
+ || $chartType instanceof Radar
+ || $chartType instanceof Scatter
+ ) {
$dataPointFills = $series->getDataPointFills();
$incRepeat = $numRange;
@@ -631,14 +647,14 @@ class ObjectsChart extends AbstractDecoratorWriter
// chart:data-point
$this->xmlContent->startElement('chart:data-point');
- $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries'.$this->numSeries.'_'.$inc);
+ $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries' . $this->numSeries . '_' . $inc);
// > chart:data-point
$this->xmlContent->endElement();
}
- $inc++;
- $incRepeat++;
+ ++$inc;
+ ++$incRepeat;
} while ($inc < $numRange);
- $incRepeat--;
+ --$incRepeat;
}
// chart:data-point
$this->xmlContent->startElement('chart:data-point');
@@ -647,10 +663,10 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
} elseif ($chartType instanceof AbstractTypePie) {
$count = count($series->getDataPointFills());
- for ($inc = 0; $inc < $count; $inc++) {
+ for ($inc = 0; $inc < $count; ++$inc) {
// chart:data-point
$this->xmlContent->startElement('chart:data-point');
- $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries'.$this->numSeries.'_'.$inc);
+ $this->xmlContent->writeAttribute('chart:style-name', 'styleSeries' . $this->numSeries . '_' . $inc);
// > chart:data-point
$this->xmlContent->endElement();
}
@@ -660,18 +676,13 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- * @param Chart\Series $series
- * @throws \Exception
- */
- private function writeSeriesStyle(Chart $chart, Chart\Series $series)
+ protected function writeSeriesStyle(Chart $chart, Chart\Series $series): void
{
$chartType = $chart->getPlotArea()->getType();
// style:style
$this->xmlContent->startElement('style:style');
- $this->xmlContent->writeAttribute('style:name', 'styleSeries'.$this->numSeries);
+ $this->xmlContent->writeAttribute('style:name', 'styleSeries' . $this->numSeries);
$this->xmlContent->writeAttribute('style:family', 'chart');
// style:chart-properties
$this->xmlContent->startElement('style:chart-properties');
@@ -695,15 +706,15 @@ class ObjectsChart extends AbstractDecoratorWriter
}
if ($chartType instanceof Line || $chartType instanceof Scatter) {
$oMarker = $series->getMarker();
- /**
+ /*
* @link : http://www.datypic.com/sc/odf/a-chart_symbol-type.html
*/
- $this->xmlContent->writeAttributeIf($oMarker->getSymbol() == Chart\Marker::SYMBOL_NONE, 'chart:symbol-type', 'none');
- /**
+ $this->xmlContent->writeAttributeIf(Chart\Marker::SYMBOL_NONE == $oMarker->getSymbol(), 'chart:symbol-type', 'none');
+ /*
* @link : http://www.datypic.com/sc/odf/a-chart_symbol-name.html
*/
- $this->xmlContent->writeAttributeIf($oMarker->getSymbol() != Chart\Marker::SYMBOL_NONE, 'chart:symbol-type', 'named-symbol');
- if ($oMarker->getSymbol() != Chart\Marker::SYMBOL_NONE) {
+ $this->xmlContent->writeAttributeIf(Chart\Marker::SYMBOL_NONE != $oMarker->getSymbol(), 'chart:symbol-type', 'named-symbol');
+ if (Chart\Marker::SYMBOL_NONE != $oMarker->getSymbol()) {
switch ($oMarker->getSymbol()) {
case Chart\Marker::SYMBOL_DASH:
$symbolName = 'horizontal-bar';
@@ -720,8 +731,8 @@ class ObjectsChart extends AbstractDecoratorWriter
}
$this->xmlContent->writeAttribute('chart:symbol-name', $symbolName);
$symbolSize = number_format(CommonDrawing::pointsToCentimeters($oMarker->getSize()), 2, '.', '');
- $this->xmlContent->writeAttribute('chart:symbol-width', $symbolSize.'cm');
- $this->xmlContent->writeAttribute('chart:symbol-height', $symbolSize.'cm');
+ $this->xmlContent->writeAttribute('chart:symbol-width', $symbolSize . 'cm');
+ $this->xmlContent->writeAttribute('chart:symbol-height', $symbolSize . 'cm');
}
}
@@ -729,7 +740,7 @@ class ObjectsChart extends AbstractDecoratorWriter
if (!empty($separator)) {
// style:chart-properties/chart:label-separator
$this->xmlContent->startElement('chart:label-separator');
- if ($separator == PHP_EOL) {
+ if (PHP_EOL == $separator) {
$this->xmlContent->writeRaw('');
} else {
$this->xmlContent->writeElement('text:p', $separator);
@@ -741,7 +752,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
// style:graphic-properties
$this->xmlContent->startElement('style:graphic-properties');
- if ($chartType instanceof Line || $chartType instanceof Scatter) {
+ if ($chartType instanceof Line || $chartType instanceof Radar || $chartType instanceof Scatter) {
$outlineWidth = '';
$outlineColor = '';
@@ -759,22 +770,22 @@ class ObjectsChart extends AbstractDecoratorWriter
if (empty($outlineColor)) {
$outlineColor = '4a7ebb';
}
- $this->xmlContent->writeAttribute('svg:stroke-width', $outlineWidth.'cm');
- $this->xmlContent->writeAttribute('svg:stroke-color', '#'.$outlineColor);
+ $this->xmlContent->writeAttribute('svg:stroke-width', $outlineWidth . 'cm');
+ $this->xmlContent->writeAttribute('svg:stroke-color', '#' . $outlineColor);
} else {
$this->xmlContent->writeAttribute('draw:stroke', 'none');
if (!($chartType instanceof Area)) {
$this->xmlContent->writeAttribute('draw:fill', $series->getFill()->getFillType());
}
}
- $this->xmlContent->writeAttribute('draw:fill-color', '#'.$series->getFill()->getStartColor()->getRGB());
+ $this->xmlContent->writeAttribute('draw:fill-color', '#' . $series->getFill()->getStartColor()->getRGB());
// > style:graphic-properties
$this->xmlContent->endElement();
// style:text-properties
$this->xmlContent->startElement('style:text-properties');
- $this->xmlContent->writeAttribute('fo:color', '#'.$series->getFont()->getColor()->getRGB());
+ $this->xmlContent->writeAttribute('fo:color', '#' . $series->getFont()->getColor()->getRGB());
$this->xmlContent->writeAttribute('fo:font-family', $series->getFont()->getName());
- $this->xmlContent->writeAttribute('fo:font-size', $series->getFont()->getSize().'pt');
+ $this->xmlContent->writeAttribute('fo:font-size', $series->getFont()->getSize() . 'pt');
// > style:text-properties
$this->xmlContent->endElement();
@@ -784,12 +795,12 @@ class ObjectsChart extends AbstractDecoratorWriter
foreach ($series->getDataPointFills() as $idx => $oFill) {
// style:style
$this->xmlContent->startElement('style:style');
- $this->xmlContent->writeAttribute('style:name', 'styleSeries'.$this->numSeries.'_'.$idx);
+ $this->xmlContent->writeAttribute('style:name', 'styleSeries' . $this->numSeries . '_' . $idx);
$this->xmlContent->writeAttribute('style:family', 'chart');
// style:graphic-properties
$this->xmlContent->startElement('style:graphic-properties');
$this->xmlContent->writeAttribute('draw:fill', $oFill->getFillType());
- $this->xmlContent->writeAttribute('draw:fill-color', '#'.$oFill->getStartColor()->getRGB());
+ $this->xmlContent->writeAttribute('draw:fill-color', '#' . $oFill->getStartColor()->getRGB());
// > style:graphic-properties
$this->xmlContent->endElement();
// > style:style
@@ -797,9 +808,7 @@ class ObjectsChart extends AbstractDecoratorWriter
}
}
- /**
- */
- private function writeTable()
+ protected function writeTable(): void
{
// table:table
$this->xmlContent->startElement('table:table');
@@ -825,40 +834,6 @@ class ObjectsChart extends AbstractDecoratorWriter
// > table:table-header-columns
$this->xmlContent->endElement();
- // table:table-rows
- $this->xmlContent->startElement('table:table-rows');
- if (empty($this->arrayData)) {
- $this->xmlContent->startElement('table:table-row');
- $this->xmlContent->startElement('table:table-cell');
- $this->xmlContent->endElement();
- $this->xmlContent->endElement();
- } else {
- foreach ($this->arrayData as $row) {
- // table:table-row
- $this->xmlContent->startElement('table:table-row');
- foreach ($row as $cell) {
- // table:table-cell
- $this->xmlContent->startElement('table:table-cell');
-
- $cellNumeric = is_numeric($cell);
- $this->xmlContent->writeAttributeIf(!$cellNumeric, 'office:value-type', 'string');
- $this->xmlContent->writeAttributeIf($cellNumeric, 'office:value-type', 'float');
- $this->xmlContent->writeAttributeIf($cellNumeric, 'office:value', $cell);
- // text:p
- $this->xmlContent->startElement('text:p');
- $this->xmlContent->text($cell);
- // > text:p
- $this->xmlContent->endElement();
- // > table:table-cell
- $this->xmlContent->endElement();
- }
- // > table:table-row
- $this->xmlContent->endElement();
- }
- }
- // > table:table-rows
- $this->xmlContent->endElement();
-
// table:table-header-rows
$this->xmlContent->startElement('table:table-header-rows');
// table:table-row
@@ -890,22 +865,52 @@ class ObjectsChart extends AbstractDecoratorWriter
// > table:table-header-rows
$this->xmlContent->endElement();
+ // table:table-rows
+ $this->xmlContent->startElement('table:table-rows');
+ if (empty($this->arrayData)) {
+ $this->xmlContent->startElement('table:table-row');
+ $this->xmlContent->startElement('table:table-cell');
+ $this->xmlContent->endElement();
+ $this->xmlContent->endElement();
+ } else {
+ foreach ($this->arrayData as $row) {
+ // table:table-row
+ $this->xmlContent->startElement('table:table-row');
+ foreach ($row as $cell) {
+ // table:table-cell
+ $this->xmlContent->startElement('table:table-cell');
+
+ $cellValueTypeFloat = is_null($cell) ? true : is_numeric($cell);
+ $this->xmlContent->writeAttributeIf(!$cellValueTypeFloat, 'office:value-type', 'string');
+ $this->xmlContent->writeAttributeIf($cellValueTypeFloat, 'office:value-type', 'float');
+ $this->xmlContent->writeAttributeIf($cellValueTypeFloat, 'office:value', is_null($cell) ? 'NaN' : $cell);
+ // text:p
+ $this->xmlContent->startElement('text:p');
+ $this->xmlContent->text(is_null($cell) ? 'NaN' : (string) $cell);
+ $this->xmlContent->endElement();
+ // > table:table-cell
+ $this->xmlContent->endElement();
+ }
+ // > table:table-row
+ $this->xmlContent->endElement();
+ }
+ }
+ // > table:table-rows
+ $this->xmlContent->endElement();
+
// > table:table
$this->xmlContent->endElement();
}
- /**
- * @param Title $oTitle
- */
- private function writeTitle(Title $oTitle)
+ protected function writeTitle(Title $oTitle): void
{
if (!$oTitle->isVisible()) {
return;
}
// chart:title
$this->xmlContent->startElement('chart:title');
- $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters($oTitle->getOffsetX()), 3) . 'cm');
- $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters($oTitle->getOffsetY()), 3) . 'cm');
+ $this->xmlContent->writeAttribute('svg:x', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $oTitle->getOffsetX()), 3) . 'cm');
+ $this->xmlContent->writeAttribute('svg:y', Text::numberFormat(CommonDrawing::pixelsToCentimeters((int) $oTitle->getOffsetY()), 3) . 'cm');
$this->xmlContent->writeAttribute('chart:style-name', 'styleTitle');
// > text:p
$this->xmlContent->startElement('text:p');
@@ -915,10 +920,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- /**
- * @param Title $oTitle
- */
- private function writeTitleStyle(Title $oTitle)
+ protected function writeTitleStyle(Title $oTitle): void
{
if (!$oTitle->isVisible()) {
return;
@@ -929,9 +931,9 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->writeAttribute('style:family', 'chart');
// style:text-properties
$this->xmlContent->startElement('style:text-properties');
- $this->xmlContent->writeAttribute('fo:color', '#'.$oTitle->getFont()->getColor()->getRGB());
+ $this->xmlContent->writeAttribute('fo:color', '#' . $oTitle->getFont()->getColor()->getRGB());
$this->xmlContent->writeAttribute('fo:font-family', $oTitle->getFont()->getName());
- $this->xmlContent->writeAttribute('fo:font-size', $oTitle->getFont()->getSize().'pt');
+ $this->xmlContent->writeAttribute('fo:font-size', $oTitle->getFont()->getSize() . 'pt');
$this->xmlContent->writeAttribute('fo:font-style', $oTitle->getFont()->isItalic() ? 'italic' : 'normal');
// > style:text-properties
$this->xmlContent->endElement();
@@ -939,7 +941,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- private function writeWall()
+ protected function writeWall(): void
{
// chart:wall
$this->xmlContent->startElement('chart:wall');
@@ -947,11 +949,7 @@ class ObjectsChart extends AbstractDecoratorWriter
$this->xmlContent->endElement();
}
- /**
- * @param Chart $chart
- * @throws \Exception
- */
- private function writeWallStyle(Chart $chart)
+ protected function writeWallStyle(Chart $chart): void
{
$chartType = $chart->getPlotArea()->getType();
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/Pictures.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/Pictures.php
old mode 100755
new mode 100644
index 6ec4e54..ac8cfa9
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/Pictures.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/Pictures.php
@@ -1,4 +1,22 @@
getDrawingHashTable()->count(); ++$i) {
$shape = $this->getDrawingHashTable()->getByIndex($i);
if (!($shape instanceof Drawing\AbstractDrawingAdapter)) {
@@ -29,10 +45,10 @@ class Pictures extends AbstractDecoratorWriter
// Add background image slide
$oBkgImage = $oSlide->getBackground();
if ($oBkgImage instanceof Image) {
- $this->getZip()->addFromString('Pictures/'.$oBkgImage->getIndexedFilename($keySlide), file_get_contents($oBkgImage->getPath()));
+ $this->getZip()->addFromString('Pictures/' . $oBkgImage->getIndexedFilename((string) $keySlide), file_get_contents($oBkgImage->getPath()));
}
}
-
+
return $this->getZip();
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/Styles.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/Styles.php
old mode 100755
new mode 100644
index 3c7fd72..6bc8cbf
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/Styles.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/Styles.php
@@ -1,49 +1,64 @@
*/
- protected $arrayGradient = array();
+ protected $arrayGradient = [];
/**
- * Stores font styles draw:stroke-dash nodes
+ * Stores font styles draw:stroke-dash nodes.
*
- * @var array
+ * @var array
*/
- protected $arrayStrokeDash = array();
+ protected $arrayStrokeDash = [];
- /**
- * @return \PhpOffice\Common\Adapter\Zip\ZipInterface
- * @throws \Exception
- */
- public function render()
+ public function render(): ZipInterface
{
$this->getZip()->addFromString('styles.xml', $this->writePart());
+
return $this->getZip();
}
/**
- * Write Meta file to XML format
+ * Write Meta file to XML format.
*
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- protected function writePart()
+ protected function writePart(): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -134,8 +149,8 @@ class Styles extends AbstractDecoratorWriter
$objWriter->writeAttribute('fo:margin-bottom', '0cm');
$objWriter->writeAttribute('fo:margin-left', '0cm');
$objWriter->writeAttribute('fo:margin-right', '0cm');
- $objWriter->writeAttribute('fo:page-width', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels($this->getPresentation()->getLayout()->getCX())), 1) . 'cm');
- $objWriter->writeAttribute('fo:page-height', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels($this->getPresentation()->getLayout()->getCY())), 1) . 'cm');
+ $objWriter->writeAttribute('fo:page-width', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels((int) $this->getPresentation()->getLayout()->getCX())), 1) . 'cm');
+ $objWriter->writeAttribute('fo:page-height', Text::numberFormat(CommonDrawing::pixelsToCentimeters(CommonDrawing::emuToPixels((int) $this->getPresentation()->getLayout()->getCY())), 1) . 'cm');
$printOrientation = 'portrait';
if ($this->getPresentation()->getLayout()->getCX() > $this->getPresentation()->getLayout()->getCY()) {
$printOrientation = 'landscape';
@@ -163,24 +178,21 @@ class Styles extends AbstractDecoratorWriter
}
/**
- * Write the default style information for a RichText shape
- *
- * @param XMLWriter $objWriter
- * @param RichText $shape
+ * Write the default style information for a RichText shape.
*/
- protected function writeRichTextStyle(XMLWriter $objWriter, RichText $shape)
+ protected function writeRichTextStyle(XMLWriter $objWriter, RichText $shape): void
{
$oFill = $shape->getFill();
- if ($oFill->getFillType() == Fill::FILL_GRADIENT_LINEAR || $oFill->getFillType() == Fill::FILL_GRADIENT_PATH) {
+ if (Fill::FILL_GRADIENT_LINEAR == $oFill->getFillType() || Fill::FILL_GRADIENT_PATH == $oFill->getFillType()) {
if (!in_array($oFill->getHashCode(), $this->arrayGradient)) {
$this->writeGradientFill($objWriter, $oFill);
}
}
$oBorder = $shape->getBorder();
- if ($oBorder->getDashStyle() != Border::DASH_SOLID) {
+ if (Border::DASH_SOLID != $oBorder->getDashStyle()) {
if (!in_array($oBorder->getDashStyle(), $this->arrayStrokeDash)) {
$objWriter->startElement('draw:stroke-dash');
- $objWriter->writeAttribute('draw:name', 'strokeDash_'.$oBorder->getDashStyle());
+ $objWriter->writeAttribute('draw:name', 'strokeDash_' . $oBorder->getDashStyle());
$objWriter->writeAttribute('draw:style', 'rect');
switch ($oBorder->getDashStyle()) {
case Border::DASH_DASH:
@@ -251,16 +263,13 @@ class Styles extends AbstractDecoratorWriter
}
/**
- * Write the default style information for a Table shape
- *
- * @param XMLWriter $objWriter
- * @param Table $shape
+ * Write the default style information for a Table shape.
*/
- protected function writeTableStyle(XMLWriter $objWriter, Table $shape)
+ protected function writeTableStyle(XMLWriter $objWriter, Table $shape): void
{
foreach ($shape->getRows() as $row) {
foreach ($row->getCells() as $cell) {
- if ($cell->getFill()->getFillType() == Fill::FILL_GRADIENT_LINEAR) {
+ if (Fill::FILL_GRADIENT_LINEAR == $cell->getFill()->getFillType()) {
if (!in_array($cell->getFill()->getHashCode(), $this->arrayGradient)) {
$this->writeGradientFill($objWriter, $cell->getFill());
}
@@ -270,12 +279,9 @@ class Styles extends AbstractDecoratorWriter
}
/**
- * Writes the style information for a group of shapes
- *
- * @param XMLWriter $objWriter
- * @param Group $group
+ * Writes the style information for a group of shapes.
*/
- protected function writeGroupStyle(XMLWriter $objWriter, Group $group)
+ protected function writeGroupStyle(XMLWriter $objWriter, Group $group): void
{
$shapes = $group->getShapeCollection();
foreach ($shapes as $shape) {
@@ -288,20 +294,18 @@ class Styles extends AbstractDecoratorWriter
}
/**
- * Write the gradient style
- * @param XMLWriter $objWriter
- * @param Fill $oFill
+ * Write the gradient style.
*/
- protected function writeGradientFill(XMLWriter $objWriter, Fill $oFill)
+ protected function writeGradientFill(XMLWriter $objWriter, Fill $oFill): void
{
$objWriter->startElement('draw:gradient');
- $objWriter->writeAttribute('draw:name', 'gradient_'.$oFill->getHashCode());
- $objWriter->writeAttribute('draw:display-name', 'gradient_'.$oFill->getHashCode());
+ $objWriter->writeAttribute('draw:name', 'gradient_' . $oFill->getHashCode());
+ $objWriter->writeAttribute('draw:display-name', 'gradient_' . $oFill->getHashCode());
$objWriter->writeAttribute('draw:style', 'linear');
$objWriter->writeAttribute('draw:start-intensity', '100%');
$objWriter->writeAttribute('draw:end-intensity', '100%');
- $objWriter->writeAttribute('draw:start-color', '#'.$oFill->getStartColor()->getRGB());
- $objWriter->writeAttribute('draw:end-color', '#'.$oFill->getEndColor()->getRGB());
+ $objWriter->writeAttribute('draw:start-color', '#' . $oFill->getStartColor()->getRGB());
+ $objWriter->writeAttribute('draw:end-color', '#' . $oFill->getEndColor()->getRGB());
$objWriter->writeAttribute('draw:border', '0%');
$objWriter->writeAttribute('draw:angle', $oFill->getRotation() - 90);
$objWriter->endElement();
@@ -309,16 +313,13 @@ class Styles extends AbstractDecoratorWriter
}
/**
- * Write the background image style
- * @param XMLWriter $objWriter
- * @param Image $oBkgImage
- * @param $numSlide
+ * Write the background image style.
*/
- protected function writeBackgroundStyle(XMLWriter $objWriter, Image $oBkgImage, $numSlide)
+ protected function writeBackgroundStyle(XMLWriter $objWriter, Image $oBkgImage, int $numSlide): void
{
$objWriter->startElement('draw:fill-image');
- $objWriter->writeAttribute('draw:name', 'background_'.$numSlide);
- $objWriter->writeAttribute('xlink:href', 'Pictures/'.str_replace(' ', '_', $oBkgImage->getIndexedFilename($numSlide)));
+ $objWriter->writeAttribute('draw:name', 'background_' . (string) $numSlide);
+ $objWriter->writeAttribute('xlink:href', 'Pictures/' . str_replace(' ', '_', $oBkgImage->getIndexedFilename((string) $numSlide)));
$objWriter->writeAttribute('xlink:type', 'simple');
$objWriter->writeAttribute('xlink:show', 'embed');
$objWriter->writeAttribute('xlink:actuate', 'onLoad');
diff --git a/PhpOffice/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php b/PhpOffice/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php
old mode 100755
new mode 100644
index b481a56..1d75433
--- a/PhpOffice/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php
+++ b/PhpOffice/PhpPresentation/Writer/ODPresentation/ThumbnailsThumbnail.php
@@ -1,14 +1,30 @@
getPresentation()->getPresentationProperties()->getThumbnailPath();
if ($pathThumbnail) {
@@ -37,6 +53,7 @@ class ThumbnailsThumbnail extends AbstractDecoratorWriter
$this->getZip()->addFromString('Thumbnails/thumbnail.png', $imageContents);
}
}
+
return $this->getZip();
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007.php
old mode 100755
new mode 100644
index 2b6e2c2..e98a618
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007.php
@@ -10,63 +10,59 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Writer;
use DirectoryIterator;
use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter;
+use PhpOffice\PhpPresentation\Exception\DirectoryNotFoundException;
+use PhpOffice\PhpPresentation\Exception\FileCopyException;
+use PhpOffice\PhpPresentation\Exception\FileRemoveException;
+use PhpOffice\PhpPresentation\Exception\InvalidParameterException;
use PhpOffice\PhpPresentation\HashTable;
use PhpOffice\PhpPresentation\PhpPresentation;
-use PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack;
-use PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\PackDefault;
+use PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractDecoratorWriter;
+use ReflectionClass;
/**
- * \PhpOffice\PhpPresentation\Writer\PowerPoint2007
+ * \PhpOffice\PhpPresentation\Writer\PowerPoint2007.
*/
class PowerPoint2007 extends AbstractWriter implements WriterInterface
{
/**
* Use disk caching where possible?
*
- * @var boolean
+ * @var bool
*/
protected $useDiskCaching = false;
/**
- * Disk caching directory
+ * Disk caching directory.
*
* @var string
*/
protected $diskCachingDir;
/**
- * Layout pack to use
- * @deprecated 0.7
- * @var \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack
- */
- protected $layoutPack;
-
- /**
- * Create a new PowerPoint2007 file
+ * Create a new PowerPoint2007 file.
*
* @param PhpPresentation $pPhpPresentation
- * @throws \Exception
*/
public function __construct(PhpPresentation $pPhpPresentation = null)
{
// Assign PhpPresentation
- $this->setPhpPresentation($pPhpPresentation);
+ $this->setPhpPresentation($pPhpPresentation ?? new PhpPresentation());
// Set up disk caching location
$this->diskCachingDir = './';
- // Set layout pack
- $this->layoutPack = new PackDefault();
-
// Set HashTable variables
$this->oDrawingHashTable = new HashTable();
@@ -74,23 +70,24 @@ class PowerPoint2007 extends AbstractWriter implements WriterInterface
}
/**
- * Save PhpPresentation to file
+ * Save PhpPresentation to file.
*
- * @param string $pFilename
- * @throws \Exception
+ * @throws FileCopyException
+ * @throws FileRemoveException
+ * @throws InvalidParameterException
*/
- public function save($pFilename)
+ public function save(string $pFilename): void
{
if (empty($pFilename)) {
- throw new \Exception("Filename is empty");
+ throw new InvalidParameterException('pFilename', '');
}
$oPresentation = $this->getPhpPresentation();
// If $pFilename is php://output or php://stdout, make it a temporary file...
$originalFilename = $pFilename;
- if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
+ if ('php://output' == strtolower($pFilename) || 'php://stdout' == strtolower($pFilename)) {
$pFilename = @tempnam('./', 'phppttmp');
- if ($pFilename == '') {
+ if ('' == $pFilename) {
$pFilename = $originalFilename;
}
}
@@ -101,20 +98,20 @@ class PowerPoint2007 extends AbstractWriter implements WriterInterface
$oZip = $this->getZipAdapter();
$oZip->open($pFilename);
- $oDir = new DirectoryIterator(dirname(__FILE__).DIRECTORY_SEPARATOR.'PowerPoint2007');
- $arrayFiles = array();
+ $oDir = new DirectoryIterator(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'PowerPoint2007');
+ $arrayFiles = [];
foreach ($oDir as $oFile) {
if (!$oFile->isFile()) {
continue;
}
$class = __NAMESPACE__ . '\\PowerPoint2007\\' . $oFile->getBasename('.php');
- $o = new \ReflectionClass($class);
+ $class = new ReflectionClass($class);
- if ($o->isAbstract() || !$o->isSubclassOf('PhpOffice\PhpPresentation\Writer\PowerPoint2007\AbstractDecoratorWriter')) {
+ if ($class->isAbstract() || !$class->isSubclassOf(AbstractDecoratorWriter::class)) {
continue;
}
- $arrayFiles[$oFile->getBasename('.php')] = $o;
+ $arrayFiles[$oFile->getBasename('.php')] = $class;
}
ksort($arrayFiles);
@@ -133,11 +130,11 @@ class PowerPoint2007 extends AbstractWriter implements WriterInterface
// If a temporary file was used, copy it to the correct file stream
if ($originalFilename != $pFilename) {
- if (copy($pFilename, $originalFilename) === false) {
- throw new \Exception("Could not copy temporary zip file $pFilename to $originalFilename.");
+ if (false === copy($pFilename, $originalFilename)) {
+ throw new FileCopyException($pFilename, $originalFilename);
}
- if (@unlink($pFilename) === false) {
- throw new \Exception('The file '.$pFilename.' could not be removed.');
+ if (false === @unlink($pFilename)) {
+ throw new FileRemoveException($pFilename);
}
}
}
@@ -145,7 +142,7 @@ class PowerPoint2007 extends AbstractWriter implements WriterInterface
/**
* Get use disk caching where possible?
*
- * @return boolean
+ * @return bool
*/
public function hasDiskCaching()
{
@@ -155,27 +152,29 @@ class PowerPoint2007 extends AbstractWriter implements WriterInterface
/**
* Set use disk caching where possible?
*
- * @param boolean $pValue
- * @param string $pDirectory Disk caching directory
- * @throws \Exception
+ * @param bool $useDiskCaching
+ * @param string $directory Disk caching directory
+ *
+ * @throws DirectoryNotFoundException
+ *
* @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007
*/
- public function setUseDiskCaching($pValue = false, $pDirectory = null)
+ public function setUseDiskCaching(bool $useDiskCaching = false, string $directory = null)
{
- $this->useDiskCaching = $pValue;
+ $this->useDiskCaching = $useDiskCaching;
- if (!is_null($pDirectory)) {
- if (!is_dir($pDirectory)) {
- throw new \Exception("Directory does not exist: $pDirectory");
+ if (!is_null($directory)) {
+ if (!is_dir($directory)) {
+ throw new DirectoryNotFoundException($directory);
}
- $this->diskCachingDir = $pDirectory;
+ $this->diskCachingDir = $directory;
}
return $this;
}
/**
- * Get disk caching directory
+ * Get disk caching directory.
*
* @return string
*/
@@ -183,29 +182,4 @@ class PowerPoint2007 extends AbstractWriter implements WriterInterface
{
return $this->diskCachingDir;
}
-
- /**
- * Get layout pack to use
- *
- * @deprecated 0.7
- * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack
- */
- public function getLayoutPack()
- {
- return $this->layoutPack;
- }
-
- /**
- * Set layout pack to use
- *
- * @deprecated 0.7
- * @param \PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\AbstractLayoutPack $pValue
- * @return \PhpOffice\PhpPresentation\Writer\PowerPoint2007
- */
- public function setLayoutPack(AbstractLayoutPack $pValue = null)
- {
- $this->layoutPack = $pValue;
-
- return $this;
- }
}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php
old mode 100755
new mode 100644
index c8d0b7a..362d941
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractDecoratorWriter.php
@@ -1,4 +1,22 @@
startElement('Relationship');
- $objWriter->writeAttribute('Id', $pId);
+ $objWriter->writeAttribute('Id', 'rId' . (string) $pId);
$objWriter->writeAttribute('Type', $pType);
$objWriter->writeAttribute('Target', $pTarget);
- if ($pTargetMode != '') {
+ if ('' != $pTargetMode) {
$objWriter->writeAttribute('TargetMode', $pTargetMode);
}
@@ -44,26 +54,26 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
}
/**
- * Write Border
+ * Write Border.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Style\Border $pBorder Border
- * @param string $pElementName Element name
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Border $pBorder Border
+ * @param string $pElementName Element name
+ * @param bool $isMarker
*/
- protected function writeBorder(XMLWriter $objWriter, $pBorder, $pElementName = 'L')
+ protected function writeBorder(XMLWriter $objWriter, Border $pBorder, string $pElementName = 'L', bool $isMarker = false): void
{
if (!($pBorder instanceof Border)) {
return;
}
- if ($pBorder->getLineStyle() == Border::LINE_NONE && $pElementName == '') {
+ if (Border::LINE_NONE == $pBorder->getLineStyle() && '' == $pElementName && !$isMarker) {
return;
}
// Line style
$lineStyle = $pBorder->getLineStyle();
- if ($lineStyle == Border::LINE_NONE) {
+ if (Border::LINE_NONE == $lineStyle) {
$lineStyle = Border::LINE_SINGLE;
}
@@ -78,7 +88,7 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
$objWriter->writeAttribute('algn', 'ctr');
// Fill?
- if ($pBorder->getLineStyle() == Border::LINE_NONE) {
+ if (Border::LINE_NONE == $pBorder->getLineStyle()) {
// a:noFill
$objWriter->writeElement('a:noFill', null);
} else {
@@ -113,12 +123,7 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
$objWriter->endElement();
}
- /**
- * @param XMLWriter $objWriter
- * @param Color $color
- * @param int|null $alpha
- */
- protected function writeColor(XMLWriter $objWriter, Color $color, $alpha = null)
+ protected function writeColor(XMLWriter $objWriter, Color $color, ?int $alpha = null): void
{
if (is_null($alpha)) {
$alpha = $color->getAlpha();
@@ -137,48 +142,49 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
}
/**
- * Write Fill
+ * Write Fill.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Fill|null $pFill Fill style
*/
- protected function writeFill(XMLWriter $objWriter, $pFill)
+ protected function writeFill(XMLWriter $objWriter, ?Fill $pFill): void
{
- if (! $pFill instanceof Fill) {
+ if (!$pFill) {
return;
}
// Is it a fill?
- if ($pFill->getFillType() == Fill::FILL_NONE) {
+ if (Fill::FILL_NONE == $pFill->getFillType()) {
$objWriter->writeElement('a:noFill');
+
return;
}
// Is it a solid fill?
- if ($pFill->getFillType() == Fill::FILL_SOLID) {
+ if (Fill::FILL_SOLID == $pFill->getFillType()) {
$this->writeSolidFill($objWriter, $pFill);
+
return;
}
- // Check if this is a pattern type or gradient type
- if ($pFill->getFillType() == Fill::FILL_GRADIENT_LINEAR || $pFill->getFillType() == Fill::FILL_GRADIENT_PATH) {
- // Gradient fill
+ // Is it a gradient fill?
+ if (Fill::FILL_GRADIENT_LINEAR == $pFill->getFillType() || Fill::FILL_GRADIENT_PATH == $pFill->getFillType()) {
$this->writeGradientFill($objWriter, $pFill);
- } else {
- // Pattern fill
- $this->writePatternFill($objWriter, $pFill);
+
+ return;
}
+
+ // Is it a pattern fill?
+ $this->writePatternFill($objWriter, $pFill);
}
/**
- * Write Solid Fill
+ * Write Solid Fill.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Fill $pFill Fill style
*/
- protected function writeSolidFill(XMLWriter $objWriter, Fill $pFill)
+ protected function writeSolidFill(XMLWriter $objWriter, Fill $pFill): void
{
// a:gradFill
$objWriter->startElement('a:solidFill');
@@ -187,13 +193,12 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
}
/**
- * Write Gradient Fill
+ * Write Gradient Fill.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Fill $pFill Fill style
*/
- protected function writeGradientFill(XMLWriter $objWriter, Fill $pFill)
+ protected function writeGradientFill(XMLWriter $objWriter, Fill $pFill): void
{
// a:gradFill
$objWriter->startElement('a:gradFill');
@@ -216,7 +221,7 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
// a:lin
$objWriter->startElement('a:lin');
- $objWriter->writeAttribute('ang', CommonDrawing::degreesToAngle($pFill->getRotation()));
+ $objWriter->writeAttribute('ang', CommonDrawing::degreesToAngle((int) $pFill->getRotation()));
$objWriter->writeAttribute('scaled', '0');
$objWriter->endElement();
@@ -224,13 +229,12 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
}
/**
- * Write Pattern Fill
+ * Write Pattern Fill.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Style\Fill $pFill Fill style
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Fill $pFill Fill style
*/
- protected function writePatternFill(XMLWriter $objWriter, Fill $pFill)
+ protected function writePatternFill(XMLWriter $objWriter, Fill $pFill): void
{
// a:pattFill
$objWriter->startElement('a:pattFill');
@@ -254,21 +258,14 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
/**
* Write Outline
- * @param XMLWriter $objWriter
- * @param Outline $oOutline
- * @throws \Exception
*/
- protected function writeOutline(XMLWriter $objWriter, $oOutline)
+ protected function writeOutline(XMLWriter $objWriter, ?Outline $oOutline): void
{
- if (!$oOutline instanceof Outline) {
+ if (!$oOutline) {
return;
}
// Width : pts
- $width = $oOutline->getWidth();
- // Width : pts => px
- $width = CommonDrawing::pointsToPixels($width);
- // Width : px => emu
- $width = CommonDrawing::pixelsToEmu($width);
+ $width = CommonDrawing::pointsToEmu($oOutline->getWidth());
// a:ln
$objWriter->startElement('a:ln');
@@ -282,19 +279,18 @@ abstract class AbstractDecoratorWriter extends \PhpOffice\PhpPresentation\Writer
}
/**
- * Determine absolute zip path
- *
- * @param string $path
- * @return string
+ * Determine absolute zip path.
*/
- protected function absoluteZipPath($path)
+ protected function absoluteZipPath(string $path): string
{
- $path = str_replace(array(
+ $path = str_replace([
'/',
- '\\'
- ), DIRECTORY_SEPARATOR, $path);
- $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
- $absolutes = array();
+ '\\',
+ ], DIRECTORY_SEPARATOR, $path);
+ $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), function (string $var) {
+ return (bool) strlen($var);
+ });
+ $absolutes = [];
foreach ($parts as $part) {
if ('.' == $part) {
continue;
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php
old mode 100755
new mode 100644
index 0c220f0..c310379
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php
@@ -10,48 +10,54 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;
+use ArrayObject;
use PhpOffice\Common\Drawing as CommonDrawing;
use PhpOffice\Common\Text;
use PhpOffice\Common\XMLWriter;
+use PhpOffice\PhpPresentation\AbstractShape;
+use PhpOffice\PhpPresentation\Exception\UndefinedChartTypeException;
use PhpOffice\PhpPresentation\Shape\AbstractGraphic;
+use PhpOffice\PhpPresentation\Shape\AutoShape;
use PhpOffice\PhpPresentation\Shape\Chart as ShapeChart;
use PhpOffice\PhpPresentation\Shape\Comment;
-use PhpOffice\PhpPresentation\Shape\Drawing\Gd as ShapeDrawingGd;
+use PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter;
use PhpOffice\PhpPresentation\Shape\Drawing\File as ShapeDrawingFile;
+use PhpOffice\PhpPresentation\Shape\Drawing\Gd as ShapeDrawingGd;
use PhpOffice\PhpPresentation\Shape\Group;
use PhpOffice\PhpPresentation\Shape\Line;
use PhpOffice\PhpPresentation\Shape\Media;
use PhpOffice\PhpPresentation\Shape\Placeholder;
use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Shape\RichText\BreakElement;
+use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
use PhpOffice\PhpPresentation\Shape\RichText\Run;
use PhpOffice\PhpPresentation\Shape\RichText\TextElement;
use PhpOffice\PhpPresentation\Shape\Table as ShapeTable;
use PhpOffice\PhpPresentation\Slide;
+use PhpOffice\PhpPresentation\Slide\AbstractSlide as AbstractSlideAlias;
use PhpOffice\PhpPresentation\Slide\Note;
use PhpOffice\PhpPresentation\Style\Alignment;
-use PhpOffice\PhpPresentation\Style\Bullet;
use PhpOffice\PhpPresentation\Style\Border;
+use PhpOffice\PhpPresentation\Style\Bullet;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Shadow;
-use PhpOffice\PhpPresentation\Slide\AbstractSlide as AbstractSlideAlias;
abstract class AbstractSlide extends AbstractDecoratorWriter
{
/**
- * @param AbstractSlideAlias $pSlideMaster
- * @param $objWriter
- * @param $relId
* @return mixed
- * @throws \Exception
*/
- protected function writeDrawingRelations(AbstractSlideAlias $pSlideMaster, $objWriter, $relId)
+ protected function writeDrawingRelations(AbstractSlideAlias $pSlideMaster, XMLWriter $objWriter, int $relId)
{
if ($pSlideMaster->getShapeCollection()->count() > 0) {
// Loop trough images and write relationships
@@ -114,14 +120,14 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * @param XMLWriter $objWriter
- * @param \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[] $shapes
+ * @param array|ArrayObject $shapes
* @param int $shapeId
- * @throws \Exception
+ *
+ * @throws UndefinedChartTypeException
*/
- protected function writeShapeCollection(XMLWriter $objWriter, $shapes = array(), &$shapeId = 0)
+ protected function writeShapeCollection(XMLWriter $objWriter, $shapes = [], &$shapeId = 0): void
{
- if (count($shapes) == 0) {
+ if (0 == count($shapes)) {
return;
}
foreach ($shapes as $shape) {
@@ -138,24 +144,25 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$this->writeShapeChart($objWriter, $shape, $shapeId);
} elseif ($shape instanceof AbstractGraphic) {
$this->writeShapePic($objWriter, $shape, $shapeId);
+ } elseif ($shape instanceof AutoShape) {
+ $this->writeShapeAutoShape($objWriter, $shape, $shapeId);
} elseif ($shape instanceof Group) {
$this->writeShapeGroup($objWriter, $shape, $shapeId);
} elseif ($shape instanceof Comment) {
} else {
- throw new \Exception("Unknown Shape type: {get_class($shape)}");
+ throw new UndefinedChartTypeException();
}
}
}
/**
- * Write txt
+ * Write txt.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\RichText $shape
- * @param int $shapeId
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param RichText $shape
+ * @param int $shapeId
*/
- protected function writeShapeText(XMLWriter $objWriter, RichText $shape, $shapeId)
+ protected function writeShapeText(XMLWriter $objWriter, RichText $shape, int $shapeId): void
{
// p:sp
$objWriter->startElement('p:sp');
@@ -200,7 +207,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
if (!$shape->isPlaceholder()) {
// p:sp\p:spPr\a:xfrm
$objWriter->startElement('a:xfrm');
- $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation()));
+ $objWriter->writeAttributeIf(0 != $shape->getRotation(), 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
// p:sp\p:spPr\a:xfrm\a:off
$objWriter->startElement('a:off');
$objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
@@ -235,17 +242,17 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->startElement('a:bodyPr');
if (!$shape->isPlaceholder()) {
$verticalAlign = $shape->getActiveParagraph()->getAlignment()->getVertical();
- if ($verticalAlign != Alignment::VERTICAL_BASE && $verticalAlign != Alignment::VERTICAL_AUTO) {
+ if (Alignment::VERTICAL_BASE != $verticalAlign && Alignment::VERTICAL_AUTO != $verticalAlign) {
$objWriter->writeAttribute('anchor', $verticalAlign);
}
- if ($shape->getWrap() != RichText::WRAP_SQUARE) {
+ if (RichText::WRAP_SQUARE != $shape->getWrap()) {
$objWriter->writeAttribute('wrap', $shape->getWrap());
}
$objWriter->writeAttribute('rtlCol', '0');
- if ($shape->getHorizontalOverflow() != RichText::OVERFLOW_OVERFLOW) {
+ if (RichText::OVERFLOW_OVERFLOW != $shape->getHorizontalOverflow()) {
$objWriter->writeAttribute('horzOverflow', $shape->getHorizontalOverflow());
}
- if ($shape->getVerticalOverflow() != RichText::OVERFLOW_OVERFLOW) {
+ if (RichText::OVERFLOW_OVERFLOW != $shape->getVerticalOverflow()) {
$objWriter->writeAttribute('vertOverflow', $shape->getVerticalOverflow());
}
if ($shape->isUpright()) {
@@ -258,12 +265,13 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->writeAttribute('lIns', CommonDrawing::pixelsToEmu($shape->getInsetLeft()));
$objWriter->writeAttribute('rIns', CommonDrawing::pixelsToEmu($shape->getInsetRight()));
$objWriter->writeAttribute('tIns', CommonDrawing::pixelsToEmu($shape->getInsetTop()));
- if ($shape->getColumns() <> 1) {
+ if (1 != $shape->getColumns()) {
$objWriter->writeAttribute('numCol', $shape->getColumns());
+ $objWriter->writeAttribute('spcCol', CommonDrawing::pixelsToEmu($shape->getColumnSpacing()));
}
// a:spAutoFit
$objWriter->startElement('a:' . $shape->getAutoFit());
- if ($shape->getAutoFit() == RichText::AUTOFIT_NORMAL) {
+ if (RichText::AUTOFIT_NORMAL == $shape->getAutoFit()) {
if (!is_null($shape->getFontScale())) {
$objWriter->writeAttribute('fontScale', $shape->getFontScale() * 1000);
}
@@ -277,16 +285,18 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
// a:lstStyle
$objWriter->writeElement('a:lstStyle', null);
if ($shape->isPlaceholder() &&
- ($shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_SLIDENUM ||
- $shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_DATETIME)
+ (Placeholder::PH_TYPE_SLIDENUM == $shape->getPlaceholder()->getType() ||
+ Placeholder::PH_TYPE_DATETIME == $shape->getPlaceholder()->getType())
) {
$objWriter->startElement('a:p');
$objWriter->startElement('a:fld');
$objWriter->writeAttribute('id', $this->getGUID());
$objWriter->writeAttribute('type', (
- $shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_SLIDENUM ? 'slidenum' : 'datetime'));
+ Placeholder::PH_TYPE_SLIDENUM == $shape->getPlaceholder()->getType() ? 'slidenum' : 'datetime'
+ ));
$objWriter->writeElement('a:t', (
- $shape->getPlaceholder()->getType() == Placeholder::PH_TYPE_SLIDENUM ? '' : '03-04-05'));
+ Placeholder::PH_TYPE_SLIDENUM == $shape->getPlaceholder()->getType() ? '' : '03-04-05'
+ ));
$objWriter->endElement();
$objWriter->endElement();
} else {
@@ -298,14 +308,13 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * Write table
+ * Write table.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Table $shape
- * @param int $shapeId
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param ShapeTable $shape
+ * @param int $shapeId
*/
- protected function writeShapeTable(XMLWriter $objWriter, ShapeTable $shape, $shapeId)
+ protected function writeShapeTable(XMLWriter $objWriter, ShapeTable $shape, int $shapeId): void
{
// p:graphicFrame
$objWriter->startElement('p:graphicFrame');
@@ -365,12 +374,12 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->startElement('a:tblGrid');
// Write cell widths
$countCells = count($shape->getRow(0)->getCells());
- for ($cell = 0; $cell < $countCells; $cell++) {
+ for ($cell = 0; $cell < $countCells; ++$cell) {
// p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tblGrid/a:gridCol
$objWriter->startElement('a:gridCol');
// Calculate column width
$width = $shape->getRow(0)->getCell($cell)->getWidth();
- if ($width == 0) {
+ if (0 == $width) {
$colCount = count($shape->getRow(0)->getCells());
$totalWidth = $shape->getWidth();
$width = $totalWidth / $colCount;
@@ -381,29 +390,24 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
// p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tblGrid/
$objWriter->endElement();
// Colspan / rowspan containers
- $colSpan = array();
- $rowSpan = array();
+ $colSpan = $rowSpan = [];
// Default border style
$defaultBorder = new Border();
// Write rows
$countRows = count($shape->getRows());
- for ($row = 0; $row < $countRows; $row++) {
+ for ($row = 0; $row < $countRows; ++$row) {
// p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr
$objWriter->startElement('a:tr');
$objWriter->writeAttribute('h', CommonDrawing::pixelsToEmu($shape->getRow($row)->getHeight()));
// Write cells
$countCells = count($shape->getRow($row)->getCells());
- for ($cell = 0; $cell < $countCells; $cell++) {
+ for ($cell = 0; $cell < $countCells; ++$cell) {
// Current cell
$currentCell = $shape->getRow($row)->getCell($cell);
// Next cell right
- $nextCellRight = $shape->getRow($row)->getCell($cell + 1, true);
+ $hasNextCellRight = $shape->getRow($row)->hasCell($cell + 1);
// Next cell below
- $nextRowBelow = $shape->getRow($row + 1, true);
- $nextCellBelow = null;
- if ($nextRowBelow != null) {
- $nextCellBelow = $nextRowBelow->getCell($cell, true);
- }
+ $hasNextRowBelow = $shape->hasRow($row + 1);
// a:tc
$objWriter->startElement('a:tc');
// Colspan
@@ -411,7 +415,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->writeAttribute('gridSpan', $currentCell->getColSpan());
$colSpan[$row] = $currentCell->getColSpan() - 1;
} elseif (isset($colSpan[$row]) && $colSpan[$row] > 0) {
- $colSpan[$row]--;
+ --$colSpan[$row];
$objWriter->writeAttribute('hMerge', '1');
}
// Rowspan
@@ -419,7 +423,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->writeAttribute('rowSpan', $currentCell->getRowSpan());
$rowSpan[$cell] = $currentCell->getRowSpan() - 1;
} elseif (isset($rowSpan[$cell]) && $rowSpan[$cell] > 0) {
- $rowSpan[$cell]--;
+ --$rowSpan[$cell];
$objWriter->writeAttribute('vMerge', '1');
}
// a:txBody
@@ -444,12 +448,12 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
// Text Direction
$textDirection = $firstParagraphAlignment->getTextDirection();
- if ($textDirection != Alignment::TEXT_DIRECTION_HORIZONTAL) {
+ if (Alignment::TEXT_DIRECTION_HORIZONTAL != $textDirection) {
$objWriter->writeAttribute('vert', $textDirection);
}
// Alignment (horizontal)
$verticalAlign = $firstParagraphAlignment->getVertical();
- if ($verticalAlign != Alignment::VERTICAL_BASE && $verticalAlign != Alignment::VERTICAL_AUTO) {
+ if (Alignment::VERTICAL_BASE != $verticalAlign && Alignment::VERTICAL_AUTO != $verticalAlign) {
$objWriter->writeAttribute('anchor', $verticalAlign);
}
@@ -467,15 +471,18 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$borderDiagonalDown = $currentCell->getBorders()->getDiagonalDown();
$borderDiagonalUp = $currentCell->getBorders()->getDiagonalUp();
// Fix PowerPoint implementation
- if (!is_null($nextCellRight)
- && $nextCellRight->getBorders()->getRight()->getHashCode() != $defaultBorder->getHashCode()
- ) {
- $borderRight = $nextCellRight->getBorders()->getLeft();
+ if ($hasNextCellRight) {
+ $nextCellRight = $shape->getRow($row)->getCell($cell + 1);
+ if ($nextCellRight->getBorders()->getRight()->getHashCode() != $defaultBorder->getHashCode()) {
+ $borderRight = $nextCellRight->getBorders()->getLeft();
+ }
}
- if (!is_null($nextCellBelow)
- && $nextCellBelow->getBorders()->getBottom()->getHashCode() != $defaultBorder->getHashCode()
- ) {
- $borderBottom = $nextCellBelow->getBorders()->getTop();
+ if ($hasNextRowBelow) {
+ $nextRowBelow = $shape->getRow($row + 1);
+ $nextCellBelow = $nextRowBelow->getCell($cell);
+ if ($nextCellBelow->getBorders()->getBottom()->getHashCode() != $defaultBorder->getHashCode()) {
+ $borderBottom = $nextCellBelow->getBorders()->getTop();
+ }
}
// Write borders
$this->writeBorder($objWriter, $borderLeft, 'L');
@@ -498,14 +505,12 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * Write paragraphs
+ * Write paragraphs.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\RichText\Paragraph[] $paragraphs
- * @param bool $bIsPlaceholder
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param array $paragraphs
*/
- protected function writeParagraphs(XMLWriter $objWriter, $paragraphs, $bIsPlaceholder = false)
+ protected function writeParagraphs(XMLWriter $objWriter, array $paragraphs, bool $bIsPlaceholder = false): void
{
// Loop trough paragraphs
foreach ($paragraphs as $paragraph) {
@@ -514,22 +519,44 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
// a:pPr
if (!$bIsPlaceholder) {
+ // a:pPr
$objWriter->startElement('a:pPr');
$objWriter->writeAttribute('algn', $paragraph->getAlignment()->getHorizontal());
+ $objWriter->writeAttribute('rtl', $paragraph->getAlignment()->isRTL() ? '1' : '0');
$objWriter->writeAttribute('fontAlgn', $paragraph->getAlignment()->getVertical());
$objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginLeft()));
$objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginRight()));
$objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getIndent()));
$objWriter->writeAttribute('lvl', $paragraph->getAlignment()->getLevel());
+ // a:pPr:a:lnSpc
$objWriter->startElement('a:lnSpc');
- $objWriter->startElement('a:spcPct');
- $objWriter->writeAttribute('val', $paragraph->getLineSpacing() * 1000);
+ if ($paragraph->getLineSpacingMode() == Paragraph::LINE_SPACING_MODE_POINT) {
+ $objWriter->startElement('a:spcPts');
+ $objWriter->writeAttribute('val', $paragraph->getLineSpacing() * 100);
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('a:spcPct');
+ $objWriter->writeAttribute('val', $paragraph->getLineSpacing() * 1000);
+ $objWriter->endElement();
+ }
+ // >a:pPr:a:lnSpc
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:spcBef');
+ $objWriter->startElement('a:spcPts');
+ $objWriter->writeAttribute('val', $paragraph->getSpacingBefore() * 100);
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:spcAft');
+ $objWriter->startElement('a:spcPts');
+ $objWriter->writeAttribute('val', $paragraph->getSpacingAfter() * 100);
$objWriter->endElement();
$objWriter->endElement();
// Bullet type specified?
- if ($paragraph->getBulletStyle()->getBulletType() != Bullet::TYPE_NONE) {
+ if (Bullet::TYPE_NONE != $paragraph->getBulletStyle()->getBulletType()) {
// Color
// a:buClr must be before a:buFont (else PowerPoint crashes at launch)
if ($paragraph->getBulletStyle()->getBulletColor() instanceof Color) {
@@ -543,16 +570,16 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->writeAttribute('typeface', $paragraph->getBulletStyle()->getBulletFont());
$objWriter->endElement();
- if ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_BULLET) {
+ if (Bullet::TYPE_BULLET == $paragraph->getBulletStyle()->getBulletType()) {
// a:buChar
$objWriter->startElement('a:buChar');
$objWriter->writeAttribute('char', $paragraph->getBulletStyle()->getBulletChar());
$objWriter->endElement();
- } elseif ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_NUMERIC) {
+ } elseif (Bullet::TYPE_NUMERIC == $paragraph->getBulletStyle()->getBulletType()) {
// a:buAutoNum
$objWriter->startElement('a:buAutoNum');
$objWriter->writeAttribute('type', $paragraph->getBulletStyle()->getBulletNumericStyle());
- if ($paragraph->getBulletStyle()->getBulletNumericStartAt() != 1) {
+ if (1 != $paragraph->getBulletStyle()->getBulletNumericStartAt()) {
$objWriter->writeAttribute('startAt', $paragraph->getBulletStyle()->getBulletNumericStartAt());
}
$objWriter->endElement();
@@ -593,8 +620,11 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$this->writeColor($objWriter, $element->getFont()->getColor());
$objWriter->endElement();
- // Font - a:latin
- $objWriter->startElement('a:latin');
+ // Font
+ // - a:latin
+ // - a:ea
+ // - a:cs
+ $objWriter->startElement('a:' . $element->getFont()->getFormat());
$objWriter->writeAttribute('typeface', $element->getFont()->getName());
$objWriter->endElement();
@@ -618,14 +648,11 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * Write Line Shape
+ * Write Line Shape.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Line $shape
- * @param int $shapeId
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
*/
- protected function writeShapeLine(XMLWriter $objWriter, Line $shape, $shapeId)
+ protected function writeShapeLine(XMLWriter $objWriter, Line $shape, int $shapeId): void
{
// p:sp
$objWriter->startElement('p:cxnSp');
@@ -713,11 +740,9 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * Write Shadow
- * @param XMLWriter $objWriter
- * @param Shadow $oShadow
+ * Write Shadow.
*/
- protected function writeShadow(XMLWriter $objWriter, $oShadow)
+ protected function writeShadow(XMLWriter $objWriter, Shadow $oShadow): void
{
if (!($oShadow instanceof Shadow)) {
return;
@@ -734,7 +759,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($oShadow->getBlurRadius()));
$objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($oShadow->getDistance()));
- $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle($oShadow->getDirection()));
+ $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle((int) $oShadow->getDirection()));
$objWriter->writeAttribute('algn', $oShadow->getAlignment());
$objWriter->writeAttribute('rotWithShape', '0');
@@ -746,13 +771,12 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * Write hyperlink
+ * Write hyperlink.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\AbstractShape|\PhpOffice\PhpPresentation\Shape\RichText\TextElement $shape
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param AbstractShape|TextElement $shape
*/
- protected function writeHyperlink(XMLWriter $objWriter, $shape)
+ protected function writeHyperlink(XMLWriter $objWriter, $shape): void
{
if (!$shape->hasHyperlink()) {
return;
@@ -764,16 +788,26 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
if ($shape->getHyperlink()->isInternal()) {
$objWriter->writeAttribute('action', $shape->getHyperlink()->getUrl());
}
+
+ if ($shape->getHyperlink()->isTextColorUsed()) {
+ $objWriter->startElement('a:extLst');
+ $objWriter->startElement('a:ext');
+ $objWriter->writeAttribute('uri', '{A12FA001-AC4F-418D-AE19-62706E023703}');
+ $objWriter->startElement('ahyp:hlinkClr');
+ $objWriter->writeAttribute('xmlns:ahyp', 'http://schemas.microsoft.com/office/drawing/2018/hyperlinkcolor');
+ $objWriter->writeAttribute('val', 'tx');
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
$objWriter->endElement();
}
/**
- * Write Note Slide
- * @param Note $pNote
- * @throws \Exception
- * @return string
+ * Write Note Slide.
*/
- protected function writeNote(Note $pNote)
+ protected function writeNote(Note $pNote): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -1054,14 +1088,109 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
return $objWriter->getData();
}
+ /**
+ * Write AutoShape
+ *
+ * @param XMLWriter $objWriter XML Writer
+ * @param AutoShape $shape
+ * @param int $shapeId
+ */
+ protected function writeShapeAutoShape(XMLWriter $objWriter, AutoShape $shape, int $shapeId): void
+ {
+ // p:sp
+ $objWriter->startElement('p:sp');
+
+ // p:sp\p:nvSpPr
+ $objWriter->startElement('p:nvSpPr');
+ // p:sp\p:nvSpPr\p:cNvPr
+ $objWriter->startElement('p:cNvPr');
+ $objWriter->writeAttribute('id', $shapeId);
+ $objWriter->writeAttribute('name', '');
+ $objWriter->writeAttribute('descr', '');
+ // p:sp\p:nvSpPr\p:cNvPr\
+ $objWriter->endElement();
+ // p:sp\p:nvSpPr\p:cNvSpPr
+ $objWriter->writeElement('p:cNvSpPr');
+ // p:sp\p:nvSpPr\p:nvPr
+ $objWriter->writeElement('p:nvPr');
+ // p:sp\p:nvSpPr\
+ $objWriter->endElement();
+
+ // p:sp\p:spPr
+ $objWriter->startElement('p:spPr');
+
+ // p:sp\p:spPr\a:xfrm
+ $objWriter->startElement('a:xfrm');
+ $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
+ // p:sp\p:spPr\a:xfrm\a:off
+ $objWriter->startElement('a:off');
+ $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
+ $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
+ $objWriter->endElement();
+ // p:sp\p:spPr\a:xfrm\a:ext
+ $objWriter->startElement('a:ext');
+ $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
+ $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
+ $objWriter->endElement();
+ // p:sp\p:spPr\a:xfrm\
+ $objWriter->endElement();
+
+ // p:sp\p:spPr\a:prstGeom
+ $objWriter->startElement('a:prstGeom');
+ $objWriter->writeAttribute('prst', $shape->getType());
+ // p:sp\p:spPr\a:prstGeom\a:avLst
+ $objWriter->writeElement('a:avLst');
+ // p:sp\p:spPr\a:prstGeom\
+ $objWriter->endElement();
+ // Fill
+ $this->writeFill($objWriter, $shape->getFill());
+ // Outline
+ $this->writeOutline($objWriter, $shape->getOutline());
+
+ // p:sp\p:spPr\
+ $objWriter->endElement();
+ // p:sp\p:txBody
+ $objWriter->startElement('p:txBody');
+ // p:sp\p:txBody\a:bodyPr
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->writeAttribute('vertOverflow', 'clip');
+ $objWriter->writeAttribute('rtlCol', '0');
+ $objWriter->writeAttribute('anchor', 'ctr');
+ // p:sp\p:txBody\a:bodyPr\
+ $objWriter->endElement();
+
+ // p:sp\p:txBody\a:lstStyle
+ $objWriter->writeElement('a:lstStyle');
+
+ // p:sp\p:txBody\a:p
+ $objWriter->startElement('a:p');
+
+ // p:sp\p:txBody\a:p\a:pPr
+ $objWriter->writeElementBlock('a:pPr', [
+ 'algn' => 'ctr',
+ ]);
+ // p:sp\p:txBody\a:p\a:r
+ $objWriter->startElement('a:r');
+ // p:sp\p:txBody\a:p\a:r\a:t
+ $objWriter->startElement('a:t');
+ $objWriter->writeCData(Text::controlCharacterPHP2OOXML($shape->getText()));
+ $objWriter->endElement();
+ // p:sp\p:txBody\a:p\a:r\
+ $objWriter->endElement();
+ // p:sp\p:txBody\a:p\
+ $objWriter->endElement();
+ // p:sp\p:txBody\
+ $objWriter->endElement();
+ // p:sp\
+ $objWriter->endElement();
+ }
+
/**
* Write chart
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart $shape
- * @param int $shapeId
+ * @param XMLWriter $objWriter XML Writer
*/
- protected function writeShapeChart(XMLWriter $objWriter, ShapeChart $shape, $shapeId)
+ protected function writeShapeChart(XMLWriter $objWriter, ShapeChart $shape, int $shapeId): void
{
// p:graphicFrame
$objWriter->startElement('p:graphicFrame');
@@ -1086,7 +1215,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->endElement();
// p:xfrm
$objWriter->startElement('p:xfrm');
- $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation()));
+ $objWriter->writeAttributeIf(0 != $shape->getRotation(), 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
// a:off
$objWriter->startElement('a:off');
$objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
@@ -1115,39 +1244,63 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
}
/**
- * Write pic
+ * Write pic.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\AbstractGraphic $shape
- * @param int $shapeId
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
*/
- protected function writeShapePic(XMLWriter $objWriter, AbstractGraphic $shape, $shapeId)
+ protected function writeShapePic(XMLWriter $objWriter, AbstractGraphic $shape, int $shapeId): void
{
// p:pic
$objWriter->startElement('p:pic');
+
// p:nvPicPr
$objWriter->startElement('p:nvPicPr');
+
// p:cNvPr
$objWriter->startElement('p:cNvPr');
$objWriter->writeAttribute('id', $shapeId);
$objWriter->writeAttribute('name', $shape->getName());
$objWriter->writeAttribute('descr', $shape->getDescription());
+
// a:hlinkClick
if ($shape->hasHyperlink()) {
$this->writeHyperlink($objWriter, $shape);
}
+
+ if ($shape instanceof AbstractDrawingAdapter && $shape->getExtension() == 'svg') {
+ $objWriter->startElement('a:extLst');
+ $objWriter->startElement('a:ext');
+ $objWriter->writeAttribute('uri', '{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}');
+ $objWriter->startElement('a16:creationId');
+ $objWriter->writeAttribute('xmlns:a16', 'http://schemas.microsoft.com/office/drawing/2014/main');
+ $objWriter->writeAttribute('id', '{F8CFD691-5332-EB49-9B42-7D7B3DB9185D}');
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
$objWriter->endElement();
+
// p:cNvPicPr
$objWriter->startElement('p:cNvPicPr');
+
// a:picLocks
$objWriter->startElement('a:picLocks');
$objWriter->writeAttribute('noChangeAspect', '1');
$objWriter->endElement();
+
+ // #p:cNvPicPr
$objWriter->endElement();
+
// p:nvPr
$objWriter->startElement('p:nvPr');
- /**
+ // PlaceHolder
+ if ($shape->isPlaceholder()) {
+ $objWriter->startElement('p:ph');
+ $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
+ $objWriter->endElement();
+ }
+ /*
* @link : https://github.com/stefslon/exportToPPTX/blob/master/exportToPPTX.m#L2128
*/
if ($shape instanceof Media) {
@@ -1162,7 +1315,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->writeAttribute('uri', '{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}');
// p:nvPr > p:extLst > p:ext > p14:media
$objWriter->startElement('p14:media');
- $objWriter->writeAttribute('r:embed', $shape->relationId);
+ $objWriter->writeAttribute('r:embed', ((int) $shape->relationId + 1));
$objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main');
// p:nvPr > p:extLst > p:ext > ##p14:media
$objWriter->endElement();
@@ -1174,38 +1327,81 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
// ##p:nvPr
$objWriter->endElement();
$objWriter->endElement();
+
// p:blipFill
$objWriter->startElement('p:blipFill');
+
// a:blip
$objWriter->startElement('a:blip');
$objWriter->writeAttribute('r:embed', $shape->relationId);
+
+ if ($shape instanceof AbstractDrawingAdapter && $shape->getExtension() == 'svg') {
+ // a:extLst
+ $objWriter->startElement('a:extLst');
+
+ // a:extLst > a:ext
+ $objWriter->startElement('a:ext');
+ $objWriter->writeAttribute('uri', '{28A0092B-C50C-407E-A947-70E740481C1C}');
+ // a:extLst > a:ext > a14:useLocalDpi
+ $objWriter->startElement('a14:useLocalDpi');
+ $objWriter->writeAttribute('xmlns:a14', 'http://schemas.microsoft.com/office/drawing/2010/main');
+ $objWriter->writeAttribute('val', '0');
+ // a:extLst > a:ext > ##a14:useLocalDpi
+ $objWriter->endElement();
+ // a:extLst > ##a:ext
+ $objWriter->endElement();
+
+ // a:extLst > a:ext
+ $objWriter->startElement('a:ext');
+ $objWriter->writeAttribute('uri', '{96DAC541-7B7A-43D3-8B79-37D633B846F1}');
+ // a:extLst > a:ext > asvg:svgBlip
+ $objWriter->startElement('asvg:svgBlip');
+ $objWriter->writeAttribute('xmlns:asvg', 'http://schemas.microsoft.com/office/drawing/2016/SVG/main');
+ $objWriter->writeAttribute('r:embed', $shape->relationId);
+ // a:extLst > a:ext > ##asvg:svgBlip
+ $objWriter->endElement();
+ // a:extLst > ##a:ext
+ $objWriter->endElement();
+
+ // ##a:extLst
+ $objWriter->endElement();
+ }
+
$objWriter->endElement();
+
// a:stretch
$objWriter->startElement('a:stretch');
- $objWriter->writeElement('a:fillRect', null);
+ $objWriter->writeElement('a:fillRect');
$objWriter->endElement();
+
$objWriter->endElement();
+
// p:spPr
$objWriter->startElement('p:spPr');
// a:xfrm
$objWriter->startElement('a:xfrm');
- $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation()));
+ $objWriter->writeAttributeIf(0 != $shape->getRotation(), 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
+
// a:off
$objWriter->startElement('a:off');
$objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
$objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
$objWriter->endElement();
+
// a:ext
$objWriter->startElement('a:ext');
$objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
$objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
$objWriter->endElement();
+
$objWriter->endElement();
+
// a:prstGeom
$objWriter->startElement('a:prstGeom');
$objWriter->writeAttribute('prst', 'rect');
- // a:avLst
+ // // a:prstGeom/a:avLst
$objWriter->writeElement('a:avLst', null);
+ // ##a:prstGeom
$objWriter->endElement();
$this->writeFill($objWriter, $shape->getFill());
@@ -1213,18 +1409,16 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$this->writeShadow($objWriter, $shape->getShadow());
$objWriter->endElement();
+
$objWriter->endElement();
}
/**
- * Write group
+ * Write group.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Group $group
- * @param int $shapeId
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
*/
- protected function writeShapeGroup(XMLWriter $objWriter, Group $group, &$shapeId)
+ protected function writeShapeGroup(XMLWriter $objWriter, Group $group, int &$shapeId): void
{
// p:grpSp
$objWriter->startElement('p:grpSp');
@@ -1273,11 +1467,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->endElement(); // p:grpSp
}
- /**
- * @param \PhpOffice\PhpPresentation\Slide\AbstractSlide $pSlide
- * @param $objWriter
- */
- protected function writeSlideBackground(AbstractSlideAlias $pSlide, XMLWriter $objWriter)
+ protected function writeSlideBackground(AbstractSlideAlias $pSlide, XMLWriter $objWriter): void
{
if (!($pSlide->getBackground() instanceof Slide\AbstractBackground)) {
return;
@@ -1324,7 +1514,7 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
// > p:bgPr
$objWriter->endElement();
}
- /**
+ /*
* @link : http://www.officeopenxml.com/prSlide-background.php
*/
if ($oBackground instanceof Slide\Background\SchemeColor) {
@@ -1342,16 +1532,14 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->endElement();
}
-
/**
- * Write Transition Slide
- * @link http://officeopenxml.com/prSlide-transitions.php
- * @param XMLWriter $objWriter
- * @param Slide\Transition $transition
+ * Write Transition Slide.
+ *
+ * @see http://officeopenxml.com/prSlide-transitions.php
*/
- protected function writeSlideTransition(XMLWriter $objWriter, $transition)
+ protected function writeSlideTransition(XMLWriter $objWriter, ?Slide\Transition $transition): void
{
- if (!$transition instanceof Slide\Transition) {
+ if (!$transition) {
return;
}
$objWriter->startElement('p:transition');
@@ -1590,21 +1778,22 @@ abstract class AbstractSlide extends AbstractDecoratorWriter
$objWriter->endElement();
}
- private function getGUID()
+ private function getGUID(): string
{
if (function_exists('com_create_guid')) {
return com_create_guid();
} else {
- mt_srand((double)microtime() * 10000);//optional for php 4.2.0 and up.
- $charid = strtoupper(md5(uniqid(rand(), true)));
- $hyphen = chr(45);// "-"
+ mt_srand(intval(microtime(true) * 10000));
+ $charid = strtoupper(md5(uniqid((string) rand(), true)));
+ $hyphen = chr(45); // "-"
$uuid = chr(123)// "{"
. substr($charid, 0, 8) . $hyphen
. substr($charid, 8, 4) . $hyphen
. substr($charid, 12, 4) . $hyphen
. substr($charid, 16, 4) . $hyphen
. substr($charid, 20, 12)
- . chr(125);// "}"
+ . chr(125); // "}"
+
return $uuid;
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php
old mode 100755
new mode 100644
index 07e5d1d..35b90e1
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/CommentAuthors.php
@@ -1,4 +1,22 @@
getPresentation()->getAllSlides() as $oSlide) {
foreach ($oSlide->getShapeCollection() as $oShape) {
if (!($oShape instanceof Comment)) {
@@ -43,6 +57,7 @@ class CommentAuthors extends AbstractDecoratorWriter
/**
* @param Author[] $arrayAuthors
+ *
* @return string
*/
protected function writeCommentsAuthors($arrayAuthors)
@@ -63,7 +78,7 @@ class CommentAuthors extends AbstractDecoratorWriter
$objWriter->writeAttribute('id', $oAuthor->getIndex());
$objWriter->writeAttribute('name', $oAuthor->getName());
$objWriter->writeAttribute('initials', $oAuthor->getInitials());
- $objWriter->writeAttribute('lastIdx', "2");
+ $objWriter->writeAttribute('lastIdx', '2');
$objWriter->writeAttribute('clrIdx', 0);
$objWriter->endElement();
}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php
old mode 100755
new mode 100644
index 34ca83e..6d0a6d7
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php
@@ -10,29 +10,28 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;
+use PhpOffice\Common\Adapter\Zip\ZipInterface;
+use PhpOffice\Common\XMLWriter;
use PhpOffice\PhpPresentation\Shape\Chart as ShapeChart;
use PhpOffice\PhpPresentation\Shape\Comment;
-use PhpOffice\PhpPresentation\Shape\Drawing as ShapeDrawing;
-use PhpOffice\Common\XMLWriter;
-use PhpOffice\PhpPresentation\Writer\PowerPoint2007;
+use PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter;
/**
- * \PhpOffice\PhpPresentation\Writer\PowerPoint2007\ContentTypes
+ * \PhpOffice\PhpPresentation\Writer\PowerPoint2007\ContentTypes.
*/
class ContentTypes extends AbstractDecoratorWriter
{
- /**
- * @return \PhpOffice\Common\Adapter\Zip\ZipInterface
- * @throws \Exception
- */
- public function render()
+ public function render(): ZipInterface
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -50,6 +49,9 @@ class ContentTypes extends AbstractDecoratorWriter
// XML
$this->writeDefaultContentType($objWriter, 'xml', 'application/xml');
+ // SVG
+ $this->writeDefaultContentType($objWriter, 'svg', 'image/svg+xml');
+
// Presentation
$this->writeOverrideContentType($objWriter, '/ppt/presentation.xml', 'application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml');
@@ -67,11 +69,12 @@ class ContentTypes extends AbstractDecoratorWriter
$sldLayoutNr = 0;
$sldLayoutId = time() + 689016272; // requires minimum value of 2 147 483 648
foreach ($this->oPresentation->getAllMasterSlides() as $idx => $oSlideMaster) {
- $oSlideMaster->setRelsIndex($idx + 1);
+ $oSlideMaster->setRelsIndex((string) ($idx + 1));
$this->writeOverrideContentType($objWriter, '/ppt/slideMasters/slideMaster' . $oSlideMaster->getRelsIndex() . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml');
$this->writeOverrideContentType($objWriter, '/ppt/theme/theme' . $oSlideMaster->getRelsIndex() . '.xml', 'application/vnd.openxmlformats-officedocument.theme+xml');
foreach ($oSlideMaster->getAllSlideLayouts() as $oSlideLayout) {
$oSlideLayout->layoutNr = ++$sldLayoutNr;
+ $oSlideLayout->setRelsIndex((string) $oSlideLayout->layoutNr);
$oSlideLayout->layoutId = ++$sldLayoutId;
$this->writeOverrideContentType($objWriter, '/ppt/slideLayouts/slideLayout' . $oSlideLayout->layoutNr . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml');
}
@@ -100,13 +103,13 @@ class ContentTypes extends AbstractDecoratorWriter
}
// Add media content-types
- $aMediaContentTypes = array();
+ $aMediaContentTypes = [];
// GIF, JPEG, PNG
- $aMediaContentTypes['gif'] = 'image/gif';
- $aMediaContentTypes['jpg'] = 'image/jpeg';
+ $aMediaContentTypes['gif'] = 'image/gif';
+ $aMediaContentTypes['jpg'] = 'image/jpeg';
$aMediaContentTypes['jpeg'] = 'image/jpeg';
- $aMediaContentTypes['png'] = 'image/png';
+ $aMediaContentTypes['png'] = 'image/png';
foreach ($aMediaContentTypes as $key => $value) {
$this->writeDefaultContentType($objWriter, $key, $value);
}
@@ -120,8 +123,12 @@ class ContentTypes extends AbstractDecoratorWriter
$shapeIndex = $this->getDrawingHashTable()->getByIndex($i);
if ($shapeIndex instanceof ShapeChart) {
// Chart content type
- $this->writeOverrideContentType($objWriter, '/ppt/charts/chart' . $shapeIndex->getImageIndex() . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml');
- } else {
+ $this->writeOverrideContentType(
+ $objWriter,
+ '/ppt/charts/chart' . $shapeIndex->getImageIndex() . '.xml',
+ 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml'
+ );
+ } elseif ($shapeIndex instanceof AbstractDrawingAdapter) {
$extension = strtolower($shapeIndex->getExtension());
$mimeType = $shapeIndex->getMimeType();
@@ -141,18 +148,14 @@ class ContentTypes extends AbstractDecoratorWriter
}
/**
- * Write Default content type
+ * Write Default content type.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param string $pPartname Part name
- * @param string $pContentType Content type
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param string $pPartname Part name
+ * @param string $pContentType Content type
*/
- private function writeDefaultContentType(XMLWriter $objWriter, $pPartname = '', $pContentType = '')
+ protected function writeDefaultContentType(XMLWriter $objWriter, string $pPartname, string $pContentType): void
{
- if ($pPartname == '' || $pContentType == '') {
- throw new \Exception("Invalid parameters passed.");
- }
// Write content type
$objWriter->startElement('Default');
$objWriter->writeAttribute('Extension', $pPartname);
@@ -161,18 +164,14 @@ class ContentTypes extends AbstractDecoratorWriter
}
/**
- * Write Override content type
+ * Write Override content type.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param string $pPartname Part name
- * @param string $pContentType Content type
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param string $pPartname Part name
+ * @param string $pContentType Content type
*/
- private function writeOverrideContentType(XMLWriter $objWriter, $pPartname = '', $pContentType = '')
+ protected function writeOverrideContentType(XMLWriter $objWriter, string $pPartname, string $pContentType): void
{
- if ($pPartname == '' || $pContentType == '') {
- throw new \Exception("Invalid parameters passed.");
- }
// Write content type
$objWriter->startElement('Override');
$objWriter->writeAttribute('PartName', $pPartname);
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php
old mode 100755
new mode 100644
index 5bb8714..fe81c50
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsApp.php
@@ -1,16 +1,31 @@
writeElement('Application', 'Microsoft Office PowerPoint');
// Slides
- $objWriter->writeElement('Slides', $this->getPresentation()->getSlideCount());
+ $objWriter->writeElement('Slides', (string) $this->getPresentation()->getSlideCount());
// ScaleCrop
$objWriter->writeElement('ScaleCrop', 'false');
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php
old mode 100755
new mode 100644
index 591f42b..f1afb28
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php
@@ -1,16 +1,31 @@
startElement('property');
$objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
- $objWriter->writeAttribute('pid', 2);
+ $objWriter->writeAttribute('pid', (++$pId) * 2);
$objWriter->writeAttribute('name', '_MarkAsFinal');
// property > vt:bool
@@ -37,6 +56,37 @@ class DocPropsCustom extends AbstractDecoratorWriter
$objWriter->endElement();
}
+ $oDocumentProperties = $this->oPresentation->getDocumentProperties();
+ foreach ($oDocumentProperties->getCustomProperties() as $customProperty) {
+ $propertyValue = $oDocumentProperties->getCustomPropertyValue($customProperty);
+ $propertyType = $oDocumentProperties->getCustomPropertyType($customProperty);
+
+ $objWriter->startElement('property');
+ $objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
+ $objWriter->writeAttribute('pid', (++$pId) * 2);
+ $objWriter->writeAttribute('name', $customProperty);
+ switch ($propertyType) {
+ case DocumentProperties::PROPERTY_TYPE_INTEGER:
+ $objWriter->writeElement('vt:i4', (string) $propertyValue);
+ break;
+ case DocumentProperties::PROPERTY_TYPE_FLOAT:
+ $objWriter->writeElement('vt:r8', (string) $propertyValue);
+ break;
+ case DocumentProperties::PROPERTY_TYPE_BOOLEAN:
+ $objWriter->writeElement('vt:bool', $propertyValue ? 'true' : 'false');
+ break;
+ case DocumentProperties::PROPERTY_TYPE_DATE:
+ $objWriter->startElement('vt:filetime');
+ $objWriter->writeRaw(date(DATE_W3C, (int) $propertyValue));
+ $objWriter->endElement();
+ break;
+ default:
+ $objWriter->writeElement('vt:lpwstr', (string) $propertyValue);
+ break;
+ }
+ $objWriter->endElement();
+ }
+
// > Properties
$objWriter->endElement();
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php
old mode 100755
new mode 100644
index 64308a4..6c54be1
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php
@@ -1,14 +1,30 @@
getPresentation()->getPresentationProperties()->getThumbnailPath();
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php
deleted file mode 100755
index 8401aa4..0000000
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/AbstractLayoutPack.php
+++ /dev/null
@@ -1,225 +0,0 @@
-masterSlides;
- }
-
- /**
- * Get master slide relations
- *
- * @return array
- */
- public function getMasterSlideRelations()
- {
- return $this->masterSlideRels;
- }
-
- /**
- * Get themes
- *
- * @return array
- */
- public function getThemes()
- {
- return $this->themes;
- }
-
- /**
- * Get theme relations
- *
- * @return array
- */
- public function getThemeRelations()
- {
- return $this->themeRelations;
- }
-
- /**
- * Get array of slide layouts
- *
- * @return array
- */
- public function getLayouts()
- {
- return $this->layouts;
- }
-
- /**
- * Get array of slide layout relations
- *
- * @return array
- */
- public function getLayoutRelations()
- {
- return $this->layoutRelations;
- }
-
- /**
- * Find specific slide layout.
- *
- * This is an array consisting of:
- * - masterid
- * - name (string)
- * - body (string)
- *
- * @param string $name
- * @param int $masterId
- * @return array
- * @throws \Exception
- */
- public function findLayout($name = '', $masterId = 1)
- {
- foreach ($this->layouts as $layout) {
- if ($layout['name'] == $name && $layout['masterid'] == $masterId) {
- return $layout;
- }
- }
-
- throw new \Exception("Could not find slide layout $name in current layout pack.");
- }
-
- /**
- * Find specific slide layout id.
- *
- * @param string $name
- * @return int
- * @throws \Exception
- */
- public function findLayoutId($name = '')
- {
- foreach ($this->layouts as $layoutId => $layout) {
- if ($layout['name'] == $name) {
- return $layoutId;
- }
- }
-
- throw new \Exception("Could not find slide layout $name in current layout pack.");
- }
-
- /**
- * Find specific slide layout name.
- *
- * @param int $idLayout
- * @return int
- * @throws \Exception
- */
- public function findLayoutName($idLayout = null)
- {
- foreach ($this->layouts as $layoutId => $layout) {
- if ($layoutId == $idLayout) {
- return $layout['name'];
- }
- }
-
- throw new \Exception("Could not find slide layout $idLayout in current layout pack.");
- }
-}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php
deleted file mode 100755
index 0df0c01..0000000
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/PackDefault.php
+++ /dev/null
@@ -1,3281 +0,0 @@
-masterSlides = array(
- array(
- 'masterid' => 1,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <#>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-'));
-
- // Master slide relations
- $this->masterSlideRels = array(
- //array('masterid' => '', 'id' => '', 'type' => '', 'contentType' => '', 'target' => '', 'contents' => '')
- );
-
- // Theme
- $this->themes = array(
- array(
- 'masterid' => 1,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-'));
-
- // Theme relations
- $this->themeRelations = array(
- //array('masterid' => 1, 'id' => '', 'type' => '', 'contentType' => '', 'target' => '', 'contents' => '')
- );
-
- // Layouts - Layout::TITLE_SLIDE
- $this->layouts[1] = array(
- 'masterid' => 1,
- 'name' => Layout::TITLE_SLIDE,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master subtitle style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::TITLE_AND_CONTENT
- $this->layouts[2] = array(
- 'masterid' => 1,
- 'name' => Layout::TITLE_AND_CONTENT,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::SECTION_HEADER
- $this->layouts[3] = array(
- 'masterid' => 1,
- 'name' => Layout::SECTION_HEADER,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::TWO_CONTENT
- $this->layouts[4] = array(
- 'masterid' => 1,
- 'name' => Layout::TWO_CONTENT,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::COMPARISON
- $this->layouts[5] = array(
- 'masterid' => 1,
- 'name' => Layout::COMPARISON,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::TITLE_ONLY
- $this->layouts[6] = array(
- 'masterid' => 1,
- 'name' => Layout::TITLE_ONLY,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::BLANK
- $this->layouts[7] = array(
- 'masterid' => 1,
- 'name' => Layout::BLANK,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::CONTENT_WITH_CAPTION
- $this->layouts[8] = array(
- 'masterid' => 1,
- 'name' => Layout::CONTENT_WITH_CAPTION,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::PICTURE_WITH_CAPTION
- $this->layouts[9] = array(
- 'masterid' => 1,
- 'name' => Layout::PICTURE_WITH_CAPTION,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::TITLE_AND_VERTICAL_TEXT
- $this->layouts[10] = array(
- 'masterid' => 1,
- 'name' => Layout::TITLE_AND_VERTICAL_TEXT,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layouts - Layout::VERTICAL_TITLE_AND_TEXT
- $this->layouts[11] = array(
- 'masterid' => 1,
- 'name' => Layout::VERTICAL_TITLE_AND_TEXT,
- 'body' => '
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master title style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click to edit Master text styles
-
-
-
-
-
-
- Second level
-
-
-
-
-
-
- Third level
-
-
-
-
-
-
- Fourth level
-
-
-
-
-
-
- Fifth level
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 16/04/2009
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ‹#›
-
-
-
-
-
-
-
-
-
-
-');
-
- // Layout relations
- $this->layoutRelations = array(
- //array('layoutId' => 0, 'id' => '', 'type' => '', 'contentType' => '', 'target' => '', 'contents' => '')
- );
- }
-}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php
deleted file mode 100755
index 7a42c4c..0000000
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/LayoutPack/TemplateBased.php
+++ /dev/null
@@ -1,203 +0,0 @@
-masterSlideRels = array();
-
- // Theme relations
- $this->themeRelations = array();
-
- // Layout relations
- $this->layoutRelations = array();
-
- // Open package
- $package = new \ZipArchive;
- $package->open($fileName);
-
- // Read relations and search for officeDocument
- $relations = simplexml_load_string($package->getFromName("_rels/.rels"));
- foreach ($relations->Relationship as $rel) {
- if ($rel["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument") {
- // Found office document! Search for master slide...
- $presentationRels = simplexml_load_string($package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/_rels/" . basename($rel["Target"]) . ".rels")));
- foreach ($presentationRels->Relationship as $presRel) {
- if ($presRel["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster") {
- // Found slide master!
- $slideMasterId = str_replace('slideMaster', '', basename($presRel["Target"], '.xml'));
- $this->masterSlides[] = array(
- 'masterid' => $slideMasterId,
- 'body' => $package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . basename($presRel["Target"])))
- );
-
- // Search for theme & slide layouts
- $masterRelations = simplexml_load_string($package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/_rels/" . basename($presRel["Target"]) . ".rels")));
- foreach ($masterRelations->Relationship as $masterRel) {
- if ($masterRel["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") {
- // Found theme!
- $themeId = str_replace('theme', '', basename($masterRel["Target"], '.xml'));
- $this->themes[$themeId - 1] = array(
- 'masterid' => $slideMasterId,
- 'body' => $package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/" . basename($masterRel["Target"])))
- );
-
- // Search for theme relations
- $themeRelations = @simplexml_load_string($package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/_rels/" . basename($masterRel["Target"]) . ".rels")));
- if ($themeRelations && $themeRelations->Relationship) {
- foreach ($themeRelations->Relationship as $themeRel) {
- if ($themeRel["Type"] != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" && $themeRel["Type"] != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" && $themeRel["Type"] != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") {
- // Theme relation
- $this->themeRelations[] = array(
- 'masterid' => $slideMasterId,
- 'id' => $themeRel["Id"],
- 'type' => $themeRel["Type"],
- 'contentType' => '',
- 'target' => $themeRel["Target"],
- 'contents' => $package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/" . dirname($themeRel["Target"]) . "/" . basename($themeRel["Target"])))
- );
- }
- }
- }
- } elseif ($masterRel["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") {
- // Found slide layout!
- $layoutId = str_replace('slideLayout', '', basename($masterRel["Target"], '.xml'));
- $layout = array(
- 'id' => $layoutId,
- 'masterid' => $slideMasterId,
- 'name' => '-unknown-',
- 'body' => $package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/" . basename($masterRel["Target"])))
- );
- if (utf8_encode(utf8_decode($layout['body'])) == $layout['body']) {
- $layoutXml = simplexml_load_string($layout['body']);
- } else {
- $layoutXml = simplexml_load_string(utf8_encode($layout['body']));
- }
- $layoutXml->registerXPathNamespace("p", "http://schemas.openxmlformats.org/presentationml/2006/main");
- $slide = $layoutXml->xpath('/p:sldLayout/p:cSld');
- $layout['name'] = (string) $slide[0]['name'];
- $this->layouts[$layoutId] = $layout;
-
- // Search for slide layout relations
- $layoutRelations = @simplexml_load_string($package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/_rels/" . basename($masterRel["Target"]) . ".rels")));
- if ($layoutRelations && $layoutRelations->Relationship) {
- foreach ($layoutRelations->Relationship as $layoutRel) {
- if ($layoutRel["Type"] != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" && $layoutRel["Type"] != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" && $layoutRel["Type"] != "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") {
- // Layout relation
- $this->layoutRelations[] = array(
- 'layoutId' => $layoutId,
- 'id' => $layoutRel["Id"],
- 'type' => $layoutRel["Type"],
- 'contentType' => '',
- 'target' => $layoutRel["Target"],
- 'contents' => $package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/" . dirname($layoutRel["Target"]) . "/" . basename($layoutRel["Target"])))
- );
- }
- }
- }
- } else {
- // Master slide relation
- $this->masterSlideRels[] = array(
- 'masterid' => $slideMasterId,
- 'id' => $masterRel["Id"],
- 'type' => $masterRel["Type"],
- 'contentType' => '',
- 'target' => $masterRel["Target"],
- 'contents' => $package->getFromName($this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($presRel["Target"]) . "/" . dirname($masterRel["Target"]) . "/" . basename($masterRel["Target"])))
- );
- }
- }
- }
- }
-
- break;
- }
- }
-
- // Sort master slides
- usort($this->masterSlides, array(
- "\PhpOffice\PhpPresentation\Writer\PowerPoint2007\LayoutPack\TemplateBased",
- "cmpMaster"
- ));
-
- // Close package
- $package->close();
- }
-
- /**
- * Compare master slides
- *
- * @param array $firstSlide
- * @param array $secondSlide
- * @return int
- */
- public static function cmpMaster($firstSlide, $secondSlide)
- {
- if ($firstSlide['masterid'] == $secondSlide['masterid']) {
- return 0;
- }
-
- return ($firstSlide['masterid'] < $secondSlide['masterid']) ? -1 : 1;
- }
-
- /**
- * Determine absolute zip path
- *
- * @param string $path
- * @return string
- */
- protected function absoluteZipPath($path)
- {
- $path = str_replace(array(
- '/',
- '\\'
- ), DIRECTORY_SEPARATOR, $path);
- $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
- $absolutes = array();
- foreach ($parts as $part) {
- if ('.' == $part) {
- continue;
- }
- if ('..' == $part) {
- array_pop($absolutes);
- } else {
- $absolutes[] = $part;
- }
- }
-
- return implode('/', $absolutes);
- }
-}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptCharts.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptCharts.php
old mode 100755
new mode 100644
index 7109881..4d03776
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptCharts.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptCharts.php
@@ -1,9 +1,30 @@
getDrawingHashTable()->count(); ++$i) {
$shape = $this->getDrawingHashTable()->getByIndex($i);
@@ -36,28 +60,26 @@ class PptCharts extends AbstractDecoratorWriter
if ($shape->hasIncludedSpreadsheet()) {
$this->getZip()->addFromString('ppt/charts/_rels/' . $shape->getIndexedFilename() . '.rels', $this->writeChartRelationships($shape));
- $pFilename = tempnam(sys_get_temp_dir(), 'PHPExcel');
+ $pFilename = tempnam(sys_get_temp_dir(), 'PhpSpreadsheet');
$this->getZip()->addFromString('ppt/embeddings/' . $shape->getIndexedFilename() . '.xlsx', $this->writeSpreadsheet($this->getPresentation(), $shape, $pFilename . '.xlsx'));
// remove temp file
- if (@unlink($pFilename) === false) {
- throw new \Exception('The file ' . $pFilename . ' could not removed.');
+ if (false === @unlink($pFilename)) {
+ throw new FileRemoveException($pFilename);
}
}
}
}
+
return $this->getZip();
}
-
/**
- * Write chart to XML format
+ * Write chart to XML format.
*
- * @param \PhpOffice\PhpPresentation\Shape\Chart $chart
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- public function writeChart(Chart $chart)
+ protected function writeChart(Chart $chart): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -105,7 +127,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:hPercent
$hPercent = $chart->getView3D()->getHeightPercent();
- $objWriter->writeElementIf($hPercent != null, 'c:hPercent', 'val', $hPercent);
+ $objWriter->writeElementIf(null != $hPercent, 'c:hPercent', 'val', $hPercent);
// c:rotY
$objWriter->startElement('c:rotY');
@@ -143,6 +165,11 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->writeAttribute('val', '1');
$objWriter->endElement();
+ // c:dispBlanksAs
+ $objWriter->startElement('c:dispBlanksAs');
+ $objWriter->writeAttribute('val', $chart->getDisplayBlankAs());
+ $objWriter->endElement();
+
$objWriter->endElement();
// c:spPr
@@ -152,7 +179,7 @@ class PptCharts extends AbstractDecoratorWriter
$this->writeFill($objWriter, $chart->getFill());
// Border
- if ($chart->getBorder()->getLineStyle() != Border::LINE_NONE) {
+ if (Border::LINE_NONE != $chart->getBorder()->getLineStyle()) {
$this->writeBorder($objWriter, $chart->getBorder(), '');
}
@@ -165,7 +192,7 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->startElement('a:outerShdw');
$objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($chart->getShadow()->getBlurRadius()));
$objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($chart->getShadow()->getDistance()));
- $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle($chart->getShadow()->getDirection()));
+ $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle((int) $chart->getShadow()->getDirection()));
$objWriter->writeAttribute('algn', $chart->getShadow()->getAlignment());
$objWriter->writeAttribute('rotWithShape', '0');
@@ -199,38 +226,31 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write chart to XML format
+ * Write chart to XML format.
*
- * @param PhpPresentation $presentation
- * @param \PhpOffice\PhpPresentation\Shape\Chart $chart
- * @param string $tempName
- * @return string String output
- * @throws \Exception
+ * @return string String output
+ *
+ * @throws FileRemoveException
*/
- public function writeSpreadsheet(PhpPresentation $presentation, $chart, $tempName)
+ protected function writeSpreadsheet(PhpPresentation $presentation, Chart $chart, string $tempName): string
{
- // Need output?
- if (!$chart->hasIncludedSpreadsheet()) {
- throw new \Exception('No spreadsheet output is required for the given chart.');
- }
-
- // Verify PHPExcel
- if (!class_exists('PHPExcel')) {
- throw new \Exception('PHPExcel has not been loaded. Include PHPExcel.php in your script, e.g. require_once \'PHPExcel.php\'.');
- }
-
// Create new spreadsheet
- $workbook = new \PHPExcel();
+ $spreadsheet = new Spreadsheet();
// Set properties
$title = $chart->getTitle()->getText();
- if (strlen($title) == 0) {
+ if (0 == strlen($title)) {
$title = 'Chart';
}
- $workbook->getProperties()->setCreator($presentation->getDocumentProperties()->getCreator())->setLastModifiedBy($presentation->getDocumentProperties()->getLastModifiedBy())->setTitle($title);
+ $spreadsheet->getProperties()
+ ->setCreator(
+ $presentation->getDocumentProperties()->getCreator())->setLastModifiedBy(
+ $presentation->getDocumentProperties()->getLastModifiedBy()
+ )
+ ->setTitle($title);
// Add chart data
- $sheet = $workbook->setActiveSheetIndex(0);
+ $sheet = $spreadsheet->setActiveSheetIndex(0);
$sheet->setTitle('Sheet1');
// Write series
@@ -242,14 +262,14 @@ class PptCharts extends AbstractDecoratorWriter
// X-axis
$axisXData = array_keys($series->getValues());
$numAxisXData = count($axisXData);
- for ($i = 0; $i < $numAxisXData; $i++) {
+ for ($i = 0; $i < $numAxisXData; ++$i) {
$sheet->setCellValueByColumnAndRow(0, $i + 2, $axisXData[$i]);
}
// Y-axis
$axisYData = array_values($series->getValues());
$numAxisYData = count($axisYData);
- for ($i = 0; $i < $numAxisYData; $i++) {
+ for ($i = 0; $i < $numAxisYData; ++$i) {
$sheet->setCellValueByColumnAndRow(1 + $seriesIndex, $i + 2, $axisYData[$i]);
}
@@ -257,26 +277,24 @@ class PptCharts extends AbstractDecoratorWriter
}
// Save to string
- $writer = \PHPExcel_IOFactory::createWriter($workbook, 'Excel2007');
+ $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save($tempName);
// Load file in memory
$returnValue = file_get_contents($tempName);
- if (@unlink($tempName) === false) {
- throw new \Exception('The file ' . $tempName . ' could not removed.');
+ if (false === @unlink($tempName)) {
+ throw new FileRemoveException($tempName);
}
return $returnValue;
}
/**
- * Write element with value attribute
+ * Write element with value attribute.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param string $elementName
- * @param string $value
+ * @param XMLWriter $objWriter XML Writer
*/
- protected function writeElementWithValAttribute($objWriter, $elementName, $value)
+ protected function writeElementWithValAttribute(XMLWriter $objWriter, string $elementName, string $value): void
{
$objWriter->startElement($elementName);
$objWriter->writeAttribute('val', $value);
@@ -284,54 +302,56 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write single value or reference
+ * Write single value or reference.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param boolean $isReference
- * @param mixed $value
- * @param string $reference
+ * @param XMLWriter $objWriter XML Writer
*/
- protected function writeSingleValueOrReference($objWriter, $isReference, $value, $reference)
+ protected function writeSingleValueOrReference(XMLWriter $objWriter, bool $isReference, string $value, string $reference): void
{
if (!$isReference) {
// Value
$objWriter->writeElement('c:v', $value);
+
return;
}
// Reference and cache
+ // c:strRef
$objWriter->startElement('c:strRef');
+ // c:strRef/c:f
$objWriter->writeElement('c:f', $reference);
+ // c:strRef/c:strCache
$objWriter->startElement('c:strCache');
+ // c:strRef/c:strCache/c:ptCount
$objWriter->startElement('c:ptCount');
$objWriter->writeAttribute('val', '1');
$objWriter->endElement();
+ // c:strRef/c:strCache/c:pt
$objWriter->startElement('c:pt');
$objWriter->writeAttribute('idx', '0');
+ // c:strRef/c:strCache/c:pt/c:v
$objWriter->writeElement('c:v', $value);
+ // c:strRef/c:strCache/c:pt
$objWriter->endElement();
+ // c:strRef/c:strCache
$objWriter->endElement();
+ // c:strRef
$objWriter->endElement();
}
/**
- * Write series value or reference
+ * Write series value or reference.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param boolean $isReference
- * @param mixed $values
- * @param string $reference
+ * @param XMLWriter $objWriter XML Writer
+ * @param array $values
*/
- protected function writeMultipleValuesOrReference($objWriter, $isReference, $values, $reference)
+ protected function writeMultipleValuesOrReference(XMLWriter $objWriter, bool $isReference, array $values, string $reference): void
{
// c:strLit / c:numLit
// c:strRef / c:numRef
$referenceType = ($isReference ? 'Ref' : 'Lit');
- $dataType = 'str';
- if (is_int($values[0]) || is_float($values[0])) {
- $dataType = 'num';
- }
+ $dataType = is_numeric($values[0]) ? 'num' : 'str';
$objWriter->startElement('c:' . $dataType . $referenceType);
$numValues = count($values);
@@ -344,11 +364,11 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
// Add points
- for ($i = 0; $i < $numValues; $i++) {
+ for ($i = 0; $i < $numValues; ++$i) {
// c:pt
$objWriter->startElement('c:pt');
$objWriter->writeAttribute('idx', $i);
- $objWriter->writeElement('c:v', $values[$i]);
+ $objWriter->writeElement('c:v', strval($values[$i]));
$objWriter->endElement();
}
} else {
@@ -362,11 +382,11 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
// Add points
- for ($i = 0; $i < $numValues; $i++) {
+ for ($i = 0; $i < $numValues; ++$i) {
// c:pt
$objWriter->startElement('c:pt');
$objWriter->writeAttribute('idx', $i);
- $objWriter->writeElement('c:v', $values[$i]);
+ $objWriter->writeElement('c:v', strval($values[$i]));
$objWriter->endElement();
}
@@ -378,12 +398,8 @@ class PptCharts extends AbstractDecoratorWriter
/**
* Write Title
- *
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Title $subject
- * @throws \Exception
*/
- protected function writeTitle(XMLWriter $objWriter, Title $subject)
+ protected function writeTitle(XMLWriter $objWriter, Title $subject): void
{
// c:title
$objWriter->startElement('c:title');
@@ -475,14 +491,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Plot Area
+ * Write Plot Area.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\PlotArea $subject
- * @param \PhpOffice\PhpPresentation\Shape\Chart $chart
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ *
+ * @throws UndefinedChartTypeException
*/
- protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, Chart $chart)
+ protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, Chart $chart): void
{
// c:plotArea
$objWriter->startElement('c:plotArea');
@@ -506,10 +521,12 @@ class PptCharts extends AbstractDecoratorWriter
$this->writeTypePie3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
} elseif ($chartType instanceof Line) {
$this->writeTypeLine($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
+ } elseif ($chartType instanceof Radar) {
+ $this->writeTypeRadar($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
} elseif ($chartType instanceof Scatter) {
$this->writeTypeScatter($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
} else {
- throw new \Exception('The chart type provided could not be rendered.');
+ throw new UndefinedChartTypeException();
}
// Write X axis?
@@ -526,13 +543,12 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Legend
+ * Write Legend.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Legend $subject
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Chart\Legend $subject
*/
- protected function writeLegend(XMLWriter $objWriter, Legend $subject)
+ protected function writeLegend(XMLWriter $objWriter, Legend $subject): void
{
// c:legend
$objWriter->startElement('c:legend');
@@ -557,7 +573,7 @@ class PptCharts extends AbstractDecoratorWriter
$this->writeFill($objWriter, $subject->getFill());
// Border
- if ($subject->getBorder()->getLineStyle() != Border::LINE_NONE) {
+ if (Border::LINE_NONE != $subject->getBorder()->getLineStyle()) {
$this->writeBorder($objWriter, $subject->getBorder(), '');
}
@@ -625,13 +641,12 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Layout
+ * Write Layout.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param mixed $subject
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Legend|PlotArea|Title $subject
*/
- protected function writeLayout(XMLWriter $objWriter, $subject)
+ protected function writeLayout(XMLWriter $objWriter, $subject): void
{
// c:layout
$objWriter->startElement('c:layout');
@@ -648,28 +663,28 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->writeAttribute('val', 'edge');
$objWriter->endElement();
- if ($subject->getOffsetX() != 0) {
+ if (0 != $subject->getOffsetX()) {
// c:x
$objWriter->startElement('c:x');
$objWriter->writeAttribute('val', $subject->getOffsetX());
$objWriter->endElement();
}
- if ($subject->getOffsetY() != 0) {
+ if (0 != $subject->getOffsetY()) {
// c:y
$objWriter->startElement('c:y');
$objWriter->writeAttribute('val', $subject->getOffsetY());
$objWriter->endElement();
}
- if ($subject->getWidth() != 0) {
+ if (0 != $subject->getWidth()) {
// c:w
$objWriter->startElement('c:w');
$objWriter->writeAttribute('val', $subject->getWidth());
$objWriter->endElement();
}
- if ($subject->getHeight() != 0) {
+ if (0 != $subject->getHeight()) {
// c:h
$objWriter->startElement('c:h');
$objWriter->writeAttribute('val', $subject->getHeight());
@@ -681,14 +696,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Type Area
+ * Write Type Area.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Area $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Chart\Type\Area $subject
+ * @param bool $includeSheet
*/
- protected function writeTypeArea(XMLWriter $objWriter, Area $subject, $includeSheet = false)
+ protected function writeTypeArea(XMLWriter $objWriter, Area $subject, bool $includeSheet = false): void
{
// c:lineChart
$objWriter->startElement('c:areaChart');
@@ -716,7 +730,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:ser > c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -739,7 +753,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:ser > ##c:dLbls
$objWriter->endElement();
- if ($series->getFill()->getFillType() != Fill::FILL_NONE) {
+ if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
// c:spPr
$objWriter->startElement('c:spPr');
// Write fill
@@ -761,7 +775,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
@@ -784,14 +798,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Type Bar
+ * Write Type Bar.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Chart\Type\Bar $subject
+ * @param bool $includeSheet
*/
- protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, $includeSheet = false)
+ protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, bool $includeSheet = false): void
{
// c:barChart
$objWriter->startElement('c:barChart');
@@ -824,7 +837,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -835,9 +848,9 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->startElement('c:dPt');
// c:idx
- $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
+ $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
- if ($value->getFillType() != Fill::FILL_NONE) {
+ if (Fill::FILL_NONE != $value->getFillType()) {
// c:spPr
$objWriter->startElement('c:spPr');
// Write fill
@@ -938,7 +951,7 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
// c:spPr
- if ($series->getFill()->getFillType() != Fill::FILL_NONE) {
+ if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
// c:spPr
$objWriter->startElement('c:spPr');
// Write fill
@@ -960,7 +973,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
@@ -975,13 +988,8 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
// c:overlap
- $barGrouping = $subject->getBarGrouping();
$objWriter->startElement('c:overlap');
- if ($barGrouping === Bar::GROUPING_CLUSTERED) {
- $objWriter->writeAttribute('val', '0');
- } elseif ($barGrouping === Bar::GROUPING_STACKED || $barGrouping === Bar::GROUPING_PERCENTSTACKED) {
- $objWriter->writeAttribute('val', '100000');
- }
+ $objWriter->writeAttribute('val', $subject->getOverlapWidthPercent());
$objWriter->endElement();
// c:axId
@@ -1002,14 +1010,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Type Bar3D
+ * Write Type Bar3D.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Chart\Type\Bar3D $subject
+ * @param bool $includeSheet
*/
- protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, $includeSheet = false)
+ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, bool $includeSheet = false): void
{
// c:bar3DChart
$objWriter->startElement('c:bar3DChart');
@@ -1042,7 +1049,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -1053,9 +1060,9 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->startElement('c:dPt');
// c:idx
- $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
+ $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
- if ($value->getFillType() != Fill::FILL_NONE) {
+ if (Fill::FILL_NONE != $value->getFillType()) {
// c:spPr
$objWriter->startElement('c:spPr');
// Write fill
@@ -1141,7 +1148,7 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
// c:spPr
- if ($series->getFill()->getFillType() != Fill::FILL_NONE) {
+ if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
// c:spPr
$objWriter->startElement('c:spPr');
// Write fill
@@ -1163,7 +1170,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
@@ -1196,14 +1203,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Type Pie
+ * Write Type Pie.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Doughnut $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Doughnut $subject
+ * @param bool $includeSheet
*/
- protected function writeTypeDoughnut(XMLWriter $objWriter, Doughnut $subject, $includeSheet = false)
+ protected function writeTypeDoughnut(XMLWriter $objWriter, Doughnut $subject, bool $includeSheet = false): void
{
// c:pieChart
$objWriter->startElement('c:doughnutChart');
@@ -1231,7 +1237,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -1240,7 +1246,7 @@ class PptCharts extends AbstractDecoratorWriter
foreach ($dataPointFills as $key => $value) {
// c:dPt
$objWriter->startElement('c:dPt');
- $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
+ $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
// c:dPt/c:spPr
$objWriter->startElement('c:spPr');
$this->writeFill($objWriter, $value);
@@ -1263,7 +1269,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
@@ -1272,96 +1278,97 @@ class PptCharts extends AbstractDecoratorWriter
++$seriesIndex;
}
- // c:dLbls
- $objWriter->startElement('c:dLbls');
+ if (isset($series) && is_object($series) && $series instanceof Chart\Series) {
+ // c:dLbls
+ $objWriter->startElement('c:dLbls');
- $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
- $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
- $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
- $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
- $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
- $this->writeElementWithValAttribute($objWriter, 'c:showBubbleSize', '0');
- $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showBubbleSize', '0');
+ $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
- if ($series->hasDlblNumFormat()) {
- //c:numFmt
- $objWriter->startElement('c:numFmt');
- $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
- $objWriter->writeAttribute('sourceLinked', '0');
+ if ($series->hasDlblNumFormat()) {
+ //c:numFmt
+ $objWriter->startElement('c:numFmt');
+ $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
+ $objWriter->writeAttribute('sourceLinked', '0');
+ $objWriter->endElement();
+ }
+
+ // c:dLbls\c:txPr
+ $objWriter->startElement('c:txPr');
+ $objWriter->writeElement('a:bodyPr', null);
+ $objWriter->writeElement('a:lstStyle', null);
+
+ // c:dLbls\c:txPr\a:p
+ $objWriter->startElement('a:p');
+
+ // c:dLbls\c:txPr\a:p\a:pPr
+ $objWriter->startElement('a:pPr');
+
+ // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr
+ $objWriter->startElement('a:defRPr');
+ $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
+ $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
+ $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
+ $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
+ $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
+ $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
+ $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
+
+ // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:solidFill
+ $objWriter->startElement('a:solidFill');
+ $this->writeColor($objWriter, $series->getFont()->getColor());
+ $objWriter->endElement();
+
+ // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:latin
+ $objWriter->startElement('a:latin');
+ $objWriter->writeAttribute('typeface', $series->getFont()->getName());
+ $objWriter->endElement();
+
+ // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\
+ $objWriter->endElement();
+ // c:dLbls\c:txPr\a:p\a:pPr\
+ $objWriter->endElement();
+
+ // c:dLbls\c:txPr\a:p\a:endParaRPr
+ $objWriter->startElement('a:endParaRPr');
+ $objWriter->writeAttribute('lang', 'en-US');
+ $objWriter->writeAttribute('dirty', '0');
+ $objWriter->endElement();
+
+ // c:dLbls\c:txPr\a:p\
+ $objWriter->endElement();
+ // c:dLbls\c:txPr\
+ $objWriter->endElement();
+
+ $separator = $series->getSeparator();
+ if (!empty($separator) && PHP_EOL != $separator) {
+ // c:dLbls\c:separator
+ $objWriter->writeElement('c:separator', $separator);
+ }
+
+ // c:dLbls\
$objWriter->endElement();
}
- // c:dLbls\c:txPr
- $objWriter->startElement('c:txPr');
- $objWriter->writeElement('a:bodyPr', null);
- $objWriter->writeElement('a:lstStyle', null);
-
- // c:dLbls\c:txPr\a:p
- $objWriter->startElement('a:p');
-
- // c:dLbls\c:txPr\a:p\a:pPr
- $objWriter->startElement('a:pPr');
-
- // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr
- $objWriter->startElement('a:defRPr');
- $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
- $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
- $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
- $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
- $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
- $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
- $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
-
- // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:solidFill
- $objWriter->startElement('a:solidFill');
- $this->writeColor($objWriter, $series->getFont()->getColor());
- $objWriter->endElement();
-
- // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:latin
- $objWriter->startElement('a:latin');
- $objWriter->writeAttribute('typeface', $series->getFont()->getName());
- $objWriter->endElement();
-
- // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\
- $objWriter->endElement();
- // c:dLbls\c:txPr\a:p\a:pPr\
- $objWriter->endElement();
-
- // c:dLbls\c:txPr\a:p\a:endParaRPr
- $objWriter->startElement('a:endParaRPr');
- $objWriter->writeAttribute('lang', 'en-US');
- $objWriter->writeAttribute('dirty', '0');
- $objWriter->endElement();
-
- // c:dLbls\c:txPr\a:p\
- $objWriter->endElement();
- // c:dLbls\c:txPr\
- $objWriter->endElement();
-
- $separator = $series->getSeparator();
- if (!empty($separator) && $separator != PHP_EOL) {
- // c:dLbls\c:separator
- $objWriter->writeElement('c:separator', $separator);
- }
-
- // c:dLbls\
- $objWriter->endElement();
-
$this->writeElementWithValAttribute($objWriter, 'c:firstSliceAng', '0');
- $this->writeElementWithValAttribute($objWriter, 'c:holeSize', $subject->getHoleSize());
+ $this->writeElementWithValAttribute($objWriter, 'c:holeSize', (string) $subject->getHoleSize());
$objWriter->endElement();
}
/**
- * Write Type Pie
+ * Write Type Pie.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Pie $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Pie $subject
+ * @param bool $includeSheet
*/
- protected function writeTypePie(XMLWriter $objWriter, Pie $subject, $includeSheet = false)
+ protected function writeTypePie(XMLWriter $objWriter, Pie $subject, bool $includeSheet = false): void
{
// c:pieChart
$objWriter->startElement('c:pieChart');
@@ -1389,7 +1396,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -1398,7 +1405,7 @@ class PptCharts extends AbstractDecoratorWriter
foreach ($dataPointFills as $key => $value) {
// c:dPt
$objWriter->startElement('c:dPt');
- $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
+ $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
// c:dPt/c:spPr
$objWriter->startElement('c:spPr');
$this->writeFill($objWriter, $value);
@@ -1507,7 +1514,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
@@ -1520,14 +1527,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Type Pie3D
+ * Write Type Pie3D.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Pie3D $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Pie3D $subject
+ * @param bool $includeSheet
*/
- protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, $includeSheet = false)
+ protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, bool $includeSheet = false): void
{
// c:pie3DChart
$objWriter->startElement('c:pie3DChart');
@@ -1555,7 +1561,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -1569,7 +1575,7 @@ class PptCharts extends AbstractDecoratorWriter
foreach ($dataPointFills as $key => $value) {
// c:dPt
$objWriter->startElement('c:dPt');
- $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
+ $this->writeElementWithValAttribute($objWriter, 'c:idx', (string) $key);
// c:dPt/c:spPr
$objWriter->startElement('c:spPr');
$this->writeFill($objWriter, $value);
@@ -1667,7 +1673,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
@@ -1680,14 +1686,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write Type Line
+ * Write Type Line.
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Line $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter XML Writer
+ * @param Line $subject
+ * @param bool $includeSheet
*/
- protected function writeTypeLine(XMLWriter $objWriter, Line $subject, $includeSheet = false)
+ protected function writeTypeLine(XMLWriter $objWriter, Line $subject, bool $includeSheet = false): void
{
// c:lineChart
$objWriter->startElement('c:lineChart');
@@ -1715,7 +1720,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -1817,10 +1822,15 @@ class PptCharts extends AbstractDecoratorWriter
// c:val
$objWriter->startElement('c:val');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
+ // c:smooth
+ $objWriter->startElement('c:smooth');
+ $objWriter->writeAttribute('val', $subject->isSmooth() ? '1' : '0');
+ $objWriter->endElement();
+
$objWriter->endElement();
++$seriesIndex;
@@ -1831,11 +1841,177 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->writeAttribute('val', '1');
$objWriter->endElement();
- // c:smooth
- $objWriter->startElement('c:smooth');
+ // c:axId
+ $objWriter->startElement('c:axId');
+ $objWriter->writeAttribute('val', '52743552');
+ $objWriter->endElement();
+
+ // c:axId
+ $objWriter->startElement('c:axId');
+ $objWriter->writeAttribute('val', '52749440');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Type Radar
+ *
+ * @param XMLWriter $objWriter XML Writer
+ * @param Radar $subject
+ * @param bool $includeSheet
+ */
+ protected function writeTypeRadar(XMLWriter $objWriter, Radar $subject, bool $includeSheet = false): void
+ {
+ // c:scatterChart
+ $objWriter->startElement('c:radarChart');
+
+ // c:radarStyle
+ $objWriter->startElement('c:radarStyle');
+ $objWriter->writeAttribute('val', 'marker');
+ $objWriter->endElement();
+
+ // c:varyColors
+ $objWriter->startElement('c:varyColors');
$objWriter->writeAttribute('val', '0');
$objWriter->endElement();
+ // Write series
+ $seriesIndex = 0;
+ foreach ($subject->getSeries() as $series) {
+ // c:ser
+ $objWriter->startElement('c:ser');
+
+ // c:idx
+ $objWriter->startElement('c:idx');
+ $objWriter->writeAttribute('val', $seriesIndex);
+ $objWriter->endElement();
+
+ // c:order
+ $objWriter->startElement('c:order');
+ $objWriter->writeAttribute('val', $seriesIndex);
+ $objWriter->endElement();
+
+ // c:tx
+ $objWriter->startElement('c:tx');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
+ $objWriter->endElement();
+
+ // Marker
+ $this->writeSeriesMarker($objWriter, $series->getMarker());
+
+ // c:dLbls
+ $objWriter->startElement('c:dLbls');
+
+ // c:txPr
+ $objWriter->startElement('c:txPr');
+
+ // a:bodyPr
+ $objWriter->writeElement('a:bodyPr', null);
+
+ // a:lstStyle
+ $objWriter->writeElement('a:lstStyle', null);
+
+ // a:p
+ $objWriter->startElement('a:p');
+
+ // a:pPr
+ $objWriter->startElement('a:pPr');
+
+ // a:defRPr
+ $objWriter->startElement('a:defRPr');
+
+ $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
+ $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
+ $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
+ $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
+ $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
+ $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000');
+ $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000');
+
+ // Font - a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ $this->writeColor($objWriter, $series->getFont()->getColor());
+
+ $objWriter->endElement();
+
+ // Font - a:latin
+ $objWriter->startElement('a:latin');
+ $objWriter->writeAttribute('typeface', $series->getFont()->getName());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:endParaRPr
+ $objWriter->startElement('a:endParaRPr');
+ $objWriter->writeAttribute('lang', 'en-US');
+ $objWriter->writeAttribute('dirty', '0');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // c:showLegendKey
+ $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
+
+ // c:showVal
+ $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
+
+ // c:showCatName
+ $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
+
+ // c:showSerName
+ $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
+
+ // c:showPercent
+ $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
+
+ // c:showLeaderLines
+ $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
+
+ $objWriter->endElement();
+
+ // c:spPr
+ $objWriter->startElement('c:spPr');
+ // Write fill
+ $this->writeFill($objWriter, $series->getFill());
+ // Write outline
+ $this->writeOutline($objWriter, $series->getOutline());
+ // ## c:spPr
+ $objWriter->endElement();
+
+ // Write X axis data
+ $axisXData = array_keys($series->getValues());
+
+ // c:cat
+ $objWriter->startElement('c:cat');
+ $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
+ $objWriter->endElement();
+
+ // Write Y axis data
+ $axisYData = array_values($series->getValues());
+
+ // c:val
+ $objWriter->startElement('c:val');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
+ $objWriter->endElement();
+
+ // c:smooth
+ $objWriter->startElement('c:smooth');
+ $objWriter->writeAttribute('val', '0');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ ++$seriesIndex;
+ }
+
// c:axId
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', '52743552');
@@ -1852,12 +2028,11 @@ class PptCharts extends AbstractDecoratorWriter
/**
* Write Type Scatter
*
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Chart\Type\Scatter $subject
- * @param boolean $includeSheet
- * @throws \Exception
+ * @param XMLWriter $objWriter
+ * @param Scatter $subject
+ * @param bool $includeSheet
*/
- protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, $includeSheet = false)
+ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, bool $includeSheet = false): void
{
// c:scatterChart
$objWriter->startElement('c:scatterChart');
@@ -1890,7 +2065,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tx
$objWriter->startElement('c:tx');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
$this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
$objWriter->endElement();
@@ -1978,7 +2153,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:separator
$separator = $series->getSeparator();
- if (!empty($separator) && $separator != PHP_EOL) {
+ if (!empty($separator) && PHP_EOL != $separator) {
// c:dLbls\c:separator
$objWriter->writeElement('c:separator', $separator);
}
@@ -2001,13 +2176,13 @@ class PptCharts extends AbstractDecoratorWriter
// c:yVal
$objWriter->startElement('c:yVal');
- $coords = ($includeSheet ? 'Sheet1!$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . \PHPExcel_Cell::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
+ $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
$this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
$objWriter->endElement();
// c:smooth
$objWriter->startElement('c:smooth');
- $objWriter->writeAttribute('val', '0');
+ $objWriter->writeAttribute('val', $subject->isSmooth() ? '1' : '0');
$objWriter->endElement();
$objWriter->endElement();
@@ -2029,13 +2204,13 @@ class PptCharts extends AbstractDecoratorWriter
}
/**
- * Write chart relationships to XML format
+ * Write chart relationships to XML format.
*
- * @param \PhpOffice\PhpPresentation\Shape\Chart $pChart
- * @return string XML Output
- * @throws \Exception
+ * @param Chart $pChart
+ *
+ * @return string XML Output
*/
- public function writeChartRelationships(Chart $pChart)
+ protected function writeChartRelationships(Chart $pChart): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -2060,20 +2235,20 @@ class PptCharts extends AbstractDecoratorWriter
/**
* @param XMLWriter $objWriter
- * @param Chart\Marker $oMarker
+ * @param Chart\Marker $marker
*/
- protected function writeSeriesMarker(XMLWriter $objWriter, Chart\Marker $oMarker)
+ protected function writeSeriesMarker(XMLWriter $objWriter, Chart\Marker $marker): void
{
// c:marker
$objWriter->startElement('c:marker');
// c:marker > c:symbol
$objWriter->startElement('c:symbol');
- $objWriter->writeAttribute('val', $oMarker->getSymbol());
+ $objWriter->writeAttribute('val', $marker->getSymbol());
$objWriter->endElement();
// Size if different of none
- if ($oMarker->getSymbol() != Chart\Marker::SYMBOL_NONE) {
- $markerSize = (int)$oMarker->getSize();
+ if (Chart\Marker::SYMBOL_NONE != $marker->getSymbol()) {
+ $markerSize = (int) $marker->getSize();
if ($markerSize < 2) {
$markerSize = 2;
}
@@ -2081,7 +2256,7 @@ class PptCharts extends AbstractDecoratorWriter
$markerSize = 72;
}
- /**
+ /*
* c:marker > c:size
* Size in points
* @link : https://msdn.microsoft.com/en-us/library/hh658135(v=office.12).aspx
@@ -2090,31 +2265,41 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->writeAttribute('val', $markerSize);
$objWriter->endElement();
}
+
+ // // c:marker > c:spPr
+ $objWriter->startElement('c:spPr');
+ $this->writeFill($objWriter, $marker->getFill());
+ $this->writeBorder($objWriter, $marker->getBorder(), '', true);
+ $objWriter->endElement();
+
+ // > c:marker
$objWriter->endElement();
}
/**
* @param XMLWriter $objWriter
* @param Chart\Axis $oAxis
- * @param $typeAxis
+ * @param string $typeAxis
* @param Chart\Type\AbstractType $typeChart
- * @throws \Exception
*/
- protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, $typeAxis, Chart\Type\AbstractType $typeChart)
+ protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, string $typeAxis, Chart\Type\AbstractType $typeChart): void
{
- if ($typeAxis != Chart\Axis::AXIS_X && $typeAxis != Chart\Axis::AXIS_Y) {
+ if (Chart\Axis::AXIS_X != $typeAxis && Chart\Axis::AXIS_Y != $typeAxis) {
return;
}
- if ($typeAxis == Chart\Axis::AXIS_X) {
+ $crossesAt = $oAxis->getCrossesAt();
+ $orientation = $oAxis->isReversedOrder() ? 'maxMin' : 'minMax';
+
+ if (Chart\Axis::AXIS_X == $typeAxis) {
$mainElement = 'c:catAx';
$axIdVal = '52743552';
- $axPosVal = 'b';
+ $axPosVal = $crossesAt === 'max' ? 't' : 'b';
$crossAxVal = '52749440';
} else {
$mainElement = 'c:valAx';
$axIdVal = '52749440';
- $axPosVal = 'l';
+ $axPosVal = $crossesAt === 'max' ? 'r' : 'l';
$crossAxVal = '52743552';
}
@@ -2131,16 +2316,16 @@ class PptCharts extends AbstractDecoratorWriter
// $mainElement > c:scaling > c:orientation
$objWriter->startElement('c:orientation');
- $objWriter->writeAttribute('val', 'minMax');
+ $objWriter->writeAttribute('val', $orientation);
$objWriter->endElement();
- if ($oAxis->getMaxBounds() != null) {
+ if (null != $oAxis->getMaxBounds()) {
$objWriter->startElement('c:max');
$objWriter->writeAttribute('val', $oAxis->getMaxBounds());
$objWriter->endElement();
}
- if ($oAxis->getMinBounds() != null) {
+ if (null != $oAxis->getMinBounds()) {
$objWriter->startElement('c:min');
$objWriter->writeAttribute('val', $oAxis->getMinBounds());
$objWriter->endElement();
@@ -2177,7 +2362,7 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
}
- if ($oAxis->getTitle() != '') {
+ if ('' != $oAxis->getTitle()) {
// c:title
$objWriter->startElement('c:title');
@@ -2188,7 +2373,9 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->startElement('c:rich');
// a:bodyPr
- $objWriter->writeElement('a:bodyPr', null);
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->writeAttributeIf($oAxis->getTitleRotation() != 0, 'rot', CommonDrawing::degreesToAngle((int) $oAxis->getTitleRotation()));
+ $objWriter->endElement();
// a:lstStyle
$objWriter->writeElement('a:lstStyle', null);
@@ -2277,7 +2464,7 @@ class PptCharts extends AbstractDecoratorWriter
// c:tickLblPos
$objWriter->startElement('c:tickLblPos');
- $objWriter->writeAttribute('val', 'nextTo');
+ $objWriter->writeAttribute('val', $oAxis->getTickLabelPosition());
$objWriter->endElement();
// c:spPr
@@ -2292,12 +2479,18 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->writeAttribute('val', $crossAxVal);
$objWriter->endElement();
- // c:crosses
- $objWriter->startElement('c:crosses');
- $objWriter->writeAttribute('val', 'autoZero');
- $objWriter->endElement();
+ // c:crosses "autoZero" | "min" | "max" | custom string value
+ if (in_array($crossesAt, ['autoZero', 'min', 'max'])) {
+ $objWriter->startElement('c:crosses');
+ $objWriter->writeAttribute('val', $crossesAt);
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('c:crossesAt');
+ $objWriter->writeAttribute('val', $crossesAt);
+ $objWriter->endElement();
+ }
- if ($typeAxis == Chart\Axis::AXIS_X) {
+ if (Chart\Axis::AXIS_X == $typeAxis) {
// c:lblAlgn
$objWriter->startElement('c:lblAlgn');
$objWriter->writeAttribute('val', 'ctr');
@@ -2307,9 +2500,16 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->startElement('c:lblOffset');
$objWriter->writeAttribute('val', '100');
$objWriter->endElement();
+
+ // c:majorUnit
+ if ($oAxis->getMajorUnit() != null) {
+ $objWriter->startElement('c:tickLblSkip');
+ $objWriter->writeAttribute('val', $oAxis->getMajorUnit());
+ $objWriter->endElement();
+ }
}
- if ($typeAxis == Chart\Axis::AXIS_Y) {
+ if (Chart\Axis::AXIS_Y == $typeAxis) {
// c:crossBetween
$objWriter->startElement('c:crossBetween');
// midCat : Position Axis On Tick Marks
@@ -2322,14 +2522,14 @@ class PptCharts extends AbstractDecoratorWriter
$objWriter->endElement();
// c:majorUnit
- if ($oAxis->getMajorUnit() != null) {
+ if (null != $oAxis->getMajorUnit()) {
$objWriter->startElement('c:majorUnit');
$objWriter->writeAttribute('val', $oAxis->getMajorUnit());
$objWriter->endElement();
}
// c:minorUnit
- if ($oAxis->getMinorUnit() != null) {
+ if (null != $oAxis->getMinorUnit()) {
$objWriter->startElement('c:minorUnit');
$objWriter->writeAttribute('val', $oAxis->getMinorUnit());
$objWriter->endElement();
@@ -2342,9 +2542,8 @@ class PptCharts extends AbstractDecoratorWriter
/**
* @param XMLWriter $objWriter
* @param Gridlines $oGridlines
- * @throws \Exception
*/
- protected function writeAxisGridlines(XMLWriter $objWriter, Gridlines $oGridlines)
+ protected function writeAxisGridlines(XMLWriter $objWriter, Gridlines $oGridlines): void
{
// c:spPr
$objWriter->startElement('c:spPr');
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptComments.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptComments.php
old mode 100755
new mode 100644
index 4c1cc19..5a30c08
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptComments.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptComments.php
@@ -1,4 +1,22 @@
getPresentation()->getAllSlides() as $numSlide => $oSlide) {
$contentXml = $this->writeSlideComments($oSlide);
if (empty($contentXml)) {
continue;
}
- $this->getZip()->addFromString('ppt/comments/comment'.($numSlide + 1).'.xml', $contentXml);
+ $this->getZip()->addFromString('ppt/comments/comment' . ($numSlide + 1) . '.xml', $contentXml);
}
+
return $this->getZip();
}
/**
- * @param Slide $oSlide
* @return string
*/
- protected function writeSlideComments(Slide $oSlide)
+ protected function writeSlideComments(Slide $oSlide): string
{
/**
* @var Comment[]
*/
- $arrayComment = array();
+ $arrayComment = [];
foreach ($oSlide->getShapeCollection() as $oShape) {
if ($oShape instanceof Comment) {
$arrayComment[] = $oShape;
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptMedia.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptMedia.php
old mode 100755
new mode 100644
index 58121be..85a7ace
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptMedia.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptMedia.php
@@ -1,16 +1,34 @@
getDrawingHashTable()->count(); ++$i) {
$shape = $this->getDrawingHashTable()->getByIndex($i);
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php
old mode 100755
new mode 100644
index d434e03..c28f12d
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresProps.php
@@ -1,15 +1,34 @@
oPresentation->getPresentationProperties();
@@ -26,11 +45,18 @@ class PptPresProps extends AbstractDecoratorWriter
$objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main');
// p:presentationPr > p:showPr
+ $objWriter->startElement('p:showPr');
if ($presentationPpts->isLoopContinuouslyUntilEsc()) {
- $objWriter->startElement('p:showPr');
$objWriter->writeAttribute('loop', '1');
- $objWriter->endElement();
}
+ // Depends on the slideshow type
+ // p:presentationPr > p:showPr > p:present
+ // p:presentationPr > p:showPr > p:browse
+ // p:presentationPr > p:showPr > p:kiosk
+ $objWriter->writeElement('p:' . $presentationPpts->getSlideshowType());
+
+ // > p:presentationPr > p:showPr
+ $objWriter->endElement();
// p:extLst
$objWriter->startElement('p:extLst');
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php
old mode 100755
new mode 100644
index a38c016..a6aa3c1
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptPresentation.php
@@ -1,17 +1,35 @@
startElement('p:sldMasterIdLst');
// Add slide masters
- $relationId = 1;
+ $relationId = 1;
$slideMasterId = 2147483648;
$countMasterSlides = count($this->oPresentation->getAllMasterSlides());
- for ($inc = 1; $inc <= $countMasterSlides; $inc++) {
+ for ($inc = 1; $inc <= $countMasterSlides; ++$inc) {
// p:sldMasterId
$objWriter->startElement('p:sldMasterId');
$objWriter->writeAttribute('id', $slideMasterId);
@@ -46,7 +64,7 @@ class PptPresentation extends AbstractDecoratorWriter
$objWriter->endElement();
// theme
- $relationId++;
+ ++$relationId;
// p:sldIdLst
$objWriter->startElement('p:sldIdLst');
@@ -65,7 +83,7 @@ class PptPresentation extends AbstractDecoratorWriter
$objWriter->startElement('p:sldSz');
$objWriter->writeAttribute('cx', $this->oPresentation->getLayout()->getCX());
$objWriter->writeAttribute('cy', $this->oPresentation->getLayout()->getCY());
- if ($this->oPresentation->getLayout()->getDocumentLayout() != DocumentLayout::LAYOUT_CUSTOM) {
+ if (DocumentLayout::LAYOUT_CUSTOM != $this->oPresentation->getLayout()->getDocumentLayout()) {
$objWriter->writeAttribute('type', $this->oPresentation->getLayout()->getDocumentLayout());
}
$objWriter->endElement();
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php
old mode 100755
new mode 100644
index fe7a694..b482579
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideLayouts.php
@@ -1,21 +1,39 @@
oPresentation->getAllMasterSlides() as $oSlideMaster) {
foreach ($oSlideMaster->getAllSlideLayouts() as $oSlideLayout) {
@@ -34,13 +52,11 @@ class PptSlideLayouts extends AbstractSlide
}
/**
- * Write slide layout relationships to XML format
+ * Write slide layout relationships to XML format.
*
- * @param \PhpOffice\PhpPresentation\Slide\SlideLayout $oSlideLayout
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- public function writeSlideLayoutRelationships(SlideLayout $oSlideLayout)
+ protected function writeSlideLayoutRelationships(SlideLayout $oSlideLayout): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -66,7 +82,7 @@ class PptSlideLayouts extends AbstractSlide
$this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename($oSlideLayout->getRelsIndex()));
$oBackground->relationId = 'rId' . $relId;
- $relId++;
+ ++$relId;
}
$objWriter->endElement();
@@ -76,13 +92,11 @@ class PptSlideLayouts extends AbstractSlide
}
/**
- * Write slide to XML format
+ * Write slide to XML format.
*
- * @param \PhpOffice\PhpPresentation\Slide\SlideLayout $pSlideLayout
* @return string XML Output
- * @throws \Exception
*/
- public function writeSlideLayout(SlideLayout $pSlideLayout)
+ protected function writeSlideLayout(SlideLayout $pSlideLayout): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -96,7 +110,7 @@ class PptSlideLayouts extends AbstractSlide
$objWriter->writeAttribute('preserve', 1);
// p:sldLayout\p:cSld
$objWriter->startElement('p:cSld');
- $objWriter->writeAttributeIf($pSlideLayout->getLayoutName() != '', 'name', $pSlideLayout->getLayoutName());
+ $objWriter->writeAttributeIf('' != $pSlideLayout->getLayoutName(), 'name', $pSlideLayout->getLayoutName());
// Background
$this->writeSlideBackground($pSlideLayout, $objWriter);
// p:sldLayout\p:cSld\p:spTree
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php
old mode 100755
new mode 100644
index 56379c2..a7758a8
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlideMasters.php
@@ -1,25 +1,39 @@
oPresentation->getAllMasterSlides() as $oMasterSlide) {
// Add the relations from the masterSlide to the ZIP file
@@ -38,14 +52,13 @@ class PptSlideMasters extends AbstractSlide
}
/**
- * Write slide master relationships to XML format
+ * Write slide master relationships to XML format.
+ *
+ * @todo Set method in protected
*
- * @param SlideMaster $oMasterSlide
* @return string XML Output
- * @throws \Exception
- * @internal param int $masterId Master slide id
*/
- public function writeSlideMasterRelationships(SlideMaster $oMasterSlide)
+ public function writeSlideMasterRelationships(SlideMaster $oMasterSlide): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -72,7 +85,7 @@ class PptSlideMasters extends AbstractSlide
$this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename($oMasterSlide->getRelsIndex()));
$oBackground->relationId = 'rId' . $relId;
- $relId++;
+ ++$relId;
}
// TODO: Write hyperlink relationships?
@@ -85,13 +98,11 @@ class PptSlideMasters extends AbstractSlide
}
/**
- * Write slide to XML format
+ * Write slide to XML format.
*
- * @param \PhpOffice\PhpPresentation\Slide\SlideMaster $pSlide
* @return string XML Output
- * @throws \Exception
*/
- public function writeSlideMaster(SlideMaster $pSlide)
+ protected function writeSlideMaster(SlideMaster $pSlide): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -178,35 +189,35 @@ class PptSlideMasters extends AbstractSlide
// p:sldMaster\p:txStyles
$objWriter->startElement('p:txStyles');
- foreach (array(
- 'p:titleStyle' => $pSlide->getTextStyles()->getTitleStyle(),
- 'p:bodyStyle' => $pSlide->getTextStyles()->getBodyStyle(),
- 'p:otherStyle' => $pSlide->getTextStyles()->getOtherStyle()
- ) as $startElement => $stylesArray) {
+ foreach ([
+ 'p:titleStyle' => $pSlide->getTextStyles()->getTitleStyle(),
+ 'p:bodyStyle' => $pSlide->getTextStyles()->getBodyStyle(),
+ 'p:otherStyle' => $pSlide->getTextStyles()->getOtherStyle(),
+ ] as $startElement => $stylesArray) {
// titleStyle
$objWriter->startElement($startElement);
foreach ($stylesArray as $lvl => $oParagraph) {
/** @var RichText\Paragraph $oParagraph */
- $elementName = ($lvl == 0 ? 'a:defPPr' : 'a:lvl' . $lvl . 'pPr');
+ $elementName = (0 == $lvl ? 'a:defPPr' : 'a:lvl' . $lvl . 'pPr');
$objWriter->startElement($elementName);
$objWriter->writeAttribute('algn', $oParagraph->getAlignment()->getHorizontal());
$objWriter->writeAttributeIf(
- $oParagraph->getAlignment()->getMarginLeft() != 0,
+ 0 != $oParagraph->getAlignment()->getMarginLeft(),
'marL',
CommonDrawing::pixelsToEmu($oParagraph->getAlignment()->getMarginLeft())
);
$objWriter->writeAttributeIf(
- $oParagraph->getAlignment()->getMarginRight() != 0,
+ 0 != $oParagraph->getAlignment()->getMarginRight(),
'marR',
CommonDrawing::pixelsToEmu($oParagraph->getAlignment()->getMarginRight())
);
$objWriter->writeAttributeIf(
- $oParagraph->getAlignment()->getIndent() != 0,
+ 0 != $oParagraph->getAlignment()->getIndent(),
'indent',
CommonDrawing::pixelsToEmu($oParagraph->getAlignment()->getIndent())
);
$objWriter->startElement('a:defRPr');
- $objWriter->writeAttributeIf($oParagraph->getFont()->getSize() != 10, 'sz', $oParagraph->getFont()->getSize() * 100);
+ $objWriter->writeAttributeIf(10 != $oParagraph->getFont()->getSize(), 'sz', $oParagraph->getFont()->getSize() * 100);
$objWriter->writeAttributeIf($oParagraph->getFont()->isBold(), 'b', 1);
$objWriter->writeAttributeIf($oParagraph->getFont()->isItalic(), 'i', 1);
$objWriter->writeAttribute('kern', '1200');
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlides.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlides.php
old mode 100755
new mode 100644
index 14e73d8..8c2999d
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlides.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptSlides.php
@@ -1,7 +1,26 @@
oPresentation->getAllSlides() as $idx => $oSlide) {
$this->oZip->addFromString('ppt/slides/_rels/slide' . ($idx + 1) . '.xml.rels', $this->writeSlideRelationships($oSlide));
@@ -40,7 +59,7 @@ class PptSlides extends AbstractSlide
// Add background image slide
$oBkgImage = $oSlide->getBackground();
if ($oBkgImage instanceof Image) {
- $this->oZip->addFromString('ppt/media/'.$oBkgImage->getIndexedFilename($idx), file_get_contents($oBkgImage->getPath()));
+ $this->oZip->addFromString('ppt/media/' . $oBkgImage->getIndexedFilename((string) $idx), file_get_contents($oBkgImage->getPath()));
}
}
@@ -48,13 +67,11 @@ class PptSlides extends AbstractSlide
}
/**
- * Write slide relationships to XML format
+ * Write slide relationships to XML format.
*
- * @param \PhpOffice\PhpPresentation\Slide $pSlide
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- protected function writeSlideRelationships(Slide $pSlide)
+ protected function writeSlideRelationships(Slide $pSlide): string
{
//@todo Group all getShapeCollection()->getIterator
@@ -110,9 +127,9 @@ class PptSlides extends AbstractSlide
if ($iterator2->current() instanceof Media) {
// Write relationship for image drawing
$iterator2->current()->relationId = 'rId' . $relId;
- $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/video', '../media/' . $iterator->current()->getIndexedFilename());
+ $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/video', '../media/' . $iterator2->current()->getIndexedFilename());
++$relId;
- $this->writeRelationship($objWriter, $relId, 'http://schemas.microsoft.com/office/2007/relationships/media', '../media/' . $iterator->current()->getIndexedFilename());
+ $this->writeRelationship($objWriter, $relId, 'http://schemas.microsoft.com/office/2007/relationships/media', '../media/' . $iterator2->current()->getIndexedFilename());
++$relId;
} elseif ($iterator2->current() instanceof ShapeDrawing\AbstractDrawingAdapter) {
// Write relationship for image drawing
@@ -138,7 +155,7 @@ class PptSlides extends AbstractSlide
// Write background relationships?
$oBackground = $pSlide->getBackground();
if ($oBackground instanceof Image) {
- $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename($idxSlide));
+ $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename((string) $idxSlide));
$oBackground->relationId = 'rId' . $relId;
++$relId;
}
@@ -151,7 +168,7 @@ class PptSlides extends AbstractSlide
// Hyperlink on shape
if ($iterator->current()->hasHyperlink()) {
// Write relationship for hyperlink
- $hyperlink = $iterator->current()->getHyperlink();
+ $hyperlink = $iterator->current()->getHyperlink();
$hyperlink->relationId = 'rId' . $relId;
if (!$hyperlink->isInternal()) {
@@ -170,7 +187,7 @@ class PptSlides extends AbstractSlide
if ($element instanceof Run || $element instanceof TextElement) {
if ($element->hasHyperlink()) {
// Write relationship for hyperlink
- $hyperlink = $element->getHyperlink();
+ $hyperlink = $element->getHyperlink();
$hyperlink->relationId = 'rId' . $relId;
if (!$hyperlink->isInternal()) {
@@ -190,10 +207,10 @@ class PptSlides extends AbstractSlide
if ($iterator->current() instanceof ShapeTable) {
// Rows
$countRows = count($iterator->current()->getRows());
- for ($row = 0; $row < $countRows; $row++) {
+ for ($row = 0; $row < $countRows; ++$row) {
// Cells in rows
$countCells = count($iterator->current()->getRow($row)->getCells());
- for ($cell = 0; $cell < $countCells; $cell++) {
+ for ($cell = 0; $cell < $countCells; ++$cell) {
$currentCell = $iterator->current()->getRow($row)->getCell($cell);
// Paragraphs in cell
foreach ($currentCell->getParagraphs() as $paragraph) {
@@ -203,7 +220,7 @@ class PptSlides extends AbstractSlide
if ($element instanceof Run || $element instanceof TextElement) {
if ($element->hasHyperlink()) {
// Write relationship for hyperlink
- $hyperlink = $element->getHyperlink();
+ $hyperlink = $element->getHyperlink();
$hyperlink->relationId = 'rId' . $relId;
if (!$hyperlink->isInternal()) {
@@ -227,7 +244,7 @@ class PptSlides extends AbstractSlide
// Hyperlink on shape
if ($iterator2->current()->hasHyperlink()) {
// Write relationship for hyperlink
- $hyperlink = $iterator2->current()->getHyperlink();
+ $hyperlink = $iterator2->current()->getHyperlink();
$hyperlink->relationId = 'rId' . $relId;
if (!$hyperlink->isInternal()) {
@@ -246,7 +263,7 @@ class PptSlides extends AbstractSlide
if ($element instanceof Run || $element instanceof TextElement) {
if ($element->hasHyperlink()) {
// Write relationship for hyperlink
- $hyperlink = $element->getHyperlink();
+ $hyperlink = $element->getHyperlink();
$hyperlink->relationId = 'rId' . $relId;
if (!$hyperlink->isInternal()) {
@@ -266,10 +283,10 @@ class PptSlides extends AbstractSlide
if ($iterator2->current() instanceof ShapeTable) {
// Rows
$countRows = count($iterator2->current()->getRows());
- for ($row = 0; $row < $countRows; $row++) {
+ for ($row = 0; $row < $countRows; ++$row) {
// Cells in rows
$countCells = count($iterator2->current()->getRow($row)->getCells());
- for ($cell = 0; $cell < $countCells; $cell++) {
+ for ($cell = 0; $cell < $countCells; ++$cell) {
$currentCell = $iterator2->current()->getRow($row)->getCell($cell);
// Paragraphs in cell
foreach ($currentCell->getParagraphs() as $paragraph) {
@@ -279,7 +296,7 @@ class PptSlides extends AbstractSlide
if ($element instanceof Run || $element instanceof TextElement) {
if ($element->hasHyperlink()) {
// Write relationship for hyperlink
- $hyperlink = $element->getHyperlink();
+ $hyperlink = $element->getHyperlink();
$hyperlink->relationId = 'rId' . $relId;
if (!$hyperlink->isInternal()) {
@@ -330,13 +347,13 @@ class PptSlides extends AbstractSlide
}
if ($hasSlideComment) {
- $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments', '../comments/comment'.($idxSlide + 1).'.xml');
+ $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments', '../comments/comment' . ($idxSlide + 1) . '.xml');
++$relId;
}
}
if ($pSlide->getNote()->getShapeCollection()->count() > 0) {
- $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide', '../notesSlides/notesSlide'.($idxSlide + 1).'.xml');
+ $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide', '../notesSlides/notesSlide' . ($idxSlide + 1) . '.xml');
}
$objWriter->endElement();
@@ -346,13 +363,11 @@ class PptSlides extends AbstractSlide
}
/**
- * Write slide to XML format
+ * Write slide to XML format.
*
- * @param \PhpOffice\PhpPresentation\Slide $pSlide
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- public function writeSlide(Slide $pSlide)
+ protected function writeSlide(Slide $pSlide): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -499,11 +514,7 @@ class PptSlides extends AbstractSlide
return $objWriter->getData();
}
- /**
- * @param XMLWriter $objWriter
- * @param Slide $oSlide
- */
- protected function writeSlideAnimations(XMLWriter $objWriter, Slide $oSlide)
+ protected function writeSlideAnimations(XMLWriter $objWriter, Slide $oSlide): void
{
$arrayAnimations = $oSlide->getAnimations();
if (empty($arrayAnimations)) {
@@ -513,8 +524,8 @@ class PptSlides extends AbstractSlide
// Variables
$shapeId = 1;
$idCount = 1;
- $hashToIdMap = array();
- $arrayAnimationIds = array();
+ $hashToIdMap = [];
+ $arrayAnimationIds = [];
foreach ($oSlide->getShapeCollection() as $shape) {
$hashToIdMap[$shape->getHashCode()] = ++$shapeId;
@@ -746,127 +757,4 @@ class PptSlides extends AbstractSlide
// ##p:timing
$objWriter->endElement();
}
-
- /**
- * Write pic
- *
- * @param \PhpOffice\Common\XMLWriter $objWriter XML Writer
- * @param \PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter $shape
- * @param int $shapeId
- * @throws \Exception
- */
- protected function writeShapeDrawing(XMLWriter $objWriter, ShapeDrawing\AbstractDrawingAdapter $shape, $shapeId)
- {
- // p:pic
- $objWriter->startElement('p:pic');
-
- // p:nvPicPr
- $objWriter->startElement('p:nvPicPr');
-
- // p:cNvPr
- $objWriter->startElement('p:cNvPr');
- $objWriter->writeAttribute('id', $shapeId);
- $objWriter->writeAttribute('name', $shape->getName());
- $objWriter->writeAttribute('descr', $shape->getDescription());
-
- // a:hlinkClick
- if ($shape->hasHyperlink()) {
- $this->writeHyperlink($objWriter, $shape);
- }
-
- $objWriter->endElement();
-
- // p:cNvPicPr
- $objWriter->startElement('p:cNvPicPr');
-
- // a:picLocks
- $objWriter->startElement('a:picLocks');
- $objWriter->writeAttribute('noChangeAspect', '1');
- $objWriter->endElement();
-
- $objWriter->endElement();
-
- // p:nvPr
- $objWriter->startElement('p:nvPr');
- // PlaceHolder
- if ($shape->isPlaceholder()) {
- $objWriter->startElement('p:ph');
- $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
- $objWriter->endElement();
- }
- /**
- * @link : https://github.com/stefslon/exportToPPTX/blob/master/exportToPPTX.m#L2128
- */
- if ($shape instanceof Media) {
- // p:nvPr > a:videoFile
- $objWriter->startElement('a:videoFile');
- $objWriter->writeAttribute('r:link', $shape->relationId);
- $objWriter->endElement();
- // p:nvPr > p:extLst
- $objWriter->startElement('p:extLst');
- // p:nvPr > p:extLst > p:ext
- $objWriter->startElement('p:ext');
- $objWriter->writeAttribute('uri', '{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}');
- // p:nvPr > p:extLst > p:ext > p14:media
- $objWriter->startElement('p14:media');
- $objWriter->writeAttribute('r:embed', ($shape->relationId + 1));
- $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main');
- // p:nvPr > p:extLst > p:ext > ##p14:media
- $objWriter->endElement();
- // p:nvPr > p:extLst > ##p:ext
- $objWriter->endElement();
- // p:nvPr > ##p:extLst
- $objWriter->endElement();
- }
- // ##p:nvPr
- $objWriter->endElement();
- $objWriter->endElement();
-
- // p:blipFill
- $objWriter->startElement('p:blipFill');
-
- // a:blip
- $objWriter->startElement('a:blip');
- $objWriter->writeAttribute('r:embed', $shape->relationId);
- $objWriter->endElement();
-
- // a:stretch
- $objWriter->startElement('a:stretch');
- $objWriter->writeElement('a:fillRect');
- $objWriter->endElement();
-
- $objWriter->endElement();
-
- // p:spPr
- $objWriter->startElement('p:spPr');
- // a:xfrm
- $objWriter->startElement('a:xfrm');
- $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation()));
-
- // a:off
- $objWriter->startElement('a:off');
- $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
- $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
- $objWriter->endElement();
-
- // a:ext
- $objWriter->startElement('a:ext');
- $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
- $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
- $objWriter->endElement();
-
- $objWriter->endElement();
-
- // a:prstGeom
- $objWriter->startElement('a:prstGeom');
- $objWriter->writeAttribute('prst', 'rect');
- // // a:prstGeom/a:avLst
- $objWriter->writeElement('a:avLst', null);
- // ##a:prstGeom
- $objWriter->endElement();
-
- $objWriter->endElement();
-
- $objWriter->endElement();
- }
}
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php
old mode 100755
new mode 100644
index e9dd71d..ff2c5f8
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptTableProps.php
@@ -1,15 +1,34 @@
oPresentation->getAllMasterSlides() as $oMasterSlide) {
$this->getZip()->addFromString('ppt/theme/theme' . $oMasterSlide->getRelsIndex() . '.xml', $this->writeTheme($oMasterSlide));
@@ -20,16 +38,14 @@ class PptTheme extends AbstractDecoratorWriter
return $this->getZip();
}
-
/**
- * Write theme to XML format
+ * Write theme to XML format.
*
- * @param Slide\SlideMaster $oMasterSlide
* @return string XML Output
*/
- protected function writeTheme(Slide\SlideMaster $oMasterSlide)
+ protected function writeTheme(Slide\SlideMaster $oMasterSlide): string
{
- $arrayFont = array(
+ $arrayFont = [
'Jpan' => 'MS Pゴシック',
'Hang' => '맑은 고딕',
'Hans' => '宋体',
@@ -59,12 +75,12 @@ class PptTheme extends AbstractDecoratorWriter
'Mong' => 'Mongolian Baiti',
'Viet' => 'Times New Roman',
'Uigh' => 'Microsoft Uighur',
- );
+ ];
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
- $name = 'Theme'.rand(1, 100);
+ $name = 'Theme' . rand(1, 100);
// XML header
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
@@ -83,13 +99,13 @@ class PptTheme extends AbstractDecoratorWriter
foreach ($oMasterSlide->getAllSchemeColors() as $oSchemeColor) {
// a:theme/a:themeElements/a:clrScheme/a:*
- $objWriter->startElement('a:'.$oSchemeColor->getValue());
+ $objWriter->startElement('a:' . $oSchemeColor->getValue());
- if (in_array($oSchemeColor->getValue(), array(
- 'dk1', 'lt1'
- ))) {
+ if (in_array($oSchemeColor->getValue(), [
+ 'dk1', 'lt1',
+ ])) {
$objWriter->startElement('a:sysClr');
- $objWriter->writeAttribute('val', ($oSchemeColor->getValue() == 'dk1' ? 'windowText' : 'window'));
+ $objWriter->writeAttribute('val', ('dk1' == $oSchemeColor->getValue() ? 'windowText' : 'window'));
$objWriter->writeAttribute('lastClr', $oSchemeColor->getRGB());
$objWriter->endElement();
} else {
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php
old mode 100755
new mode 100644
index f014fe7..2a89473
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/PptViewProps.php
@@ -1,15 +1,34 @@
startElement('a:sx');
$objWriter->writeAttribute('d', '100');
- $objWriter->writeAttribute('n', (int)($this->getPresentation()->getPresentationProperties()->getZoom() * 100));
+ $objWriter->writeAttribute('n', (int) ($this->getPresentation()->getPresentationProperties()->getZoom() * 100));
$objWriter->endElement();
$objWriter->startElement('a:sy');
$objWriter->writeAttribute('d', '100');
- $objWriter->writeAttribute('n', (int)($this->getPresentation()->getPresentationProperties()->getZoom() * 100));
+ $objWriter->writeAttribute('n', (int) ($this->getPresentation()->getPresentationProperties()->getZoom() * 100));
$objWriter->endElement();
// > // p:viewPr > p:slideViewPr > p:cSldViewPr > p:cViewPr > p:scale
diff --git a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/Relationships.php b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/Relationships.php
old mode 100755
new mode 100644
index ccc5768..9069ed4
--- a/PhpOffice/PhpPresentation/Writer/PowerPoint2007/Relationships.php
+++ b/PhpOffice/PhpPresentation/Writer/PowerPoint2007/Relationships.php
@@ -1,19 +1,38 @@
getZip()->addFromString('_rels/.rels', $this->writeRelationships());
$this->getZip()->addFromString('ppt/_rels/presentation.xml.rels', $this->writePresentationRelationships());
@@ -22,12 +41,11 @@ class Relationships extends AbstractDecoratorWriter
}
/**
- * Write relationships to XML format
+ * Write relationships to XML format.
*
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- public function writeRelationships()
+ protected function writeRelationships(): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -67,12 +85,11 @@ class Relationships extends AbstractDecoratorWriter
}
/**
- * Write presentation relationships to XML format
+ * Write presentation relationships to XML format.
*
- * @return string XML Output
- * @throws \Exception
+ * @return string XML Output
*/
- public function writePresentationRelationships()
+ protected function writePresentationRelationships(): string
{
// Create XML writer
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
@@ -106,7 +123,6 @@ class Relationships extends AbstractDecoratorWriter
$this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps', 'viewProps.xml');
$this->writeRelationship($objWriter, $relationId++, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles', 'tableStyles.xml');
-
// Comments Authors
foreach ($this->getPresentation()->getAllSlides() as $oSlide) {
foreach ($oSlide->getShapeCollection() as $oShape) {
diff --git a/PhpOffice/PhpPresentation/Writer/Serialized.php b/PhpOffice/PhpPresentation/Writer/Serialized.php
old mode 100755
new mode 100644
index 2d57e84..5482c1e
--- a/PhpOffice/PhpPresentation/Writer/Serialized.php
+++ b/PhpOffice/PhpPresentation/Writer/Serialized.php
@@ -10,48 +10,56 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Writer;
use PhpOffice\Common\Adapter\Zip\ZipArchiveAdapter;
use PhpOffice\Common\XMLWriter;
+use PhpOffice\PhpPresentation\Exception\DirectoryNotFoundException;
+use PhpOffice\PhpPresentation\Exception\InvalidParameterException;
use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter;
+use PhpOffice\PhpPresentation\Shape\Drawing\File;
/**
- * \PhpOffice\PhpPresentation\Writer\Serialized
+ * \PhpOffice\PhpPresentation\Writer\Serialized.
*/
class Serialized extends AbstractWriter implements WriterInterface
{
/**
- * Create a new \PhpOffice\PhpPresentation\Writer\Serialized
+ * Create a new \PhpOffice\PhpPresentation\Writer\Serialized.
*
* @param \PhpOffice\PhpPresentation\PhpPresentation $pPhpPresentation
- * @throws \Exception
*/
public function __construct(PhpPresentation $pPhpPresentation = null)
{
// Set PhpPresentation
- $this->setPhpPresentation($pPhpPresentation);
+ $this->setPhpPresentation($pPhpPresentation ?? new PhpPresentation());
// Set ZIP Adapter
$this->setZipAdapter(new ZipArchiveAdapter());
}
/**
- * Save PhpPresentation to file
+ * Save PhpPresentation to file.
*
- * @param string $pFilename
- * @throws \Exception
+ * @throws DirectoryNotFoundException
+ * @throws InvalidParameterException
*/
- public function save($pFilename)
+ public function save(string $pFilename): void
{
if (empty($pFilename)) {
- throw new \Exception("Filename is empty.");
+ throw new InvalidParameterException('pFilename', '');
+ }
+ if (!is_dir(dirname($pFilename))) {
+ throw new DirectoryNotFoundException(dirname($pFilename));
}
$oPresentation = $this->getPhpPresentation();
@@ -67,7 +75,10 @@ class Serialized extends AbstractWriter implements WriterInterface
for ($j = 0; $j < $oPresentation->getSlide($i)->getShapeCollection()->count(); ++$j) {
if ($oPresentation->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) {
$imgTemp = $oPresentation->getSlide($i)->getShapeCollection()->offsetGet($j);
- $objZip->addFromString('media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME), file_get_contents($imgTemp->getPath()));
+ $objZip->addFromString(
+ 'media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME),
+ file_get_contents($imgTemp->getPath())
+ );
}
}
}
@@ -80,14 +91,14 @@ class Serialized extends AbstractWriter implements WriterInterface
}
/**
- * Serialize PhpPresentation object to XML
+ * Serialize PhpPresentation object to XML.
*
- * @param PhpPresentation $pPhpPresentation
- * @param string $pFilename
- * @return string XML Output
- * @throws \Exception
+ * @param PhpPresentation|null $pPhpPresentation
+ * @param string $pFilename
+ *
+ * @return string XML Output
*/
- private function writeSerialized(PhpPresentation $pPhpPresentation = null, $pFilename = '')
+ protected function writeSerialized(PhpPresentation $pPhpPresentation = null, $pFilename = '')
{
// Clone $pPhpPresentation
$pPhpPresentation = clone $pPhpPresentation;
@@ -98,7 +109,12 @@ class Serialized extends AbstractWriter implements WriterInterface
for ($j = 0; $j < $pPhpPresentation->getSlide($i)->getShapeCollection()->count(); ++$j) {
if ($pPhpPresentation->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) {
$imgTemp = $pPhpPresentation->getSlide($i)->getShapeCollection()->offsetGet($j);
- $imgTemp->setPath('zip://' . $pFilename . '#media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME), false);
+ $imgPath = 'zip://' . $pFilename . '#media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME);
+ if ($imgTemp instanceof File) {
+ $imgTemp->setPath($imgPath, false);
+ } else {
+ $imgTemp->setPath($imgPath);
+ }
}
}
}
diff --git a/PhpOffice/PhpPresentation/Writer/WriterInterface.php b/PhpOffice/PhpPresentation/Writer/WriterInterface.php
old mode 100755
new mode 100644
index bf63a35..ab64192
--- a/PhpOffice/PhpPresentation/Writer/WriterInterface.php
+++ b/PhpOffice/PhpPresentation/Writer/WriterInterface.php
@@ -10,23 +10,23 @@
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
*
- * @link https://github.com/PHPOffice/PHPPresentation
+ * @see https://github.com/PHPOffice/PHPPresentation
+ *
* @copyright 2009-2015 PHPPresentation contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
+declare(strict_types=1);
+
namespace PhpOffice\PhpPresentation\Writer;
/**
- * Writer interface
+ * Writer interface.
*/
interface WriterInterface
{
/**
* Save PhpPresentation to file
- *
- * @param string $pFilename
- * @throws \Exception
*/
- public function save($pFilename);
+ public function save(string $pFilename): void;
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/ArrayEnabled.php b/PhpOffice/PhpSpreadsheet/Calculation/ArrayEnabled.php
new file mode 100644
index 0000000..1e3f697
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/ArrayEnabled.php
@@ -0,0 +1,133 @@
+initialise(($arguments === false) ? [] : $arguments);
+ }
+
+ /**
+ * Handles array argument processing when the function accepts a single argument that can be an array argument.
+ * Example use for:
+ * DAYOFMONTH() or FACT().
+ */
+ protected static function evaluateSingleArgumentArray(callable $method, array $values): array
+ {
+ $result = [];
+ foreach ($values as $value) {
+ $result[] = $method($value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * and any of them can be an array argument.
+ * Example use for:
+ * ROUND() or DATE().
+ *
+ * @param mixed ...$arguments
+ */
+ protected static function evaluateArrayArguments(callable $method, ...$arguments): array
+ {
+ self::initialiseHelper($arguments);
+ $arguments = self::$arrayArgumentHelper->arguments();
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * but only the first few (up to limit) can be an array arguments.
+ * Example use for:
+ * NETWORKDAYS() or CONCATENATE(), where the last argument is a matrix (or a series of values) that need
+ * to be treated as a such rather than as an array arguments.
+ *
+ * @param mixed ...$arguments
+ */
+ protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, ...$arguments): array
+ {
+ self::initialiseHelper(array_slice($arguments, 0, $limit));
+ $trailingArguments = array_slice($arguments, $limit);
+ $arguments = self::$arrayArgumentHelper->arguments();
+ $arguments = array_merge($arguments, $trailingArguments);
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+
+ /**
+ * @param mixed $value
+ */
+ private static function testFalse($value): bool
+ {
+ return $value === false;
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * but only the last few (from start) can be an array arguments.
+ * Example use for:
+ * Z.TEST() or INDEX(), where the first argument 1 is a matrix that needs to be treated as a dataset
+ * rather than as an array argument.
+ *
+ * @param mixed ...$arguments
+ */
+ protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, ...$arguments): array
+ {
+ $arrayArgumentsSubset = array_combine(
+ range($start, count($arguments) - $start),
+ array_slice($arguments, $start)
+ );
+ if (self::testFalse($arrayArgumentsSubset)) {
+ return ['#VALUE!'];
+ }
+
+ self::initialiseHelper($arrayArgumentsSubset);
+ $leadingArguments = array_slice($arguments, 0, $start);
+ $arguments = self::$arrayArgumentHelper->arguments();
+ $arguments = array_merge($leadingArguments, $arguments);
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * and any of them can be an array argument except for the one specified by ignore.
+ * Example use for:
+ * HLOOKUP() and VLOOKUP(), where argument 1 is a matrix that needs to be treated as a database
+ * rather than as an array argument.
+ *
+ * @param mixed ...$arguments
+ */
+ protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, ...$arguments): array
+ {
+ $leadingArguments = array_slice($arguments, 0, $ignore);
+ $ignoreArgument = array_slice($arguments, $ignore, 1);
+ $trailingArguments = array_slice($arguments, $ignore + 1);
+
+ self::initialiseHelper(array_merge($leadingArguments, [[null]], $trailingArguments));
+ $arguments = self::$arrayArgumentHelper->arguments();
+
+ array_splice($arguments, $ignore, 1, $ignoreArgument);
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/BinaryComparison.php b/PhpOffice/PhpSpreadsheet/Calculation/BinaryComparison.php
new file mode 100644
index 0000000..5d4f261
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/BinaryComparison.php
@@ -0,0 +1,181 @@
+ '' && $operand1[0] == Calculation::FORMULA_STRING_QUOTE) {
+ $operand1 = Calculation::unwrapResult($operand1);
+ }
+ if (is_string($operand2) && $operand2 > '' && $operand2[0] == Calculation::FORMULA_STRING_QUOTE) {
+ $operand2 = Calculation::unwrapResult($operand2);
+ }
+
+ // Use case insensitive comparaison if not OpenOffice mode
+ if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
+ if (is_string($operand1)) {
+ $operand1 = StringHelper::strToUpper($operand1);
+ }
+ if (is_string($operand2)) {
+ $operand2 = StringHelper::strToUpper($operand2);
+ }
+ }
+
+ $useLowercaseFirstComparison = is_string($operand1) &&
+ is_string($operand2) &&
+ Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
+
+ return self::evaluateComparison($operand1, $operand2, $operator, $useLowercaseFirstComparison);
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function evaluateComparison($operand1, $operand2, string $operator, bool $useLowercaseFirstComparison): bool
+ {
+ switch ($operator) {
+ // Equality
+ case '=':
+ return self::equal($operand1, $operand2);
+ // Greater than
+ case '>':
+ return self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison);
+ // Less than
+ case '<':
+ return self::lessThan($operand1, $operand2, $useLowercaseFirstComparison);
+ // Greater than or equal
+ case '>=':
+ return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison);
+ // Less than or equal
+ case '<=':
+ return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison);
+ // Inequality
+ case '<>':
+ return self::notEqual($operand1, $operand2);
+ default:
+ throw new Exception('Unsupported binary comparison operator');
+ }
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function equal($operand1, $operand2): bool
+ {
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = (abs($operand1 - $operand2) < self::DELTA);
+ } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+ $result = $operand1 == $operand2;
+ } else {
+ $result = self::strcmpAllowNull($operand1, $operand2) == 0;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function greaterThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 > $operand2));
+ } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+ $result = $operand1 >= $operand2;
+ } elseif ($useLowercaseFirstComparison) {
+ $result = self::strcmpLowercaseFirst($operand1, $operand2) >= 0;
+ } else {
+ $result = self::strcmpAllowNull($operand1, $operand2) >= 0;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function lessThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 < $operand2));
+ } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+ $result = $operand1 <= $operand2;
+ } elseif ($useLowercaseFirstComparison) {
+ $result = self::strcmpLowercaseFirst($operand1, $operand2) <= 0;
+ } else {
+ $result = self::strcmpAllowNull($operand1, $operand2) <= 0;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function greaterThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function lessThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ */
+ private static function notEqual($operand1, $operand2): bool
+ {
+ return self::equal($operand1, $operand2) !== true;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Calculation.php b/PhpOffice/PhpSpreadsheet/Calculation/Calculation.php
old mode 100755
new mode 100644
index b16caa1..616784d
--- a/PhpOffice/PhpSpreadsheet/Calculation/Calculation.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Calculation.php
@@ -2,15 +2,23 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Engine\BranchPruner;
use PhpOffice\PhpSpreadsheet\Calculation\Engine\CyclicReferenceStack;
use PhpOffice\PhpSpreadsheet\Calculation\Engine\Logger;
+use PhpOffice\PhpSpreadsheet\Calculation\Engine\Operands;
use PhpOffice\PhpSpreadsheet\Calculation\Token\Stack;
+use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
-use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\DefinedName;
+use PhpOffice\PhpSpreadsheet\ReferenceHelper;
use PhpOffice\PhpSpreadsheet\Shared;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use ReflectionClassConstant;
+use ReflectionMethod;
+use ReflectionParameter;
class Calculation
{
@@ -23,11 +31,21 @@ class Calculation
// Opening bracket
const CALCULATION_REGEXP_OPENBRACE = '\(';
// Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
- const CALCULATION_REGEXP_FUNCTION = '@?(?:_xlfn\.)?([A-Z][A-Z0-9\.]*)[\s]*\(';
+ const CALCULATION_REGEXP_FUNCTION = '@?(?:_xlfn\.)?(?:_xlws\.)?([\p{L}][\p{L}\p{N}\.]*)[\s]*\(';
// Cell reference (cell or range of cells, with or without a sheet reference)
- const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})';
- // Named Range of cells
- const CALCULATION_REGEXP_NAMEDRANGE = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)';
+ const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
+ // Cell reference (with or without a sheet reference) ensuring absolute/relative
+ const CALCULATION_REGEXP_CELLREF_RELATIVE = '((([^\s\(,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?(\$?\b[a-z]{1,3})(\$?\d{1,7})(?![\w.])';
+ const CALCULATION_REGEXP_COLUMN_RANGE = '(((([^\s\(,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\".(?:[^\"]|\"[^!])?\"))!)?(\$?[a-z]{1,3})):(?![.*])';
+ const CALCULATION_REGEXP_ROW_RANGE = '(((([^\s\(,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?(\$?[1-9][0-9]{0,6})):(?![.*])';
+ // Cell reference (with or without a sheet reference) ensuring absolute/relative
+ // Cell ranges ensuring absolute/relative
+ const CALCULATION_REGEXP_COLUMNRANGE_RELATIVE = '(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
+ const CALCULATION_REGEXP_ROWRANGE_RELATIVE = '(\$?\d{1,7}):(\$?\d{1,7})';
+ // Defined Names: Named Range of cells, or Named Formulae
+ const CALCULATION_REGEXP_DEFINEDNAME = '((([^\s,!&%^\/\*\+<>=-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?([_\p{L}][_\p{L}\p{N}\.]*)';
+ // Structured Reference (Fully Qualified and Unqualified)
+ const CALCULATION_REGEXP_STRUCTURED_REFERENCE = '([\p{L}_\\\\][\p{L}\p{N}\._]+)?(\[(?:[^\d\]+-])?)';
// Error
const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?';
@@ -36,19 +54,26 @@ class Calculation
const RETURN_ARRAY_AS_VALUE = 'value';
const RETURN_ARRAY_AS_ARRAY = 'array';
+ const FORMULA_OPEN_FUNCTION_BRACE = '(';
+ const FORMULA_CLOSE_FUNCTION_BRACE = ')';
+ const FORMULA_OPEN_MATRIX_BRACE = '{';
+ const FORMULA_CLOSE_MATRIX_BRACE = '}';
+ const FORMULA_STRING_QUOTE = '"';
+
+ /** @var string */
private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
/**
* Instance of this class.
*
- * @var Calculation
+ * @var ?Calculation
*/
private static $instance;
/**
* Instance of the spreadsheet this Calculation Engine is using.
*
- * @var Spreadsheet
+ * @var ?Spreadsheet
*/
private $spreadsheet;
@@ -67,37 +92,35 @@ class Calculation
private $calculationCacheEnabled = true;
/**
- * Used to generate unique store keys.
- *
- * @var int
+ * @var BranchPruner
*/
- private $branchStoreKeyCounter = 0;
+ private $branchPruner;
+ /**
+ * @var bool
+ */
private $branchPruningEnabled = true;
/**
* List of operators that can be used within formulae
* The true/false value indicates whether it is a binary operator or a unary operator.
- *
- * @var array
*/
- private static $operators = [
+ private const CALCULATION_OPERATORS = [
'+' => true, '-' => true, '*' => true, '/' => true,
'^' => true, '&' => true, '%' => false, '~' => false,
'>' => true, '<' => true, '=' => true, '>=' => true,
- '<=' => true, '<>' => true, '|' => true, ':' => true,
+ '<=' => true, '<>' => true, '∩' => true, '∪' => true,
+ ':' => true,
];
/**
* List of binary operators (those that expect two operands).
- *
- * @var array
*/
- private static $binaryOperators = [
+ private const BINARY_OPERATORS = [
'+' => true, '-' => true, '*' => true, '/' => true,
'^' => true, '&' => true, '>' => true, '<' => true,
'=' => true, '>=' => true, '<=' => true, '<>' => true,
- '|' => true, ':' => true,
+ '∩' => true, '∪' => true, ':' => true,
];
/**
@@ -112,17 +135,29 @@ class Calculation
* If true, then a user error will be triggered
* If false, then an exception will be thrown.
*
- * @var bool
+ * @var ?bool
+ *
+ * @deprecated 1.25.2 use setSuppressFormulaErrors() instead
*/
- public $suppressFormulaErrors = false;
+ public $suppressFormulaErrors;
+
+ /** @var bool */
+ private $suppressFormulaErrorsNew = false;
/**
* Error message for any error that was raised/thrown by the calculation engine.
*
- * @var string
+ * @var null|string
*/
public $formulaError;
+ /**
+ * Reference Helper.
+ *
+ * @var ReferenceHelper
+ */
+ private static $referenceHelper;
+
/**
* An array of the nested cell references accessed by the calculation engine, used for the debug log.
*
@@ -130,6 +165,7 @@ class Calculation
*/
private $cyclicReferenceStack;
+ /** @var array */
private $cellStack = [];
/**
@@ -141,6 +177,7 @@ class Calculation
*/
private $cyclicFormulaCounter = 1;
+ /** @var string */
private $cyclicFormulaCell = '';
/**
@@ -150,13 +187,6 @@ class Calculation
*/
public $cyclicFormulaCount = 1;
- /**
- * Epsilon Precision used for comparisons in calculations.
- *
- * @var float
- */
- private $delta = 0.1e-12;
-
/**
* The current locale setting.
*
@@ -181,24 +211,30 @@ class Calculation
*/
private static $localeArgumentSeparator = ',';
+ /** @var array */
private static $localeFunctions = [];
/**
* Locale-specific translations for Excel constants (True, False and Null).
*
- * @var string[]
+ * @var array
*/
- public static $localeBoolean = [
+ private static $localeBoolean = [
'TRUE' => 'TRUE',
'FALSE' => 'FALSE',
'NULL' => 'NULL',
];
+ public static function getLocaleBoolean(string $index): string
+ {
+ return self::$localeBoolean[$index];
+ }
+
/**
* Excel constant string translations to their PHP equivalents
* Constant conversion from text name/value to actual (datatyped) value.
*
- * @var string[]
+ * @var array
*/
private static $excelConstants = [
'TRUE' => true,
@@ -206,68 +242,105 @@ class Calculation
'NULL' => null,
];
- // PhpSpreadsheet functions
+ public static function keyInExcelConstants(string $key): bool
+ {
+ return array_key_exists($key, self::$excelConstants);
+ }
+
+ /** @return mixed */
+ public static function getExcelConstants(string $key)
+ {
+ return self::$excelConstants[$key];
+ }
+
+ /**
+ * Array of functions usable on Spreadsheet.
+ * In theory, this could be const rather than static;
+ * however, Phpstan breaks trying to analyze it when attempted.
+ *
+ *@var array
+ */
private static $phpSpreadsheetFunctions = [
'ABS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'abs',
+ 'functionCall' => [MathTrig\Absolute::class, 'evaluate'],
'argumentCount' => '1',
],
'ACCRINT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ACCRINT'],
- 'argumentCount' => '4-7',
+ 'functionCall' => [Financial\Securities\AccruedInterest::class, 'periodic'],
+ 'argumentCount' => '4-8',
],
'ACCRINTM' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ACCRINTM'],
+ 'functionCall' => [Financial\Securities\AccruedInterest::class, 'atMaturity'],
'argumentCount' => '3-5',
],
'ACOS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'acos',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'acos'],
'argumentCount' => '1',
],
'ACOSH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'acosh',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'acosh'],
'argumentCount' => '1',
],
'ACOT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ACOT'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'acot'],
'argumentCount' => '1',
],
'ACOTH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ACOTH'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'acoth'],
'argumentCount' => '1',
],
'ADDRESS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'cellAddress'],
+ 'functionCall' => [LookupRef\Address::class, 'cell'],
'argumentCount' => '2-5',
],
+ 'AGGREGATE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3+',
+ ],
'AMORDEGRC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'AMORDEGRC'],
+ 'functionCall' => [Financial\Amortization::class, 'AMORDEGRC'],
'argumentCount' => '6,7',
],
'AMORLINC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'AMORLINC'],
+ 'functionCall' => [Financial\Amortization::class, 'AMORLINC'],
'argumentCount' => '6,7',
],
+ 'ANCHORARRAY' => [
+ 'category' => Category::CATEGORY_UNCATEGORISED,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'AND' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalAnd'],
+ 'functionCall' => [Logical\Operations::class, 'logicalAnd'],
'argumentCount' => '1+',
],
+ 'ARABIC' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Arabic::class, 'evaluate'],
+ 'argumentCount' => '1',
+ ],
'AREAS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1',
],
+ 'ARRAYTOTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'fromArray'],
+ 'argumentCount' => '1,2',
+ ],
'ASC' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
'functionCall' => [Functions::class, 'DUMMY'],
@@ -275,52 +348,52 @@ class Calculation
],
'ASIN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'asin',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'asin'],
'argumentCount' => '1',
],
'ASINH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'asinh',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'asinh'],
'argumentCount' => '1',
],
'ATAN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'atan',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atan'],
'argumentCount' => '1',
],
'ATAN2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ATAN2'],
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atan2'],
'argumentCount' => '2',
],
'ATANH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'atanh',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atanh'],
'argumentCount' => '1',
],
'AVEDEV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVEDEV'],
+ 'functionCall' => [Statistical\Averages::class, 'averageDeviations'],
'argumentCount' => '1+',
],
'AVERAGE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGE'],
+ 'functionCall' => [Statistical\Averages::class, 'average'],
'argumentCount' => '1+',
],
'AVERAGEA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGEA'],
+ 'functionCall' => [Statistical\Averages::class, 'averageA'],
'argumentCount' => '1+',
],
'AVERAGEIF' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGEIF'],
+ 'functionCall' => [Statistical\Conditional::class, 'AVERAGEIF'],
'argumentCount' => '2,3',
],
'AVERAGEIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Conditional::class, 'AVERAGEIFS'],
'argumentCount' => '3+',
],
'BAHTTEXT' => [
@@ -328,85 +401,135 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1',
],
+ 'BASE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Base::class, 'evaluate'],
+ 'argumentCount' => '2,3',
+ ],
'BESSELI' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELI'],
+ 'functionCall' => [Engineering\BesselI::class, 'BESSELI'],
'argumentCount' => '2',
],
'BESSELJ' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELJ'],
+ 'functionCall' => [Engineering\BesselJ::class, 'BESSELJ'],
'argumentCount' => '2',
],
'BESSELK' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELK'],
+ 'functionCall' => [Engineering\BesselK::class, 'BESSELK'],
'argumentCount' => '2',
],
'BESSELY' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELY'],
+ 'functionCall' => [Engineering\BesselY::class, 'BESSELY'],
'argumentCount' => '2',
],
'BETADIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETADIST'],
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'distribution'],
'argumentCount' => '3-5',
],
+ 'BETA.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4-6',
+ ],
'BETAINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETAINV'],
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'],
+ 'argumentCount' => '3-5',
+ ],
+ 'BETA.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'],
'argumentCount' => '3-5',
],
'BIN2DEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTODEC'],
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toDecimal'],
'argumentCount' => '1',
],
'BIN2HEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTOHEX'],
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toHex'],
'argumentCount' => '1,2',
],
'BIN2OCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTOOCT'],
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toOctal'],
'argumentCount' => '1,2',
],
'BINOMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BINOMDIST'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'distribution'],
'argumentCount' => '4',
],
+ 'BINOM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'BINOM.DIST.RANGE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'range'],
+ 'argumentCount' => '3,4',
+ ],
+ 'BINOM.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
'BITAND' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITAND'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITAND'],
'argumentCount' => '2',
],
'BITOR' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITOR'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITOR'],
'argumentCount' => '2',
],
'BITXOR' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITOR'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITXOR'],
'argumentCount' => '2',
],
'BITLSHIFT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITLSHIFT'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITLSHIFT'],
'argumentCount' => '2',
],
'BITRSHIFT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITRSHIFT'],
+ 'functionCall' => [Engineering\BitWise::class, 'BITRSHIFT'],
'argumentCount' => '2',
],
+ 'BYCOL' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'BYROW' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'CEILING' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CEILING'],
- 'argumentCount' => '2',
+ 'functionCall' => [MathTrig\Ceiling::class, 'ceiling'],
+ 'argumentCount' => '1-2', // 2 for Excel, 1-2 for Ods/Gnumeric
+ ],
+ 'CEILING.MATH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Ceiling::class, 'math'],
+ 'argumentCount' => '1-3',
+ ],
+ 'CEILING.PRECISE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Ceiling::class, 'precise'],
+ 'argumentCount' => '1,2',
],
'CELL' => [
'category' => Category::CATEGORY_INFORMATION,
@@ -415,178 +538,239 @@ class Calculation
],
'CHAR' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CHARACTER'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'character'],
'argumentCount' => '1',
],
'CHIDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIDIST'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionLeftTail'],
+ 'argumentCount' => '3',
+ ],
+ 'CHISQ.DIST.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
'argumentCount' => '2',
],
'CHIINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIINV'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseLeftTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.INV.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
'argumentCount' => '2',
],
'CHITEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'test'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'test'],
'argumentCount' => '2',
],
'CHOOSE' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'CHOOSE'],
+ 'functionCall' => [LookupRef\Selection::class, 'CHOOSE'],
+ 'argumentCount' => '2+',
+ ],
+ 'CHOOSECOLS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2+',
+ ],
+ 'CHOOSEROWS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '2+',
],
'CLEAN' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TRIMNONPRINTABLE'],
+ 'functionCall' => [TextData\Trim::class, 'nonPrintable'],
'argumentCount' => '1',
],
'CODE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'ASCIICODE'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'code'],
'argumentCount' => '1',
],
'COLUMN' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'COLUMN'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMN'],
'argumentCount' => '-1',
+ 'passCellReference' => true,
'passByReference' => [true],
],
'COLUMNS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'COLUMNS'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMNS'],
'argumentCount' => '1',
],
'COMBIN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COMBIN'],
+ 'functionCall' => [MathTrig\Combinations::class, 'withoutRepetition'],
+ 'argumentCount' => '2',
+ ],
+ 'COMBINA' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Combinations::class, 'withRepetition'],
'argumentCount' => '2',
],
'COMPLEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'COMPLEX'],
+ 'functionCall' => [Engineering\Complex::class, 'COMPLEX'],
'argumentCount' => '2,3',
],
'CONCAT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CONCATENATE'],
+ 'functionCall' => [TextData\Concatenate::class, 'CONCATENATE'],
'argumentCount' => '1+',
],
'CONCATENATE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CONCATENATE'],
+ 'functionCall' => [TextData\Concatenate::class, 'CONCATENATE'],
'argumentCount' => '1+',
],
'CONFIDENCE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CONFIDENCE'],
+ 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
+ 'argumentCount' => '3',
+ ],
+ 'CONFIDENCE.NORM' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
+ 'argumentCount' => '3',
+ ],
+ 'CONFIDENCE.T' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '3',
],
'CONVERT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'CONVERTUOM'],
+ 'functionCall' => [Engineering\ConvertUOM::class, 'CONVERT'],
'argumentCount' => '3',
],
'CORREL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CORREL'],
+ 'functionCall' => [Statistical\Trends::class, 'CORREL'],
'argumentCount' => '2',
],
'COS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'cos',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'cos'],
'argumentCount' => '1',
],
'COSH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'cosh',
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'cosh'],
'argumentCount' => '1',
],
'COT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COT'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'cot'],
'argumentCount' => '1',
],
'COTH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COTH'],
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'coth'],
'argumentCount' => '1',
],
'COUNT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNT'],
+ 'functionCall' => [Statistical\Counts::class, 'COUNT'],
'argumentCount' => '1+',
],
'COUNTA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTA'],
+ 'functionCall' => [Statistical\Counts::class, 'COUNTA'],
'argumentCount' => '1+',
],
'COUNTBLANK' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTBLANK'],
+ 'functionCall' => [Statistical\Counts::class, 'COUNTBLANK'],
'argumentCount' => '1',
],
'COUNTIF' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTIF'],
+ 'functionCall' => [Statistical\Conditional::class, 'COUNTIF'],
'argumentCount' => '2',
],
'COUNTIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'COUNTIFS'],
'argumentCount' => '2+',
],
'COUPDAYBS' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYBS'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYBS'],
'argumentCount' => '3,4',
],
'COUPDAYS' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYS'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYS'],
'argumentCount' => '3,4',
],
'COUPDAYSNC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYSNC'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYSNC'],
'argumentCount' => '3,4',
],
'COUPNCD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPNCD'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPNCD'],
'argumentCount' => '3,4',
],
'COUPNUM' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPNUM'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPNUM'],
'argumentCount' => '3,4',
],
'COUPPCD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPPCD'],
+ 'functionCall' => [Financial\Coupons::class, 'COUPPCD'],
'argumentCount' => '3,4',
],
'COVAR' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COVAR'],
+ 'functionCall' => [Statistical\Trends::class, 'COVAR'],
+ 'argumentCount' => '2',
+ ],
+ 'COVARIANCE.P' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'COVAR'],
+ 'argumentCount' => '2',
+ ],
+ 'COVARIANCE.S' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '2',
],
'CRITBINOM' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CRITBINOM'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'inverse'],
'argumentCount' => '3',
],
'CSC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CSC'],
+ 'functionCall' => [MathTrig\Trig\Cosecant::class, 'csc'],
'argumentCount' => '1',
],
'CSCH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CSCH'],
+ 'functionCall' => [MathTrig\Trig\Cosecant::class, 'csch'],
'argumentCount' => '1',
],
'CUBEKPIMEMBER' => [
@@ -626,152 +810,172 @@ class Calculation
],
'CUMIPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'CUMIPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Cumulative::class, 'interest'],
'argumentCount' => '6',
],
'CUMPRINC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'CUMPRINC'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Cumulative::class, 'principal'],
'argumentCount' => '6',
],
'DATE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATE'],
+ 'functionCall' => [DateTimeExcel\Date::class, 'fromYMD'],
'argumentCount' => '3',
],
'DATEDIF' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATEDIF'],
+ 'functionCall' => [DateTimeExcel\Difference::class, 'interval'],
'argumentCount' => '2,3',
],
+ 'DATESTRING' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'DATEVALUE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATEVALUE'],
+ 'functionCall' => [DateTimeExcel\DateValue::class, 'fromString'],
'argumentCount' => '1',
],
'DAVERAGE' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DAVERAGE'],
+ 'functionCall' => [Database\DAverage::class, 'evaluate'],
'argumentCount' => '3',
],
'DAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYOFMONTH'],
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'day'],
'argumentCount' => '1',
],
'DAYS' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYS'],
+ 'functionCall' => [DateTimeExcel\Days::class, 'between'],
'argumentCount' => '2',
],
'DAYS360' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYS360'],
+ 'functionCall' => [DateTimeExcel\Days360::class, 'between'],
'argumentCount' => '2,3',
],
'DB' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DB'],
+ 'functionCall' => [Financial\Depreciation::class, 'DB'],
'argumentCount' => '4,5',
],
+ 'DBCS' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
'DCOUNT' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DCOUNT'],
+ 'functionCall' => [Database\DCount::class, 'evaluate'],
'argumentCount' => '3',
],
'DCOUNTA' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DCOUNTA'],
+ 'functionCall' => [Database\DCountA::class, 'evaluate'],
'argumentCount' => '3',
],
'DDB' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DDB'],
+ 'functionCall' => [Financial\Depreciation::class, 'DDB'],
'argumentCount' => '4,5',
],
'DEC2BIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOBIN'],
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toBinary'],
'argumentCount' => '1,2',
],
'DEC2HEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOHEX'],
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toHex'],
'argumentCount' => '1,2',
],
'DEC2OCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOOCT'],
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toOctal'],
'argumentCount' => '1,2',
],
+ 'DECIMAL' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
'DEGREES' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'rad2deg',
+ 'functionCall' => [MathTrig\Angle::class, 'toDegrees'],
'argumentCount' => '1',
],
'DELTA' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DELTA'],
+ 'functionCall' => [Engineering\Compare::class, 'DELTA'],
'argumentCount' => '1,2',
],
'DEVSQ' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'DEVSQ'],
+ 'functionCall' => [Statistical\Deviations::class, 'sumSquares'],
'argumentCount' => '1+',
],
'DGET' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DGET'],
+ 'functionCall' => [Database\DGet::class, 'evaluate'],
'argumentCount' => '3',
],
'DISC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DISC'],
+ 'functionCall' => [Financial\Securities\Rates::class, 'discount'],
'argumentCount' => '4,5',
],
'DMAX' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DMAX'],
+ 'functionCall' => [Database\DMax::class, 'evaluate'],
'argumentCount' => '3',
],
'DMIN' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DMIN'],
+ 'functionCall' => [Database\DMin::class, 'evaluate'],
'argumentCount' => '3',
],
'DOLLAR' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'DOLLAR'],
+ 'functionCall' => [TextData\Format::class, 'DOLLAR'],
'argumentCount' => '1,2',
],
'DOLLARDE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DOLLARDE'],
+ 'functionCall' => [Financial\Dollar::class, 'decimal'],
'argumentCount' => '2',
],
'DOLLARFR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DOLLARFR'],
+ 'functionCall' => [Financial\Dollar::class, 'fractional'],
'argumentCount' => '2',
],
'DPRODUCT' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DPRODUCT'],
+ 'functionCall' => [Database\DProduct::class, 'evaluate'],
'argumentCount' => '3',
],
+ 'DROP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
'DSTDEV' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSTDEV'],
+ 'functionCall' => [Database\DStDev::class, 'evaluate'],
'argumentCount' => '3',
],
'DSTDEVP' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSTDEVP'],
+ 'functionCall' => [Database\DStDevP::class, 'evaluate'],
'argumentCount' => '3',
],
'DSUM' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSUM'],
+ 'functionCall' => [Database\DSum::class, 'evaluate'],
'argumentCount' => '3',
],
'DURATION' => [
@@ -781,87 +985,107 @@ class Calculation
],
'DVAR' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DVAR'],
+ 'functionCall' => [Database\DVar::class, 'evaluate'],
'argumentCount' => '3',
],
'DVARP' => [
'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DVARP'],
+ 'functionCall' => [Database\DVarP::class, 'evaluate'],
'argumentCount' => '3',
],
+ 'ECMA.CEILING' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1,2',
+ ],
'EDATE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'EDATE'],
+ 'functionCall' => [DateTimeExcel\Month::class, 'adjust'],
'argumentCount' => '2',
],
'EFFECT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'EFFECT'],
+ 'functionCall' => [Financial\InterestRate::class, 'effective'],
'argumentCount' => '2',
],
+ 'ENCODEURL' => [
+ 'category' => Category::CATEGORY_WEB,
+ 'functionCall' => [Web\Service::class, 'urlEncode'],
+ 'argumentCount' => '1',
+ ],
'EOMONTH' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'EOMONTH'],
+ 'functionCall' => [DateTimeExcel\Month::class, 'lastDay'],
'argumentCount' => '2',
],
'ERF' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERF'],
+ 'functionCall' => [Engineering\Erf::class, 'ERF'],
'argumentCount' => '1,2',
],
'ERF.PRECISE' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFPRECISE'],
+ 'functionCall' => [Engineering\Erf::class, 'ERFPRECISE'],
'argumentCount' => '1',
],
'ERFC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFC'],
+ 'functionCall' => [Engineering\ErfC::class, 'ERFC'],
'argumentCount' => '1',
],
'ERFC.PRECISE' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFC'],
+ 'functionCall' => [Engineering\ErfC::class, 'ERFC'],
'argumentCount' => '1',
],
'ERROR.TYPE' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'errorType'],
+ 'functionCall' => [Information\ExcelError::class, 'type'],
'argumentCount' => '1',
],
'EVEN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'EVEN'],
+ 'functionCall' => [MathTrig\Round::class, 'even'],
'argumentCount' => '1',
],
'EXACT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'EXACT'],
+ 'functionCall' => [TextData\Text::class, 'exact'],
'argumentCount' => '2',
],
'EXP' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'exp',
+ 'functionCall' => [MathTrig\Exp::class, 'evaluate'],
'argumentCount' => '1',
],
+ 'EXPAND' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
'EXPONDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'EXPONDIST'],
+ 'functionCall' => [Statistical\Distributions\Exponential::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'EXPON.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Exponential::class, 'distribution'],
'argumentCount' => '3',
],
'FACT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FACT'],
+ 'functionCall' => [MathTrig\Factorial::class, 'fact'],
'argumentCount' => '1',
],
'FACTDOUBLE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FACTDOUBLE'],
+ 'functionCall' => [MathTrig\Factorial::class, 'factDouble'],
'argumentCount' => '1',
],
'FALSE' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'FALSE'],
+ 'functionCall' => [Logical\Boolean::class, 'FALSE'],
'argumentCount' => '0',
],
'FDIST' => [
@@ -869,14 +1093,34 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '3',
],
+ 'F.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\F::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'F.DIST.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'FILTER' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Filter::class, 'filter'],
+ 'argumentCount' => '2-3',
+ ],
+ 'FILTERXML' => [
+ 'category' => Category::CATEGORY_WEB,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
'FIND' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'sensitive'],
'argumentCount' => '2,3',
],
'FINDB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'sensitive'],
'argumentCount' => '2,3',
],
'FINV' => [
@@ -884,34 +1128,79 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '3',
],
+ 'F.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'F.INV.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
'FISHER' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FISHER'],
+ 'functionCall' => [Statistical\Distributions\Fisher::class, 'distribution'],
'argumentCount' => '1',
],
'FISHERINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FISHERINV'],
+ 'functionCall' => [Statistical\Distributions\Fisher::class, 'inverse'],
'argumentCount' => '1',
],
'FIXED' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'FIXEDFORMAT'],
+ 'functionCall' => [TextData\Format::class, 'FIXEDFORMAT'],
'argumentCount' => '1-3',
],
'FLOOR' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOOR'],
- 'argumentCount' => '2',
+ 'functionCall' => [MathTrig\Floor::class, 'floor'],
+ 'argumentCount' => '1-2', // Excel requries 2, Ods/Gnumeric 1-2
+ ],
+ 'FLOOR.MATH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Floor::class, 'math'],
+ 'argumentCount' => '1-3',
+ ],
+ 'FLOOR.PRECISE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Floor::class, 'precise'],
+ 'argumentCount' => '1-2',
],
'FORECAST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FORECAST'],
+ 'functionCall' => [Statistical\Trends::class, 'FORECAST'],
+ 'argumentCount' => '3',
+ ],
+ 'FORECAST.ETS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'FORECAST.ETS.CONFINT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'FORECAST.ETS.SEASONALITY' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
+ 'FORECAST.ETS.STAT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'FORECAST.LINEAR' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'FORECAST'],
'argumentCount' => '3',
],
'FORMULATEXT' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'FORMULATEXT'],
+ 'functionCall' => [LookupRef\Formula::class, 'text'],
'argumentCount' => '1',
'passCellReference' => true,
'passByReference' => [true],
@@ -926,44 +1215,74 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '2',
],
+ 'F.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
'FV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'FV'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'futureValue'],
'argumentCount' => '3-5',
],
'FVSCHEDULE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'FVSCHEDULE'],
+ 'functionCall' => [Financial\CashFlow\Single::class, 'futureValue'],
'argumentCount' => '2',
],
+ 'GAMMA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'gamma'],
+ 'argumentCount' => '1',
+ ],
'GAMMADIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMADIST'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'GAMMA.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'],
'argumentCount' => '4',
],
'GAMMAINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAINV'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'GAMMA.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'],
'argumentCount' => '3',
],
'GAMMALN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMALN'],
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'],
+ 'argumentCount' => '1',
+ ],
+ 'GAMMALN.PRECISE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'],
+ 'argumentCount' => '1',
+ ],
+ 'GAUSS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'gauss'],
'argumentCount' => '1',
],
'GCD' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'GCD'],
+ 'functionCall' => [MathTrig\Gcd::class, 'evaluate'],
'argumentCount' => '1+',
],
'GEOMEAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GEOMEAN'],
+ 'functionCall' => [Statistical\Averages\Mean::class, 'geometric'],
'argumentCount' => '1+',
],
'GESTEP' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'GESTEP'],
+ 'functionCall' => [Engineering\Compare::class, 'GESTEP'],
'argumentCount' => '1,2',
],
'GETPIVOTDATA' => [
@@ -973,198 +1292,213 @@ class Calculation
],
'GROWTH' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GROWTH'],
+ 'functionCall' => [Statistical\Trends::class, 'GROWTH'],
'argumentCount' => '1-4',
],
'HARMEAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'HARMEAN'],
+ 'functionCall' => [Statistical\Averages\Mean::class, 'harmonic'],
'argumentCount' => '1+',
],
'HEX2BIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTOBIN'],
+ 'functionCall' => [Engineering\ConvertHex::class, 'toBinary'],
'argumentCount' => '1,2',
],
'HEX2DEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTODEC'],
+ 'functionCall' => [Engineering\ConvertHex::class, 'toDecimal'],
'argumentCount' => '1',
],
'HEX2OCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTOOCT'],
+ 'functionCall' => [Engineering\ConvertHex::class, 'toOctal'],
'argumentCount' => '1,2',
],
'HLOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'HLOOKUP'],
+ 'functionCall' => [LookupRef\HLookup::class, 'lookup'],
'argumentCount' => '3,4',
],
'HOUR' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'HOUROFDAY'],
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'hour'],
'argumentCount' => '1',
],
+ 'HSTACK' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
'HYPERLINK' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'HYPERLINK'],
+ 'functionCall' => [LookupRef\Hyperlink::class, 'set'],
'argumentCount' => '1,2',
'passCellReference' => true,
],
'HYPGEOMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'HYPGEOMDIST'],
+ 'functionCall' => [Statistical\Distributions\HyperGeometric::class, 'distribution'],
'argumentCount' => '4',
],
+ 'HYPGEOM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '5',
+ ],
'IF' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'statementIf'],
+ 'functionCall' => [Logical\Conditional::class, 'statementIf'],
'argumentCount' => '1-3',
],
'IFERROR' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFERROR'],
+ 'functionCall' => [Logical\Conditional::class, 'IFERROR'],
'argumentCount' => '2',
],
'IFNA' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFNA'],
+ 'functionCall' => [Logical\Conditional::class, 'IFNA'],
'argumentCount' => '2',
],
+ 'IFS' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Conditional::class, 'IFS'],
+ 'argumentCount' => '2+',
+ ],
'IMABS' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMABS'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMABS'],
'argumentCount' => '1',
],
'IMAGINARY' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMAGINARY'],
+ 'functionCall' => [Engineering\Complex::class, 'IMAGINARY'],
'argumentCount' => '1',
],
'IMARGUMENT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMARGUMENT'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMARGUMENT'],
'argumentCount' => '1',
],
'IMCONJUGATE' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCONJUGATE'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCONJUGATE'],
'argumentCount' => '1',
],
'IMCOS' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOS'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOS'],
'argumentCount' => '1',
],
'IMCOSH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOSH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOSH'],
'argumentCount' => '1',
],
'IMCOT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOT'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOT'],
'argumentCount' => '1',
],
'IMCSC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCSC'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCSC'],
'argumentCount' => '1',
],
'IMCSCH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCSCH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCSCH'],
'argumentCount' => '1',
],
'IMDIV' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMDIV'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMDIV'],
'argumentCount' => '2',
],
'IMEXP' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMEXP'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMEXP'],
'argumentCount' => '1',
],
'IMLN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLN'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLN'],
'argumentCount' => '1',
],
'IMLOG10' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLOG10'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLOG10'],
'argumentCount' => '1',
],
'IMLOG2' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLOG2'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLOG2'],
'argumentCount' => '1',
],
'IMPOWER' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMPOWER'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMPOWER'],
'argumentCount' => '2',
],
'IMPRODUCT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMPRODUCT'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMPRODUCT'],
'argumentCount' => '1+',
],
'IMREAL' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMREAL'],
+ 'functionCall' => [Engineering\Complex::class, 'IMREAL'],
'argumentCount' => '1',
],
'IMSEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSEC'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSEC'],
'argumentCount' => '1',
],
'IMSECH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSECH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSECH'],
'argumentCount' => '1',
],
'IMSIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSIN'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSIN'],
'argumentCount' => '1',
],
'IMSINH' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSINH'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSINH'],
'argumentCount' => '1',
],
'IMSQRT' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSQRT'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSQRT'],
'argumentCount' => '1',
],
'IMSUB' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSUB'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMSUB'],
'argumentCount' => '2',
],
'IMSUM' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSUM'],
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMSUM'],
'argumentCount' => '1+',
],
'IMTAN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMTAN'],
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMTAN'],
'argumentCount' => '1',
],
'INDEX' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'INDEX'],
- 'argumentCount' => '1-4',
+ 'functionCall' => [LookupRef\Matrix::class, 'index'],
+ 'argumentCount' => '2-4',
],
'INDIRECT' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'INDIRECT'],
+ 'functionCall' => [LookupRef\Indirect::class, 'INDIRECT'],
'argumentCount' => '1,2',
'passCellReference' => true,
],
@@ -1175,101 +1509,118 @@ class Calculation
],
'INT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'INT'],
+ 'functionCall' => [MathTrig\IntClass::class, 'evaluate'],
'argumentCount' => '1',
],
'INTERCEPT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'INTERCEPT'],
+ 'functionCall' => [Statistical\Trends::class, 'INTERCEPT'],
'argumentCount' => '2',
],
'INTRATE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'INTRATE'],
+ 'functionCall' => [Financial\Securities\Rates::class, 'interest'],
'argumentCount' => '4,5',
],
'IPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'IPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'payment'],
'argumentCount' => '4-6',
],
'IRR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'IRR'],
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'rate'],
'argumentCount' => '1,2',
],
'ISBLANK' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isBlank'],
+ 'functionCall' => [Information\Value::class, 'isBlank'],
'argumentCount' => '1',
],
'ISERR' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isErr'],
+ 'functionCall' => [Information\ErrorValue::class, 'isErr'],
'argumentCount' => '1',
],
'ISERROR' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isError'],
+ 'functionCall' => [Information\ErrorValue::class, 'isError'],
'argumentCount' => '1',
],
'ISEVEN' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isEven'],
+ 'functionCall' => [Information\Value::class, 'isEven'],
'argumentCount' => '1',
],
'ISFORMULA' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isFormula'],
+ 'functionCall' => [Information\Value::class, 'isFormula'],
'argumentCount' => '1',
'passCellReference' => true,
'passByReference' => [true],
],
'ISLOGICAL' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isLogical'],
+ 'functionCall' => [Information\Value::class, 'isLogical'],
'argumentCount' => '1',
],
'ISNA' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isNa'],
+ 'functionCall' => [Information\ErrorValue::class, 'isNa'],
'argumentCount' => '1',
],
'ISNONTEXT' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isNonText'],
+ 'functionCall' => [Information\Value::class, 'isNonText'],
'argumentCount' => '1',
],
'ISNUMBER' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isNumber'],
+ 'functionCall' => [Information\Value::class, 'isNumber'],
'argumentCount' => '1',
],
+ 'ISO.CEILING' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1,2',
+ ],
'ISODD' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isOdd'],
+ 'functionCall' => [Information\Value::class, 'isOdd'],
'argumentCount' => '1',
],
+ 'ISOMITTED' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'ISOWEEKNUM' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'ISOWEEKNUM'],
+ 'functionCall' => [DateTimeExcel\Week::class, 'isoWeekNumber'],
'argumentCount' => '1',
],
'ISPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ISPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'schedulePayment'],
'argumentCount' => '4',
],
'ISREF' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Information\Value::class, 'isRef'],
'argumentCount' => '1',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
],
'ISTEXT' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isText'],
+ 'functionCall' => [Information\Value::class, 'isText'],
'argumentCount' => '1',
],
+ 'ISTHAIDIGIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'JIS' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
'functionCall' => [Functions::class, 'DUMMY'],
@@ -1277,107 +1628,137 @@ class Calculation
],
'KURT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'KURT'],
+ 'functionCall' => [Statistical\Deviations::class, 'kurtosis'],
'argumentCount' => '1+',
],
+ 'LAMBDA' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'LARGE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LARGE'],
+ 'functionCall' => [Statistical\Size::class, 'large'],
'argumentCount' => '2',
],
'LCM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'LCM'],
+ 'functionCall' => [MathTrig\Lcm::class, 'evaluate'],
'argumentCount' => '1+',
],
'LEFT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LEFT'],
+ 'functionCall' => [TextData\Extract::class, 'left'],
'argumentCount' => '1,2',
],
'LEFTB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LEFT'],
+ 'functionCall' => [TextData\Extract::class, 'left'],
'argumentCount' => '1,2',
],
'LEN' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'STRINGLENGTH'],
+ 'functionCall' => [TextData\Text::class, 'length'],
'argumentCount' => '1',
],
'LENB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'STRINGLENGTH'],
+ 'functionCall' => [TextData\Text::class, 'length'],
'argumentCount' => '1',
],
+ 'LET' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'LINEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LINEST'],
+ 'functionCall' => [Statistical\Trends::class, 'LINEST'],
'argumentCount' => '1-4',
],
'LN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'log',
+ 'functionCall' => [MathTrig\Logarithms::class, 'natural'],
'argumentCount' => '1',
],
'LOG' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'logBase'],
+ 'functionCall' => [MathTrig\Logarithms::class, 'withBase'],
'argumentCount' => '1,2',
],
'LOG10' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'log10',
+ 'functionCall' => [MathTrig\Logarithms::class, 'base10'],
'argumentCount' => '1',
],
'LOGEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGEST'],
+ 'functionCall' => [Statistical\Trends::class, 'LOGEST'],
'argumentCount' => '1-4',
],
'LOGINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGINV'],
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'inverse'],
'argumentCount' => '3',
],
'LOGNORMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGNORMDIST'],
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'cumulative'],
+ 'argumentCount' => '3',
+ ],
+ 'LOGNORM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'LOGNORM.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'inverse'],
'argumentCount' => '3',
],
'LOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'LOOKUP'],
+ 'functionCall' => [LookupRef\Lookup::class, 'lookup'],
'argumentCount' => '2,3',
],
'LOWER' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LOWERCASE'],
+ 'functionCall' => [TextData\CaseConvert::class, 'lower'],
'argumentCount' => '1',
],
+ 'MAKEARRAY' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'MAP' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'MATCH' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'MATCH'],
+ 'functionCall' => [LookupRef\ExcelMatch::class, 'MATCH'],
'argumentCount' => '2,3',
],
'MAX' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAX'],
+ 'functionCall' => [Statistical\Maximum::class, 'max'],
'argumentCount' => '1+',
],
'MAXA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXA'],
+ 'functionCall' => [Statistical\Maximum::class, 'maxA'],
'argumentCount' => '1+',
],
'MAXIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'MAXIFS'],
'argumentCount' => '3+',
],
'MDETERM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MDETERM'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'determinant'],
'argumentCount' => '1',
],
'MDURATION' => [
@@ -1387,7 +1768,7 @@ class Calculation
],
'MEDIAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MEDIAN'],
+ 'functionCall' => [Statistical\Averages::class, 'median'],
'argumentCount' => '1+',
],
'MEDIANIF' => [
@@ -1397,167 +1778,212 @@ class Calculation
],
'MID' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'MID'],
+ 'functionCall' => [TextData\Extract::class, 'mid'],
'argumentCount' => '3',
],
'MIDB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'MID'],
+ 'functionCall' => [TextData\Extract::class, 'mid'],
'argumentCount' => '3',
],
'MIN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MIN'],
+ 'functionCall' => [Statistical\Minimum::class, 'min'],
'argumentCount' => '1+',
],
'MINA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINA'],
+ 'functionCall' => [Statistical\Minimum::class, 'minA'],
'argumentCount' => '1+',
],
'MINIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'MINIFS'],
'argumentCount' => '3+',
],
'MINUTE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'MINUTE'],
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'minute'],
'argumentCount' => '1',
],
'MINVERSE' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MINVERSE'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'inverse'],
'argumentCount' => '1',
],
'MIRR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'MIRR'],
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'modifiedRate'],
'argumentCount' => '3',
],
'MMULT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MMULT'],
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'multiply'],
'argumentCount' => '2',
],
'MOD' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MOD'],
+ 'functionCall' => [MathTrig\Operations::class, 'mod'],
'argumentCount' => '2',
],
'MODE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MODE'],
+ 'functionCall' => [Statistical\Averages::class, 'mode'],
+ 'argumentCount' => '1+',
+ ],
+ 'MODE.MULT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1+',
],
'MODE.SNGL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MODE'],
+ 'functionCall' => [Statistical\Averages::class, 'mode'],
'argumentCount' => '1+',
],
'MONTH' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'MONTHOFYEAR'],
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'month'],
'argumentCount' => '1',
],
'MROUND' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MROUND'],
+ 'functionCall' => [MathTrig\Round::class, 'multiple'],
'argumentCount' => '2',
],
'MULTINOMIAL' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MULTINOMIAL'],
+ 'functionCall' => [MathTrig\Factorial::class, 'multinomial'],
'argumentCount' => '1+',
],
+ 'MUNIT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'identity'],
+ 'argumentCount' => '1',
+ ],
'N' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'n'],
+ 'functionCall' => [Information\Value::class, 'asNumber'],
'argumentCount' => '1',
],
'NA' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'NA'],
+ 'functionCall' => [Information\ExcelError::class, 'NA'],
'argumentCount' => '0',
],
'NEGBINOMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NEGBINOMDIST'],
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'negative'],
'argumentCount' => '3',
],
+ 'NEGBINOM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4',
+ ],
'NETWORKDAYS' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'NETWORKDAYS'],
- 'argumentCount' => '2+',
+ 'functionCall' => [DateTimeExcel\NetworkDays::class, 'count'],
+ 'argumentCount' => '2-3',
+ ],
+ 'NETWORKDAYS.INTL' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
],
'NOMINAL' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NOMINAL'],
+ 'functionCall' => [Financial\InterestRate::class, 'nominal'],
'argumentCount' => '2',
],
'NORMDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMDIST'],
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'NORM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'distribution'],
'argumentCount' => '4',
],
'NORMINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMINV'],
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'NORM.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'inverse'],
'argumentCount' => '3',
],
'NORMSDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSDIST'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'cumulative'],
'argumentCount' => '1',
],
+ 'NORM.S.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'distribution'],
+ 'argumentCount' => '1,2',
+ ],
'NORMSINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSINV'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'inverse'],
+ 'argumentCount' => '1',
+ ],
+ 'NORM.S.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'inverse'],
'argumentCount' => '1',
],
'NOT' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'NOT'],
+ 'functionCall' => [Logical\Operations::class, 'NOT'],
'argumentCount' => '1',
],
'NOW' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATETIMENOW'],
+ 'functionCall' => [DateTimeExcel\Current::class, 'now'],
'argumentCount' => '0',
],
'NPER' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NPER'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'periods'],
'argumentCount' => '3-5',
],
'NPV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NPV'],
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'presentValue'],
'argumentCount' => '2+',
],
+ 'NUMBERSTRING' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'NUMBERVALUE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'NUMBERVALUE'],
+ 'functionCall' => [TextData\Format::class, 'NUMBERVALUE'],
'argumentCount' => '1+',
],
'OCT2BIN' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTOBIN'],
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toBinary'],
'argumentCount' => '1,2',
],
'OCT2DEC' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTODEC'],
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toDecimal'],
'argumentCount' => '1',
],
'OCT2HEX' => [
'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTOHEX'],
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toHex'],
'argumentCount' => '1,2',
],
'ODD' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ODD'],
+ 'functionCall' => [MathTrig\Round::class, 'odd'],
'argumentCount' => '1',
],
'ODDFPRICE' => [
@@ -1582,39 +2008,64 @@ class Calculation
],
'OFFSET' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'OFFSET'],
+ 'functionCall' => [LookupRef\Offset::class, 'OFFSET'],
'argumentCount' => '3-5',
'passCellReference' => true,
'passByReference' => [true],
],
'OR' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalOr'],
+ 'functionCall' => [Logical\Operations::class, 'logicalOr'],
'argumentCount' => '1+',
],
'PDURATION' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PDURATION'],
+ 'functionCall' => [Financial\CashFlow\Single::class, 'periods'],
'argumentCount' => '3',
],
'PEARSON' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CORREL'],
+ 'functionCall' => [Statistical\Trends::class, 'CORREL'],
'argumentCount' => '2',
],
'PERCENTILE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTILE'],
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTILE'],
+ 'argumentCount' => '2',
+ ],
+ 'PERCENTILE.EXC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'PERCENTILE.INC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTILE'],
'argumentCount' => '2',
],
'PERCENTRANK' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTRANK'],
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTRANK'],
+ 'argumentCount' => '2,3',
+ ],
+ 'PERCENTRANK.EXC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2,3',
+ ],
+ 'PERCENTRANK.INC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTRANK'],
'argumentCount' => '2,3',
],
'PERMUT' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERMUT'],
+ 'functionCall' => [Statistical\Permutations::class, 'PERMUT'],
+ 'argumentCount' => '2',
+ ],
+ 'PERMUTATIONA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Permutations::class, 'PERMUTATIONA'],
'argumentCount' => '2',
],
'PHONETIC' => [
@@ -1622,6 +2073,11 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1',
],
+ 'PHI' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
'PI' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
'functionCall' => 'pi',
@@ -1629,37 +2085,42 @@ class Calculation
],
'PMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Payments::class, 'annuity'],
'argumentCount' => '3-5',
],
'POISSON' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'POISSON'],
+ 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'POISSON.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'],
'argumentCount' => '3',
],
'POWER' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'POWER'],
+ 'functionCall' => [MathTrig\Operations::class, 'power'],
'argumentCount' => '2',
],
'PPMT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PPMT'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Payments::class, 'interestPayment'],
'argumentCount' => '4-6',
],
'PRICE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICE'],
+ 'functionCall' => [Financial\Securities\Price::class, 'price'],
'argumentCount' => '6,7',
],
'PRICEDISC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICEDISC'],
+ 'functionCall' => [Financial\Securities\Price::class, 'priceDiscounted'],
'argumentCount' => '4,5',
],
'PRICEMAT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICEMAT'],
+ 'functionCall' => [Financial\Securities\Price::class, 'priceAtMaturity'],
'argumentCount' => '5,6',
],
'PROB' => [
@@ -1669,123 +2130,164 @@ class Calculation
],
'PRODUCT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'PRODUCT'],
+ 'functionCall' => [MathTrig\Operations::class, 'product'],
'argumentCount' => '1+',
],
'PROPER' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'PROPERCASE'],
+ 'functionCall' => [TextData\CaseConvert::class, 'proper'],
'argumentCount' => '1',
],
'PV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PV'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'presentValue'],
'argumentCount' => '3-5',
],
'QUARTILE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'QUARTILE'],
+ 'functionCall' => [Statistical\Percentiles::class, 'QUARTILE'],
+ 'argumentCount' => '2',
+ ],
+ 'QUARTILE.EXC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'QUARTILE.INC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'QUARTILE'],
'argumentCount' => '2',
],
'QUOTIENT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'QUOTIENT'],
+ 'functionCall' => [MathTrig\Operations::class, 'quotient'],
'argumentCount' => '2',
],
'RADIANS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'deg2rad',
+ 'functionCall' => [MathTrig\Angle::class, 'toRadians'],
'argumentCount' => '1',
],
'RAND' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'RAND'],
+ 'functionCall' => [MathTrig\Random::class, 'rand'],
'argumentCount' => '0',
],
+ 'RANDARRAY' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Random::class, 'randArray'],
+ 'argumentCount' => '0-5',
+ ],
'RANDBETWEEN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'RAND'],
+ 'functionCall' => [MathTrig\Random::class, 'randBetween'],
'argumentCount' => '2',
],
'RANK' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RANK'],
+ 'functionCall' => [Statistical\Percentiles::class, 'RANK'],
+ 'argumentCount' => '2,3',
+ ],
+ 'RANK.AVG' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2,3',
+ ],
+ 'RANK.EQ' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'RANK'],
'argumentCount' => '2,3',
],
'RATE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RATE'],
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'rate'],
'argumentCount' => '3-6',
],
'RECEIVED' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RECEIVED'],
+ 'functionCall' => [Financial\Securities\Price::class, 'received'],
'argumentCount' => '4-5',
],
+ 'REDUCE' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'REPLACE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'REPLACE'],
+ 'functionCall' => [TextData\Replace::class, 'replace'],
'argumentCount' => '4',
],
'REPLACEB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'REPLACE'],
+ 'functionCall' => [TextData\Replace::class, 'replace'],
'argumentCount' => '4',
],
'REPT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => 'str_repeat',
+ 'functionCall' => [TextData\Concatenate::class, 'builtinREPT'],
'argumentCount' => '2',
],
'RIGHT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RIGHT'],
+ 'functionCall' => [TextData\Extract::class, 'right'],
'argumentCount' => '1,2',
],
'RIGHTB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RIGHT'],
+ 'functionCall' => [TextData\Extract::class, 'right'],
'argumentCount' => '1,2',
],
'ROMAN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROMAN'],
+ 'functionCall' => [MathTrig\Roman::class, 'evaluate'],
'argumentCount' => '1,2',
],
'ROUND' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'round',
+ 'functionCall' => [MathTrig\Round::class, 'round'],
'argumentCount' => '2',
],
+ 'ROUNDBAHTDOWN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'ROUNDBAHTUP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'ROUNDDOWN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROUNDDOWN'],
+ 'functionCall' => [MathTrig\Round::class, 'down'],
'argumentCount' => '2',
],
'ROUNDUP' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROUNDUP'],
+ 'functionCall' => [MathTrig\Round::class, 'up'],
'argumentCount' => '2',
],
'ROW' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'ROW'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'ROW'],
'argumentCount' => '-1',
+ 'passCellReference' => true,
'passByReference' => [true],
],
'ROWS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'ROWS'],
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'ROWS'],
'argumentCount' => '1',
],
'RRI' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RRI'],
+ 'functionCall' => [Financial\CashFlow\Single::class, 'interestRate'],
'argumentCount' => '3',
],
'RSQ' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RSQ'],
+ 'functionCall' => [Statistical\Trends::class, 'RSQ'],
'argumentCount' => '2',
],
'RTD' => [
@@ -1795,273 +2297,403 @@ class Calculation
],
'SEARCH' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'insensitive'],
'argumentCount' => '2,3',
],
+ 'SCAN' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'SEARCHB' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'],
+ 'functionCall' => [TextData\Search::class, 'insensitive'],
'argumentCount' => '2,3',
],
'SEC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SEC'],
+ 'functionCall' => [MathTrig\Trig\Secant::class, 'sec'],
'argumentCount' => '1',
],
'SECH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SECH'],
+ 'functionCall' => [MathTrig\Trig\Secant::class, 'sech'],
'argumentCount' => '1',
],
'SECOND' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'SECOND'],
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'second'],
'argumentCount' => '1',
],
+ 'SEQUENCE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'sequence'],
+ 'argumentCount' => '1-4',
+ ],
'SERIESSUM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SERIESSUM'],
+ 'functionCall' => [MathTrig\SeriesSum::class, 'evaluate'],
'argumentCount' => '4',
],
+ 'SHEET' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '0,1',
+ ],
+ 'SHEETS' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '0,1',
+ ],
'SIGN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SIGN'],
+ 'functionCall' => [MathTrig\Sign::class, 'evaluate'],
'argumentCount' => '1',
],
'SIN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sin',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'sin'],
'argumentCount' => '1',
],
+ 'SINGLE' => [
+ 'category' => Category::CATEGORY_UNCATEGORISED,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
'SINH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sinh',
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'sinh'],
'argumentCount' => '1',
],
'SKEW' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SKEW'],
+ 'functionCall' => [Statistical\Deviations::class, 'skew'],
+ 'argumentCount' => '1+',
+ ],
+ 'SKEW.P' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1+',
],
'SLN' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'SLN'],
+ 'functionCall' => [Financial\Depreciation::class, 'SLN'],
'argumentCount' => '3',
],
'SLOPE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SLOPE'],
+ 'functionCall' => [Statistical\Trends::class, 'SLOPE'],
'argumentCount' => '2',
],
'SMALL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SMALL'],
+ 'functionCall' => [Statistical\Size::class, 'small'],
'argumentCount' => '2',
],
+ 'SORT' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Sort::class, 'sort'],
+ 'argumentCount' => '1-4',
+ ],
+ 'SORTBY' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Sort::class, 'sortBy'],
+ 'argumentCount' => '2+',
+ ],
'SQRT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sqrt',
+ 'functionCall' => [MathTrig\Sqrt::class, 'sqrt'],
'argumentCount' => '1',
],
'SQRTPI' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SQRTPI'],
+ 'functionCall' => [MathTrig\Sqrt::class, 'pi'],
'argumentCount' => '1',
],
'STANDARDIZE' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STANDARDIZE'],
+ 'functionCall' => [Statistical\Standardize::class, 'execute'],
'argumentCount' => '3',
],
'STDEV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEV'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEV'],
'argumentCount' => '1+',
],
'STDEV.S' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEV'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEV'],
'argumentCount' => '1+',
],
'STDEV.P' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVP'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
'argumentCount' => '1+',
],
'STDEVA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVA'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVA'],
'argumentCount' => '1+',
],
'STDEVP' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVP'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
'argumentCount' => '1+',
],
'STDEVPA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVPA'],
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVPA'],
'argumentCount' => '1+',
],
'STEYX' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STEYX'],
+ 'functionCall' => [Statistical\Trends::class, 'STEYX'],
'argumentCount' => '2',
],
'SUBSTITUTE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SUBSTITUTE'],
+ 'functionCall' => [TextData\Replace::class, 'substitute'],
'argumentCount' => '3,4',
],
'SUBTOTAL' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUBTOTAL'],
+ 'functionCall' => [MathTrig\Subtotal::class, 'evaluate'],
'argumentCount' => '2+',
'passCellReference' => true,
],
'SUM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUM'],
+ 'functionCall' => [MathTrig\Sum::class, 'sumErroringStrings'],
'argumentCount' => '1+',
],
'SUMIF' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMIF'],
+ 'functionCall' => [Statistical\Conditional::class, 'SUMIF'],
'argumentCount' => '2,3',
],
'SUMIFS' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMIFS'],
+ 'functionCall' => [Statistical\Conditional::class, 'SUMIFS'],
'argumentCount' => '3+',
],
'SUMPRODUCT' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMPRODUCT'],
+ 'functionCall' => [MathTrig\Sum::class, 'product'],
'argumentCount' => '1+',
],
'SUMSQ' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMSQ'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumSquare'],
'argumentCount' => '1+',
],
'SUMX2MY2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMX2MY2'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXSquaredMinusYSquared'],
'argumentCount' => '2',
],
'SUMX2PY2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMX2PY2'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXSquaredPlusYSquared'],
'argumentCount' => '2',
],
'SUMXMY2' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMXMY2'],
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXMinusYSquared'],
'argumentCount' => '2',
],
'SWITCH' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'statementSwitch'],
+ 'functionCall' => [Logical\Conditional::class, 'statementSwitch'],
'argumentCount' => '3+',
],
'SYD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'SYD'],
+ 'functionCall' => [Financial\Depreciation::class, 'SYD'],
'argumentCount' => '4',
],
'T' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RETURNSTRING'],
+ 'functionCall' => [TextData\Text::class, 'test'],
'argumentCount' => '1',
],
+ 'TAKE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
'TAN' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'tan',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'tan'],
'argumentCount' => '1',
],
'TANH' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'tanh',
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'tanh'],
'argumentCount' => '1',
],
'TBILLEQ' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLEQ'],
+ 'functionCall' => [Financial\TreasuryBill::class, 'bondEquivalentYield'],
'argumentCount' => '3',
],
'TBILLPRICE' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLPRICE'],
+ 'functionCall' => [Financial\TreasuryBill::class, 'price'],
'argumentCount' => '3',
],
'TBILLYIELD' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLYIELD'],
+ 'functionCall' => [Financial\TreasuryBill::class, 'yield'],
'argumentCount' => '3',
],
'TDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TDIST'],
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'distribution'],
'argumentCount' => '3',
],
+ 'T.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'T.DIST.2T' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'T.DIST.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
'TEXT' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TEXTFORMAT'],
+ 'functionCall' => [TextData\Format::class, 'TEXTFORMAT'],
'argumentCount' => '2',
],
+ 'TEXTAFTER' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'after'],
+ 'argumentCount' => '2-6',
+ ],
+ 'TEXTBEFORE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'before'],
+ 'argumentCount' => '2-6',
+ ],
'TEXTJOIN' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TEXTJOIN'],
+ 'functionCall' => [TextData\Concatenate::class, 'TEXTJOIN'],
'argumentCount' => '3+',
],
+ 'TEXTSPLIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'split'],
+ 'argumentCount' => '2-6',
+ ],
+ 'THAIDAYOFWEEK' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIDIGIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIMONTHOFYEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAINUMSOUND' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAINUMSTRING' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAISTRINGLENGTH' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIYEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
'TIME' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'TIME'],
+ 'functionCall' => [DateTimeExcel\Time::class, 'fromHMS'],
'argumentCount' => '3',
],
'TIMEVALUE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'TIMEVALUE'],
+ 'functionCall' => [DateTimeExcel\TimeValue::class, 'fromString'],
'argumentCount' => '1',
],
'TINV' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TINV'],
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'inverse'],
+ 'argumentCount' => '2',
+ ],
+ 'T.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'inverse'],
+ 'argumentCount' => '2',
+ ],
+ 'T.INV.2T' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '2',
],
'TODAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATENOW'],
+ 'functionCall' => [DateTimeExcel\Current::class, 'today'],
'argumentCount' => '0',
],
+ 'TOCOL' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1-3',
+ ],
+ 'TOROW' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1-3',
+ ],
'TRANSPOSE' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'TRANSPOSE'],
+ 'functionCall' => [LookupRef\Matrix::class, 'transpose'],
'argumentCount' => '1',
],
'TREND' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TREND'],
+ 'functionCall' => [Statistical\Trends::class, 'TREND'],
'argumentCount' => '1-4',
],
'TRIM' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TRIMSPACES'],
+ 'functionCall' => [TextData\Trim::class, 'spaces'],
'argumentCount' => '1',
],
'TRIMMEAN' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TRIMMEAN'],
+ 'functionCall' => [Statistical\Averages\Mean::class, 'trim'],
'argumentCount' => '2',
],
'TRUE' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'TRUE'],
+ 'functionCall' => [Logical\Boolean::class, 'TRUE'],
'argumentCount' => '0',
],
'TRUNC' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'TRUNC'],
+ 'functionCall' => [MathTrig\Trunc::class, 'evaluate'],
'argumentCount' => '1,2',
],
'TTEST' => [
@@ -2069,64 +2701,79 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '4',
],
+ 'T.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4',
+ ],
'TYPE' => [
'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'TYPE'],
+ 'functionCall' => [Information\Value::class, 'type'],
'argumentCount' => '1',
],
'UNICHAR' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CHARACTER'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'character'],
'argumentCount' => '1',
],
'UNICODE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'ASCIICODE'],
+ 'functionCall' => [TextData\CharacterConvert::class, 'code'],
'argumentCount' => '1',
],
+ 'UNIQUE' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Unique::class, 'unique'],
+ 'argumentCount' => '1+',
+ ],
'UPPER' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'UPPERCASE'],
+ 'functionCall' => [TextData\CaseConvert::class, 'upper'],
'argumentCount' => '1',
],
'USDOLLAR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Financial\Dollar::class, 'format'],
'argumentCount' => '2',
],
'VALUE' => [
'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'VALUE'],
+ 'functionCall' => [TextData\Format::class, 'VALUE'],
'argumentCount' => '1',
],
+ 'VALUETOTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'valueToText'],
+ 'argumentCount' => '1,2',
+ ],
'VAR' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARFunc'],
+ 'functionCall' => [Statistical\Variances::class, 'VAR'],
'argumentCount' => '1+',
],
'VAR.P' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARP'],
+ 'functionCall' => [Statistical\Variances::class, 'VARP'],
'argumentCount' => '1+',
],
'VAR.S' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARFunc'],
+ 'functionCall' => [Statistical\Variances::class, 'VAR'],
'argumentCount' => '1+',
],
'VARA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARA'],
+ 'functionCall' => [Statistical\Variances::class, 'VARA'],
'argumentCount' => '1+',
],
'VARP' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARP'],
+ 'functionCall' => [Statistical\Variances::class, 'VARP'],
'argumentCount' => '1+',
],
'VARPA' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARPA'],
+ 'functionCall' => [Statistical\Variances::class, 'VARPA'],
'argumentCount' => '1+',
],
'VDB' => [
@@ -2136,52 +2783,92 @@ class Calculation
],
'VLOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'VLOOKUP'],
+ 'functionCall' => [LookupRef\VLookup::class, 'lookup'],
'argumentCount' => '3,4',
],
+ 'VSTACK' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
+ 'WEBSERVICE' => [
+ 'category' => Category::CATEGORY_WEB,
+ 'functionCall' => [Web\Service::class, 'webService'],
+ 'argumentCount' => '1',
+ ],
'WEEKDAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WEEKDAY'],
+ 'functionCall' => [DateTimeExcel\Week::class, 'day'],
'argumentCount' => '1,2',
],
'WEEKNUM' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WEEKNUM'],
+ 'functionCall' => [DateTimeExcel\Week::class, 'number'],
'argumentCount' => '1,2',
],
'WEIBULL' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'WEIBULL'],
+ 'functionCall' => [Statistical\Distributions\Weibull::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'WEIBULL.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Weibull::class, 'distribution'],
'argumentCount' => '4',
],
'WORKDAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WORKDAY'],
- 'argumentCount' => '2+',
+ 'functionCall' => [DateTimeExcel\WorkDay::class, 'date'],
+ 'argumentCount' => '2-3',
+ ],
+ 'WORKDAY.INTL' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
+ 'WRAPCOLS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
+ 'WRAPROWS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
],
'XIRR' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'XIRR'],
+ 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'rate'],
'argumentCount' => '2,3',
],
+ 'XLOOKUP' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
'XNPV' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'XNPV'],
+ 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'presentValue'],
'argumentCount' => '3',
],
+ 'XMATCH' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2,3',
+ ],
'XOR' => [
'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalXor'],
+ 'functionCall' => [Logical\Operations::class, 'logicalXor'],
'argumentCount' => '1+',
],
'YEAR' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'YEAR'],
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'year'],
'argumentCount' => '1',
],
'YEARFRAC' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'YEARFRAC'],
+ 'functionCall' => [DateTimeExcel\YearFrac::class, 'fraction'],
'argumentCount' => '2,3',
],
'YIELD' => [
@@ -2191,42 +2878,60 @@ class Calculation
],
'YIELDDISC' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'YIELDDISC'],
+ 'functionCall' => [Financial\Securities\Yields::class, 'yieldDiscounted'],
'argumentCount' => '4,5',
],
'YIELDMAT' => [
'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'YIELDMAT'],
+ 'functionCall' => [Financial\Securities\Yields::class, 'yieldAtMaturity'],
'argumentCount' => '5,6',
],
'ZTEST' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'ZTEST'],
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'zTest'],
+ 'argumentCount' => '2-3',
+ ],
+ 'Z.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'zTest'],
'argumentCount' => '2-3',
],
];
- // Internal functions used for special control purposes
+ /**
+ * Internal functions used for special control purposes.
+ *
+ * @var array
+ */
private static $controlFunctions = [
'MKMATRIX' => [
'argumentCount' => '*',
- 'functionCall' => 'self::mkMatrix',
+ 'functionCall' => [Internal\MakeMatrix::class, 'make'],
+ ],
+ 'NAME.ERROR' => [
+ 'argumentCount' => '*',
+ 'functionCall' => [Functions::class, 'NAME'],
+ ],
+ 'WILDCARDMATCH' => [
+ 'argumentCount' => '2',
+ 'functionCall' => [Internal\WildcardMatch::class, 'compare'],
],
];
- public function __construct(Spreadsheet $spreadsheet = null)
+ public function __construct(?Spreadsheet $spreadsheet = null)
{
- $this->delta = 1 * pow(10, 0 - ini_get('precision'));
-
$this->spreadsheet = $spreadsheet;
$this->cyclicReferenceStack = new CyclicReferenceStack();
$this->debugLog = new Logger($this->cyclicReferenceStack);
+ $this->branchPruner = new BranchPruner($this->branchPruningEnabled);
+ self::$referenceHelper = ReferenceHelper::getInstance();
}
- private static function loadLocales()
+ private static function loadLocales(): void
{
$localeFileDirectory = __DIR__ . '/locale/';
- foreach (glob($localeFileDirectory . '*', GLOB_ONLYDIR) as $filename) {
+ $localeFileNames = glob($localeFileDirectory . '*', GLOB_ONLYDIR) ?: [];
+ foreach ($localeFileNames as $filename) {
$filename = substr($filename, strlen($localeFileDirectory));
if ($filename != 'en') {
self::$validLocaleLanguages[] = $filename;
@@ -2237,12 +2942,10 @@ class Calculation
/**
* Get an instance of this class.
*
- * @param Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
- * or NULL to create a standalone claculation engine
- *
- * @return Calculation
+ * @param ?Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
+ * or NULL to create a standalone calculation engine
*/
- public static function getInstance(Spreadsheet $spreadsheet = null)
+ public static function getInstance(?Spreadsheet $spreadsheet = null): self
{
if ($spreadsheet !== null) {
$instance = $spreadsheet->getCalculationEngine();
@@ -2262,10 +2965,10 @@ class Calculation
* Flush the calculation cache for any existing instance of this class
* but only if a Calculation instance exists.
*/
- public function flushInstance()
+ public function flushInstance(): void
{
$this->clearCalculationCache();
- $this->clearBranchStore();
+ $this->branchPruner->clearBranchStore();
}
/**
@@ -2280,8 +2983,6 @@ class Calculation
/**
* __clone implementation. Cloning should not be allowed in a Singleton!
- *
- * @throws Exception
*/
final public function __clone()
{
@@ -2293,7 +2994,7 @@ class Calculation
*
* @return string locale-specific translation of TRUE
*/
- public static function getTRUE()
+ public static function getTRUE(): string
{
return self::$localeBoolean['TRUE'];
}
@@ -2303,7 +3004,7 @@ class Calculation
*
* @return string locale-specific translation of FALSE
*/
- public static function getFALSE()
+ public static function getFALSE(): string
{
return self::$localeBoolean['FALSE'];
}
@@ -2317,9 +3018,11 @@ class Calculation
*/
public static function setArrayReturnType($returnType)
{
- if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
+ if (
+ ($returnType == self::RETURN_ARRAY_AS_VALUE) ||
($returnType == self::RETURN_ARRAY_AS_ERROR) ||
- ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
+ ($returnType == self::RETURN_ARRAY_AS_ARRAY)
+ ) {
self::$returnArrayAsType = $returnType;
return true;
@@ -2351,18 +3054,18 @@ class Calculation
/**
* Enable/disable calculation cache.
*
- * @param bool $pValue
+ * @param bool $calculationCacheEnabled
*/
- public function setCalculationCacheEnabled($pValue)
+ public function setCalculationCacheEnabled($calculationCacheEnabled): void
{
- $this->calculationCacheEnabled = $pValue;
+ $this->calculationCacheEnabled = $calculationCacheEnabled;
$this->clearCalculationCache();
}
/**
* Enable calculation cache.
*/
- public function enableCalculationCache()
+ public function enableCalculationCache(): void
{
$this->setCalculationCacheEnabled(true);
}
@@ -2370,7 +3073,7 @@ class Calculation
/**
* Disable calculation cache.
*/
- public function disableCalculationCache()
+ public function disableCalculationCache(): void
{
$this->setCalculationCacheEnabled(false);
}
@@ -2378,7 +3081,7 @@ class Calculation
/**
* Clear calculation cache.
*/
- public function clearCalculationCache()
+ public function clearCalculationCache(): void
{
$this->calculationCache = [];
}
@@ -2388,7 +3091,7 @@ class Calculation
*
* @param string $worksheetName
*/
- public function clearCalculationCacheForWorksheet($worksheetName)
+ public function clearCalculationCacheForWorksheet($worksheetName): void
{
if (isset($this->calculationCache[$worksheetName])) {
unset($this->calculationCache[$worksheetName]);
@@ -2401,7 +3104,7 @@ class Calculation
* @param string $fromWorksheetName
* @param string $toWorksheetName
*/
- public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName)
+ public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName): void
{
if (isset($this->calculationCache[$fromWorksheetName])) {
$this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
@@ -2412,29 +3115,24 @@ class Calculation
/**
* Enable/disable calculation cache.
*
- * @param bool $pValue
* @param mixed $enabled
*/
- public function setBranchPruningEnabled($enabled)
+ public function setBranchPruningEnabled($enabled): void
{
$this->branchPruningEnabled = $enabled;
+ $this->branchPruner = new BranchPruner($this->branchPruningEnabled);
}
- public function enableBranchPruning()
+ public function enableBranchPruning(): void
{
$this->setBranchPruningEnabled(true);
}
- public function disableBranchPruning()
+ public function disableBranchPruning(): void
{
$this->setBranchPruningEnabled(false);
}
- public function clearBranchStore()
- {
- $this->branchStoreKeyCounter = 0;
- }
-
/**
* Get the currently defined locale code.
*
@@ -2445,6 +3143,21 @@ class Calculation
return self::$localeLanguage;
}
+ private function getLocaleFile(string $localeDir, string $locale, string $language, string $file): string
+ {
+ $localeFileName = $localeDir . str_replace('_', DIRECTORY_SEPARATOR, $locale) .
+ DIRECTORY_SEPARATOR . $file;
+ if (!file_exists($localeFileName)) {
+ // If there isn't a locale specific file, look for a language specific file
+ $localeFileName = $localeDir . $language . DIRECTORY_SEPARATOR . $file;
+ if (!file_exists($localeFileName)) {
+ throw new Exception('Locale file not found');
+ }
+ }
+
+ return $localeFileName;
+ }
+
/**
* Set the locale code.
*
@@ -2452,7 +3165,7 @@ class Calculation
*
* @return bool
*/
- public function setLocale($locale)
+ public function setLocale(string $locale)
{
// Identify our locale and language
$language = $locale = strtolower($locale);
@@ -2462,32 +3175,31 @@ class Calculation
if (count(self::$validLocaleLanguages) == 1) {
self::loadLocales();
}
+
// Test whether we have any language data for this language (any locale)
- if (in_array($language, self::$validLocaleLanguages)) {
+ if (in_array($language, self::$validLocaleLanguages, true)) {
// initialise language/locale settings
self::$localeFunctions = [];
self::$localeArgumentSeparator = ',';
self::$localeBoolean = ['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') {
+
+ // Default is US English, if user isn't requesting US english, then read the necessary data from the locale files
+ if ($locale !== 'en_us') {
+ $localeDir = implode(DIRECTORY_SEPARATOR, [__DIR__, 'locale', null]);
// Search for a file with a list of function names for locale
- $functionNamesFile = __DIR__ . '/locale/' . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'functions';
- if (!file_exists($functionNamesFile)) {
- // If there isn't a locale specific function file, look for a language specific function file
- $functionNamesFile = __DIR__ . '/locale/' . $language . DIRECTORY_SEPARATOR . 'functions';
- if (!file_exists($functionNamesFile)) {
- return false;
- }
+ try {
+ $functionNamesFile = $this->getLocaleFile($localeDir, $locale, $language, 'functions');
+ } catch (Exception $e) {
+ return false;
}
+
// Retrieve the list of locale or language specific function names
- $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
foreach ($localeFunctions as $localeFunction) {
[$localeFunction] = explode('##', $localeFunction); // Strip out comments
if (strpos($localeFunction, '=') !== false) {
- [$fName, $lfName] = explode('=', $localeFunction);
- $fName = trim($fName);
- $lfName = trim($lfName);
- if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
+ [$fName, $lfName] = array_map('trim', explode('=', $localeFunction));
+ if ((substr($fName, 0, 1) === '*' || isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
self::$localeFunctions[$fName] = $lfName;
}
}
@@ -2500,20 +3212,22 @@ class Calculation
self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
}
- $configFile = __DIR__ . '/locale/' . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'config';
- if (!file_exists($configFile)) {
- $configFile = __DIR__ . '/locale/' . $language . DIRECTORY_SEPARATOR . 'config';
+ try {
+ $configFile = $this->getLocaleFile($localeDir, $locale, $language, 'config');
+ } catch (Exception $e) {
+ return false;
}
- if (file_exists($configFile)) {
- $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- foreach ($localeSettings as $localeSetting) {
- [$localeSetting] = explode('##', $localeSetting); // Strip out comments
- if (strpos($localeSetting, '=') !== false) {
- [$settingName, $settingValue] = explode('=', $localeSetting);
- $settingName = strtoupper(trim($settingName));
+
+ $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
+ foreach ($localeSettings as $localeSetting) {
+ [$localeSetting] = explode('##', $localeSetting); // Strip out comments
+ if (strpos($localeSetting, '=') !== false) {
+ [$settingName, $settingValue] = array_map('trim', explode('=', $localeSetting));
+ $settingName = strtoupper($settingName);
+ if ($settingValue !== '') {
switch ($settingName) {
case 'ARGUMENTSEPARATOR':
- self::$localeArgumentSeparator = trim($settingValue);
+ self::$localeArgumentSeparator = $settingValue;
break;
}
@@ -2532,30 +3246,28 @@ class Calculation
return false;
}
- /**
- * @param string $fromSeparator
- * @param string $toSeparator
- * @param string $formula
- * @param bool $inBraces
- *
- * @return string
- */
- public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
- {
+ public static function translateSeparator(
+ string $fromSeparator,
+ string $toSeparator,
+ string $formula,
+ int &$inBracesLevel,
+ string $openBrace = self::FORMULA_OPEN_FUNCTION_BRACE,
+ string $closeBrace = self::FORMULA_CLOSE_FUNCTION_BRACE
+ ): string {
$strlen = mb_strlen($formula);
for ($i = 0; $i < $strlen; ++$i) {
$chr = mb_substr($formula, $i, 1);
switch ($chr) {
- case '{':
- $inBraces = true;
+ case $openBrace:
+ ++$inBracesLevel;
break;
- case '}':
- $inBraces = false;
+ case $closeBrace:
+ --$inBracesLevel;
break;
case $fromSeparator:
- if (!$inBraces) {
+ if ($inBracesLevel > 0) {
$formula = mb_substr($formula, 0, $i) . $toSeparator . mb_substr($formula, $i + 1);
}
}
@@ -2564,59 +3276,83 @@ class Calculation
return $formula;
}
- /**
- * @param string[] $from
- * @param string[] $to
- * @param string $formula
- * @param string $fromSeparator
- * @param string $toSeparator
- *
- * @return string
- */
- private static function translateFormula(array $from, array $to, $formula, $fromSeparator, $toSeparator)
+ private static function translateFormulaBlock(
+ array $from,
+ array $to,
+ string $formula,
+ int &$inFunctionBracesLevel,
+ int &$inMatrixBracesLevel,
+ string $fromSeparator,
+ string $toSeparator
+ ): string {
+ // Function Names
+ $formula = (string) preg_replace($from, $to, $formula);
+
+ // Temporarily adjust matrix separators so that they won't be confused with function arguments
+ $formula = self::translateSeparator(';', '|', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+ $formula = self::translateSeparator(',', '!', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+ // Function Argument Separators
+ $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inFunctionBracesLevel);
+ // Restore matrix separators
+ $formula = self::translateSeparator('|', ';', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+ $formula = self::translateSeparator('!', ',', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+
+ return $formula;
+ }
+
+ private static function translateFormula(array $from, array $to, string $formula, string $fromSeparator, string $toSeparator): string
{
- // Convert any Excel function names to the required language
+ // Convert any Excel function names and constant names to the required language;
+ // and adjust function argument separators
if (self::$localeLanguage !== 'en_us') {
- $inBraces = 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);
- $i = false;
+ $inFunctionBracesLevel = 0;
+ $inMatrixBracesLevel = 0;
+ // If there is the possibility of separators within a quoted string, then we treat them as literals
+ if (strpos($formula, self::FORMULA_STRING_QUOTE) !== 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(self::FORMULA_STRING_QUOTE, $formula);
+ $notWithinQuotes = false;
foreach ($temp as &$value) {
- // Only count/replace in alternating array entries
- if ($i = !$i) {
- $value = preg_replace($from, $to, $value);
- $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces);
+ // Only adjust in alternating array entries
+ $notWithinQuotes = $notWithinQuotes === false;
+ if ($notWithinQuotes === true) {
+ $value = self::translateFormulaBlock($from, $to, $value, $inFunctionBracesLevel, $inMatrixBracesLevel, $fromSeparator, $toSeparator);
}
}
unset($value);
// Then rebuild the formula string
- $formula = implode('"', $temp);
+ $formula = implode(self::FORMULA_STRING_QUOTE, $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, $inBraces);
+ $formula = self::translateFormulaBlock($from, $to, $formula, $inFunctionBracesLevel, $inMatrixBracesLevel, $fromSeparator, $toSeparator);
}
}
return $formula;
}
- private static $functionReplaceFromExcel = null;
+ /** @var ?array */
+ private static $functionReplaceFromExcel;
- private static $functionReplaceToLocale = null;
+ /** @var ?array */
+ private static $functionReplaceToLocale;
+ /**
+ * @param string $formula
+ *
+ * @return string
+ */
public function _translateFormulaToLocale($formula)
{
+ // Build list of function names and constants for translation
if (self::$functionReplaceFromExcel === null) {
self::$functionReplaceFromExcel = [];
foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
- self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelFunctionName, '/') . '([\s]*\()/Ui';
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelFunctionName, '/') . '([\s]*\()/ui';
}
foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
- self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/Ui';
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/ui';
}
}
@@ -2630,22 +3366,35 @@ class Calculation
}
}
- return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
+ return self::translateFormula(
+ self::$functionReplaceFromExcel,
+ self::$functionReplaceToLocale,
+ $formula,
+ ',',
+ self::$localeArgumentSeparator
+ );
}
- private static $functionReplaceFromLocale = null;
+ /** @var ?array */
+ private static $functionReplaceFromLocale;
- private static $functionReplaceToExcel = null;
+ /** @var ?array */
+ private static $functionReplaceToExcel;
+ /**
+ * @param string $formula
+ *
+ * @return string
+ */
public function _translateFormulaToEnglish($formula)
{
if (self::$functionReplaceFromLocale === null) {
self::$functionReplaceFromLocale = [];
foreach (self::$localeFunctions as $localeFunctionName) {
- self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($localeFunctionName, '/') . '([\s]*\()/Ui';
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($localeFunctionName, '/') . '([\s]*\()/ui';
}
foreach (self::$localeBoolean as $excelBoolean) {
- self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/Ui';
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/ui';
}
}
@@ -2662,6 +3411,11 @@ class Calculation
return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
}
+ /**
+ * @param string $function
+ *
+ * @return string
+ */
public static function localeFunc($function)
{
if (self::$localeLanguage !== 'en_us') {
@@ -2693,11 +3447,12 @@ class Calculation
// Return Excel errors "as is"
return $value;
}
+
// Return strings wrapped in quotes
- return '"' . $value . '"';
- // Convert numeric errors to NaN error
+ return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
} elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
- return Functions::NAN();
+ // Convert numeric errors to NaN error
+ return Information\ExcelError::NAN();
}
return $value;
@@ -2713,12 +3468,12 @@ class Calculation
public static function unwrapResult($value)
{
if (is_string($value)) {
- if ((isset($value[0])) && ($value[0] == '"') && (substr($value, -1) == '"')) {
+ if ((isset($value[0])) && ($value[0] == self::FORMULA_STRING_QUOTE) && (substr($value, -1) == self::FORMULA_STRING_QUOTE)) {
return substr($value, 1, -1);
}
// Convert numeric errors to NAN error
} elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
- return Functions::NAN();
+ return Information\ExcelError::NAN();
}
return $value;
@@ -2728,16 +3483,14 @@ class Calculation
* Calculate cell value (using formula from a cell ID)
* Retained for backward compatibility.
*
- * @param Cell $pCell Cell to calculate
- *
- * @throws Exception
+ * @param Cell $cell Cell to calculate
*
* @return mixed
*/
- public function calculate(Cell $pCell = null)
+ public function calculate(?Cell $cell = null)
{
try {
- return $this->calculateCellValue($pCell);
+ return $this->calculateCellValue($cell);
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
@@ -2746,16 +3499,14 @@ class Calculation
/**
* Calculate the value of a cell formula.
*
- * @param Cell $pCell Cell to calculate
+ * @param Cell $cell Cell to calculate
* @param bool $resetLog Flag indicating whether the debug log should be reset or not
*
- * @throws \PhpOffice\PhpSpreadsheet\Exception
- *
* @return mixed
*/
- public function calculateCellValue(Cell $pCell = null, $resetLog = true)
+ public function calculateCellValue(?Cell $cell = null, $resetLog = true)
{
- if ($pCell === null) {
+ if ($cell === null) {
return null;
}
@@ -2772,17 +3523,38 @@ class Calculation
// Execute the calculation for the cell formula
$this->cellStack[] = [
- 'sheet' => $pCell->getWorksheet()->getTitle(),
- 'cell' => $pCell->getCoordinate(),
+ 'sheet' => $cell->getWorksheet()->getTitle(),
+ 'cell' => $cell->getCoordinate(),
];
+ $cellAddressAttempted = false;
+ $cellAddress = null;
+
try {
- $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
+ $result = self::unwrapResult($this->_calculateFormulaValue($cell->getValue(), $cell->getCoordinate(), $cell));
+ if ($this->spreadsheet === null) {
+ throw new Exception('null spreadsheet in calculateCellValue');
+ }
+ $cellAddressAttempted = true;
$cellAddress = array_pop($this->cellStack);
- $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
+ if ($cellAddress === null) {
+ throw new Exception('null cellAddress in calculateCellValue');
+ }
+ $testSheet = $this->spreadsheet->getSheetByName($cellAddress['sheet']);
+ if ($testSheet === null) {
+ throw new Exception('worksheet not found in calculateCellValue');
+ }
+ $testSheet->getCell($cellAddress['cell']);
} catch (\Exception $e) {
- $cellAddress = array_pop($this->cellStack);
- $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
+ if (!$cellAddressAttempted) {
+ $cellAddress = array_pop($this->cellStack);
+ }
+ if ($this->spreadsheet !== null && is_array($cellAddress) && array_key_exists('sheet', $cellAddress)) {
+ $testSheet = $this->spreadsheet->getSheetByName($cellAddress['sheet']);
+ if ($testSheet !== null && array_key_exists('cell', $cellAddress)) {
+ $testSheet->getCell($cellAddress['cell']);
+ }
+ }
throw new Exception($e->getMessage());
}
@@ -2791,7 +3563,7 @@ class Calculation
self::$returnArrayAsType = $returnArrayAsType;
$testResult = Functions::flattenArray($result);
if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
- return Functions::VALUE();
+ return Information\ExcelError::VALUE();
}
// If there's only a single cell in the array, then we allow it
if (count($testResult) != 1) {
@@ -2799,13 +3571,13 @@ class Calculation
$r = array_keys($result);
$r = array_shift($r);
if (!is_numeric($r)) {
- return Functions::VALUE();
+ return Information\ExcelError::VALUE();
}
if (is_array($result[$r])) {
$c = array_keys($result[$r]);
$c = array_shift($c);
if (!is_numeric($c)) {
- return Functions::VALUE();
+ return Information\ExcelError::VALUE();
}
}
}
@@ -2813,10 +3585,10 @@ class Calculation
}
self::$returnArrayAsType = $returnArrayAsType;
- if ($result === null) {
+ if ($result === null && $cell->getWorksheet()->getSheetView()->getShowZeros()) {
return 0;
} elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
- return Functions::NAN();
+ return Information\ExcelError::NAN();
}
return $result;
@@ -2843,7 +3615,7 @@ class Calculation
}
// Parse the formula and return the token stack
- return $this->_parseFormula($formula);
+ return $this->internalParseFormula($formula);
}
/**
@@ -2851,32 +3623,30 @@ class Calculation
*
* @param string $formula Formula to parse
* @param string $cellID Address of the cell to calculate
- * @param Cell $pCell Cell to calculate
- *
- * @throws \PhpOffice\PhpSpreadsheet\Exception
+ * @param Cell $cell Cell to calculate
*
* @return mixed
*/
- public function calculateFormula($formula, $cellID = null, Cell $pCell = null)
+ public function calculateFormula($formula, $cellID = null, ?Cell $cell = null)
{
// Initialise the logging settings
$this->formulaError = null;
$this->debugLog->clearLog();
$this->cyclicReferenceStack->clear();
- if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
+ $resetCache = $this->getCalculationCacheEnabled();
+ if ($this->spreadsheet !== null && $cellID === null && $cell === null) {
$cellID = 'A1';
- $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
+ $cell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
} else {
// Disable calculation cacheing because it only applies to cell calculations, not straight formulae
// But don't actually flush any cache
- $resetCache = $this->getCalculationCacheEnabled();
$this->calculationCacheEnabled = false;
}
// Execute the calculation
try {
- $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
+ $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $cell));
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
@@ -2890,18 +3660,15 @@ class Calculation
}
/**
- * @param string $cellReference
* @param mixed $cellValue
- *
- * @return bool
*/
- public function getValueFromCache($cellReference, &$cellValue)
+ public function getValueFromCache(string $cellReference, &$cellValue): bool
{
+ $this->debugLog->writeDebugLog('Testing cache value for cell %s', $cellReference);
// Is calculation cacheing enabled?
- // Is the value present in calculation cache?
- $this->debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
+ // If so, is the required value present in calculation cache?
if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
- $this->debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
+ $this->debugLog->writeDebugLog('Retrieving value for cell %s from cache', $cellReference);
// Return the cached result
$cellValue = $this->calculationCache[$cellReference];
@@ -2916,7 +3683,7 @@ class Calculation
* @param string $cellReference
* @param mixed $cellValue
*/
- public function saveValueToCache($cellReference, $cellValue)
+ public function saveValueToCache($cellReference, $cellValue): void
{
if ($this->calculationCacheEnabled) {
$this->calculationCache[$cellReference] = $cellValue;
@@ -2928,18 +3695,16 @@ class Calculation
*
* @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 Cell $pCell Cell to calculate
- *
- * @throws Exception
+ * @param Cell $cell Cell to calculate
*
* @return mixed
*/
- public function _calculateFormulaValue($formula, $cellID = null, Cell $pCell = null)
+ public function _calculateFormulaValue($formula, $cellID = null, ?Cell $cell = null)
{
$cellValue = null;
// Quote-Prefixed cell values cannot be formulae, but are treated as strings
- if ($pCell !== null && $pCell->getStyle()->getQuotePrefix() === true) {
+ if ($cell !== null && $cell->getStyle()->getQuotePrefix() === true) {
return self::wrapResult((string) $formula);
}
@@ -2958,13 +3723,14 @@ class Calculation
return self::wrapResult($formula);
}
- $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
$wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
$wsCellReference = $wsTitle . '!' . $cellID;
if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
return $cellValue;
}
+ $this->debugLog->writeDebugLog('Evaluating formula for cell %s', $wsCellReference);
if (($wsTitle[0] !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
if ($this->cyclicFormulaCount <= 0) {
@@ -2986,9 +3752,11 @@ class Calculation
}
}
+ $this->debugLog->writeDebugLog('Formula for cell %s is %s', $wsCellReference, $formula);
// Parse the formula onto the token stack and calculate the value
$this->cyclicReferenceStack->push($wsCellReference);
- $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
+
+ $cellValue = $this->processTokenStack($this->internalParseFormula($formula, $cell), $cellID, $cell);
$this->cyclicReferenceStack->pop();
// Save to calculation cache
@@ -3003,8 +3771,8 @@ class Calculation
/**
* 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 mixed $operand1 First matrix operand
+ * @param mixed $operand2 Second matrix operand
* @param int $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.
@@ -3048,7 +3816,7 @@ class Calculation
/**
* Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0.
*
- * @param array &$matrix matrix operand
+ * @param array $matrix matrix operand
*
* @return int[] An array comprising the number of rows, and number of columns
*/
@@ -3073,14 +3841,14 @@ class Calculation
/**
* Ensure that paired matrix operands are both matrices of the same size.
*
- * @param mixed &$matrix1 First matrix operand
- * @param mixed &$matrix2 Second matrix operand
+ * @param mixed $matrix1 First matrix operand
+ * @param mixed $matrix2 Second matrix operand
* @param int $matrix1Rows Row size of first matrix operand
* @param int $matrix1Columns Column size of first matrix operand
* @param int $matrix2Rows Row size of second matrix operand
* @param int $matrix2Columns Column size of second matrix operand
*/
- private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
+ private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
{
if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
if ($matrix2Rows < $matrix1Rows) {
@@ -3116,14 +3884,14 @@ class Calculation
/**
* Ensure that paired matrix operands are both matrices of the same size.
*
- * @param mixed &$matrix1 First matrix operand
- * @param mixed &$matrix2 Second matrix operand
+ * @param mixed $matrix1 First matrix operand
+ * @param mixed $matrix2 Second matrix operand
* @param int $matrix1Rows Row size of first matrix operand
* @param int $matrix1Columns Column size of first matrix operand
* @param int $matrix2Rows Row size of second matrix operand
* @param int $matrix2Columns Column size of second matrix operand
*/
- private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
+ private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
{
if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
if ($matrix2Columns < $matrix1Columns) {
@@ -3188,10 +3956,12 @@ class Calculation
}
return '{ ' . implode($rpad, $returnMatrix) . ' }';
- } elseif (is_string($value) && (trim($value, '"') == $value)) {
- return '"' . $value . '"';
+ } elseif (is_string($value) && (trim($value, self::FORMULA_STRING_QUOTE) == $value)) {
+ return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
} elseif (is_bool($value)) {
return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+ } elseif ($value === null) {
+ return self::$localeBoolean['NULL'];
}
}
@@ -3234,43 +4004,46 @@ class Calculation
return $typeString . ' with a value of ' . $this->showValue($value);
}
+
+ return null;
}
/**
* @param string $formula
*
- * @return string
+ * @return false|string False indicates an error
*/
private function convertMatrixReferences($formula)
{
- static $matrixReplaceFrom = ['{', ';', '}'];
+ static $matrixReplaceFrom = [self::FORMULA_OPEN_MATRIX_BRACE, ';', self::FORMULA_CLOSE_MATRIX_BRACE];
static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'];
// Convert any Excel matrix references to the MKMATRIX() function
- if (strpos($formula, '{') !== false) {
+ if (strpos($formula, self::FORMULA_OPEN_MATRIX_BRACE) !== 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) {
+ if (strpos($formula, self::FORMULA_STRING_QUOTE) !== 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);
+ $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
// Open and Closed counts used for trapping mismatched braces in the formula
$openCount = $closeCount = 0;
- $i = false;
+ $notWithinQuotes = false;
foreach ($temp as &$value) {
// Only count/replace in alternating array entries
- if ($i = !$i) {
- $openCount += substr_count($value, '{');
- $closeCount += substr_count($value, '}');
+ $notWithinQuotes = $notWithinQuotes === false;
+ if ($notWithinQuotes === true) {
+ $openCount += substr_count($value, self::FORMULA_OPEN_MATRIX_BRACE);
+ $closeCount += substr_count($value, self::FORMULA_CLOSE_MATRIX_BRACE);
$value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
}
}
unset($value);
// Then rebuild the formula string
- $formula = implode('"', $temp);
+ $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
} else {
// If there's no quoted strings, then we do a simple count/replace
- $openCount = substr_count($formula, '{');
- $closeCount = substr_count($formula, '}');
+ $openCount = substr_count($formula, self::FORMULA_OPEN_MATRIX_BRACE);
+ $closeCount = substr_count($formula, self::FORMULA_CLOSE_MATRIX_BRACE);
$formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
}
// Trap for mismatched braces and trigger an appropriate error
@@ -3292,33 +4065,41 @@ class Calculation
return $formula;
}
- private static function mkMatrix(...$args)
- {
- return $args;
- }
-
- // 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
+ /**
+ * 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.
+ *
+ * @var array
+ */
private static $operatorAssociativity = [
'^' => 0, // Exponentiation
'*' => 0, '/' => 0, // Multiplication and Division
'+' => 0, '-' => 0, // Addition and Subtraction
'&' => 0, // Concatenation
- '|' => 0, ':' => 0, // Intersect and Range
+ '∪' => 0, '∩' => 0, ':' => 0, // Union, 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
+ /**
+ * Comparison (Boolean) Operators.
+ * These operators work on two values, but always return a boolean result.
+ *
+ * @var array
+ */
private static $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true];
- // 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
+ /**
+ * 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.
+ *
+ * @var array
+ */
private static $operatorPrecedence = [
- ':' => 8, // Range
- '|' => 7, // Intersect
+ ':' => 9, // Range
+ '∩' => 8, // Intersect
+ '∪' => 7, // Union
'~' => 6, // Negation
'%' => 5, // Percentage
'^' => 4, // Exponentiation
@@ -3332,11 +4113,10 @@ class Calculation
/**
* @param string $formula
- * @param null|\PhpOffice\PhpSpreadsheet\Cell\Cell $pCell
*
- * @return bool
+ * @return array|false
*/
- private function _parseFormula($formula, Cell $pCell = null)
+ private function internalParseFormula($formula, ?Cell $cell = null)
{
if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
return false;
@@ -3344,153 +4124,115 @@ class Calculation
// 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 = ($pCell !== null) ? $pCell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
- $regexpMatchString = '/^(' . self::CALCULATION_REGEXP_FUNCTION .
- '|' . self::CALCULATION_REGEXP_CELLREF .
- '|' . self::CALCULATION_REGEXP_NUMBER .
- '|' . self::CALCULATION_REGEXP_STRING .
- '|' . self::CALCULATION_REGEXP_OPENBRACE .
- '|' . self::CALCULATION_REGEXP_NAMEDRANGE .
- '|' . self::CALCULATION_REGEXP_ERROR .
- ')/si';
+ $regexpMatchString = '/^((?' . self::CALCULATION_REGEXP_STRING .
+ ')|(?' . self::CALCULATION_REGEXP_FUNCTION .
+ ')|(?' . self::CALCULATION_REGEXP_CELLREF .
+ ')|(?' . self::CALCULATION_REGEXP_COLUMN_RANGE .
+ ')|(?' . self::CALCULATION_REGEXP_ROW_RANGE .
+ ')|(?' . self::CALCULATION_REGEXP_NUMBER .
+ ')|(?' . self::CALCULATION_REGEXP_OPENBRACE .
+ ')|(?' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE .
+ ')|(?' . self::CALCULATION_REGEXP_DEFINEDNAME .
+ ')|(?' . self::CALCULATION_REGEXP_ERROR .
+ '))/sui';
// Start with initialisation
$index = 0;
- $stack = new Stack();
+ $stack = new Stack($this->branchPruner);
$output = [];
$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
+ // - 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
-
- // IF branch pruning
- // currently pending storeKey (last item of the storeKeysStack
- $pendingStoreKey = null;
- // stores a list of storeKeys (string[])
- $pendingStoreKeysStack = [];
- $expectingConditionMap = []; // ['storeKey' => true, ...]
- $expectingThenMap = []; // ['storeKey' => true, ...]
- $expectingElseMap = []; // ['storeKey' => true, ...]
- $parenthesisDepthMap = []; // ['storeKey' => 4, ...]
+ // 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) {
// Branch pruning: we adapt the output item to the context (it will
// be used to limit its computation)
- $currentCondition = null;
- $currentOnlyIf = null;
- $currentOnlyIfNot = null;
- $previousStoreKey = null;
- $pendingStoreKey = end($pendingStoreKeysStack);
-
- if ($this->branchPruningEnabled) {
- // this is a condition ?
- if (isset($expectingConditionMap[$pendingStoreKey]) && $expectingConditionMap[$pendingStoreKey]) {
- $currentCondition = $pendingStoreKey;
- $stackDepth = count($pendingStoreKeysStack);
- if ($stackDepth > 1) { // nested if
- $previousStoreKey = $pendingStoreKeysStack[$stackDepth - 2];
- }
- }
- if (isset($expectingThenMap[$pendingStoreKey]) && $expectingThenMap[$pendingStoreKey]) {
- $currentOnlyIf = $pendingStoreKey;
- } elseif (isset($previousStoreKey)) {
- if (isset($expectingThenMap[$previousStoreKey]) && $expectingThenMap[$previousStoreKey]) {
- $currentOnlyIf = $previousStoreKey;
- }
- }
- if (isset($expectingElseMap[$pendingStoreKey]) && $expectingElseMap[$pendingStoreKey]) {
- $currentOnlyIfNot = $pendingStoreKey;
- } elseif (isset($previousStoreKey)) {
- if (isset($expectingElseMap[$previousStoreKey]) && $expectingElseMap[$previousStoreKey]) {
- $currentOnlyIfNot = $previousStoreKey;
- }
- }
- }
+ $this->branchPruner->initialiseForLoop();
$opCharacter = $formula[$index]; // Get the first character of the value at the current index position
+
+ // Check for two-character operators (e.g. >=, <=, <>)
if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
$opCharacter .= $formula[++$index];
}
+ // Find out if we're currently at the beginning of a number, variable, cell/row/column reference,
+ // function, defined name, structured reference, parenthesis, error or operand
+ $isOperandOrFunction = (bool) preg_match($regexpMatchString, substr($formula, $index), $match);
- // 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);
-
- if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
+ $expectingOperatorCopy = $expectingOperator;
+ if ($opCharacter === '-' && !$expectingOperator) { // Is it a negation instead of a minus?
// Put a negation on the stack
- $stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+ $stack->push('Unary Operator', '~');
++$index; // and drop the negation symbol
- } elseif ($opCharacter == '%' && $expectingOperator) {
+ } elseif ($opCharacter === '%' && $expectingOperator) {
// Put a percentage on the stack
- $stack->push('Unary Operator', '%', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+ $stack->push('Unary Operator', '%');
++$index;
- } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
+ } elseif ($opCharacter === '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
++$index; // Drop the redundant plus symbol
- } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal
+ } elseif ((($opCharacter === '~') || ($opCharacter === '∩') || ($opCharacter === '∪')) && (!$isOperandOrFunction)) {
+ // We have to explicitly deny a tilde, union or intersect because they are legal
return $this->raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression
- } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
- while ($stack->count() > 0 &&
+ } elseif ((isset(self::CALCULATION_OPERATORS[$opCharacter]) || $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
+ while (
+ $stack->count() > 0 &&
($o2 = $stack->last()) &&
- isset(self::$operators[$o2['value']]) &&
- @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
+ isset(self::CALCULATION_OPERATORS[$o2['value']]) &&
+ @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
+ ) {
$output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
}
// Finally put our current operator onto the stack
- $stack->push('Binary Operator', $opCharacter, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+ $stack->push('Binary Operator', $opCharacter);
++$index;
$expectingOperator = false;
- } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
+ } elseif ($opCharacter === ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
$expectingOperand = false;
- while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
- if ($o2 === null) {
- return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"');
- }
+ while (($o2 = $stack->pop()) && $o2['value'] !== '(') { // Pop off the stack back to the last (
$output[] = $o2;
}
$d = $stack->last(2);
// Branch pruning we decrease the depth whether is it a function
// call or a parenthesis
- if (!empty($pendingStoreKey)) {
- $parenthesisDepthMap[$pendingStoreKey] -= 1;
- }
+ $this->branchPruner->decrementDepth();
- if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
- if (!empty($pendingStoreKey) && $parenthesisDepthMap[$pendingStoreKey] == -1) {
- // we are closing an IF(
- if ($d['value'] != 'IF(') {
- return $this->raiseFormulaError('Parser bug we should be in an "IF("');
- }
- if ($expectingConditionMap[$pendingStoreKey]) {
- return $this->raiseFormulaError('We should not be expecting a condition');
- }
- $expectingThenMap[$pendingStoreKey] = false;
- $expectingElseMap[$pendingStoreKey] = false;
- $parenthesisDepthMap[$pendingStoreKey] -= 1;
- array_pop($pendingStoreKeysStack);
- unset($pendingStoreKey);
+ if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'], $matches)) {
+ // Did this parenthesis just close a function?
+ try {
+ $this->branchPruner->closingBrace($d['value']);
+ } catch (Exception $e) {
+ return $this->raiseFormulaError($e->getMessage());
}
$functionName = $matches[1]; // Get the function name
$d = $stack->pop();
- $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack)
+ $argumentCount = $d['value'] ?? 0; // See how many arguments there were (argument count is the next value stored on the stack)
$output[] = $d; // Dump the argument count on the output
$output[] = $stack->pop(); // Pop the function and push onto the output
if (isset(self::$controlFunctions[$functionName])) {
$expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
+ // Scrutinizer says functionCall is unused after this assignment.
+ // It might be right, but I'm too lazy to confirm.
$functionCall = self::$controlFunctions[$functionName]['functionCall'];
+ self::doNothing($functionCall);
} elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) {
$expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount'];
$functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
+ self::doNothing($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;
+ $expectedArgumentCountString = null;
if (is_numeric($expectedArgumentCount)) {
if ($expectedArgumentCount < 0) {
if ($argumentCount > abs($expectedArgumentCount)) {
@@ -3505,6 +4247,7 @@ class Calculation
}
} elseif ($expectedArgumentCount != '*') {
$isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
+ self::doNothing($isOperandOrFunction);
switch ($argMatch[2]) {
case '+':
if ($argumentCount < $argMatch[1]) {
@@ -3534,217 +4277,324 @@ class Calculation
}
}
++$index;
- } elseif ($opCharacter == ',') { // Is this the separator for function arguments?
- if (!empty($pendingStoreKey) &&
- $parenthesisDepthMap[$pendingStoreKey] == 0
- ) {
- // We must go to the IF next argument
- if ($expectingConditionMap[$pendingStoreKey]) {
- $expectingConditionMap[$pendingStoreKey] = false;
- $expectingThenMap[$pendingStoreKey] = true;
- } elseif ($expectingThenMap[$pendingStoreKey]) {
- $expectingThenMap[$pendingStoreKey] = false;
- $expectingElseMap[$pendingStoreKey] = true;
- } elseif ($expectingElseMap[$pendingStoreKey]) {
- return $this->raiseFormulaError('Reaching fourth argument of an IF');
- }
+ } elseif ($opCharacter === ',') { // Is this the separator for function arguments?
+ try {
+ $this->branchPruner->argumentSeparator();
+ } catch (Exception $e) {
+ return $this->raiseFormulaError($e->getMessage());
}
- while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
- if ($o2 === null) {
- return $this->raiseFormulaError('Formula Error: Unexpected ,');
- }
+
+ while (($o2 = $stack->pop()) && $o2['value'] !== '(') { // Pop off the stack back to the last (
$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[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
+ $output[] = ['type' => 'Empty Argument', '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)) {
+ if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'] ?? '', $matches)) {
+ // Can we inject a dummy function at this point so that the braces at least have some context
+ // because at least the braces are paired up (at this stage in the formula)
+ // MS Excel allows this if the content is cell references; but doesn't allow actual values,
+ // but at this point, we can't differentiate (so allow both)
return $this->raiseFormulaError('Formula Error: Unexpected ,');
}
+
+ /** @var array $d */
$d = $stack->pop();
- $itemStoreKey = $d['storeKey'] ?? null;
- $itemOnlyIf = $d['onlyIf'] ?? null;
- $itemOnlyIfNot = $d['onlyIfNot'] ?? null;
- $stack->push($d['type'], ++$d['value'], $d['reference'], $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // increment the argument count
- $stack->push('Brace', '(', null, $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // put the ( back on, we'll need to pop back to it again
+ ++$d['value']; // increment the argument count
+
+ $stack->pushStackItem($d);
+ $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
+
$expectingOperator = false;
$expectingOperand = true;
++$index;
- } elseif ($opCharacter == '(' && !$expectingOperator) {
- if (!empty($pendingStoreKey)) { // Branch pruning: we go deeper
- $parenthesisDepthMap[$pendingStoreKey] += 1;
- }
- $stack->push('Brace', '(', null, $currentCondition, $currentOnlyIf, $currentOnlyIf);
+ } elseif ($opCharacter === '(' && !$expectingOperator) {
+ // Branch pruning: we go deeper
+ $this->branchPruner->incrementDepth();
+ $stack->push('Brace', '(', null);
++$index;
- } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
+ } elseif ($isOperandOrFunction && !$expectingOperatorCopy) {
+ // do we now have a function/variable/number?
$expectingOperator = true;
$expectingOperand = false;
$val = $match[1];
$length = strlen($val);
- if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) {
- $val = preg_replace('/\s/u', '', $val);
+ if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
+ $val = (string) preg_replace('/\s/u', '', $val);
if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
$valToUpper = strtoupper($val);
- // here $matches[1] will contain values like "IF"
- // and $val "IF("
- if ($this->branchPruningEnabled && ($valToUpper == 'IF(')) { // we handle a new if
- $pendingStoreKey = $this->getUnusedBranchStoreKey();
- $pendingStoreKeysStack[] = $pendingStoreKey;
- $expectingConditionMap[$pendingStoreKey] = true;
- $parenthesisDepthMap[$pendingStoreKey] = 0;
- } else { // this is not a if but we good deeper
- if (!empty($pendingStoreKey) && array_key_exists($pendingStoreKey, $parenthesisDepthMap)) {
- $parenthesisDepthMap[$pendingStoreKey] += 1;
- }
- }
-
- $stack->push('Function', $valToUpper, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- // tests if the function is closed right after opening
- $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index + $length), $amatch);
- if ($ax) {
- $stack->push('Operand Count for Function ' . $valToUpper . ')', 0, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- $expectingOperator = true;
- } else {
- $stack->push('Operand Count for Function ' . $valToUpper . ')', 1, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- $expectingOperator = false;
- }
- $stack->push('Brace', '(');
- } else { // it's a var w/ implicit multiplication
- $output[] = ['type' => 'Value', 'value' => $matches[1], 'reference' => null];
+ } else {
+ $valToUpper = 'NAME.ERROR(';
}
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $val, $matches)) {
+ // here $matches[1] will contain values like "IF"
+ // and $val "IF("
+
+ $this->branchPruner->functionCall($valToUpper);
+
+ $stack->push('Function', $valToUpper);
+ // tests if the function is closed right after opening
+ $ax = preg_match('/^\s*\)/u', substr($formula, $index + $length));
+ if ($ax) {
+ $stack->push('Operand Count for Function ' . $valToUpper . ')', 0);
+ $expectingOperator = true;
+ } else {
+ $stack->push('Operand Count for Function ' . $valToUpper . ')', 1);
+ $expectingOperator = false;
+ }
+ $stack->push('Brace', '(');
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $val, $matches)) {
// 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 !== null && $testPrevOp['value'] == ':') {
+ if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
// If we have a worksheet reference, then we're playing with a 3D reference
- if ($matches[2] == '') {
+ 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;
+ $rangeStartCellRef = $output[count($output) - 1]['value'] ?? '';
+ if ($rangeStartCellRef === ':') {
+ // Do we have chained range operators?
+ $rangeStartCellRef = $output[count($output) - 2]['value'] ?? '';
+ }
+ preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $rangeStartCellRef, $rangeStartMatches);
+ if ($rangeStartMatches[2] > '') {
+ $val = $rangeStartMatches[2] . '!' . $val;
}
} else {
- return $this->raiseFormulaError('3D Range references are not yet supported');
+ $rangeStartCellRef = $output[count($output) - 1]['value'] ?? '';
+ if ($rangeStartCellRef === ':') {
+ // Do we have chained range operators?
+ $rangeStartCellRef = $output[count($output) - 2]['value'] ?? '';
+ }
+ preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $rangeStartCellRef, $rangeStartMatches);
+ if ($rangeStartMatches[2] !== $matches[2]) {
+ return $this->raiseFormulaError('3D Range references are not yet supported');
+ }
}
+ } elseif (strpos($val, '!') === false && $pCellParent !== null) {
+ $worksheet = $pCellParent->getTitle();
+ $val = "'{$worksheet}'!{$val}";
}
-
- $outputItem = $stack->getStackItem('Cell Reference', $val, $val, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+ // unescape any apostrophes or double quotes in worksheet name
+ $val = str_replace(["''", '""'], ["'", '"'], $val);
+ $outputItem = $stack->getStackItem('Cell Reference', $val, $val);
$output[] = $outputItem;
- } else { // it's 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 !== null && $testPrevOp['value'] == ':') {
- $startRowColRef = $output[count($output) - 1]['value'];
- [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
- if ($rangeWS1 != '') {
- $rangeWS1 .= '!';
- }
- [$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
- if ($rangeWS2 != '') {
- $rangeWS2 .= '!';
- } else {
- $rangeWS2 = $rangeWS1;
- }
- if ((is_int($startRowColRef)) && (ctype_digit($val)) &&
- ($startRowColRef <= 1048576) && ($val <= 1048576)) {
- // Row range
- $endRowColRef = ($pCellParent !== null) ? $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 = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
- $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1';
- $val = $rangeWS2 . $val . $endRowColRef;
- }
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE . '$/miu', $val, $matches)) {
+ try {
+ $structuredReference = Operands\StructuredReference::fromParser($formula, $index, $matches);
+ } catch (Exception $e) {
+ return $this->raiseFormulaError($e->getMessage());
}
+ $val = $structuredReference->value();
+ $length = strlen($val);
+ $outputItem = $stack->getStackItem(Operands\StructuredReference::NAME, $structuredReference, null);
+
+ $output[] = $outputItem;
+ $expectingOperator = true;
+ } else {
+ // it's a variable, constant, string, number or boolean
$localeConstant = false;
- if ($opCharacter == '"') {
+ $stackItemType = 'Value';
+ $stackItemReference = null;
+
+ // 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 !== null && $testPrevOp['value'] === ':') {
+ $stackItemType = 'Cell Reference';
+
+ if (
+ !is_numeric($val) &&
+ ((ctype_alpha($val) === false || strlen($val) > 3)) &&
+ (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $val) !== false) &&
+ ($this->spreadsheet === null || $this->spreadsheet->getNamedRange($val) !== null)
+ ) {
+ $namedRange = ($this->spreadsheet === null) ? null : $this->spreadsheet->getNamedRange($val);
+ if ($namedRange !== null) {
+ $stackItemType = 'Defined Name';
+ $address = str_replace('$', '', $namedRange->getValue());
+ $stackItemReference = $val;
+ if (strpos($address, ':') !== false) {
+ // We'll need to manipulate the stack for an actual named range rather than a named cell
+ $fromTo = explode(':', $address);
+ $to = array_pop($fromTo);
+ foreach ($fromTo as $from) {
+ $output[] = $stack->getStackItem($stackItemType, $from, $stackItemReference);
+ $output[] = $stack->getStackItem('Binary Operator', ':');
+ }
+ $address = $to;
+ }
+ $val = $address;
+ }
+ } else {
+ $startRowColRef = $output[count($output) - 1]['value'] ?? '';
+ [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
+ $rangeSheetRef = $rangeWS1;
+ if ($rangeWS1 !== '') {
+ $rangeWS1 .= '!';
+ }
+ $rangeSheetRef = trim($rangeSheetRef, "'");
+ [$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
+ if ($rangeWS2 !== '') {
+ $rangeWS2 .= '!';
+ } else {
+ $rangeWS2 = $rangeWS1;
+ }
+
+ $refSheet = $pCellParent;
+ if ($pCellParent !== null && $rangeSheetRef !== '' && $rangeSheetRef !== $pCellParent->getTitle()) {
+ $refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef);
+ }
+
+ if (ctype_digit($val) && $val <= 1048576) {
+ // Row range
+ $stackItemType = 'Row Reference';
+ /** @var int $valx */
+ $valx = $val;
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($valx) : AddressRange::MAX_COLUMN; // Max 16,384 columns for Excel2007
+ $val = "{$rangeWS2}{$endRowColRef}{$val}";
+ } elseif (ctype_alpha($val) && strlen($val) <= 3) {
+ // Column range
+ $stackItemType = 'Column Reference';
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : AddressRange::MAX_ROW; // Max 1,048,576 rows for Excel2007
+ $val = "{$rangeWS2}{$val}{$endRowColRef}";
+ }
+ $stackItemReference = $val;
+ }
+ } elseif ($opCharacter === self::FORMULA_STRING_QUOTE) {
// UnEscape any quotes within the string
- $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val)));
+ $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val)));
+ } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
+ $stackItemType = 'Constant';
+ $excelConstant = trim(strtoupper($val));
+ $val = self::$excelConstants[$excelConstant];
+ $stackItemReference = $excelConstant;
+ } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
+ $stackItemType = 'Constant';
+ $val = self::$excelConstants[$localeConstant];
+ $stackItemReference = $localeConstant;
+ } elseif (
+ preg_match('/^' . self::CALCULATION_REGEXP_ROW_RANGE . '/miu', substr($formula, $index), $rowRangeReference)
+ ) {
+ $val = $rowRangeReference[1];
+ $length = strlen($rowRangeReference[1]);
+ $stackItemType = 'Row Reference';
+ // unescape any apostrophes or double quotes in worksheet name
+ $val = str_replace(["''", '""'], ["'", '"'], $val);
+ $column = 'A';
+ if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) {
+ $column = $pCellParent->getHighestDataColumn($val);
+ }
+ $val = "{$rowRangeReference[2]}{$column}{$rowRangeReference[7]}";
+ $stackItemReference = $val;
+ } elseif (
+ preg_match('/^' . self::CALCULATION_REGEXP_COLUMN_RANGE . '/miu', substr($formula, $index), $columnRangeReference)
+ ) {
+ $val = $columnRangeReference[1];
+ $length = strlen($val);
+ $stackItemType = 'Column Reference';
+ // unescape any apostrophes or double quotes in worksheet name
+ $val = str_replace(["''", '""'], ["'", '"'], $val);
+ $row = '1';
+ if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) {
+ $row = $pCellParent->getHighestDataRow($val);
+ }
+ $val = "{$val}{$row}";
+ $stackItemReference = $val;
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', $val, $match)) {
+ $stackItemType = 'Defined Name';
+ $stackItemReference = $val;
} elseif (is_numeric($val)) {
- if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+ if ((strpos((string) $val, '.') !== false) || (stripos((string) $val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
$val = (float) $val;
} else {
$val = (int) $val;
}
- } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
- $excelConstant = trim(strtoupper($val));
- $val = self::$excelConstants[$excelConstant];
- } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
- $val = self::$excelConstants[$localeConstant];
}
- $details = $stack->getStackItem('Value', $val, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+
+ $details = $stack->getStackItem($stackItemType, $val, $stackItemReference);
if ($localeConstant) {
$details['localeValue'] = $localeConstant;
}
$output[] = $details;
}
$index += $length;
- } elseif ($opCharacter == '$') { // absolute row or column range
+ } elseif ($opCharacter === '$') { // absolute row or column range
++$index;
- } elseif ($opCharacter == ')') { // miscellaneous error checking
+ } elseif ($opCharacter === ')') { // miscellaneous error checking
if ($expectingOperand) {
- $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
+ $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => 'NULL'];
$expectingOperand = false;
$expectingOperator = true;
} else {
return $this->raiseFormulaError("Formula Error: Unexpected ')'");
}
- } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
+ } elseif (isset(self::CALCULATION_OPERATORS[$opCharacter]) && !$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');
+ return $this->raiseFormulaError('Formula Error: An unexpected error occurred');
}
// Test for end of formula string
if ($index == strlen($formula)) {
// Did we end with an operator?.
// Only valid for the % unary operator
- if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
+ if ((isset(self::CALCULATION_OPERATORS[$opCharacter])) && ($opCharacter != '%')) {
return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
}
break;
}
// Ignore white space
- while (($formula[$index] == "\n") || ($formula[$index] == "\r")) {
+ while (($formula[$index] === "\n") || ($formula[$index] === "\r")) {
++$index;
}
- if ($formula[$index] == ' ') {
- while ($formula[$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
- if (($expectingOperator) && (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
- ($output[count($output) - 1]['type'] == 'Cell Reference')) {
- while ($stack->count() > 0 &&
+ // Cell References, Defined Names or Structured References) then we have an INTERSECTION operator
+ $countOutputMinus1 = count($output) - 1;
+ if (
+ ($expectingOperator) &&
+ array_key_exists($countOutputMinus1, $output) &&
+ is_array($output[$countOutputMinus1]) &&
+ array_key_exists('type', $output[$countOutputMinus1]) &&
+ (
+ (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/miu', substr($formula, $index), $match)) &&
+ ($output[$countOutputMinus1]['type'] === 'Cell Reference') ||
+ (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match)) &&
+ ($output[$countOutputMinus1]['type'] === 'Defined Name' || $output[$countOutputMinus1]['type'] === 'Value') ||
+ (preg_match('/^' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE . '.*/miu', substr($formula, $index), $match)) &&
+ ($output[$countOutputMinus1]['type'] === Operands\StructuredReference::NAME || $output[$countOutputMinus1]['type'] === 'Value')
+ )
+ ) {
+ while (
+ $stack->count() > 0 &&
($o2 = $stack->last()) &&
- isset(self::$operators[$o2['value']]) &&
- @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
+ isset(self::CALCULATION_OPERATORS[$o2['value']]) &&
+ @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$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
+ $stack->push('Binary Operator', '∩'); // Put an Intersect Operator on the stack
$expectingOperator = false;
}
}
}
- while (($op = $stack->pop()) !== null) { // pop everything off the stack and push onto output
- if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
+ while (($op = $stack->pop()) !== null) {
+ // pop everything off the stack and push onto output
+ if ((is_array($op) && $op['value'] == '(')) {
return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
}
$output[] = $op;
@@ -3753,15 +4603,26 @@ class Calculation
return $output;
}
+ /**
+ * @param array $operandData
+ *
+ * @return mixed
+ */
private static function dataTestReference(&$operandData)
{
$operand = $operandData['value'];
if (($operandData['reference'] === null) && (is_array($operand))) {
$rKeys = array_keys($operand);
$rowKey = array_shift($rKeys);
+ if (is_array($operand[$rowKey]) === false) {
+ $operandData['value'] = $operand[$rowKey];
+
+ return $operand[$rowKey];
+ }
+
$cKeys = array_keys(array_keys($operand[$rowKey]));
$colKey = array_shift($cKeys);
- if (ctype_upper($colKey)) {
+ if (ctype_upper("$colKey")) {
$operandData['reference'] = $colKey . $rowKey;
}
}
@@ -3769,48 +4630,46 @@ class Calculation
return $operand;
}
- // evaluate postfix notation
-
/**
* @param mixed $tokens
* @param null|string $cellID
- * @param null|Cell $pCell
*
- * @return bool
+ * @return array|false
*/
- private function processTokenStack($tokens, $cellID = null, Cell $pCell = null)
+ private function processTokenStack($tokens, $cellID = null, ?Cell $cell = null)
{
- if ($tokens == false) {
+ 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 cell collection),
// so we store the parent cell collection so that we can re-attach it when necessary
- $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
- $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
- $stack = new Stack();
+ $pCellWorksheet = ($cell !== null) ? $cell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getParent() : null;
+ $stack = new Stack($this->branchPruner);
// Stores branches that have been pruned
$fakedForBranchPruning = [];
// help us to know when pruning ['branchTestId' => true/false]
$branchStore = [];
-
// Loop through each token in turn
foreach ($tokens as $tokenData) {
$token = $tokenData['value'];
-
// Branch pruning: skip useless resolutions
$storeKey = $tokenData['storeKey'] ?? null;
if ($this->branchPruningEnabled && isset($tokenData['onlyIf'])) {
$onlyIfStoreKey = $tokenData['onlyIf'];
$storeValue = $branchStore[$onlyIfStoreKey] ?? null;
+ $storeValueAsBool = ($storeValue === null) ?
+ true : (bool) Functions::flattenSingleValue($storeValue);
if (is_array($storeValue)) {
$wrappedItem = end($storeValue);
- $storeValue = end($wrappedItem);
+ $storeValue = is_array($wrappedItem) ? end($wrappedItem) : $wrappedItem;
}
- if (isset($storeValue) && (($storeValue !== true)
- || ($storeValue === 'Pruned branch'))
+ if (
+ (isset($storeValue) || $tokenData['reference'] === 'NULL')
+ && (!$storeValueAsBool || Information\ErrorValue::isError($storeValue) || ($storeValue === 'Pruned branch'))
) {
// If branching value is not true, we don't need to compute
if (!isset($fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey])) {
@@ -3833,12 +4692,16 @@ class Calculation
if ($this->branchPruningEnabled && isset($tokenData['onlyIfNot'])) {
$onlyIfNotStoreKey = $tokenData['onlyIfNot'];
$storeValue = $branchStore[$onlyIfNotStoreKey] ?? null;
+ $storeValueAsBool = ($storeValue === null) ?
+ true : (bool) Functions::flattenSingleValue($storeValue);
if (is_array($storeValue)) {
$wrappedItem = end($storeValue);
- $storeValue = end($wrappedItem);
+ $storeValue = is_array($wrappedItem) ? end($wrappedItem) : $wrappedItem;
}
- if (isset($storeValue) && ($storeValue
- || ($storeValue === 'Pruned branch'))
+
+ if (
+ (isset($storeValue) || $tokenData['reference'] === 'NULL')
+ && ($storeValueAsBool || Information\ErrorValue::isError($storeValue) || ($storeValue === 'Pruned branch'))
) {
// If branching value is true, we don't need to compute
if (!isset($fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey])) {
@@ -3858,10 +4721,34 @@ class Calculation
}
}
- $tIndex = \is_float($token) ? (int) $token : $token;
+ if ($token instanceof Operands\StructuredReference) {
+ if ($cell === null) {
+ return $this->raiseFormulaError('Structured References must exist in a Cell context');
+ }
- // 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 (isset(self::$binaryOperators[$tIndex])) {
+ try {
+ $cellRange = $token->parse($cell);
+ if (strpos($cellRange, ':') !== false) {
+ $this->debugLog->writeDebugLog('Evaluating Structured Reference %s as Cell Range %s', $token->value(), $cellRange);
+ $rangeValue = self::getInstance($cell->getWorksheet()->getParent())->_calculateFormulaValue("={$cellRange}", $token->value(), $cell);
+ $stack->push('Value', $rangeValue);
+ $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as value %s', $token->value(), $this->showValue($rangeValue));
+ } else {
+ $this->debugLog->writeDebugLog('Evaluating Structured Reference %s as Cell %s', $token->value(), $cellRange);
+ $cellValue = $cell->getWorksheet()->getCell($cellRange)->getCalculatedValue(false);
+ $stack->push('Cell Reference', $cellValue, $cellRange);
+ $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as value %s', $token->value(), $this->showValue($cellValue));
+ }
+ } catch (Exception $e) {
+ if ($e->getCode() === Exception::CALCULATION_ENGINE_PUSH_TO_STACK) {
+ $stack->push('Error', Information\ExcelError::REF(), null);
+ $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as error value %s', $token->value(), Information\ExcelError::REF());
+ } else {
+ return $this->raiseFormulaError($e->getMessage());
+ }
+ }
+ } elseif (!is_numeric($token) && !is_object($token) && isset(self::BINARY_OPERATORS[$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
// We must have two operands, error if we don't
if (($operand2Data = $stack->pop()) === null) {
return $this->raiseFormulaError('Internal error - Operand value missing from stack');
@@ -3875,32 +4762,40 @@ class Calculation
// Log what we're doing
if ($token == ':') {
- $this->debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference']));
+ $this->debugLog->writeDebugLog('Evaluating Range %s %s %s', $this->showValue($operand1Data['reference']), $token, $this->showValue($operand2Data['reference']));
} else {
- $this->debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2));
+ $this->debugLog->writeDebugLog('Evaluating %s %s %s', $this->showValue($operand1), $token, $this->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
- $result = $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
+ // 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
+ $result = $this->executeBinaryComparisonOperation($operand1, $operand2, (string) $token, $stack);
if (isset($storeKey)) {
$branchStore[$storeKey] = $result;
}
break;
- // Binary Operators
- case ':': // Range
+ // Binary Operators
+ case ':': // Range
+ if ($operand1Data['type'] === 'Defined Name') {
+ if (preg_match('/$' . self::CALCULATION_REGEXP_DEFINEDNAME . '^/mui', $operand1Data['reference']) !== false && $this->spreadsheet !== null) {
+ $definedName = $this->spreadsheet->getNamedRange($operand1Data['reference']);
+ if ($definedName !== null) {
+ $operand1Data['reference'] = $operand1Data['value'] = str_replace('$', '', $definedName->getValue());
+ }
+ }
+ }
if (strpos($operand1Data['reference'], '!') !== false) {
[$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
} else {
- $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
+ $sheet1 = ($pCellWorksheet !== null) ? $pCellWorksheet->getTitle() : '';
}
[$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
@@ -3908,23 +4803,27 @@ class Calculation
$sheet2 = $sheet1;
}
- if ($sheet1 == $sheet2) {
- if ($operand1Data['reference'] === null) {
- if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
- $operand1Data['reference'] = $pCell->getColumn() . $operand1Data['value'];
- } elseif (trim($operand1Data['reference']) == '') {
- $operand1Data['reference'] = $pCell->getCoordinate();
+ if (trim($sheet1, "'") === trim($sheet2, "'")) {
+ if ($operand1Data['reference'] === null && $cell !== null) {
+ if (is_array($operand1Data['value'])) {
+ $operand1Data['reference'] = $cell->getCoordinate();
+ } elseif ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
+ $operand1Data['reference'] = $cell->getColumn() . $operand1Data['value'];
+ } elseif (trim($operand1Data['value']) == '') {
+ $operand1Data['reference'] = $cell->getCoordinate();
} else {
- $operand1Data['reference'] = $operand1Data['value'] . $pCell->getRow();
+ $operand1Data['reference'] = $operand1Data['value'] . $cell->getRow();
}
}
- if ($operand2Data['reference'] === null) {
- if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
- $operand2Data['reference'] = $pCell->getColumn() . $operand2Data['value'];
- } elseif (trim($operand2Data['reference']) == '') {
- $operand2Data['reference'] = $pCell->getCoordinate();
+ if ($operand2Data['reference'] === null && $cell !== null) {
+ if (is_array($operand2Data['value'])) {
+ $operand2Data['reference'] = $cell->getCoordinate();
+ } elseif ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
+ $operand2Data['reference'] = $cell->getColumn() . $operand2Data['value'];
+ } elseif (trim($operand2Data['value']) == '') {
+ $operand2Data['reference'] = $cell->getCoordinate();
} else {
- $operand2Data['reference'] = $operand2Data['value'] . $pCell->getRow();
+ $operand2Data['reference'] = $operand2Data['value'] . $cell->getRow();
}
}
@@ -3936,47 +4835,24 @@ class Calculation
$oRow[] = $oCR[1];
}
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
- if ($pCellParent !== null) {
+ if ($pCellParent !== null && $this->spreadsheet !== null) {
$cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false);
} else {
return $this->raiseFormulaError('Unable to access Cell Reference');
}
+
$stack->push('Cell Reference', $cellValue, $cellRef);
} else {
- $stack->push('Error', Functions::REF(), null);
+ $stack->push('Error', Information\ExcelError::REF(), null);
}
break;
case '+': // Addition
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
case '-': // Subtraction
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
case '*': // Multiplication
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
case '/': // Division
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
case '^': // Exponential
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack);
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, $stack);
if (isset($storeKey)) {
$branchStore[$storeKey] = $result;
}
@@ -3986,30 +4862,39 @@ class Calculation
// 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 Shared\JAMA\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->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
- $result = '#VALUE!';
+ $operand1 = self::boolToString($operand1);
+ $operand2 = self::boolToString($operand2);
+ if (is_array($operand1) || is_array($operand2)) {
+ if (is_string($operand1)) {
+ $operand1 = self::unwrapResult($operand1);
}
+ if (is_string($operand2)) {
+ $operand2 = self::unwrapResult($operand2);
+ }
+ // Ensure that both operands are arrays/matrices
+ [$rows, $columns] = self::checkMatrixOperands($operand1, $operand2, 2);
+
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ $operand1[$row][$column] =
+ Shared\StringHelper::substring(
+ self::boolToString($operand1[$row][$column])
+ . self::boolToString($operand2[$row][$column]),
+ 0,
+ DataType::MAX_STRING_LENGTH
+ );
+ }
+ }
+ $result = $operand1;
} else {
- $result = '"' . str_replace('""', '"', self::unwrapResult($operand1) . self::unwrapResult($operand2)) . '"';
+ // In theory, we should truncate here.
+ // But I can't figure out a formula
+ // using the concatenation operator
+ // with literals that fits in 32K,
+ // so I don't think we can overflow here.
+ $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
}
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
$stack->push('Value', $result);
if (isset($storeKey)) {
@@ -4017,7 +4902,7 @@ class Calculation
}
break;
- case '|': // Intersect
+ case '∩': // Intersect
$rowIntersect = array_intersect_key($operand1, $operand2);
$cellIntersect = $oCol = $oRow = [];
foreach (array_keys($rowIntersect) as $row) {
@@ -4027,51 +4912,60 @@ class Calculation
$cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
}
}
- $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
- $stack->push('Value', $cellIntersect, $cellRef);
+ if (count(Functions::flattenArray($cellIntersect)) === 0) {
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
+ $stack->push('Error', Information\ExcelError::null(), null);
+ } else {
+ $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' .
+ Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->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 === '%')) {
+ // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
if (($arg = $stack->pop()) === null) {
return $this->raiseFormulaError('Internal error - Operand value missing from stack');
}
$arg = $arg['value'];
if ($token === '~') {
- $this->debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg));
+ $this->debugLog->writeDebugLog('Evaluating Negation of %s', $this->showValue($arg));
$multiplier = -1;
} else {
- $this->debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg));
+ $this->debugLog->writeDebugLog('Evaluating Percentile of %s', $this->showValue($arg));
$multiplier = 0.01;
}
if (is_array($arg)) {
- self::checkMatrixOperands($arg, $multiplier, 2);
-
- try {
- $matrix1 = new Shared\JAMA\Matrix($arg);
- $matrixResult = $matrix1->arrayTimesEquals($multiplier);
- $result = $matrixResult->getArray();
- } catch (\Exception $ex) {
- $this->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
- $result = '#VALUE!';
+ $operand2 = $multiplier;
+ $result = $arg;
+ [$rows, $columns] = self::checkMatrixOperands($result, $operand2, 0);
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ if (is_numeric($result[$row][$column])) {
+ $result[$row][$column] *= $multiplier;
+ } else {
+ $result[$row][$column] = Information\ExcelError::VALUE();
+ }
+ }
}
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
$stack->push('Value', $result);
if (isset($storeKey)) {
$branchStore[$storeKey] = $result;
}
} else {
- $this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack);
+ $this->executeNumericBinaryOperation($multiplier, $arg, '*', $stack);
}
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) {
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token ?? '', $matches)) {
$cellRef = null;
+
if (isset($matches[8])) {
- if ($pCell === null) {
- // We can't access the range, so return a REF error
- $cellValue = Functions::REF();
+ if ($cell === null) {
+ // We can't access the range, so return a REF error
+ $cellValue = Information\ExcelError::REF();
} else {
$cellRef = $matches[6] . $matches[7] . ':' . $matches[9] . $matches[10];
if ($matches[2] > '') {
@@ -4081,27 +4975,27 @@ class Calculation
return $this->raiseFormulaError('Unable to access External Workbook');
}
$matches[2] = trim($matches[2], "\"'");
- $this->debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
- if ($pCellParent !== null) {
+ $this->debugLog->writeDebugLog('Evaluating Cell Range %s in worksheet %s', $cellRef, $matches[2]);
+ if ($pCellParent !== null && $this->spreadsheet !== null) {
$cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
} else {
return $this->raiseFormulaError('Unable to access Cell Reference');
}
- $this->debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
+ $this->debugLog->writeDebugLog('Evaluation Result for cells %s in worksheet %s is %s', $cellRef, $matches[2], $this->showTypeDetails($cellValue));
} else {
- $this->debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
+ $this->debugLog->writeDebugLog('Evaluating Cell Range %s in current worksheet', $cellRef);
if ($pCellParent !== null) {
$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
} else {
return $this->raiseFormulaError('Unable to access Cell Reference');
}
- $this->debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
+ $this->debugLog->writeDebugLog('Evaluation Result for cells %s is %s', $cellRef, $this->showTypeDetails($cellValue));
}
}
} else {
- if ($pCell === null) {
- // We can't access the cell, so return a REF error
- $cellValue = Functions::REF();
+ if ($cell === null) {
+ // We can't access the cell, so return a REF error
+ $cellValue = Information\ExcelError::REF();
} else {
$cellRef = $matches[6] . $matches[7];
if ($matches[2] > '') {
@@ -4110,56 +5004,53 @@ class Calculation
// It's a Reference to an external spreadsheet (not currently supported)
return $this->raiseFormulaError('Unable to access External Workbook');
}
- $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
- if ($pCellParent !== null) {
+ $this->debugLog->writeDebugLog('Evaluating Cell %s in worksheet %s', $cellRef, $matches[2]);
+ if ($pCellParent !== null && $this->spreadsheet !== null) {
$cellSheet = $this->spreadsheet->getSheetByName($matches[2]);
if ($cellSheet && $cellSheet->cellExists($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
- $pCell->attach($pCellParent);
+ $cell->attach($pCellParent);
} else {
- $cellValue = null;
+ $cellRef = ($cellSheet !== null) ? "'{$matches[2]}'!{$cellRef}" : $cellRef;
+ $cellValue = ($cellSheet !== null) ? null : Information\ExcelError::REF();
}
} else {
return $this->raiseFormulaError('Unable to access Cell Reference');
}
- $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
+ $this->debugLog->writeDebugLog('Evaluation Result for cell %s in worksheet %s is %s', $cellRef, $matches[2], $this->showTypeDetails($cellValue));
} else {
- $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
- if ($pCellParent->has($cellRef)) {
+ $this->debugLog->writeDebugLog('Evaluating Cell %s in current worksheet', $cellRef);
+ if ($pCellParent !== null && $pCellParent->has($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
- $pCell->attach($pCellParent);
+ $cell->attach($pCellParent);
} else {
$cellValue = null;
}
- $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
+ $this->debugLog->writeDebugLog('Evaluation Result for cell %s is %s', $cellRef, $this->showTypeDetails($cellValue));
}
}
}
- $stack->push('Value', $cellValue, $cellRef);
+
+ $stack->push('Cell Value', $cellValue, $cellRef);
if (isset($storeKey)) {
$branchStore[$storeKey] = $cellValue;
}
-
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $token ?? '', $matches)) {
// 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)) {
- if ($pCellParent) {
- $pCell->attach($pCellParent);
- }
- if (($cellID == 'AC99') || (isset($pCell) && $pCell->getCoordinate() == 'AC99')) {
- if (defined('RESOLVING')) {
- define('RESOLVING2', true);
- } else {
- define('RESOLVING', true);
- }
+ if ($cell !== null && $pCellParent !== null) {
+ $cell->attach($pCellParent);
}
$functionName = $matches[1];
$argCount = $stack->pop();
$argCount = $argCount['value'];
- if ($functionName != 'MKMATRIX') {
- $this->debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
+ if ($functionName !== 'MKMATRIX') {
+ $this->debugLog->writeDebugLog('Evaluating Function %s() with %s argument%s', self::localeFunc($functionName), (($argCount == 0) ? 'no' : $argCount), (($argCount == 1) ? '' : 's'));
}
if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function
+ $passByReference = false;
+ $passCellReference = false;
+ $functionCall = null;
if (isset(self::$phpSpreadsheetFunctions[$functionName])) {
$functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
$passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']);
@@ -4169,28 +5060,33 @@ class Calculation
$passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
$passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
}
+
// get the arguments for this function
$args = $argArrayVals = [];
+ $emptyArguments = [];
for ($i = 0; $i < $argCount; ++$i) {
$arg = $stack->pop();
$a = $argCount - $i - 1;
- if (($passByReference) &&
+ if (
+ ($passByReference) &&
(isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) &&
- (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) {
+ (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])
+ ) {
if ($arg['reference'] === null) {
$args[] = $cellID;
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$argArrayVals[] = $this->showValue($cellID);
}
} else {
$args[] = $arg['reference'];
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$argArrayVals[] = $this->showValue($arg['reference']);
}
}
} else {
+ $emptyArguments[] = ($arg['type'] === 'Empty Argument');
$args[] = self::unwrapResult($arg['value']);
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
$argArrayVals[] = $this->showValue($arg['value']);
}
}
@@ -4198,21 +5094,26 @@ class Calculation
// Reverse the order of the arguments
krsort($args);
+ krsort($emptyArguments);
+
+ if ($argCount > 0) {
+ $args = $this->addDefaultArgumentValues($functionCall, $args, $emptyArguments);
+ }
if (($passByReference) && ($argCount == 0)) {
$args[] = $cellID;
$argArrayVals[] = $this->showValue($cellID);
}
- if ($functionName != 'MKMATRIX') {
+ if ($functionName !== 'MKMATRIX') {
if ($this->debugLog->getWriteDebugLog()) {
krsort($argArrayVals);
- $this->debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator . ' ', Functions::flattenArray($argArrayVals)), ' )');
+ $this->debugLog->writeDebugLog('Evaluating %s ( %s )', self::localeFunc($functionName), implode(self::$localeArgumentSeparator . ' ', Functions::flattenArray($argArrayVals)));
}
}
// Process the argument with the appropriate function call
- $args = $this->addCellReference($args, $passCellReference, $functionCall, $pCell);
+ $args = $this->addCellReference($args, $passCellReference, $functionCall, $cell);
if (!is_array($functionCall)) {
foreach ($args as &$arg) {
@@ -4223,8 +5124,8 @@ class Calculation
$result = call_user_func_array($functionCall, $args);
- if ($functionName != 'MKMATRIX') {
- $this->debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
+ if ($functionName !== 'MKMATRIX') {
+ $this->debugLog->writeDebugLog('Evaluation Result for %s() function call is %s', self::localeFunc($functionName), $this->showTypeDetails($result));
}
$stack->push('Value', self::wrapResult($result));
if (isset($storeKey)) {
@@ -4233,32 +5134,37 @@ class Calculation
}
} else {
// if the token is a number, boolean, string or an Excel error, push it onto the stack
- if (isset(self::$excelConstants[strtoupper($token)])) {
+ if (isset(self::$excelConstants[strtoupper($token ?? '')])) {
$excelConstant = strtoupper($token);
$stack->push('Constant Value', self::$excelConstants[$excelConstant]);
if (isset($storeKey)) {
$branchStore[$storeKey] = self::$excelConstants[$excelConstant];
}
- $this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
- } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == '"') || ($token[0] == '#')) {
- $stack->push('Value', $token);
+ $this->debugLog->writeDebugLog('Evaluating Constant %s as %s', $excelConstant, $this->showTypeDetails(self::$excelConstants[$excelConstant]));
+ } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == self::FORMULA_STRING_QUOTE) || ($token[0] == '#')) {
+ $stack->push($tokenData['type'], $token, $tokenData['reference']);
if (isset($storeKey)) {
$branchStore[$storeKey] = $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)) {
- $namedRange = $matches[6];
- $this->debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) {
+ // if the token is a named range or formula, evaluate it and push the result onto the stack
+ $definedName = $matches[6];
+ if ($cell === null || $pCellWorksheet === null) {
+ return $this->raiseFormulaError("undefined name '$token'");
+ }
- $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false);
- $pCell->attach($pCellParent);
- $this->debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
- $stack->push('Named Range', $cellValue, $namedRange);
+ $this->debugLog->writeDebugLog('Evaluating Defined Name %s', $definedName);
+ $namedRange = DefinedName::resolveName($definedName, $pCellWorksheet);
+ if ($namedRange === null) {
+ return $this->raiseFormulaError("undefined name '$definedName'");
+ }
+
+ $result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack);
if (isset($storeKey)) {
- $branchStore[$storeKey] = $cellValue;
+ $branchStore[$storeKey] = $result;
}
} else {
- return $this->raiseFormulaError("undefined variable '$token'");
+ return $this->raiseFormulaError("undefined name '$token'");
}
}
}
@@ -4272,6 +5178,12 @@ class Calculation
return $output;
}
+ /**
+ * @param mixed $operand
+ * @param mixed $stack
+ *
+ * @return bool
+ */
private function validateBinaryOperand(&$operand, &$stack)
{
if (is_array($operand)) {
@@ -4285,7 +5197,7 @@ class Calculation
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] == '"') {
+ if ($operand > '' && $operand[0] == self::FORMULA_STRING_QUOTE) {
$operand = self::unwrapResult($operand);
}
// If the string is a numeric value, we treat it as a numeric, so no further testing
@@ -4293,274 +5205,242 @@ class Calculation
// 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->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand));
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($operand));
return false;
- } elseif (!Shared\StringHelper::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->debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
+ } elseif (Engine\FormattedNumber::convertToNumberIfFormatted($operand) === false) {
+ // If not a numeric, a fraction or a percentage, then it's a text string, and so can't be used in mathematical binary operations
+ $stack->push('Error', '#VALUE!');
+ $this->debugLog->writeDebugLog('Evaluation Result is a %s', $this->showTypeDetails('#VALUE!'));
return false;
}
}
}
- // return a true if the value of the operand is one that we can use in normal binary operations
+ // return a true if the value of the operand is one that we can use in normal binary mathematical operations
return true;
}
/**
- * @param null|string $cellID
* @param mixed $operand1
* @param mixed $operand2
* @param string $operation
- * @param Stack $stack
+ *
+ * @return array
+ */
+ private function executeArrayComparison($operand1, $operand2, $operation, Stack &$stack, bool $recursingArrays)
+ {
+ $result = [];
+ if (!is_array($operand2)) {
+ // Operand 1 is an array, Operand 2 is a scalar
+ foreach ($operand1 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2));
+ $this->executeBinaryComparisonOperation($operandData, $operand2, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } elseif (!is_array($operand1)) {
+ // Operand 1 is a scalar, Operand 2 is an array
+ foreach ($operand2 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operand1), $operation, $this->showValue($operandData));
+ $this->executeBinaryComparisonOperation($operand1, $operandData, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } else {
+ // Operand 1 and Operand 2 are both arrays
+ if (!$recursingArrays) {
+ self::checkMatrixOperands($operand1, $operand2, 2);
+ }
+ foreach ($operand1 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2[$x]));
+ $this->executeBinaryComparisonOperation($operandData, $operand2[$x], $operation, $stack, true);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->debugLog->writeDebugLog('Comparison Evaluation Result is %s', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Array', $result);
+
+ return $result;
+ }
+
+ /**
+ * @param mixed $operand1
+ * @param mixed $operand2
+ * @param string $operation
* @param bool $recursingArrays
*
* @return mixed
*/
- private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false)
+ private function executeBinaryComparisonOperation($operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false)
{
// If we're dealing with matrix operations, we want a matrix result
if ((is_array($operand1)) || (is_array($operand2))) {
- $result = [];
- if ((is_array($operand1)) && (!is_array($operand2))) {
- foreach ($operand1 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->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->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->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->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
- $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- }
- // Log the result details
- $this->debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
- // And push the result onto the stack
- $stack->push('Array', $result);
-
- return $result;
+ return $this->executeArrayComparison($operand1, $operand2, $operation, $stack, $recursingArrays);
}
- // 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);
- }
-
- // Use case insensitive comparaison if not OpenOffice mode
- if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
- if (is_string($operand1)) {
- $operand1 = strtoupper($operand1);
- }
- if (is_string($operand2)) {
- $operand2 = strtoupper($operand2);
- }
- }
-
- $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE;
-
- // execute the necessary operation
- switch ($operation) {
- // Greater than
- case '>':
- if ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
- } else {
- $result = ($operand1 > $operand2);
- }
-
- break;
- // Less than
- case '<':
- if ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
- } else {
- $result = ($operand1 < $operand2);
- }
-
- break;
- // Equality
- case '=':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = (abs($operand1 - $operand2) < $this->delta);
- } else {
- $result = strcmp($operand1, $operand2) == 0;
- }
-
- break;
- // Greater than or equal
- case '>=':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
- } elseif ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
- } else {
- $result = strcmp($operand1, $operand2) >= 0;
- }
-
- break;
- // Less than or equal
- case '<=':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
- } elseif ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
- } else {
- $result = strcmp($operand1, $operand2) <= 0;
- }
-
- break;
- // Inequality
- case '<>':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = (abs($operand1 - $operand2) > 1E-14);
- } else {
- $result = strcmp($operand1, $operand2) != 0;
- }
-
- break;
- }
+ $result = BinaryComparison::compare($operand1, $operand2, $operation);
// Log the result details
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
// And push the result onto the stack
$stack->push('Value', $result);
return $result;
}
- /**
- * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
- *
- * @param string $str1 First string value for the comparison
- * @param string $str2 Second string value for the comparison
- *
- * @return int
- */
- private function strcmpLowercaseFirst($str1, $str2)
- {
- $inversedStr1 = Shared\StringHelper::strCaseReverse($str1);
- $inversedStr2 = Shared\StringHelper::strCaseReverse($str2);
-
- return strcmp($inversedStr1, $inversedStr2);
- }
-
/**
* @param mixed $operand1
* @param mixed $operand2
- * @param mixed $operation
- * @param string $matrixFunction
- * @param mixed $stack
+ * @param string $operation
+ * @param Stack $stack
*
* @return bool|mixed
*/
- private function executeNumericBinaryOperation($operand1, $operand2, $operation, $matrixFunction, &$stack)
+ private function executeNumericBinaryOperation($operand1, $operand2, $operation, &$stack)
{
// Validate the two operands
- if (!$this->validateBinaryOperand($operand1, $stack)) {
- return false;
- }
- if (!$this->validateBinaryOperand($operand2, $stack)) {
+ if (
+ ($this->validateBinaryOperand($operand1, $stack) === false) ||
+ ($this->validateBinaryOperand($operand2, $stack) === false)
+ ) {
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 of the same size
- self::checkMatrixOperands($operand1, $operand2, 2);
-
- try {
- // Convert operand 1 from a PHP array to a matrix
- $matrix = new Shared\JAMA\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->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
- $result = '#VALUE!';
- }
- } else {
- if ((Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) &&
- ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1) > 0) ||
- (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0))) {
- $result = Functions::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->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
-
- return false;
- }
- $result = $operand1 / $operand2;
-
- break;
- // Power
- case '^':
- $result = pow($operand1, $operand2);
-
- break;
+ if (
+ (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) &&
+ ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1) > 0) ||
+ (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0))
+ ) {
+ $result = Information\ExcelError::VALUE();
+ } elseif (is_array($operand1) || is_array($operand2)) {
+ // Ensure that both operands are arrays/matrices
+ if (is_array($operand1)) {
+ foreach ($operand1 as $key => $value) {
+ $operand1[$key] = Functions::flattenArray($value);
}
}
+ if (is_array($operand2)) {
+ foreach ($operand2 as $key => $value) {
+ $operand2[$key] = Functions::flattenArray($value);
+ }
+ }
+ [$rows, $columns] = self::checkMatrixOperands($operand1, $operand2, 2);
+
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ if ($operand1[$row][$column] === null) {
+ $operand1[$row][$column] = 0;
+ } elseif (!is_numeric($operand1[$row][$column])) {
+ $operand1[$row][$column] = Information\ExcelError::VALUE();
+
+ continue;
+ }
+ if ($operand2[$row][$column] === null) {
+ $operand2[$row][$column] = 0;
+ } elseif (!is_numeric($operand2[$row][$column])) {
+ $operand1[$row][$column] = Information\ExcelError::VALUE();
+
+ continue;
+ }
+ switch ($operation) {
+ case '+':
+ $operand1[$row][$column] += $operand2[$row][$column];
+
+ break;
+ case '-':
+ $operand1[$row][$column] -= $operand2[$row][$column];
+
+ break;
+ case '*':
+ $operand1[$row][$column] *= $operand2[$row][$column];
+
+ break;
+ case '/':
+ if ($operand2[$row][$column] == 0) {
+ $operand1[$row][$column] = Information\ExcelError::DIV0();
+ } else {
+ $operand1[$row][$column] /= $operand2[$row][$column];
+ }
+
+ break;
+ case '^':
+ $operand1[$row][$column] = $operand1[$row][$column] ** $operand2[$row][$column];
+
+ break;
+
+ default:
+ throw new Exception('Unsupported numeric binary operation');
+ }
+ }
+ }
+ $result = $operand1;
+ } 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('Error', Information\ExcelError::DIV0());
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails(Information\ExcelError::DIV0()));
+
+ return false;
+ }
+ $result = $operand1 / $operand2;
+
+ break;
+ // Power
+ case '^':
+ $result = $operand1 ** $operand2;
+
+ break;
+
+ default:
+ throw new Exception('Unsupported numeric binary operation');
+ }
}
// Log the result details
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
// And push the result onto the stack
$stack->push('Value', $result);
return $result;
}
- // trigger an error, but nicely, if need be
- protected function raiseFormulaError($errorMessage)
+ /**
+ * Trigger an error, but nicely, if need be.
+ *
+ * @return false
+ */
+ protected function raiseFormulaError(string $errorMessage)
{
$this->formulaError = $errorMessage;
$this->cyclicReferenceStack->clear();
- if (!$this->suppressFormulaErrors) {
+ $suppress = /** @scrutinizer ignore-deprecated */ $this->suppressFormulaErrors ?? $this->suppressFormulaErrorsNew;
+ if (!$suppress) {
throw new Exception($errorMessage);
}
- trigger_error($errorMessage, E_USER_ERROR);
return false;
}
@@ -4568,46 +5448,45 @@ class Calculation
/**
* Extract range values.
*
- * @param string &$pRange String based range representation
- * @param Worksheet $pSheet Worksheet
+ * @param string $range String based range representation
+ * @param Worksheet $worksheet Worksheet
* @param bool $resetLog Flag indicating whether calculation log should be reset or not
*
* @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
*/
- public function extractCellRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true)
+ public function extractCellRange(&$range = 'A1', ?Worksheet $worksheet = null, $resetLog = true)
{
// Return value
$returnValue = [];
- if ($pSheet !== null) {
- $pSheetName = $pSheet->getTitle();
- if (strpos($pRange, '!') !== false) {
- [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
- $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
+ if ($worksheet !== null) {
+ $worksheetName = $worksheet->getTitle();
+
+ if (strpos($range, '!') !== false) {
+ [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
+ $worksheet = ($this->spreadsheet === null) ? null : $this->spreadsheet->getSheetByName($worksheetName);
}
// Extract range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
- $pRange = $pSheetName . '!' . $pRange;
+ $aReferences = Coordinate::extractAllCellReferencesInRange($range);
+ $range = "'" . $worksheetName . "'" . '!' . $range;
+ $currentCol = '';
+ $currentRow = 0;
if (!isset($aReferences[1])) {
- $currentCol = '';
- $currentRow = 0;
// Single cell in range
sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
- if ($pSheet->cellExists($aReferences[0])) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ if ($worksheet !== null && $worksheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
} else {
// Extract cell data for all cells in the range
foreach ($aReferences as $reference) {
- $currentCol = '';
- $currentRow = 0;
// Extract range
sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
- if ($pSheet->cellExists($reference)) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ if ($worksheet !== null && $worksheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($reference)->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -4621,47 +5500,46 @@ class Calculation
/**
* Extract range values.
*
- * @param string &$pRange String based range representation
- * @param Worksheet $pSheet Worksheet
+ * @param string $range String based range representation
+ * @param null|Worksheet $worksheet Worksheet
* @param bool $resetLog Flag indicating whether calculation log should be reset or not
*
* @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
*/
- public function extractNamedRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true)
+ public function extractNamedRange(string &$range = 'A1', ?Worksheet $worksheet = null, $resetLog = true)
{
// Return value
$returnValue = [];
- if ($pSheet !== null) {
- $pSheetName = $pSheet->getTitle();
- if (strpos($pRange, '!') !== false) {
- [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
- $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
+ if ($worksheet !== null) {
+ if (strpos($range, '!') !== false) {
+ [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
+ $worksheet = ($this->spreadsheet === null) ? null : $this->spreadsheet->getSheetByName($worksheetName);
}
// Named range?
- $namedRange = NamedRange::resolveRange($pRange, $pSheet);
- if ($namedRange !== null) {
- $pSheet = $namedRange->getWorksheet();
- $pRange = $namedRange->getRange();
- $splitRange = Coordinate::splitRange($pRange);
- // Convert row and column references
- if (ctype_alpha($splitRange[0][0])) {
- $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
- } elseif (ctype_digit($splitRange[0][0])) {
- $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
- }
- } else {
- return Functions::REF();
+ $namedRange = ($worksheet === null) ? null : DefinedName::resolveName($range, $worksheet);
+ if ($namedRange === null) {
+ return Information\ExcelError::REF();
+ }
+
+ $worksheet = $namedRange->getWorksheet();
+ $range = $namedRange->getValue();
+ $splitRange = Coordinate::splitRange($range);
+ // Convert row and column references
+ if ($worksheet !== null && ctype_alpha($splitRange[0][0])) {
+ $range = $splitRange[0][0] . '1:' . $splitRange[0][1] . $worksheet->getHighestRow();
+ } elseif ($worksheet !== null && ctype_digit($splitRange[0][0])) {
+ $range = 'A' . $splitRange[0][0] . ':' . $worksheet->getHighestColumn() . $splitRange[0][1];
}
// Extract range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
+ $aReferences = Coordinate::extractAllCellReferencesInRange($range);
if (!isset($aReferences[1])) {
// Single cell (or single column or row) in range
[$currentCol, $currentRow] = Coordinate::coordinateFromString($aReferences[0]);
- if ($pSheet->cellExists($aReferences[0])) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ if ($worksheet !== null && $worksheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -4670,8 +5548,8 @@ class Calculation
foreach ($aReferences as $reference) {
// Extract range
[$currentCol, $currentRow] = Coordinate::coordinateFromString($reference);
- if ($pSheet->cellExists($reference)) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ if ($worksheet !== null && $worksheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($reference)->getCalculatedValue($resetLog);
} else {
$returnValue[$currentRow][$currentCol] = null;
}
@@ -4685,24 +5563,22 @@ class Calculation
/**
* Is a specific function implemented?
*
- * @param string $pFunction Function Name
+ * @param string $function Function Name
*
* @return bool
*/
- public function isImplemented($pFunction)
+ public function isImplemented($function)
{
- $pFunction = strtoupper($pFunction);
- $notImplemented = !isset(self::$phpSpreadsheetFunctions[$pFunction]) || (is_array(self::$phpSpreadsheetFunctions[$pFunction]['functionCall']) && self::$phpSpreadsheetFunctions[$pFunction]['functionCall'][1] === 'DUMMY');
+ $function = strtoupper($function);
+ $notImplemented = !isset(self::$phpSpreadsheetFunctions[$function]) || (is_array(self::$phpSpreadsheetFunctions[$function]['functionCall']) && self::$phpSpreadsheetFunctions[$function]['functionCall'][1] === 'DUMMY');
return !$notImplemented;
}
/**
* Get a list of all implemented functions as an array of function objects.
- *
- * @return array of Category
*/
- public function getFunctions()
+ public static function getFunctions(): array
{
return self::$phpSpreadsheetFunctions;
}
@@ -4724,56 +5600,167 @@ class Calculation
return $returnValue;
}
+ private function addDefaultArgumentValues(array $functionCall, array $args, array $emptyArguments): array
+ {
+ $reflector = new ReflectionMethod(implode('::', $functionCall));
+ $methodArguments = $reflector->getParameters();
+
+ if (count($methodArguments) > 0) {
+ // Apply any defaults for empty argument values
+ foreach ($emptyArguments as $argumentId => $isArgumentEmpty) {
+ if ($isArgumentEmpty === true) {
+ $reflectedArgumentId = count($args) - (int) $argumentId - 1;
+ if (
+ !array_key_exists($reflectedArgumentId, $methodArguments) ||
+ $methodArguments[$reflectedArgumentId]->isVariadic()
+ ) {
+ break;
+ }
+
+ $args[$argumentId] = $this->getArgumentDefaultValue($methodArguments[$reflectedArgumentId]);
+ }
+ }
+ }
+
+ return $args;
+ }
+
+ /**
+ * @return null|mixed
+ */
+ private function getArgumentDefaultValue(ReflectionParameter $methodArgument)
+ {
+ $defaultValue = null;
+
+ if ($methodArgument->isDefaultValueAvailable()) {
+ $defaultValue = $methodArgument->getDefaultValue();
+ if ($methodArgument->isDefaultValueConstant()) {
+ $constantName = $methodArgument->getDefaultValueConstantName() ?? '';
+ // read constant value
+ if (strpos($constantName, '::') !== false) {
+ [$className, $constantName] = explode('::', $constantName);
+ $constantReflector = new ReflectionClassConstant($className, $constantName);
+
+ return $constantReflector->getValue();
+ }
+
+ return constant($constantName);
+ }
+ }
+
+ return $defaultValue;
+ }
+
/**
* Add cell reference if needed while making sure that it is the last argument.
*
- * @param array $args
* @param bool $passCellReference
* @param array|string $functionCall
- * @param null|Cell $pCell
*
* @return array
*/
- private function addCellReference(array $args, $passCellReference, $functionCall, Cell $pCell = null)
+ private function addCellReference(array $args, $passCellReference, $functionCall, ?Cell $cell = null)
{
if ($passCellReference) {
if (is_array($functionCall)) {
$className = $functionCall[0];
$methodName = $functionCall[1];
- $reflectionMethod = new \ReflectionMethod($className, $methodName);
+ $reflectionMethod = new ReflectionMethod($className, $methodName);
$argumentCount = count($reflectionMethod->getParameters());
while (count($args) < $argumentCount - 1) {
$args[] = null;
}
}
- $args[] = $pCell;
+ $args[] = $cell;
}
return $args;
}
- private function getUnusedBranchStoreKey()
+ /**
+ * @return mixed|string
+ */
+ private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksheet $cellWorksheet, Stack $stack)
{
- $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
- ++$this->branchStoreKeyCounter;
+ $definedNameScope = $namedRange->getScope();
+ if ($definedNameScope !== null && $definedNameScope !== $cellWorksheet) {
+ // The defined name isn't in our current scope, so #REF
+ $result = Information\ExcelError::REF();
+ $stack->push('Error', $result, $namedRange->getName());
- return $storeKeyValue;
+ return $result;
+ }
+
+ $definedNameValue = $namedRange->getValue();
+ $definedNameType = $namedRange->isFormula() ? 'Formula' : 'Range';
+ $definedNameWorksheet = $namedRange->getWorksheet();
+
+ if ($definedNameValue[0] !== '=') {
+ $definedNameValue = '=' . $definedNameValue;
+ }
+
+ $this->debugLog->writeDebugLog('Defined Name is a %s with a value of %s', $definedNameType, $definedNameValue);
+
+ $recursiveCalculationCell = ($definedNameWorksheet !== null && $definedNameWorksheet !== $cellWorksheet)
+ ? $definedNameWorksheet->getCell('A1')
+ : $cell;
+ $recursiveCalculationCellAddress = $recursiveCalculationCell->getCoordinate();
+
+ // Adjust relative references in ranges and formulae so that we execute the calculation for the correct rows and columns
+ $definedNameValue = self::$referenceHelper->updateFormulaReferencesAnyWorksheet(
+ $definedNameValue,
+ Coordinate::columnIndexFromString($cell->getColumn()) - 1,
+ $cell->getRow() - 1
+ );
+
+ $this->debugLog->writeDebugLog('Value adjusted for relative references is %s', $definedNameValue);
+
+ $recursiveCalculator = new self($this->spreadsheet);
+ $recursiveCalculator->getDebugLog()->setWriteDebugLog($this->getDebugLog()->getWriteDebugLog());
+ $recursiveCalculator->getDebugLog()->setEchoDebugLog($this->getDebugLog()->getEchoDebugLog());
+ $result = $recursiveCalculator->_calculateFormulaValue($definedNameValue, $recursiveCalculationCellAddress, $recursiveCalculationCell);
+
+ if ($this->getDebugLog()->getWriteDebugLog()) {
+ $this->debugLog->mergeDebugLog(array_slice($recursiveCalculator->getDebugLog()->getLog(), 3));
+ $this->debugLog->writeDebugLog('Evaluation Result for Named %s %s is %s', $definedNameType, $namedRange->getName(), $this->showTypeDetails($result));
+ }
+
+ $stack->push('Defined Name', $result, $namedRange->getName());
+
+ return $result;
}
- private function getTokensAsString($tokens)
+ public function setSuppressFormulaErrors(bool $suppressFormulaErrors): void
{
- $tokensStr = array_map(function ($token) {
- $value = $token['value'] ?? 'no value';
- while (is_array($value)) {
- $value = array_pop($value);
- }
+ $this->suppressFormulaErrorsNew = $suppressFormulaErrors;
+ }
- return $value;
- }, $tokens);
- $str = '[ ' . implode(' | ', $tokensStr) . ' ]';
+ public function getSuppressFormulaErrors(): bool
+ {
+ return $this->suppressFormulaErrorsNew;
+ }
- return $str;
+ /** @param mixed $arg */
+ private static function doNothing($arg): bool
+ {
+ return (bool) $arg;
+ }
+
+ /**
+ * @param mixed $operand1
+ *
+ * @return mixed
+ */
+ private static function boolToString($operand1)
+ {
+ if (is_bool($operand1)) {
+ $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+ } elseif ($operand1 === null) {
+ $operand1 = '';
+ }
+
+ return $operand1;
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Category.php b/PhpOffice/PhpSpreadsheet/Calculation/Category.php
old mode 100755
new mode 100644
index 7574cb4..b661faf
--- a/PhpOffice/PhpSpreadsheet/Calculation/Category.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Category.php
@@ -16,4 +16,6 @@ abstract class Category
const CATEGORY_MATH_AND_TRIG = 'Math and Trig';
const CATEGORY_STATISTICAL = 'Statistical';
const CATEGORY_TEXT_AND_DATA = 'Text and Data';
+ const CATEGORY_WEB = 'Web';
+ const CATEGORY_UNCATEGORISED = 'Uncategorised';
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Database.php b/PhpOffice/PhpSpreadsheet/Calculation/Database.php
old mode 100755
new mode 100644
index d31b00d..017c571
--- a/PhpOffice/PhpSpreadsheet/Calculation/Database.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Database.php
@@ -2,126 +2,13 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
+/**
+ * @deprecated 1.17.0
+ *
+ * @codeCoverageIgnore
+ */
class Database
{
- /**
- * fieldExtract.
- *
- * Extracts the column ID to use for the data field.
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param mixed $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- *
- * @return null|string
- */
- private static function fieldExtract($database, $field)
- {
- $field = strtoupper(Functions::flattenSingleValue($field));
- $fieldNames = array_map('strtoupper', array_shift($database));
-
- if (is_numeric($field)) {
- $keys = array_keys($fieldNames);
-
- return $keys[$field - 1];
- }
- $key = array_search($field, $fieldNames);
-
- return ($key) ? $key : null;
- }
-
- /**
- * filter.
- *
- * Parses the selection criteria, extracts the database rows that match those criteria, and
- * returns that subset of rows.
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return array of mixed
- */
- private static function filter($database, $criteria)
- {
- $fieldNames = array_shift($database);
- $criteriaNames = array_shift($criteria);
-
- // Convert the criteria into a set of AND/OR conditions with [:placeholders]
- $testConditions = $testValues = [];
- $testConditionsCount = 0;
- foreach ($criteriaNames as $key => $criteriaName) {
- $testCondition = [];
- $testConditionCount = 0;
- foreach ($criteria as $row => $criterion) {
- if ($criterion[$key] > '') {
- $testCondition[] = '[:' . $criteriaName . ']' . Functions::ifCondition($criterion[$key]);
- ++$testConditionCount;
- }
- }
- if ($testConditionCount > 1) {
- $testConditions[] = 'OR(' . implode(',', $testCondition) . ')';
- ++$testConditionsCount;
- } elseif ($testConditionCount == 1) {
- $testConditions[] = $testCondition[0];
- ++$testConditionsCount;
- }
- }
-
- if ($testConditionsCount > 1) {
- $testConditionSet = 'AND(' . implode(',', $testConditions) . ')';
- } elseif ($testConditionsCount == 1) {
- $testConditionSet = $testConditions[0];
- }
-
- // Loop through each row of the database
- foreach ($database as $dataRow => $dataValues) {
- // Substitute actual values from the database row for our [:placeholders]
- $testConditionList = $testConditionSet;
- foreach ($criteriaNames as $key => $criteriaName) {
- $k = array_search($criteriaName, $fieldNames);
- if (isset($dataValues[$k])) {
- $dataValue = $dataValues[$k];
- $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
- $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
- }
- }
- // evaluate the criteria against the row data
- $result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList);
- // If the row failed to meet the criteria, remove it from the database
- if (!$result) {
- unset($database[$dataRow]);
- }
- }
-
- return $database;
- }
-
- private static function getFilteredColumn($database, $field, $criteria)
- {
- // reduce the database to a set of rows that match all the criteria
- $database = self::filter($database, $criteria);
- // extract an array of values for the requested column
- $colData = [];
- foreach ($database as $row) {
- $colData[] = $row[$field];
- }
-
- return $colData;
- }
-
/**
* DAVERAGE.
*
@@ -130,7 +17,9 @@ class Database
* Excel Function:
* DAVERAGE(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DAverage class instead
+ * @see Database\DAverage::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -151,15 +40,7 @@ class Database
*/
public static function DAVERAGE($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::AVERAGE(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DAverage::evaluate($database, $field, $criteria);
}
/**
@@ -171,16 +52,15 @@ class Database
* Excel Function:
* DCOUNT(database,[field],criteria)
*
- * Excel Function:
- * DAVERAGE(database,field,criteria)
- *
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DCount class instead
+ * @see Database\DCount::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
+ * @param null|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
@@ -191,22 +71,14 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return int
+ * @return int|string
*
* @TODO The field argument is optional. If field is omitted, DCOUNT counts all records in the
* database that match the criteria.
*/
public static function DCOUNT($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::COUNT(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DCount::evaluate($database, $field, $criteria);
}
/**
@@ -217,7 +89,9 @@ class Database
* Excel Function:
* DCOUNTA(database,[field],criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DCountA class instead
+ * @see Database\DCountA::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -234,30 +108,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return int
- *
- * @TODO The field argument is optional. If field is omitted, DCOUNTA counts all records in the
- * database that match the criteria.
+ * @return int|string
*/
public static function DCOUNTA($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // reduce the database to a set of rows that match all the criteria
- $database = self::filter($database, $criteria);
- // extract an array of values for the requested column
- $colData = [];
- foreach ($database as $row) {
- $colData[] = $row[$field];
- }
-
- // Return
- return Statistical::COUNTA(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DCountA::evaluate($database, $field, $criteria);
}
/**
@@ -269,7 +124,9 @@ class Database
* Excel Function:
* DGET(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DGet class instead
+ * @see Database\DGet::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -290,18 +147,7 @@ class Database
*/
public static function DGET($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- $colData = self::getFilteredColumn($database, $field, $criteria);
- if (count($colData) > 1) {
- return Functions::NAN();
- }
-
- return $colData[0];
+ return Database\DGet::evaluate($database, $field, $criteria);
}
/**
@@ -313,7 +159,9 @@ class Database
* Excel Function:
* DMAX(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DMax class instead
+ * @see Database\DMax::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -330,19 +178,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return null|float|string
*/
public static function DMAX($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::MAX(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DMax::evaluate($database, $field, $criteria);
}
/**
@@ -354,7 +194,9 @@ class Database
* Excel Function:
* DMIN(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DMin class instead
+ * @see Database\DMin::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -371,19 +213,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return null|float|string
*/
public static function DMIN($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::MIN(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DMin::evaluate($database, $field, $criteria);
}
/**
@@ -394,7 +228,9 @@ class Database
* Excel Function:
* DPRODUCT(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DProduct class instead
+ * @see Database\DProduct::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -411,19 +247,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string
*/
public static function DPRODUCT($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return MathTrig::PRODUCT(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DProduct::evaluate($database, $field, $criteria);
}
/**
@@ -435,7 +263,9 @@ class Database
* Excel Function:
* DSTDEV(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DStDev class instead
+ * @see Database\DStDev::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -456,15 +286,7 @@ class Database
*/
public static function DSTDEV($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::STDEV(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DStDev::evaluate($database, $field, $criteria);
}
/**
@@ -476,7 +298,9 @@ class Database
* Excel Function:
* DSTDEVP(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DStDevP class instead
+ * @see Database\DStDevP::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -497,15 +321,7 @@ class Database
*/
public static function DSTDEVP($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::STDEVP(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DStDevP::evaluate($database, $field, $criteria);
}
/**
@@ -516,7 +332,9 @@ class Database
* Excel Function:
* DSUM(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DSum class instead
+ * @see Database\DSum::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -533,19 +351,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return null|float|string
*/
public static function DSUM($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return MathTrig::SUM(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DSum::evaluate($database, $field, $criteria);
}
/**
@@ -557,7 +367,9 @@ class Database
* Excel Function:
* DVAR(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DVar class instead
+ * @see Database\DVar::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -574,19 +386,11 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string (string if result is an error)
*/
public static function DVAR($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::VARFunc(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DVar::evaluate($database, $field, $criteria);
}
/**
@@ -598,7 +402,9 @@ class Database
* Excel Function:
* DVARP(database,field,criteria)
*
- * @category Database Functions
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the Database\DVarP class instead
+ * @see Database\DVarP::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
@@ -615,18 +421,10 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string (string if result is an error)
*/
public static function DVARP($database, $field, $criteria)
{
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::VARP(
- self::getFilteredColumn($database, $field, $criteria)
- );
+ return Database\DVarP::evaluate($database, $field, $criteria);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Database/DAverage.php b/PhpOffice/PhpSpreadsheet/Calculation/Database/DAverage.php
new file mode 100644
index 0000000..245e970
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Database/DAverage.php
@@ -0,0 +1,46 @@
+ 1) {
+ return ExcelError::NAN();
+ }
+
+ $row = array_pop($columnData);
+
+ return array_pop($row);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Database/DMax.php b/PhpOffice/PhpSpreadsheet/Calculation/Database/DMax.php
new file mode 100644
index 0000000..748fd2f
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Database/DMax.php
@@ -0,0 +1,47 @@
+= count($fieldNames)) {
+ return null;
+ }
+
+ return $field;
+ }
+ $key = array_search($field, array_values($fieldNames), true);
+
+ return ($key !== false) ? (int) $key : null;
+ }
+
+ /**
+ * filter.
+ *
+ * Parses the selection criteria, extracts the database rows that match those criteria, and
+ * returns that subset of rows.
+ *
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ *
+ * @return mixed[]
+ */
+ protected static function filter(array $database, array $criteria): array
+ {
+ $fieldNames = array_shift($database);
+ $criteriaNames = array_shift($criteria);
+
+ // Convert the criteria into a set of AND/OR conditions with [:placeholders]
+ $query = self::buildQuery($criteriaNames, $criteria);
+
+ // Loop through each row of the database
+ return self::executeQuery($database, $query, $criteriaNames, $fieldNames);
+ }
+
+ protected static function getFilteredColumn(array $database, ?int $field, array $criteria): array
+ {
+ // reduce the database to a set of rows that match all the criteria
+ $database = self::filter($database, $criteria);
+ $defaultReturnColumnValue = ($field === null) ? 1 : null;
+
+ // extract an array of values for the requested column
+ $columnData = [];
+ foreach ($database as $rowKey => $row) {
+ $keys = array_keys($row);
+ $key = $keys[$field] ?? null;
+ $columnKey = $key ?? 'A';
+ $columnData[$rowKey][$columnKey] = $row[$key] ?? $defaultReturnColumnValue;
+ }
+
+ return $columnData;
+ }
+
+ private static function buildQuery(array $criteriaNames, array $criteria): string
+ {
+ $baseQuery = [];
+ foreach ($criteria as $key => $criterion) {
+ foreach ($criterion as $field => $value) {
+ $criterionName = $criteriaNames[$field];
+ if ($value !== null) {
+ $condition = self::buildCondition($value, $criterionName);
+ $baseQuery[$key][] = $condition;
+ }
+ }
+ }
+
+ $rowQuery = array_map(
+ function ($rowValue) {
+ return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? '');
+ },
+ $baseQuery
+ );
+
+ return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
+ }
+
+ /**
+ * @param mixed $criterion
+ */
+ private static function buildCondition($criterion, string $criterionName): string
+ {
+ $ifCondition = Functions::ifCondition($criterion);
+
+ // Check for wildcard characters used in the condition
+ $result = preg_match('/(?[^"]*)(?".*[*?].*")/ui', $ifCondition, $matches);
+ if ($result !== 1) {
+ return "[:{$criterionName}]{$ifCondition}";
+ }
+
+ $trueFalse = ($matches['operator'] !== '<>');
+ $wildcard = WildcardMatch::wildcard($matches['operand']);
+ $condition = "WILDCARDMATCH([:{$criterionName}],{$wildcard})";
+ if ($trueFalse === false) {
+ $condition = "NOT({$condition})";
+ }
+
+ return $condition;
+ }
+
+ private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
+ {
+ foreach ($database as $dataRow => $dataValues) {
+ // Substitute actual values from the database row for our [:placeholders]
+ $conditions = $query;
+ foreach ($criteria as $criterion) {
+ $conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
+ }
+
+ // evaluate the criteria against the row data
+ $result = Calculation::getInstance()->_calculateFormulaValue('=' . $conditions);
+
+ // If the row failed to meet the criteria, remove it from the database
+ if ($result !== true) {
+ unset($database[$dataRow]);
+ }
+ }
+
+ return $database;
+ }
+
+ /**
+ * @return mixed
+ */
+ private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
+ {
+ $key = array_search($criterion, $fields, true);
+
+ $dataValue = 'NULL';
+ if (is_bool($dataValues[$key])) {
+ $dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
+ } elseif ($dataValues[$key] !== null) {
+ $dataValue = $dataValues[$key];
+ // escape quotes if we have a string containing quotes
+ if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
+ $dataValue = str_replace('"', '""', $dataValue);
+ }
+ $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
+ }
+
+ return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTime.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTime.php
old mode 100755
new mode 100644
index 796e795..26b667c
--- a/PhpOffice/PhpSpreadsheet/Calculation/DateTime.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTime.php
@@ -2,129 +2,47 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use PhpOffice\PhpSpreadsheet\Shared\Date;
-use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use DateTimeInterface;
+/**
+ * @deprecated 1.18.0
+ */
class DateTime
{
/**
* Identify if a year is a leap year or not.
*
+ * @deprecated 1.18.0
+ * Use the isLeapYear method in the DateTimeExcel\Helpers class instead
+ * @see DateTimeExcel\Helpers::isLeapYear()
+ *
* @param int|string $year The year to test
*
* @return bool TRUE if the year is a leap year, otherwise FALSE
*/
public static function isLeapYear($year)
{
- return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
- }
-
- /**
- * Return the number of days between two dates based on a 360 day calendar.
- *
- * @param int $startDay Day of month of the start date
- * @param int $startMonth Month of the start date
- * @param int $startYear Year of the start date
- * @param int $endDay Day of month of the start date
- * @param int $endMonth Month of the start date
- * @param int $endYear Year of the start date
- * @param bool $methodUS Whether to use the US method or the European method of calculation
- *
- * @return int Number of days between the start date and the end date
- */
- 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;
+ return DateTimeExcel\Helpers::isLeapYear($year);
}
/**
* getDateValue.
*
- * @param string $dateValue
+ * @deprecated 1.18.0
+ * Use the getDateValue method in the DateTimeExcel\Helpers class instead
+ * @see DateTimeExcel\Helpers::getDateValue()
+ *
+ * @param mixed $dateValue
*
* @return mixed Excel date/time serial value, or string if error
*/
public static function getDateValue($dateValue)
{
- if (!is_numeric($dateValue)) {
- if ((is_string($dateValue)) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
- return Functions::VALUE();
- }
- if ((is_object($dateValue)) && ($dateValue instanceof \DateTimeInterface)) {
- $dateValue = Date::PHPToExcel($dateValue);
- } else {
- $saveReturnDateType = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
- $dateValue = self::DATEVALUE($dateValue);
- Functions::setReturnDateType($saveReturnDateType);
- }
+ try {
+ return DateTimeExcel\Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
}
-
- return $dateValue;
- }
-
- /**
- * getTimeValue.
- *
- * @param string $timeValue
- *
- * @return mixed Excel date/time serial value, or string if error
- */
- private static function getTimeValue($timeValue)
- {
- $saveReturnDateType = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
- $timeValue = self::TIMEVALUE($timeValue);
- Functions::setReturnDateType($saveReturnDateType);
-
- return $timeValue;
- }
-
- private static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0)
- {
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($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;
}
/**
@@ -141,33 +59,16 @@ class DateTime
* Excel Function:
* NOW()
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the now method in the DateTimeExcel\Current class instead
+ * @see DateTimeExcel\Current::now()
*
* @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 (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $retValue = (float) Date::PHPToExcel(time());
-
- break;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- $retValue = (int) time();
-
- break;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $retValue = new \DateTime();
-
- break;
- }
- date_default_timezone_set($saveTimeZone);
-
- return $retValue;
+ return DateTimeExcel\Current::now();
}
/**
@@ -184,34 +85,16 @@ class DateTime
* Excel Function:
* TODAY()
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the today method in the DateTimeExcel\Current class instead
+ * @see DateTimeExcel\Current::today()
*
* @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(Date::PHPToExcel(time()));
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $retValue = (float) $excelDateTime;
-
- break;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- $retValue = (int) Date::excelToTimestamp($excelDateTime);
-
- break;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $retValue = Date::excelToDateTimeObject($excelDateTime);
-
- break;
- }
- date_default_timezone_set($saveTimeZone);
-
- return $retValue;
+ return DateTimeExcel\Current::today();
}
/**
@@ -222,15 +105,18 @@ class DateTime
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
+ *
* Excel Function:
* DATE(year,month,day)
*
+ * @deprecated 1.18.0
+ * Use the fromYMD method in the DateTimeExcel\Date class instead
+ * @see DateTimeExcel\Date::fromYMD()
+ *
* PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
* A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
* as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
*
- * @category Date/Time Functions
- *
* @param int $year The value of the year argument can include one to four digits.
* Excel interprets the year argument according to the configured
* date system: 1900 or 1904.
@@ -267,69 +153,7 @@ class DateTime
*/
public static function DATE($year = 0, $month = 1, $day = 1)
{
- $year = Functions::flattenSingleValue($year);
- $month = Functions::flattenSingleValue($month);
- $day = Functions::flattenSingleValue($day);
-
- if (($month !== null) && (!is_numeric($month))) {
- $month = Date::monthStringToNumber($month);
- }
-
- if (($day !== null) && (!is_numeric($day))) {
- $day = Date::dayStringToNumber($day);
- }
-
- $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0;
- $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0;
- $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0;
- if ((!is_numeric($year)) ||
- (!is_numeric($month)) ||
- (!is_numeric($day))) {
- return Functions::VALUE();
- }
- $year = (int) $year;
- $month = (int) $month;
- $day = (int) $day;
-
- $baseYear = Date::getExcelCalendar();
- // Validate parameters
- if ($year < ($baseYear - 1900)) {
- return Functions::NAN();
- }
- if ((($baseYear - 1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
- return Functions::NAN();
- }
-
- 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 Functions::NAN();
- }
-
- // Execute function
- $excelDateValue = Date::formattedPHPToExcel($year, $month, $day);
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return Date::excelToDateTimeObject($excelDateValue);
- }
+ return DateTimeExcel\Date::fromYMD($year, $month, $day);
}
/**
@@ -343,7 +167,9 @@ class DateTime
* Excel Function:
* TIME(hour,minute,second)
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the fromHMS method in the DateTimeExcel\Time class instead
+ * @see DateTimeExcel\Time::fromHMS()
*
* @param int $hour A number from 0 (zero) to 32767 representing the hour.
* Any value greater than 23 will be divided by 24 and the remainder
@@ -362,85 +188,7 @@ class DateTime
*/
public static function TIME($hour = 0, $minute = 0, $second = 0)
{
- $hour = Functions::flattenSingleValue($hour);
- $minute = Functions::flattenSingleValue($minute);
- $second = Functions::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 Functions::VALUE();
- }
- $hour = (int) $hour;
- $minute = (int) $minute;
- $second = (int) $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 Functions::NAN();
- }
-
- // Execute function
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $date = 0;
- $calendar = Date::getExcelCalendar();
- if ($calendar != Date::CALENDAR_WINDOWS_1900) {
- $date = 1;
- }
-
- return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
- case Functions::RETURNDATE_PHP_DATETIME_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;
- }
+ return DateTimeExcel\Time::fromHMS($hour, $minute, $second);
}
/**
@@ -456,7 +204,9 @@ class DateTime
* Excel Function:
* DATEVALUE(dateValue)
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the fromString method in the DateTimeExcel\DateValue class instead
+ * @see DateTimeExcel\DateValue::fromString()
*
* @param string $dateValue Text that represents a date in a Microsoft Excel date format.
* For example, "1/30/2008" or "30-Jan-2008" are text strings within
@@ -470,112 +220,9 @@ class DateTime
* @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)
+ public static function DATEVALUE($dateValue)
{
- $dateValue = trim(Functions::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(['/', '.', '-', ' '], ' ', $dateValue);
-
- $yearFound = false;
- $t1 = explode(' ', $dateValue);
- foreach ($t1 as &$t) {
- if ((is_numeric($t)) && ($t > 31)) {
- if ($yearFound) {
- return Functions::VALUE();
- }
- 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 {
- if ($t1[1] > 29) {
- $t1[1] += 1900;
- array_unshift($t1, 1);
- } else {
- $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 Functions::VALUE();
- }
- } else {
- return Functions::VALUE();
- }
- if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
- $testVal3 += 2000;
- }
- $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 Functions::VALUE();
- }
- }
- }
-
- if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
- // Execute function
- if ($PHPDateArray['year'] == '') {
- $PHPDateArray['year'] = strftime('%Y');
- }
- if ($PHPDateArray['year'] < 1900) {
- return Functions::VALUE();
- }
- if ($PHPDateArray['month'] == '') {
- $PHPDateArray['month'] = strftime('%m');
- }
- if ($PHPDateArray['day'] == '') {
- $PHPDateArray['day'] = strftime('%d');
- }
- if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
- return Functions::VALUE();
- }
- $excelDateValue = floor(
- Date::formattedPHPToExcel(
- $PHPDateArray['year'],
- $PHPDateArray['month'],
- $PHPDateArray['day'],
- $PHPDateArray['hour'],
- $PHPDateArray['minute'],
- $PHPDateArray['second']
- )
- );
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
- }
- }
-
- return Functions::VALUE();
+ return DateTimeExcel\DateValue::fromString($dateValue);
}
/**
@@ -591,7 +238,9 @@ class DateTime
* Excel Function:
* TIMEVALUE(timeValue)
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the fromString method in the DateTimeExcel\TimeValue class instead
+ * @see DateTimeExcel\TimeValue::fromString()
*
* @param string $timeValue A text string that represents a time in any one of the Microsoft
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
@@ -603,163 +252,30 @@ class DateTime
*/
public static function TIMEVALUE($timeValue)
{
- $timeValue = trim(Functions::flattenSingleValue($timeValue), '"');
- $timeValue = str_replace(['/', '.'], '-', $timeValue);
-
- $arraySplit = preg_split('/[\/:\-\s]/', $timeValue);
- if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
- $arraySplit[0] = ($arraySplit[0] % 24);
- $timeValue = implode(':', $arraySplit);
- }
-
- $PHPDateArray = date_parse($timeValue);
- if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $excelDateValue = Date::formattedPHPToExcel(
- $PHPDateArray['year'],
- $PHPDateArray['month'],
- $PHPDateArray['day'],
- $PHPDateArray['hour'],
- $PHPDateArray['minute'],
- $PHPDateArray['second']
- );
- } else {
- $excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
- }
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
- }
- }
-
- return Functions::VALUE();
+ return DateTimeExcel\TimeValue::fromString($timeValue);
}
/**
* DATEDIF.
*
+ * Excel Function:
+ * DATEDIF(startdate, enddate, unit)
+ *
+ * @deprecated 1.18.0
+ * Use the interval method in the DateTimeExcel\Difference class instead
+ * @see DateTimeExcel\Difference::interval()
+ *
* @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
* @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
- * @param string $unit
+ * @param array|string $unit
*
- * @return int|string Interval between the dates
+ * @return array|int|string Interval between the dates
*/
public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- $unit = strtoupper(Functions::flattenSingleValue($unit));
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- // Validate parameters
- if ($startDate > $endDate) {
- return Functions::NAN();
- }
-
- // Execute function
- $difference = $endDate - $startDate;
-
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $startDays = $PHPStartDateObject->format('j');
- $startMonths = $PHPStartDateObject->format('n');
- $startYears = $PHPStartDateObject->format('Y');
-
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
- $endDays = $PHPEndDateObject->format('j');
- $endMonths = $PHPEndDateObject->format('n');
- $endYears = $PHPEndDateObject->format('Y');
-
- switch ($unit) {
- case 'D':
- $retVal = (int) $difference;
-
- break;
- case 'M':
- $retVal = (int) ($endMonths - $startMonths) + ((int) ($endYears - $startYears) * 12);
- // We're only interested in full months
- if ($endDays < $startDays) {
- --$retVal;
- }
-
- break;
- case 'Y':
- $retVal = (int) ($endYears - $startYears);
- // We're only interested in full months
- if ($endMonths < $startMonths) {
- --$retVal;
- } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
- // Remove start month
- --$retVal;
- // Remove end month
- --$retVal;
- }
-
- break;
- case 'MD':
- if ($endDays < $startDays) {
- $retVal = $endDays;
- $PHPEndDateObject->modify('-' . $endDays . ' days');
- $adjustDays = $PHPEndDateObject->format('j');
- $retVal += ($adjustDays - $startDays);
- } else {
- $retVal = $endDays - $startDays;
- }
-
- break;
- case 'YM':
- $retVal = (int) ($endMonths - $startMonths);
- if ($retVal < 0) {
- $retVal += 12;
- }
- // We're only interested in full months
- if ($endDays < $startDays) {
- --$retVal;
- }
-
- break;
- case 'YD':
- $retVal = (int) $difference;
- if ($endYears > $startYears) {
- $isLeapStartYear = $PHPStartDateObject->format('L');
- $wasLeapEndYear = $PHPEndDateObject->format('L');
-
- // Adjust end year to be as close as possible as start year
- while ($PHPEndDateObject >= $PHPStartDateObject) {
- $PHPEndDateObject->modify('-1 year');
- $endYears = $PHPEndDateObject->format('Y');
- }
- $PHPEndDateObject->modify('+1 year');
-
- // Get the result
- $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
-
- // Adjust for leap years cases
- $isLeapEndYear = $PHPEndDateObject->format('L');
- $limit = new \DateTime($PHPEndDateObject->format('Y-02-29'));
- if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
- --$retVal;
- }
- }
-
- break;
- default:
- $retVal = Functions::VALUE();
- }
-
- return $retVal;
+ return DateTimeExcel\Difference::interval($startDate, $endDate, $unit);
}
/**
@@ -770,42 +286,20 @@ class DateTime
* Excel Function:
* DAYS(endDate, startDate)
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the between method in the DateTimeExcel\Days class instead
+ * @see DateTimeExcel\Days::between()
*
- * @param \DateTimeImmutable|float|int|string $endDate Excel date serial value (float),
+ * @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
- * @param \DateTimeImmutable|float|int|string $startDate Excel date serial value (float),
+ * @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
*
- * @return int|string Number of days between start date and end date or an error
+ * @return array|int|string Number of days between start date and end date or an error
*/
public static function DAYS($endDate = 0, $startDate = 0)
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
-
- $startDate = self::getDateValue($startDate);
- if (is_string($startDate)) {
- return Functions::VALUE();
- }
-
- $endDate = self::getDateValue($endDate);
- if (is_string($endDate)) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
-
- $diff = $PHPStartDateObject->diff($PHPEndDateObject);
- $days = $diff->days;
-
- if ($diff->invert) {
- $days = -$days;
- }
-
- return $days;
+ return DateTimeExcel\Days::between($endDate, $startDate);
}
/**
@@ -818,13 +312,15 @@ class DateTime
* Excel Function:
* DAYS360(startDate,endDate[,method])
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the between method in the DateTimeExcel\Days360 class instead
+ * @see DateTimeExcel\Days360::between()
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
- * @param bool $method US or European Method
+ * @param array|bool $method US or European Method
* FALSE or omitted: U.S. (NASD) method. If the starting date is
* the last day of a month, it becomes equal to the 30th of the
* same month. If the ending date is the last day of a month and
@@ -836,36 +332,11 @@ class DateTime
* occur on the 31st of a month become equal to the 30th of the
* same month.
*
- * @return int|string Number of days between start date and end date
+ * @return array|int|string Number of days between start date and end date
*/
public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- if (!is_bool($method)) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $startDay = $PHPStartDateObject->format('j');
- $startMonth = $PHPStartDateObject->format('n');
- $startYear = $PHPStartDateObject->format('Y');
-
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
- $endDay = $PHPEndDateObject->format('j');
- $endMonth = $PHPEndDateObject->format('n');
- $endYear = $PHPEndDateObject->format('Y');
-
- return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
+ return DateTimeExcel\Days360::between($startDate, $endDate, $method);
}
/**
@@ -879,93 +350,29 @@ class DateTime
* Excel Function:
* YEARFRAC(startDate,endDate[,method])
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the fraction method in the DateTimeExcel\YearFrac class instead
+ * @see DateTimeExcel\YearFrac::fraction()
+ *
+ * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
+ * for description of algorithm used in Excel
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
- * @param int $method Method used for the calculation
+ * @param array|int $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
+ * @return array|float|string fraction of the year, or a string containing an error
*/
public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
{
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- $method = Functions::flattenSingleValue($method);
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
- switch ($method) {
- case 0:
- return self::DAYS360($startDate, $endDate) / 360;
- 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);
- case 2:
- return self::DATEDIF($startDate, $endDate) / 360;
- case 3:
- return self::DATEDIF($startDate, $endDate) / 365;
- case 4:
- return self::DAYS360($startDate, $endDate, true) / 360;
- }
- }
-
- return Functions::VALUE();
+ return DateTimeExcel\YearFrac::fraction($startDate, $endDate, $method);
}
/**
@@ -979,73 +386,21 @@ class DateTime
* Excel Function:
* NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the count method in the DateTimeExcel\NetworkDays class instead
+ * @see DateTimeExcel\NetworkDays::count()
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
+ * @param mixed $dateArgs
*
- * @return int|string Interval between the dates
+ * @return array|int|string Interval between the dates
*/
public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
{
- // Retrieve the mandatory start and end date that are referenced in the function definition
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- // Get the optional days
- $dateArgs = Functions::flattenArray($dateArgs);
-
- // Validate the start and end dates
- if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- $startDate = (float) floor($startDate);
- if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
- $endDate = (float) floor($endDate);
-
- if ($sDate > $eDate) {
- $startDate = $eDate;
- $endDate = $sDate;
- }
-
- // Execute function
- $startDoW = 6 - self::WEEKDAY($startDate, 2);
- if ($startDoW < 0) {
- $startDoW = 0;
- }
- $endDoW = self::WEEKDAY($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 = [];
- foreach ($dateArgs as $holidayDate) {
- if (is_string($holidayDate = self::getDateValue($holidayDate))) {
- return Functions::VALUE();
- }
- if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
- if ((self::WEEKDAY($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
- --$partWeekDays;
- $holidayCountedArray[] = $holidayDate;
- }
- }
- }
-
- if ($sDate > $eDate) {
- return 0 - ($wholeWeekDays + $partWeekDays);
- }
-
- return $wholeWeekDays + $partWeekDays;
+ return DateTimeExcel\NetworkDays::count($startDate, $endDate, ...$dateArgs);
}
/**
@@ -1059,104 +414,23 @@ class DateTime
* Excel Function:
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
*
- * @category Date/Time Functions
+ * @deprecated 1.18.0
+ * Use the date method in the DateTimeExcel\WorkDay class instead
+ * @see DateTimeExcel\WorkDay::date()
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $endDays The number of nonweekend and nonholiday days before or after
* startDate. A positive value for days yields a future date; a
* negative value yields a past date.
+ * @param mixed $dateArgs
*
* @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 WORKDAY($startDate, $endDays, ...$dateArgs)
{
- // Retrieve the mandatory start date and days that are referenced in the function definition
- $startDate = Functions::flattenSingleValue($startDate);
- $endDays = Functions::flattenSingleValue($endDays);
- // Get the optional days
- $dateArgs = Functions::flattenArray($dateArgs);
-
- if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
- return Functions::VALUE();
- }
- $startDate = (float) floor($startDate);
- $endDays = (int) floor($endDays);
- // If endDays is 0, we always return startDate
- if ($endDays == 0) {
- return $startDate;
- }
-
- $decrementing = $endDays < 0;
-
- // Adjust the start date if it falls over a weekend
-
- $startDoW = self::WEEKDAY($startDate, 3);
- if (self::WEEKDAY($startDate, 3) >= 5) {
- $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
- ($decrementing) ? $endDays++ : $endDays--;
- }
-
- // Add endDays
- $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);
-
- // Adjust the calculated end date if it falls over a weekend
- $endDoW = self::WEEKDAY($endDate, 3);
- if ($endDoW >= 5) {
- $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
- }
-
- // Test any extra holiday parameters
- if (!empty($dateArgs)) {
- $holidayCountedArray = $holidayDates = [];
- foreach ($dateArgs as $holidayDate) {
- if (($holidayDate !== null) && (trim($holidayDate) > '')) {
- if (is_string($holidayDate = self::getDateValue($holidayDate))) {
- return Functions::VALUE();
- }
- if (self::WEEKDAY($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::WEEKDAY($endDate, 3);
- if ($endDoW >= 5) {
- $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
- }
- }
- }
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $endDate;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($endDate);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return Date::excelToDateTimeObject($endDate);
- }
+ return DateTimeExcel\WorkDay::date($startDate, $endDays, ...$dateArgs);
}
/**
@@ -1168,33 +442,18 @@ class DateTime
* Excel Function:
* DAY(dateValue)
*
+ * @deprecated 1.18.0
+ * Use the day method in the DateTimeExcel\DateParts class instead
+ * @see DateTimeExcel\DateParts::day()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
- * @return int|string Day of the month
+ * @return array|int|string Day of the month
*/
public static function DAYOFMONTH($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
- if ($dateValue < 0.0) {
- return Functions::NAN();
- } elseif ($dateValue < 1.0) {
- return 0;
- }
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('j');
+ return DateTimeExcel\DateParts::day($dateValue);
}
/**
@@ -1206,73 +465,197 @@ class DateTime
* Excel Function:
* WEEKDAY(dateValue[,style])
*
- * @param int $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * @deprecated 1.18.0
+ * Use the day method in the DateTimeExcel\Week class instead
+ * @see DateTimeExcel\Week::day()
+ *
+ * @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $style A number that determines the type of return value
* 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
* 2 Numbers 1 (Monday) through 7 (Sunday).
* 3 Numbers 0 (Monday) through 6 (Sunday).
*
- * @return int|string Day of the week value
+ * @return array|int|string Day of the week value
*/
public static function WEEKDAY($dateValue = 1, $style = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $style = Functions::flattenSingleValue($style);
-
- if (!is_numeric($style)) {
- return Functions::VALUE();
- } elseif (($style < 1) || ($style > 3)) {
- return Functions::NAN();
- }
- $style = floor($style);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- $DoW = (int) $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;
- }
- if (Functions::getCompatibilityMode() == Functions::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 $DoW;
+ return DateTimeExcel\Week::day($dateValue, $style);
}
+ /**
+ * STARTWEEK_SUNDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_SUNDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_SUNDAY
+ */
+ const STARTWEEK_SUNDAY = 1;
+
+ /**
+ * STARTWEEK_MONDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_MONDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_MONDAY
+ */
+ const STARTWEEK_MONDAY = 2;
+
+ /**
+ * STARTWEEK_MONDAY_ALT.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
+ * @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
+ */
+ const STARTWEEK_MONDAY_ALT = 11;
+
+ /**
+ * STARTWEEK_TUESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_TUESDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_TUESDAY
+ */
+ const STARTWEEK_TUESDAY = 12;
+
+ /**
+ * STARTWEEK_WEDNESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
+ */
+ const STARTWEEK_WEDNESDAY = 13;
+
+ /**
+ * STARTWEEK_THURSDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_THURSDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_THURSDAY
+ */
+ const STARTWEEK_THURSDAY = 14;
+
+ /**
+ * STARTWEEK_FRIDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_FRIDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_FRIDAY
+ */
+ const STARTWEEK_FRIDAY = 15;
+
+ /**
+ * STARTWEEK_SATURDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_SATURDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_SATURDAY
+ */
+ const STARTWEEK_SATURDAY = 16;
+
+ /**
+ * STARTWEEK_SUNDAY_ALT.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
+ * @see DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
+ */
+ const STARTWEEK_SUNDAY_ALT = 17;
+
+ /**
+ * DOW_SUNDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_SUNDAY
+ * @see DateTimeExcel\Constants::DOW_SUNDAY
+ */
+ const DOW_SUNDAY = 1;
+
+ /**
+ * DOW_MONDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_MONDAY
+ * @see DateTimeExcel\Constants::DOW_MONDAY
+ */
+ const DOW_MONDAY = 2;
+
+ /**
+ * DOW_TUESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_TUESDAY
+ * @see DateTimeExcel\Constants::DOW_TUESDAY
+ */
+ const DOW_TUESDAY = 3;
+
+ /**
+ * DOW_WEDNESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_WEDNESDAY
+ * @see DateTimeExcel\Constants::DOW_WEDNESDAY
+ */
+ const DOW_WEDNESDAY = 4;
+
+ /**
+ * DOW_THURSDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_THURSDAY
+ * @see DateTimeExcel\Constants::DOW_THURSDAY
+ */
+ const DOW_THURSDAY = 5;
+
+ /**
+ * DOW_FRIDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_FRIDAY
+ * @see DateTimeExcel\Constants::DOW_FRIDAY
+ */
+ const DOW_FRIDAY = 6;
+
+ /**
+ * DOW_SATURDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_SATURDAY
+ * @see DateTimeExcel\Constants::DOW_SATURDAY
+ */
+ const DOW_SATURDAY = 7;
+
+ /**
+ * STARTWEEK_MONDAY_ISO.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
+ * @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
+ */
+ const STARTWEEK_MONDAY_ISO = 21;
+
+ /**
+ * METHODARR.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::METHODARR
+ * @see DateTimeExcel\Constants::METHODARR
+ */
+ const METHODARR = [
+ self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
+ self::DOW_MONDAY,
+ self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
+ self::DOW_TUESDAY,
+ self::DOW_WEDNESDAY,
+ self::DOW_THURSDAY,
+ self::DOW_FRIDAY,
+ self::DOW_SATURDAY,
+ self::DOW_SUNDAY,
+ self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
+ ];
+
/**
* WEEKNUM.
*
@@ -1286,48 +669,29 @@ class DateTime
* Excel Function:
* WEEKNUM(dateValue[,style])
*
+ * @deprecated 1.18.0
+ * Use the number method in the DateTimeExcel\Week class instead
+ * @see DateTimeExcel\Week::number()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $method Week begins on Sunday or Monday
* 1 or omitted Week begins on Sunday.
* 2 Week begins on Monday.
+ * 11 Week begins on Monday.
+ * 12 Week begins on Tuesday.
+ * 13 Week begins on Wednesday.
+ * 14 Week begins on Thursday.
+ * 15 Week begins on Friday.
+ * 16 Week begins on Saturday.
+ * 17 Week begins on Sunday.
+ * 21 ISO (Jan. 4 is week 1, begins on Monday).
*
- * @return int|string Week Number
+ * @return array|int|string Week Number
*/
- public static function WEEKNUM($dateValue = 1, $method = 1)
+ public static function WEEKNUM($dateValue = 1, $method = /** @scrutinizer ignore-deprecated */ self::STARTWEEK_SUNDAY)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $method = Functions::flattenSingleValue($method);
-
- if (!is_numeric($method)) {
- return Functions::VALUE();
- } elseif (($method < 1) || ($method > 2)) {
- return Functions::NAN();
- }
- $method = floor($method);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- $dayOfYear = $PHPDateObject->format('z');
- $PHPDateObject->modify('-' . $dayOfYear . ' days');
- $firstDayOfFirstWeek = $PHPDateObject->format('w');
- $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
- $interval = $dayOfYear - $daysInFirstWeek;
- $weekOfYear = floor($interval / 7) + 1;
-
- if ($daysInFirstWeek) {
- ++$weekOfYear;
- }
-
- return (int) $weekOfYear;
+ return DateTimeExcel\Week::number($dateValue, $method);
}
/**
@@ -1338,27 +702,18 @@ class DateTime
* Excel Function:
* ISOWEEKNUM(dateValue)
*
+ * @deprecated 1.18.0
+ * Use the isoWeekNumber method in the DateTimeExcel\Week class instead
+ * @see DateTimeExcel\Week::isoWeekNumber()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
- * @return int|string Week Number
+ * @return array|int|string Week Number
*/
public static function ISOWEEKNUM($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('W');
+ return DateTimeExcel\Week::isoWeekNumber($dateValue);
}
/**
@@ -1370,28 +725,18 @@ class DateTime
* Excel Function:
* MONTH(dateValue)
*
+ * @deprecated 1.18.0
+ * Use the month method in the DateTimeExcel\DateParts class instead
+ * @see DateTimeExcel\DateParts::month()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
- * @return int|string Month of the year
+ * @return array|int|string Month of the year
*/
public static function MONTHOFYEAR($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if (empty($dateValue)) {
- $dateValue = 1;
- }
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('n');
+ return DateTimeExcel\DateParts::month($dateValue);
}
/**
@@ -1403,27 +748,18 @@ class DateTime
* Excel Function:
* YEAR(dateValue)
*
+ * @deprecated 1.18.0
+ * Use the ear method in the DateTimeExcel\DateParts class instead
+ * @see DateTimeExcel\DateParts::year()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
- * @return int|string Year
+ * @return array|int|string Year
*/
public static function YEAR($dateValue = 1)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('Y');
+ return DateTimeExcel\DateParts::year($dateValue);
}
/**
@@ -1435,36 +771,18 @@ class DateTime
* Excel Function:
* HOUR(timeValue)
*
+ * @deprecated 1.18.0
+ * Use the hour method in the DateTimeExcel\TimeParts class instead
+ * @see DateTimeExcel\TimeParts::hour()
+ *
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
- * @return int|string Hour
+ * @return array|int|string Hour
*/
public static function HOUROFDAY($timeValue = 0)
{
- $timeValue = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('G', $timeValue);
+ return DateTimeExcel\TimeParts::hour($timeValue);
}
/**
@@ -1476,36 +794,18 @@ class DateTime
* Excel Function:
* MINUTE(timeValue)
*
+ * @deprecated 1.18.0
+ * Use the minute method in the DateTimeExcel\TimeParts class instead
+ * @see DateTimeExcel\TimeParts::minute()
+ *
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
- * @return int|string Minute
+ * @return array|int|string Minute
*/
public static function MINUTE($timeValue = 0)
{
- $timeValue = $timeTester = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('i', $timeValue);
+ return DateTimeExcel\TimeParts::minute($timeValue);
}
/**
@@ -1517,36 +817,18 @@ class DateTime
* Excel Function:
* SECOND(timeValue)
*
+ * @deprecated 1.18.0
+ * Use the second method in the DateTimeExcel\TimeParts class instead
+ * @see DateTimeExcel\TimeParts::second()
+ *
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
- * @return int|string Second
+ * @return array|int|string Second
*/
public static function SECOND($timeValue = 0)
{
- $timeValue = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('s', $timeValue);
+ return DateTimeExcel\TimeParts::second($timeValue);
}
/**
@@ -1560,6 +842,10 @@ class DateTime
* Excel Function:
* EDATE(dateValue,adjustmentMonths)
*
+ * @deprecated 1.18.0
+ * Use the adjust method in the DateTimeExcel\Edate class instead
+ * @see DateTimeExcel\Month::adjust()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
@@ -1571,29 +857,7 @@ class DateTime
*/
public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
-
- if (!is_numeric($adjustmentMonths)) {
- return Functions::VALUE();
- }
- $adjustmentMonths = floor($adjustmentMonths);
-
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return $PHPDateObject;
- }
+ return DateTimeExcel\Month::adjust($dateValue, $adjustmentMonths);
}
/**
@@ -1606,6 +870,10 @@ class DateTime
* Excel Function:
* EOMONTH(dateValue,adjustmentMonths)
*
+ * @deprecated 1.18.0
+ * Use the lastDay method in the DateTimeExcel\EoMonth class instead
+ * @see DateTimeExcel\Month::lastDay()
+ *
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
@@ -1617,31 +885,6 @@ class DateTime
*/
public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
{
- $dateValue = Functions::flattenSingleValue($dateValue);
- $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
-
- if (!is_numeric($adjustmentMonths)) {
- return Functions::VALUE();
- }
- $adjustmentMonths = floor($adjustmentMonths);
-
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
- $adjustDays = (int) $PHPDateObject->format('d');
- $adjustDaysString = '-' . $adjustDays . ' days';
- $PHPDateObject->modify($adjustDaysString);
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return $PHPDateObject;
- }
+ return DateTimeExcel\Month::lastDay($dateValue, $adjustmentMonths);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
new file mode 100644
index 0000000..1165eb1
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
@@ -0,0 +1,38 @@
+ self::DOW_SUNDAY,
+ self::DOW_MONDAY,
+ self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
+ self::DOW_TUESDAY,
+ self::DOW_WEDNESDAY,
+ self::DOW_THURSDAY,
+ self::DOW_FRIDAY,
+ self::DOW_SATURDAY,
+ self::DOW_SUNDAY,
+ self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
+ ];
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
new file mode 100644
index 0000000..5de671d
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
@@ -0,0 +1,59 @@
+format('c'));
+
+ return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : ExcelError::VALUE();
+ }
+
+ /**
+ * DATETIMENOW.
+ *
+ * Returns the current date and time.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * NOW()
+ *
+ * @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 now()
+ {
+ $dti = new DateTimeImmutable();
+ $dateArray = Helpers::dateParse($dti->format('c'));
+
+ return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : ExcelError::VALUE();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
new file mode 100644
index 0000000..6b55e79
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
@@ -0,0 +1,172 @@
+getMessage();
+ }
+
+ // Execute function
+ $excelDateValue = SharedDateHelper::formattedPHPToExcel(/** @scrutinizer ignore-type */ $year, $month, $day);
+
+ return Helpers::returnIn3FormatsFloat($excelDateValue);
+ }
+
+ /**
+ * Convert year from multiple formats to int.
+ *
+ * @param mixed $year
+ */
+ private static function getYear($year, int $baseYear): int
+ {
+ $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
+ if (!is_numeric($year)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ $year = (int) $year;
+
+ if ($year < ($baseYear - 1900)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
+ $year += 1900;
+ }
+
+ return (int) $year;
+ }
+
+ /**
+ * Convert month from multiple formats to int.
+ *
+ * @param mixed $month
+ */
+ private static function getMonth($month): int
+ {
+ if (($month !== null) && (!is_numeric($month))) {
+ $month = SharedDateHelper::monthStringToNumber($month);
+ }
+
+ $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
+ if (!is_numeric($month)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return (int) $month;
+ }
+
+ /**
+ * Convert day from multiple formats to int.
+ *
+ * @param mixed $day
+ */
+ private static function getDay($day): int
+ {
+ if (($day !== null) && (!is_numeric($day))) {
+ $day = SharedDateHelper::dayStringToNumber($day);
+ }
+
+ $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
+ if (!is_numeric($day)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return (int) $day;
+ }
+
+ private static function adjustYearMonth(int &$year, int &$month, int $baseYear): void
+ {
+ 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)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
new file mode 100644
index 0000000..b669eb0
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
@@ -0,0 +1,151 @@
+= 0) {
+ return $weirdResult;
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+
+ return (int) $PHPDateObject->format('j');
+ }
+
+ /**
+ * MONTHOFYEAR.
+ *
+ * Returns the month of a date represented by a serial number.
+ * The month is given as an integer, ranging from 1 (January) to 12 (December).
+ *
+ * Excel Function:
+ * MONTH(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ *
+ * @return array|int|string Month of the year
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function month($dateValue)
+ {
+ if (is_array($dateValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
+ return 1;
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+
+ return (int) $PHPDateObject->format('n');
+ }
+
+ /**
+ * YEAR.
+ *
+ * Returns the year corresponding to a date.
+ * The year is returned as an integer in the range 1900-9999.
+ *
+ * Excel Function:
+ * YEAR(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ *
+ * @return array|int|string Year
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function year($dateValue)
+ {
+ if (is_array($dateValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
+ return 1900;
+ }
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+
+ return (int) $PHPDateObject->format('Y');
+ }
+
+ /**
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ */
+ private static function weirdCondition($dateValue): int
+ {
+ // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
+ if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
+ if (is_bool($dateValue)) {
+ return (int) $dateValue;
+ }
+ if ($dateValue === null) {
+ return 0;
+ }
+ if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) {
+ return 0;
+ }
+ }
+
+ return -1;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
new file mode 100644
index 0000000..52543a7
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
@@ -0,0 +1,157 @@
+ 31)) {
+ if ($yearFound) {
+ return ExcelError::VALUE();
+ }
+ if ($t < 100) {
+ $t += 1900;
+ }
+ $yearFound = true;
+ }
+ }
+ if (count($t1) === 1) {
+ // We've been fed a time value without any date
+ return ((strpos((string) $t, ':') === false)) ? ExcelError::Value() : 0.0;
+ }
+ unset($t);
+
+ $dateValue = self::t1ToString($t1, $dti, $yearFound);
+
+ $PHPDateArray = self::setUpArray($dateValue, $dti);
+
+ return self::finalResults($PHPDateArray, $dti, $baseYear);
+ }
+
+ private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
+ {
+ if (count($t1) == 2) {
+ // We only have two parts of the date: either day/month or month/year
+ if ($yearFound) {
+ array_unshift($t1, 1);
+ } else {
+ if (is_numeric($t1[1]) && $t1[1] > 29) {
+ $t1[1] += 1900;
+ array_unshift($t1, 1);
+ } else {
+ $t1[] = $dti->format('Y');
+ }
+ }
+ }
+ $dateValue = implode(' ', $t1);
+
+ return $dateValue;
+ }
+
+ /**
+ * Parse date.
+ */
+ private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
+ {
+ $PHPDateArray = Helpers::dateParse($dateValue);
+ if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+ // If original count was 1, we've already returned.
+ // If it was 2, we added another.
+ // Therefore, neither of the first 2 stroks below can fail.
+ $testVal1 = strtok($dateValue, '- ');
+ $testVal2 = strtok('- ');
+ $testVal3 = strtok('- ') ?: $dti->format('Y');
+ Helpers::adjustYear((string) $testVal1, (string) $testVal2, $testVal3);
+ $PHPDateArray = Helpers::dateParse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
+ if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+ $PHPDateArray = Helpers::dateParse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
+ }
+ }
+
+ return $PHPDateArray;
+ }
+
+ /**
+ * Final results.
+ *
+ * @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
+ */
+ private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
+ {
+ $retValue = ExcelError::Value();
+ if (Helpers::dateParseSucceeded($PHPDateArray)) {
+ // Execute function
+ Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
+ if ($PHPDateArray['year'] < $baseYear) {
+ return ExcelError::VALUE();
+ }
+ Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
+ Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
+ $PHPDateArray['hour'] = 0;
+ $PHPDateArray['minute'] = 0;
+ $PHPDateArray['second'] = 0;
+ $month = (int) $PHPDateArray['month'];
+ $day = (int) $PHPDateArray['day'];
+ $year = (int) $PHPDateArray['year'];
+ if (!checkdate($month, $day, $year)) {
+ return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
+ }
+ $retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
+ }
+
+ return $retValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
new file mode 100644
index 0000000..a3b9745
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
@@ -0,0 +1,62 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+
+ $days = ExcelError::VALUE();
+ $diff = $PHPStartDateObject->diff($PHPEndDateObject);
+ if ($diff !== false && !is_bool($diff->days)) {
+ $days = $diff->days;
+ if ($diff->invert) {
+ $days = -$days;
+ }
+ }
+
+ return $days;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
new file mode 100644
index 0000000..6f71621
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
@@ -0,0 +1,118 @@
+getMessage();
+ }
+
+ if (!is_bool($method)) {
+ return ExcelError::VALUE();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $startDay = $PHPStartDateObject->format('j');
+ $startMonth = $PHPStartDateObject->format('n');
+ $startYear = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+ $endDay = $PHPEndDateObject->format('j');
+ $endMonth = $PHPEndDateObject->format('n');
+ $endYear = $PHPEndDateObject->format('Y');
+
+ return self::dateDiff360((int) $startDay, (int) $startMonth, (int) $startYear, (int) $endDay, (int) $endMonth, (int) $endYear, !$method);
+ }
+
+ /**
+ * Return the number of days between two dates based on a 360 day calendar.
+ */
+ private static function dateDiff360(int $startDay, int $startMonth, int $startYear, int $endDay, int $endMonth, int $endYear, bool $methodUS): int
+ {
+ $startDay = self::getStartDay($startDay, $startMonth, $startYear, $methodUS);
+ $endDay = self::getEndDay($endDay, $endMonth, $endYear, $startDay, $methodUS);
+
+ return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
+ }
+
+ private static function getStartDay(int $startDay, int $startMonth, int $startYear, bool $methodUS): int
+ {
+ if ($startDay == 31) {
+ --$startDay;
+ } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !Helpers::isLeapYear($startYear))))) {
+ $startDay = 30;
+ }
+
+ return $startDay;
+ }
+
+ private static function getEndDay(int $endDay, int &$endMonth, int &$endYear, int $startDay, bool $methodUS): int
+ {
+ if ($endDay == 31) {
+ if ($methodUS && $startDay != 30) {
+ $endDay = 1;
+ if ($endMonth == 12) {
+ ++$endYear;
+ $endMonth = 1;
+ } else {
+ ++$endMonth;
+ }
+ } else {
+ $endDay = 30;
+ }
+ }
+
+ return $endDay;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
new file mode 100644
index 0000000..fd71c9b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
@@ -0,0 +1,158 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $startDays = (int) $PHPStartDateObject->format('j');
+ //$startMonths = (int) $PHPStartDateObject->format('n');
+ $startYears = (int) $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+ $endDays = (int) $PHPEndDateObject->format('j');
+ //$endMonths = (int) $PHPEndDateObject->format('n');
+ $endYears = (int) $PHPEndDateObject->format('Y');
+
+ $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
+
+ $retVal = false;
+ $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
+ $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
+
+ return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
+ }
+
+ private static function initialDiff(float $startDate, float $endDate): float
+ {
+ // Validate parameters
+ if ($startDate > $endDate) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $endDate - $startDate;
+ }
+
+ /**
+ * Decide whether it's time to set retVal.
+ *
+ * @param bool|int $retVal
+ *
+ * @return null|bool|int
+ */
+ private static function replaceRetValue($retVal, string $unit, string $compare)
+ {
+ if ($retVal !== false || $unit !== $compare) {
+ return $retVal;
+ }
+
+ return null;
+ }
+
+ private static function datedifD(float $difference): int
+ {
+ return (int) $difference;
+ }
+
+ private static function datedifM(DateInterval $PHPDiffDateObject): int
+ {
+ return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
+ }
+
+ private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
+ {
+ if ($endDays < $startDays) {
+ $retVal = $endDays;
+ $PHPEndDateObject->modify('-' . $endDays . ' days');
+ $adjustDays = (int) $PHPEndDateObject->format('j');
+ $retVal += ($adjustDays - $startDays);
+ } else {
+ $retVal = (int) $PHPDiffDateObject->format('%d');
+ }
+
+ return $retVal;
+ }
+
+ private static function datedifY(DateInterval $PHPDiffDateObject): int
+ {
+ return (int) $PHPDiffDateObject->format('%y');
+ }
+
+ private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
+ {
+ $retVal = (int) $difference;
+ if ($endYears > $startYears) {
+ $isLeapStartYear = $PHPStartDateObject->format('L');
+ $wasLeapEndYear = $PHPEndDateObject->format('L');
+
+ // Adjust end year to be as close as possible as start year
+ while ($PHPEndDateObject >= $PHPStartDateObject) {
+ $PHPEndDateObject->modify('-1 year');
+ //$endYears = $PHPEndDateObject->format('Y');
+ }
+ $PHPEndDateObject->modify('+1 year');
+
+ // Get the result
+ $retVal = (int) $PHPEndDateObject->diff($PHPStartDateObject)->days;
+
+ // Adjust for leap years cases
+ $isLeapEndYear = $PHPEndDateObject->format('L');
+ $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
+ if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
+ --$retVal;
+ }
+ }
+
+ return (int) $retVal;
+ }
+
+ private static function datedifYM(DateInterval $PHPDiffDateObject): int
+ {
+ return (int) $PHPDiffDateObject->format('%m');
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
new file mode 100644
index 0000000..2384515
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
@@ -0,0 +1,307 @@
+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;
+ }
+
+ /**
+ * Help reduce perceived complexity of some tests.
+ *
+ * @param mixed $value
+ * @param mixed $altValue
+ */
+ public static function replaceIfEmpty(&$value, $altValue): void
+ {
+ $value = $value ?: $altValue;
+ }
+
+ /**
+ * Adjust year in ambiguous situations.
+ */
+ public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
+ {
+ if (!is_numeric($testVal1) || $testVal1 < 31) {
+ if (!is_numeric($testVal2) || $testVal2 < 12) {
+ if (is_numeric($testVal3) && $testVal3 < 12) {
+ $testVal3 += 2000;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
+ return new DateTime(
+ $dateArray['year']
+ . '-' . $dateArray['month']
+ . '-' . $dateArray['day']
+ . ' ' . $dateArray['hour']
+ . ':' . $dateArray['minute']
+ . ':' . $dateArray['second']
+ );
+ }
+ $excelDateValue =
+ SharedDateHelper::formattedPHPToExcel(
+ $dateArray['year'],
+ $dateArray['month'],
+ $dateArray['day'],
+ $dateArray['hour'],
+ $dateArray['minute'],
+ $dateArray['second']
+ );
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
+ }
+ // RETURNDATE_UNIX_TIMESTAMP)
+
+ return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsFloat(float $excelDateValue)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return $excelDateValue;
+ }
+ if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+ }
+ // RETURNDATE_PHP_DATETIME_OBJECT
+
+ return SharedDateHelper::excelToDateTimeObject($excelDateValue);
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsObject(DateTime $PHPDateObject)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
+ return $PHPDateObject;
+ }
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
+ }
+ // RETURNDATE_UNIX_TIMESTAMP
+ $stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
+ $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
+
+ return (int) SharedDateHelper::excelToTimestamp($stamp);
+ }
+
+ private static function baseDate(): int
+ {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ return 0;
+ }
+ if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @param mixed $number
+ */
+ public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
+ {
+ $number = Functions::flattenSingleValue($number);
+ $nullVal = self::baseDate();
+ if ($number === null) {
+ $number = $nullVal;
+ } elseif ($allowBool && is_bool($number)) {
+ $number = $nullVal + (int) $number;
+ }
+ }
+
+ /**
+ * Many functions accept null argument treated as 0.
+ *
+ * @param mixed $number
+ *
+ * @return float|int
+ */
+ public static function validateNumericNull($number)
+ {
+ $number = Functions::flattenSingleValue($number);
+ if ($number === null) {
+ return 0;
+ }
+ if (is_int($number)) {
+ return $number;
+ }
+ if (is_numeric($number)) {
+ return (float) $number;
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @param mixed $number
+ *
+ * @return float
+ */
+ public static function validateNotNegative($number)
+ {
+ if (!is_numeric($number)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ if ($number >= 0) {
+ return (float) $number;
+ }
+
+ throw new Exception(ExcelError::NAN());
+ }
+
+ public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
+ {
+ $isoDate = $PHPDateObject->format('c');
+ if ($isoDate < '1900-03-01') {
+ $PHPDateObject->modify($mod);
+ }
+ }
+
+ public static function dateParse(string $string): array
+ {
+ return self::forceArray(date_parse($string));
+ }
+
+ public static function dateParseSucceeded(array $dateArray): bool
+ {
+ return $dateArray['error_count'] === 0;
+ }
+
+ /**
+ * Despite documentation, date_parse probably never returns false.
+ * Just in case, this routine helps guarantee it.
+ *
+ * @param array|false $dateArray
+ */
+ private static function forceArray($dateArray): array
+ {
+ return is_array($dateArray) ? $dateArray : ['error_count' => 1];
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
new file mode 100644
index 0000000..c72d006
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
@@ -0,0 +1,101 @@
+getMessage();
+ }
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ // Execute function
+ $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths);
+
+ return Helpers::returnIn3FormatsObject($PHPDateObject);
+ }
+
+ /**
+ * EOMONTH.
+ *
+ * Returns the date value 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.
+ *
+ * Excel Function:
+ * EOMONTH(dateValue,adjustmentMonths)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ * @param array|int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ * Or can be an array of adjustment values
+ *
+ * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ * If an array of values is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function lastDay($dateValue, $adjustmentMonths)
+ {
+ if (is_array($dateValue) || is_array($adjustmentMonths)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue, false);
+ $adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ // Execute function
+ $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-' . $adjustDays . ' days';
+ $PHPDateObject->modify($adjustDaysString);
+
+ return Helpers::returnIn3FormatsObject($PHPDateObject);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
new file mode 100644
index 0000000..3b8942b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
@@ -0,0 +1,119 @@
+getMessage();
+ }
+
+ // Execute function
+ $startDow = self::calcStartDow($startDate);
+ $endDow = self::calcEndDow($endDate);
+ $wholeWeekDays = (int) floor(($endDate - $startDate) / 7) * 5;
+ $partWeekDays = self::calcPartWeekDays($startDow, $endDow);
+
+ // Test any extra holiday parameters
+ $holidayCountedArray = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((Week::day($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
+ --$partWeekDays;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+
+ return self::applySign($wholeWeekDays + $partWeekDays, $sDate, $eDate);
+ }
+
+ private static function calcStartDow(float $startDate): int
+ {
+ $startDow = 6 - (int) Week::day($startDate, 2);
+ if ($startDow < 0) {
+ $startDow = 5;
+ }
+
+ return $startDow;
+ }
+
+ private static function calcEndDow(float $endDate): int
+ {
+ $endDow = (int) Week::day($endDate, 2);
+ if ($endDow >= 6) {
+ $endDow = 0;
+ }
+
+ return $endDow;
+ }
+
+ private static function calcPartWeekDays(int $startDow, int $endDow): int
+ {
+ $partWeekDays = $endDow + $startDow;
+ if ($partWeekDays > 5) {
+ $partWeekDays -= 5;
+ }
+
+ return $partWeekDays;
+ }
+
+ private static function applySign(int $result, float $sDate, float $eDate): int
+ {
+ return ($sDate > $eDate) ? -$result : $result;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
new file mode 100644
index 0000000..4ff7198
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
@@ -0,0 +1,130 @@
+getMessage();
+ }
+
+ self::adjustSecond($second, $minute);
+ self::adjustMinute($minute, $hour);
+
+ if ($hour > 23) {
+ $hour = $hour % 24;
+ } elseif ($hour < 0) {
+ return ExcelError::NAN();
+ }
+
+ // Execute function
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ $calendar = SharedDateHelper::getExcelCalendar();
+ $date = (int) ($calendar !== SharedDateHelper::CALENDAR_WINDOWS_1900);
+
+ return (float) SharedDateHelper::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
+ }
+ if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ return (int) SharedDateHelper::excelToTimestamp(SharedDateHelper::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
+ }
+ // RETURNDATE_PHP_DATETIME_OBJECT
+ // Hour has already been normalized (0-23) above
+ $phpDateObject = new DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
+
+ return $phpDateObject;
+ }
+
+ private static function adjustSecond(int &$second, int &$minute): void
+ {
+ 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;
+ }
+ }
+
+ private static function adjustMinute(int &$minute, int &$hour): void
+ {
+ 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;
+ }
+ }
+
+ /**
+ * @param mixed $value expect int
+ */
+ private static function toIntWithNullBool($value): int
+ {
+ $value = $value ?? 0;
+ if (is_bool($value)) {
+ $value = (int) $value;
+ }
+ if (!is_numeric($value)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return (int) $value;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
new file mode 100644
index 0000000..d9b99f3
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
@@ -0,0 +1,132 @@
+getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+
+ return (int) $timeValue->format('H');
+ }
+
+ /**
+ * MINUTE.
+ *
+ * Returns the minutes of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * MINUTE(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * Or can be an array of date/time values
+ *
+ * @return array|int|string Minute
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function minute($timeValue)
+ {
+ if (is_array($timeValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+ }
+
+ try {
+ Helpers::nullFalseTrueToNumber($timeValue);
+ if (!is_numeric($timeValue)) {
+ $timeValue = Helpers::getTimeValue($timeValue);
+ }
+ Helpers::validateNotNegative($timeValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+
+ return (int) $timeValue->format('i');
+ }
+
+ /**
+ * SECOND.
+ *
+ * Returns the seconds of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * SECOND(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * Or can be an array of date/time values
+ *
+ * @return array|int|string Second
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function second($timeValue)
+ {
+ if (is_array($timeValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+ }
+
+ try {
+ Helpers::nullFalseTrueToNumber($timeValue);
+ if (!is_numeric($timeValue)) {
+ $timeValue = Helpers::getTimeValue($timeValue);
+ }
+ Helpers::validateNotNegative($timeValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+
+ return (int) $timeValue->format('s');
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
new file mode 100644
index 0000000..4abdd75
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
@@ -0,0 +1,78 @@
+ 24) {
+ $arraySplit[0] = ($arraySplit[0] % 24);
+ $timeValue = implode(':', $arraySplit);
+ }
+
+ $PHPDateArray = Helpers::dateParse($timeValue);
+ $retValue = ExcelError::VALUE();
+ if (Helpers::dateParseSucceeded($PHPDateArray)) {
+ /** @var int */
+ $hour = $PHPDateArray['hour'];
+ /** @var int */
+ $minute = $PHPDateArray['minute'];
+ /** @var int */
+ $second = $PHPDateArray['second'];
+ // OpenOffice-specific code removed - it works just like Excel
+ $excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
+
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ $retValue = (float) $excelDateValue;
+ } elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ $retValue = (int) SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
+ } else {
+ $retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
+ }
+ }
+
+ return $retValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
new file mode 100644
index 0000000..2f69007
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
@@ -0,0 +1,278 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ if ($method == Constants::STARTWEEK_MONDAY_ISO) {
+ Helpers::silly1900($PHPDateObject);
+
+ return (int) $PHPDateObject->format('W');
+ }
+ if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
+ return 0;
+ }
+ Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
+ $dayOfYear = (int) $PHPDateObject->format('z');
+ $PHPDateObject->modify('-' . $dayOfYear . ' days');
+ $firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
+ $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
+ $daysInFirstWeek += 7 * !$daysInFirstWeek;
+ $endFirstWeek = $daysInFirstWeek - 1;
+ $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
+
+ return (int) $weekOfYear;
+ }
+
+ /**
+ * ISOWEEKNUM.
+ *
+ * Returns the ISO 8601 week number of the year for a specified date.
+ *
+ * Excel Function:
+ * ISOWEEKNUM(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ *
+ * @return array|int|string Week Number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isoWeekNumber($dateValue)
+ {
+ if (is_array($dateValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+ }
+
+ if (self::apparentBug($dateValue)) {
+ return 52;
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ Helpers::silly1900($PHPDateObject);
+
+ return (int) $PHPDateObject->format('W');
+ }
+
+ /**
+ * WEEKDAY.
+ *
+ * Returns the day of the week for a specified date. The day is given as an integer
+ * ranging from 0 to 7 (dependent on the requested style).
+ *
+ * Excel Function:
+ * WEEKDAY(dateValue[,style])
+ *
+ * @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ * @param mixed $style A number that determines the type of return value
+ * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
+ * 2 Numbers 1 (Monday) through 7 (Sunday).
+ * 3 Numbers 0 (Monday) through 6 (Sunday).
+ * Or can be an array of styles
+ *
+ * @return array|int|string Day of the week value
+ * If an array of values is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function day($dateValue, $style = 1)
+ {
+ if (is_array($dateValue) || is_array($style)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ $style = self::validateStyle($style);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ Helpers::silly1900($PHPDateObject);
+ $DoW = (int) $PHPDateObject->format('w');
+
+ switch ($style) {
+ case 1:
+ ++$DoW;
+
+ break;
+ case 2:
+ $DoW = self::dow0Becomes7($DoW);
+
+ break;
+ case 3:
+ $DoW = self::dow0Becomes7($DoW) - 1;
+
+ break;
+ }
+
+ return $DoW;
+ }
+
+ /**
+ * @param mixed $style expect int
+ */
+ private static function validateStyle($style): int
+ {
+ if (!is_numeric($style)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ $style = (int) $style;
+ if (($style < 1) || ($style > 3)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $style;
+ }
+
+ private static function dow0Becomes7(int $DoW): int
+ {
+ return ($DoW === 0) ? 7 : $DoW;
+ }
+
+ /**
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ */
+ private static function apparentBug($dateValue): bool
+ {
+ if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
+ if (is_bool($dateValue)) {
+ return true;
+ }
+ if (is_numeric($dateValue) && !((int) $dateValue)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Validate dateValue parameter.
+ *
+ * @param mixed $dateValue
+ */
+ private static function validateDateValue($dateValue): float
+ {
+ if (is_bool($dateValue)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return Helpers::getDateValue($dateValue);
+ }
+
+ /**
+ * Validate method parameter.
+ *
+ * @param mixed $method
+ */
+ private static function validateMethod($method): int
+ {
+ if ($method === null) {
+ $method = Constants::STARTWEEK_SUNDAY;
+ }
+
+ if (!is_numeric($method)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $method = (int) $method;
+ if (!array_key_exists($method, Constants::METHODARR)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ $method = Constants::METHODARR[$method];
+
+ return $method;
+ }
+
+ private static function buggyWeekNum1900(int $method): bool
+ {
+ return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
+ }
+
+ private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
+ {
+ // This appears to be another Excel bug.
+
+ return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
+ !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
new file mode 100644
index 0000000..1f5735e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
@@ -0,0 +1,201 @@
+getMessage();
+ }
+
+ $startDate = (float) floor($startDate);
+ $endDays = (int) floor($endDays);
+ // If endDays is 0, we always return startDate
+ if ($endDays == 0) {
+ return $startDate;
+ }
+ if ($endDays < 0) {
+ return self::decrementing($startDate, $endDays, $holidayArray);
+ }
+
+ return self::incrementing($startDate, $endDays, $holidayArray);
+ }
+
+ /**
+ * Use incrementing logic to determine Workday.
+ *
+ * @return mixed
+ */
+ private static function incrementing(float $startDate, int $endDays, array $holidayArray)
+ {
+ // Adjust the start date if it falls over a weekend
+ $startDoW = self::getWeekDay($startDate, 3);
+ if ($startDoW >= 5) {
+ $startDate += 7 - $startDoW;
+ --$endDays;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
+ $endDays = $endDays % 5;
+ while ($endDays > 0) {
+ ++$endDate;
+ // Adjust the calculated end date if it falls over a weekend
+ $endDow = self::getWeekDay($endDate, 3);
+ if ($endDow >= 5) {
+ $endDate += 7 - $endDow;
+ }
+ --$endDays;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($holidayArray)) {
+ $endDate = self::incrementingArray($startDate, $endDate, $holidayArray);
+ }
+
+ return Helpers::returnIn3FormatsFloat($endDate);
+ }
+
+ private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
+ {
+ $holidayCountedArray = $holidayDates = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (self::getWeekDay($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ sort($holidayDates, SORT_NUMERIC);
+ foreach ($holidayDates as $holidayDate) {
+ 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::getWeekDay($endDate, 3);
+ if ($endDoW >= 5) {
+ $endDate += 7 - $endDoW;
+ }
+ }
+
+ return $endDate;
+ }
+
+ /**
+ * Use decrementing logic to determine Workday.
+ *
+ * @return mixed
+ */
+ private static function decrementing(float $startDate, int $endDays, array $holidayArray)
+ {
+ // Adjust the start date if it falls over a weekend
+ $startDoW = self::getWeekDay($startDate, 3);
+ if ($startDoW >= 5) {
+ $startDate += -$startDoW + 4;
+ ++$endDays;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
+ $endDays = $endDays % 5;
+ while ($endDays < 0) {
+ --$endDate;
+ // Adjust the calculated end date if it falls over a weekend
+ $endDow = self::getWeekDay($endDate, 3);
+ if ($endDow >= 5) {
+ $endDate += 4 - $endDow;
+ }
+ ++$endDays;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($holidayArray)) {
+ $endDate = self::decrementingArray($startDate, $endDate, $holidayArray);
+ }
+
+ return Helpers::returnIn3FormatsFloat($endDate);
+ }
+
+ private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
+ {
+ $holidayCountedArray = $holidayDates = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (self::getWeekDay($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ rsort($holidayDates, SORT_NUMERIC);
+ foreach ($holidayDates as $holidayDate) {
+ 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::getWeekDay($endDate, 3);
+ /** int $endDoW */
+ if ($endDoW >= 5) {
+ $endDate += -$endDoW + 4;
+ }
+ }
+
+ return $endDate;
+ }
+
+ private static function getWeekDay(float $date, int $wd): int
+ {
+ $result = Functions::scalar(Week::day($date, $wd));
+
+ return is_int($result) ? $result : -1;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
new file mode 100644
index 0000000..394c6b7
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
@@ -0,0 +1,133 @@
+getMessage();
+ }
+
+ switch ($method) {
+ case 0:
+ return Functions::scalar(Days360::between($startDate, $endDate)) / 360;
+ case 1:
+ return self::method1($startDate, $endDate);
+ case 2:
+ return Functions::scalar(Difference::interval($startDate, $endDate)) / 360;
+ case 3:
+ return Functions::scalar(Difference::interval($startDate, $endDate)) / 365;
+ case 4:
+ return Functions::scalar(Days360::between($startDate, $endDate, true)) / 360;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
+ *
+ * @param mixed $startDate
+ * @param mixed $endDate
+ */
+ private static function excelBug(float $sDate, $startDate, $endDate, int $method): float
+ {
+ if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
+ if ($endDate === null && $startDate !== null) {
+ if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) {
+ $sDate += 2;
+ } else {
+ ++$sDate;
+ }
+ }
+ }
+
+ return $sDate;
+ }
+
+ private static function method1(float $startDate, float $endDate): float
+ {
+ $days = Functions::scalar(Difference::interval($startDate, $endDate));
+ $startYear = (int) DateParts::year($startDate);
+ $endYear = (int) DateParts::year($endDate);
+ $years = $endYear - $startYear + 1;
+ $startMonth = (int) DateParts::month($startDate);
+ $startDay = (int) DateParts::day($startDate);
+ $endMonth = (int) DateParts::month($endDate);
+ $endDay = (int) DateParts::day($endDate);
+ $startMonthDay = 100 * $startMonth + $startDay;
+ $endMonthDay = 100 * $endMonth + $endDay;
+ if ($years == 1) {
+ $tmpCalcAnnualBasis = 365 + (int) Helpers::isLeapYear($endYear);
+ } elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
+ if (Helpers::isLeapYear($startYear)) {
+ $tmpCalcAnnualBasis = 365 + (int) ($startMonthDay <= 229);
+ } elseif (Helpers::isLeapYear($endYear)) {
+ $tmpCalcAnnualBasis = 365 + (int) ($endMonthDay >= 229);
+ } else {
+ $tmpCalcAnnualBasis = 365;
+ }
+ } else {
+ $tmpCalcAnnualBasis = 0;
+ for ($year = $startYear; $year <= $endYear; ++$year) {
+ $tmpCalcAnnualBasis += 365 + (int) Helpers::isLeapYear($year);
+ }
+ $tmpCalcAnnualBasis /= $years;
+ }
+
+ return $days / $tmpCalcAnnualBasis;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
new file mode 100644
index 0000000..fae9d90
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
@@ -0,0 +1,209 @@
+indexStart = (int) array_shift($keys);
+ $this->rows = $this->rows($arguments);
+ $this->columns = $this->columns($arguments);
+
+ $this->argumentCount = count($arguments);
+ $this->arguments = $this->flattenSingleCellArrays($arguments, $this->rows, $this->columns);
+
+ $this->rows = $this->rows($arguments);
+ $this->columns = $this->columns($arguments);
+
+ if ($this->arrayArguments() > 2) {
+ throw new Exception('Formulae with more than two array arguments are not supported');
+ }
+ }
+
+ public function arguments(): array
+ {
+ return $this->arguments;
+ }
+
+ public function hasArrayArgument(): bool
+ {
+ return $this->arrayArguments() > 0;
+ }
+
+ public function getFirstArrayArgumentNumber(): int
+ {
+ $rowArrays = $this->filterArray($this->rows);
+ $columnArrays = $this->filterArray($this->columns);
+
+ for ($index = $this->indexStart; $index < $this->argumentCount; ++$index) {
+ if (isset($rowArrays[$index]) || isset($columnArrays[$index])) {
+ return ++$index;
+ }
+ }
+
+ return 0;
+ }
+
+ public function getSingleRowVector(): ?int
+ {
+ $rowVectors = $this->getRowVectors();
+
+ return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
+ }
+
+ private function getRowVectors(): array
+ {
+ $rowVectors = [];
+ for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
+ if ($this->rows[$index] === 1 && $this->columns[$index] > 1) {
+ $rowVectors[] = $index;
+ }
+ }
+
+ return $rowVectors;
+ }
+
+ public function getSingleColumnVector(): ?int
+ {
+ $columnVectors = $this->getColumnVectors();
+
+ return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
+ }
+
+ private function getColumnVectors(): array
+ {
+ $columnVectors = [];
+ for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
+ if ($this->rows[$index] > 1 && $this->columns[$index] === 1) {
+ $columnVectors[] = $index;
+ }
+ }
+
+ return $columnVectors;
+ }
+
+ public function getMatrixPair(): array
+ {
+ for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
+ for ($j = $i + 1; $j < $this->argumentCount; ++$j) {
+ if (isset($this->rows[$i], $this->rows[$j])) {
+ return [$i, $j];
+ }
+ }
+ }
+
+ return [];
+ }
+
+ public function isVector(int $argument): bool
+ {
+ return $this->rows[$argument] === 1 || $this->columns[$argument] === 1;
+ }
+
+ public function isRowVector(int $argument): bool
+ {
+ return $this->rows[$argument] === 1;
+ }
+
+ public function isColumnVector(int $argument): bool
+ {
+ return $this->columns[$argument] === 1;
+ }
+
+ public function rowCount(int $argument): int
+ {
+ return $this->rows[$argument];
+ }
+
+ public function columnCount(int $argument): int
+ {
+ return $this->columns[$argument];
+ }
+
+ private function rows(array $arguments): array
+ {
+ return array_map(
+ function ($argument) {
+ return is_countable($argument) ? count($argument) : 1;
+ },
+ $arguments
+ );
+ }
+
+ private function columns(array $arguments): array
+ {
+ return array_map(
+ function ($argument) {
+ return is_array($argument) && is_array($argument[array_keys($argument)[0]])
+ ? count($argument[array_keys($argument)[0]])
+ : 1;
+ },
+ $arguments
+ );
+ }
+
+ public function arrayArguments(): int
+ {
+ $count = 0;
+ foreach (array_keys($this->arguments) as $argument) {
+ if ($this->rows[$argument] > 1 || $this->columns[$argument] > 1) {
+ ++$count;
+ }
+ }
+
+ return $count;
+ }
+
+ private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
+ {
+ foreach ($arguments as $index => $argument) {
+ if ($rows[$index] === 1 && $columns[$index] === 1) {
+ while (is_array($argument)) {
+ $argument = array_pop($argument);
+ }
+ $arguments[$index] = $argument;
+ }
+ }
+
+ return $arguments;
+ }
+
+ private function filterArray(array $array): array
+ {
+ return array_filter(
+ $array,
+ function ($value) {
+ return $value > 1;
+ }
+ );
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
new file mode 100644
index 0000000..3e69d77
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
@@ -0,0 +1,175 @@
+hasArrayArgument() === false) {
+ return [$method(...$arguments)];
+ }
+
+ if (self::$arrayArgumentHelper->arrayArguments() === 1) {
+ $nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber();
+
+ return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments);
+ }
+
+ $singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector();
+ $singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector();
+
+ if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) {
+ // Basic logic for a single row vector and a single column vector
+ return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments);
+ }
+
+ $matrixPair = self::$arrayArgumentHelper->getMatrixPair();
+ if ($matrixPair !== []) {
+ if (
+ (self::$arrayArgumentHelper->isVector($matrixPair[0]) === true &&
+ self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) ||
+ (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false &&
+ self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
+ ) {
+ // Logic for a matrix and a vector (row or column)
+ return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments);
+ }
+ // Logic for matrix/matrix, column vector/column vector or row vector/row vector
+ return self::evaluateMatrixPair($method, $matrixPair, ...$arguments);
+ }
+
+ // Still need to work out the logic for more than two array arguments,
+ // For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper
+ return ['#VALUE!'];
+ }
+
+ /**
+ * @param mixed ...$arguments
+ */
+ private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
+ {
+ $matrix2 = array_pop($matrixIndexes);
+ /** @var array $matrixValues2 */
+ $matrixValues2 = $arguments[$matrix2];
+ $matrix1 = array_pop($matrixIndexes);
+ /** @var array $matrixValues1 */
+ $matrixValues1 = $arguments[$matrix1];
+
+ $rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
+ $columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
+
+ if ($rows === 1) {
+ $rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
+ }
+ if ($columns === 1) {
+ $columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
+ }
+
+ $result = [];
+ for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) {
+ for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) {
+ $rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex;
+ $columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex;
+ $value1 = $matrixValues1[$rowIndex1][$columnIndex1];
+ $rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex;
+ $columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex;
+ $value2 = $matrixValues2[$rowIndex2][$columnIndex2];
+ $arguments[$matrix1] = $value1;
+ $arguments[$matrix2] = $value2;
+
+ $result[$rowIndex][$columnIndex] = $method(...$arguments);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param mixed ...$arguments
+ */
+ private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
+ {
+ $matrix2 = array_pop($matrixIndexes);
+ /** @var array $matrixValues2 */
+ $matrixValues2 = $arguments[$matrix2];
+ $matrix1 = array_pop($matrixIndexes);
+ /** @var array $matrixValues1 */
+ $matrixValues1 = $arguments[$matrix1];
+
+ $result = [];
+ foreach ($matrixValues1 as $rowIndex => $row) {
+ foreach ($row as $columnIndex => $value1) {
+ if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) {
+ continue;
+ }
+
+ $value2 = $matrixValues2[$rowIndex][$columnIndex];
+ $arguments[$matrix1] = $value1;
+ $arguments[$matrix2] = $value2;
+
+ $result[$rowIndex][$columnIndex] = $method(...$arguments);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param mixed ...$arguments
+ */
+ private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array
+ {
+ $rowVector = Functions::flattenArray($arguments[$rowIndex]);
+ $columnVector = Functions::flattenArray($arguments[$columnIndex]);
+
+ $result = [];
+ foreach ($columnVector as $column) {
+ $rowResults = [];
+ foreach ($rowVector as $row) {
+ $arguments[$rowIndex] = $row;
+ $arguments[$columnIndex] = $column;
+
+ $rowResults[] = $method(...$arguments);
+ }
+ $result[] = $rowResults;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Note, offset is from 1 (for the first argument) rather than from 0.
+ *
+ * @param mixed ...$arguments
+ */
+ private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array
+ {
+ $values = array_slice($arguments, $nthArgument - 1, 1);
+ /** @var array $values */
+ $values = array_pop($values);
+
+ $result = [];
+ foreach ($values as $value) {
+ $arguments[$nthArgument - 1] = $value;
+ $result[] = $method(...$arguments);
+ }
+
+ return $result;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/BranchPruner.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
new file mode 100644
index 0000000..9cd767e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
@@ -0,0 +1,223 @@
+branchPruningEnabled = $branchPruningEnabled;
+ }
+
+ public function clearBranchStore(): void
+ {
+ $this->branchStoreKeyCounter = 0;
+ }
+
+ public function initialiseForLoop(): void
+ {
+ $this->currentCondition = null;
+ $this->currentOnlyIf = null;
+ $this->currentOnlyIfNot = null;
+ $this->previousStoreKey = null;
+ $this->pendingStoreKey = empty($this->storeKeysStack) ? null : end($this->storeKeysStack);
+
+ if ($this->branchPruningEnabled) {
+ $this->initialiseCondition();
+ $this->initialiseThen();
+ $this->initialiseElse();
+ }
+ }
+
+ private function initialiseCondition(): void
+ {
+ if (isset($this->conditionMap[$this->pendingStoreKey]) && $this->conditionMap[$this->pendingStoreKey]) {
+ $this->currentCondition = $this->pendingStoreKey;
+ $stackDepth = count($this->storeKeysStack);
+ if ($stackDepth > 1) {
+ // nested if
+ $this->previousStoreKey = $this->storeKeysStack[$stackDepth - 2];
+ }
+ }
+ }
+
+ private function initialiseThen(): void
+ {
+ if (isset($this->thenMap[$this->pendingStoreKey]) && $this->thenMap[$this->pendingStoreKey]) {
+ $this->currentOnlyIf = $this->pendingStoreKey;
+ } elseif (
+ isset($this->previousStoreKey, $this->thenMap[$this->previousStoreKey])
+ && $this->thenMap[$this->previousStoreKey]
+ ) {
+ $this->currentOnlyIf = $this->previousStoreKey;
+ }
+ }
+
+ private function initialiseElse(): void
+ {
+ if (isset($this->elseMap[$this->pendingStoreKey]) && $this->elseMap[$this->pendingStoreKey]) {
+ $this->currentOnlyIfNot = $this->pendingStoreKey;
+ } elseif (
+ isset($this->previousStoreKey, $this->elseMap[$this->previousStoreKey])
+ && $this->elseMap[$this->previousStoreKey]
+ ) {
+ $this->currentOnlyIfNot = $this->previousStoreKey;
+ }
+ }
+
+ public function decrementDepth(): void
+ {
+ if (!empty($this->pendingStoreKey)) {
+ --$this->braceDepthMap[$this->pendingStoreKey];
+ }
+ }
+
+ public function incrementDepth(): void
+ {
+ if (!empty($this->pendingStoreKey)) {
+ ++$this->braceDepthMap[$this->pendingStoreKey];
+ }
+ }
+
+ public function functionCall(string $functionName): void
+ {
+ if ($this->branchPruningEnabled && ($functionName === 'IF(')) {
+ // we handle a new if
+ $this->pendingStoreKey = $this->getUnusedBranchStoreKey();
+ $this->storeKeysStack[] = $this->pendingStoreKey;
+ $this->conditionMap[$this->pendingStoreKey] = true;
+ $this->braceDepthMap[$this->pendingStoreKey] = 0;
+ } elseif (!empty($this->pendingStoreKey) && array_key_exists($this->pendingStoreKey, $this->braceDepthMap)) {
+ // this is not an if but we go deeper
+ ++$this->braceDepthMap[$this->pendingStoreKey];
+ }
+ }
+
+ public function argumentSeparator(): void
+ {
+ if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === 0) {
+ // We must go to the IF next argument
+ if ($this->conditionMap[$this->pendingStoreKey]) {
+ $this->conditionMap[$this->pendingStoreKey] = false;
+ $this->thenMap[$this->pendingStoreKey] = true;
+ } elseif ($this->thenMap[$this->pendingStoreKey]) {
+ $this->thenMap[$this->pendingStoreKey] = false;
+ $this->elseMap[$this->pendingStoreKey] = true;
+ } elseif ($this->elseMap[$this->pendingStoreKey]) {
+ throw new Exception('Reaching fourth argument of an IF');
+ }
+ }
+ }
+
+ /**
+ * @param mixed $value
+ */
+ public function closingBrace($value): void
+ {
+ if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
+ // we are closing an IF(
+ if ($value !== 'IF(') {
+ throw new Exception('Parser bug we should be in an "IF("');
+ }
+
+ if ($this->conditionMap[$this->pendingStoreKey]) {
+ throw new Exception('We should not be expecting a condition');
+ }
+
+ $this->thenMap[$this->pendingStoreKey] = false;
+ $this->elseMap[$this->pendingStoreKey] = false;
+ --$this->braceDepthMap[$this->pendingStoreKey];
+ array_pop($this->storeKeysStack);
+ $this->pendingStoreKey = null;
+ }
+ }
+
+ public function currentCondition(): ?string
+ {
+ return $this->currentCondition;
+ }
+
+ public function currentOnlyIf(): ?string
+ {
+ return $this->currentOnlyIf;
+ }
+
+ public function currentOnlyIfNot(): ?string
+ {
+ return $this->currentOnlyIfNot;
+ }
+
+ private function getUnusedBranchStoreKey(): string
+ {
+ $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
+ ++$this->branchStoreKeyCounter;
+
+ return $storeKeyValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
old mode 100755
new mode 100644
index 5a54d83..b688e05
--- a/PhpOffice/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
@@ -26,7 +26,7 @@ class CyclicReferenceStack
*
* @param mixed $value
*/
- public function push($value)
+ public function push($value): void
{
$this->stack[$value] = $value;
}
@@ -56,7 +56,7 @@ class CyclicReferenceStack
/**
* Clear the stack.
*/
- public function clear()
+ public function clear(): void
{
$this->stack = [];
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
new file mode 100644
index 0000000..8fe7aa4
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
@@ -0,0 +1,126 @@
+[-+])? *\% *(?[-+])? *(?[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *\% *))$~i';
+
+ private const STRING_CONVERSION_LIST = [
+ [self::class, 'convertToNumberIfNumeric'],
+ [self::class, 'convertToNumberIfFraction'],
+ [self::class, 'convertToNumberIfPercent'],
+ [self::class, 'convertToNumberIfCurrency'],
+ ];
+
+ /**
+ * Identify whether a string contains a formatted numeric value,
+ * and convert it to a numeric if it is.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfFormatted(string &$operand): bool
+ {
+ foreach (self::STRING_CONVERSION_LIST as $conversionMethod) {
+ if ($conversionMethod($operand) === true) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a numeric value,
+ * and convert it to a numeric if it is.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfNumeric(string &$operand): bool
+ {
+ $value = preg_replace(['/(\d),(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
+
+ if (is_numeric($value)) {
+ $operand = (float) $value;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a fractional numeric value,
+ * and convert it to a numeric if it is.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfFraction(string &$operand): bool
+ {
+ if (preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
+ $sign = ($match[1] === '-') ? '-' : '+';
+ $wholePart = ($match[3] === '') ? '' : ($sign . $match[3]);
+ $fractionFormula = '=' . $wholePart . $sign . $match[4];
+ $operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a percentage, and if so,
+ * convert it to a numeric.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfPercent(string &$operand): bool
+ {
+ $value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
+
+ $match = [];
+ if ($value !== null && preg_match(self::STRING_REGEXP_PERCENT, $value, $match, PREG_UNMATCHED_AS_NULL)) {
+ //Calculate the percentage
+ $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
+ $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue'])) / 100;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a currency value, and if so,
+ * convert it to a numeric.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfCurrency(string &$operand): bool
+ {
+ $quotedCurrencyCode = preg_quote(StringHelper::getCurrencyCode());
+
+ $value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
+ $regExp = '~^(?:(?: *(?[-+])? *' . $quotedCurrencyCode . ' *(?[-+])? *(?[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *' . $quotedCurrencyCode . ' *))$~ui';
+
+ $match = [];
+ if ($value !== null && preg_match($regExp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
+ //Determine the sign
+ $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
+ //Cast to a float
+ $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue']));
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/Logger.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/Logger.php
old mode 100755
new mode 100644
index 6793dad..256c3ef
--- a/PhpOffice/PhpSpreadsheet/Calculation/Engine/Logger.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/Logger.php
@@ -39,8 +39,6 @@ class Logger
/**
* Instantiate a Calculation engine logger.
- *
- * @param CyclicReferenceStack $stack
*/
public function __construct(CyclicReferenceStack $stack)
{
@@ -50,11 +48,11 @@ class Logger
/**
* Enable/Disable Calculation engine logging.
*
- * @param bool $pValue
+ * @param bool $writeDebugLog
*/
- public function setWriteDebugLog($pValue)
+ public function setWriteDebugLog($writeDebugLog): void
{
- $this->writeDebugLog = $pValue;
+ $this->writeDebugLog = $writeDebugLog;
}
/**
@@ -70,11 +68,11 @@ class Logger
/**
* Enable/Disable echoing of debug log information.
*
- * @param bool $pValue
+ * @param bool $echoDebugLog
*/
- public function setEchoDebugLog($pValue)
+ public function setEchoDebugLog($echoDebugLog): void
{
- $this->echoDebugLog = $pValue;
+ $this->echoDebugLog = $echoDebugLog;
}
/**
@@ -89,18 +87,20 @@ class Logger
/**
* Write an entry to the calculation engine debug log.
+ *
+ * @param mixed $args
*/
- public function writeDebugLog(...$args)
+ public function writeDebugLog(string $message, ...$args): void
{
// Only write the debug log if logging is enabled
if ($this->writeDebugLog) {
- $message = implode($args);
+ $message = sprintf($message, ...$args);
$cellReference = implode(' -> ', $this->cellStack->showStack());
if ($this->echoDebugLog) {
echo $cellReference,
- ($this->cellStack->count() > 0 ? ' => ' : ''),
- $message,
- PHP_EOL;
+ ($this->cellStack->count() > 0 ? ' => ' : ''),
+ $message,
+ PHP_EOL;
}
$this->debugLog[] = $cellReference .
($this->cellStack->count() > 0 ? ' => ' : '') .
@@ -108,10 +108,24 @@ class Logger
}
}
+ /**
+ * Write a series of entries to the calculation engine debug log.
+ *
+ * @param string[] $args
+ */
+ public function mergeDebugLog(array $args): void
+ {
+ if ($this->writeDebugLog) {
+ foreach ($args as $entry) {
+ $this->writeDebugLog($entry);
+ }
+ }
+ }
+
/**
* Clear the calculation engine debug log.
*/
- public function clearLog()
+ public function clearLog(): void
{
$this->debugLog = [];
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php b/PhpOffice/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php
new file mode 100644
index 0000000..05264c3
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php
@@ -0,0 +1,10 @@
+value = $structuredReference;
+ }
+
+ public static function fromParser(string $formula, int $index, array $matches): self
+ {
+ $val = $matches[0];
+
+ $srCount = substr_count($val, self::OPEN_BRACE)
+ - substr_count($val, self::CLOSE_BRACE);
+ while ($srCount > 0) {
+ $srIndex = strlen($val);
+ $srStringRemainder = substr($formula, $index + $srIndex);
+ $closingPos = strpos($srStringRemainder, self::CLOSE_BRACE);
+ if ($closingPos === false) {
+ throw new Exception("Formula Error: No closing ']' to match opening '['");
+ }
+ $srStringRemainder = substr($srStringRemainder, 0, $closingPos + 1);
+ --$srCount;
+ if (strpos($srStringRemainder, self::OPEN_BRACE) !== false) {
+ ++$srCount;
+ }
+ $val .= $srStringRemainder;
+ }
+
+ return new self($val);
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public function parse(Cell $cell): string
+ {
+ $this->getTableStructure($cell);
+ $cellRange = ($this->isRowReference()) ? $this->getRowReference($cell) : $this->getColumnReference();
+
+ return $cellRange;
+ }
+
+ private function isRowReference(): bool
+ {
+ return strpos($this->value, '[@') !== false
+ || strpos($this->value, '[' . self::ITEM_SPECIFIER_THIS_ROW . ']') !== false;
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getTableStructure(Cell $cell): void
+ {
+ preg_match(self::TABLE_REFERENCE, $this->value, $matches);
+
+ $this->tableName = $matches[1];
+ $this->table = ($this->tableName === '')
+ ? $this->getTableForCell($cell)
+ : $this->getTableByName($cell);
+ $this->reference = $matches[2];
+ $tableRange = Coordinate::getRangeBoundaries($this->table->getRange());
+
+ $this->headersRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] : null;
+ $this->firstDataRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] + 1 : $tableRange[0][1];
+ $this->totalsRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] : null;
+ $this->lastDataRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] - 1 : $tableRange[1][1];
+
+ $this->columns = $this->getColumns($cell, $tableRange);
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getTableForCell(Cell $cell): Table
+ {
+ $tables = $cell->getWorksheet()->getTableCollection();
+ foreach ($tables as $table) {
+ /** @var Table $table */
+ $range = $table->getRange();
+ if ($cell->isInRange($range) === true) {
+ $this->tableName = $table->getName();
+
+ return $table;
+ }
+ }
+
+ throw new Exception('Table for Structured Reference cannot be identified');
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getTableByName(Cell $cell): Table
+ {
+ $table = $cell->getWorksheet()->getTableByName($this->tableName);
+
+ if ($table === null) {
+ throw new Exception("Table {$this->tableName} for Structured Reference cannot be located");
+ }
+
+ return $table;
+ }
+
+ private function getColumns(Cell $cell, array $tableRange): array
+ {
+ $worksheet = $cell->getWorksheet();
+ $cellReference = $cell->getCoordinate();
+
+ $columns = [];
+ $lastColumn = ++$tableRange[1][0];
+ for ($column = $tableRange[0][0]; $column !== $lastColumn; ++$column) {
+ $columns[$column] = $worksheet
+ ->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
+ ->getCalculatedValue();
+ }
+
+ $cell = $worksheet->getCell($cellReference);
+
+ return $columns;
+ }
+
+ private function getRowReference(Cell $cell): string
+ {
+ $reference = str_replace("\u{a0}", ' ', $this->reference);
+ /** @var string $reference */
+ $reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
+
+ foreach ($this->columns as $columnId => $columnName) {
+ $columnName = str_replace("\u{a0}", ' ', $columnName);
+ $cellReference = $columnId . $cell->getRow();
+ $pattern1 = '/\[' . preg_quote($columnName) . '\]/miu';
+ $pattern2 = '/@' . preg_quote($columnName) . '/miu';
+ /** @var string $reference */
+ if (preg_match($pattern1, $reference) === 1) {
+ $reference = preg_replace($pattern1, $cellReference, $reference);
+ } elseif (preg_match($pattern2, $reference) === 1) {
+ $reference = preg_replace($pattern2, $cellReference, $reference);
+ }
+ }
+
+ /** @var string $reference */
+ return $this->validateParsedReference(trim($reference, '[]@, '));
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getColumnReference(): string
+ {
+ $reference = str_replace("\u{a0}", ' ', $this->reference);
+ $startRow = ($this->totalsRow === null) ? $this->lastDataRow : $this->totalsRow;
+ $endRow = ($this->headersRow === null) ? $this->firstDataRow : $this->headersRow;
+
+ [$startRow, $endRow] = $this->getRowsForColumnReference($reference, $startRow, $endRow);
+ $reference = $this->getColumnsForColumnReference($reference, $startRow, $endRow);
+
+ $reference = trim($reference, '[]@, ');
+ if (substr_count($reference, ':') > 1) {
+ $cells = explode(':', $reference);
+ $firstCell = array_shift($cells);
+ $lastCell = array_pop($cells);
+ $reference = "{$firstCell}:{$lastCell}";
+ }
+
+ return $this->validateParsedReference($reference);
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function validateParsedReference(string $reference): string
+ {
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . ':' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
+ throw new Exception("Invalid Structured Reference {$this->reference} {$reference}");
+ }
+ }
+
+ return $reference;
+ }
+
+ private function fullData(int $startRow, int $endRow): string
+ {
+ $columns = array_keys($this->columns);
+ $firstColumn = array_shift($columns);
+ $lastColumn = (empty($columns)) ? $firstColumn : array_pop($columns);
+
+ return "{$firstColumn}{$startRow}:{$lastColumn}{$endRow}";
+ }
+
+ private function getMinimumRow(string $reference): int
+ {
+ switch ($reference) {
+ case self::ITEM_SPECIFIER_ALL:
+ case self::ITEM_SPECIFIER_HEADERS:
+ return $this->headersRow ?? $this->firstDataRow;
+ case self::ITEM_SPECIFIER_DATA:
+ return $this->firstDataRow;
+ case self::ITEM_SPECIFIER_TOTALS:
+ return $this->totalsRow ?? $this->lastDataRow;
+ }
+
+ return $this->headersRow ?? $this->firstDataRow;
+ }
+
+ private function getMaximumRow(string $reference): int
+ {
+ switch ($reference) {
+ case self::ITEM_SPECIFIER_HEADERS:
+ return $this->headersRow ?? $this->firstDataRow;
+ case self::ITEM_SPECIFIER_DATA:
+ return $this->lastDataRow;
+ case self::ITEM_SPECIFIER_ALL:
+ case self::ITEM_SPECIFIER_TOTALS:
+ return $this->totalsRow ?? $this->lastDataRow;
+ }
+
+ return $this->totalsRow ?? $this->lastDataRow;
+ }
+
+ public function value(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * @return array
+ */
+ private function getRowsForColumnReference(string &$reference, int $startRow, int $endRow): array
+ {
+ $rowsSelected = false;
+ foreach (self::ITEM_SPECIFIER_ROWS_SET as $rowReference) {
+ $pattern = '/\[' . $rowReference . '\]/mui';
+ /** @var string $reference */
+ if (preg_match($pattern, $reference) === 1) {
+ if (($rowReference === self::ITEM_SPECIFIER_HEADERS) && ($this->table->getShowHeaderRow() === false)) {
+ throw new Exception(
+ 'Table Headers are Hidden, and should not be Referenced',
+ Exception::CALCULATION_ENGINE_PUSH_TO_STACK
+ );
+ }
+ $rowsSelected = true;
+ $startRow = min($startRow, $this->getMinimumRow($rowReference));
+ $endRow = max($endRow, $this->getMaximumRow($rowReference));
+ $reference = preg_replace($pattern, '', $reference);
+ }
+ }
+ if ($rowsSelected === false) {
+ // If there isn't any Special Item Identifier specified, then the selection defaults to data rows only.
+ $startRow = $this->firstDataRow;
+ $endRow = $this->lastDataRow;
+ }
+
+ return [$startRow, $endRow];
+ }
+
+ private function getColumnsForColumnReference(string $reference, int $startRow, int $endRow): string
+ {
+ $columnsSelected = false;
+ foreach ($this->columns as $columnId => $columnName) {
+ $columnName = str_replace("\u{a0}", ' ', $columnName);
+ $cellFrom = "{$columnId}{$startRow}";
+ $cellTo = "{$columnId}{$endRow}";
+ $cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
+ $pattern = '/\[' . preg_quote($columnName) . '\]/mui';
+ if (preg_match($pattern, $reference) === 1) {
+ $columnsSelected = true;
+ $reference = preg_replace($pattern, $cellReference, $reference);
+ }
+ /** @var string $reference */
+ }
+ if ($columnsSelected === false) {
+ return $this->fullData($startRow, $endRow);
+ }
+
+ return $reference;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering.php
old mode 100755
new mode 100644
index 458d0e8..e3a2bd6
--- a/PhpOffice/PhpSpreadsheet/Calculation/Engineering.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering.php
@@ -3,725 +3,29 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
use Complex\Complex;
-use Complex\Exception as ComplexException;
+use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ComplexFunctions;
+use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ComplexOperations;
+/**
+ * @deprecated 1.18.0
+ */
class Engineering
{
/**
* EULER.
- */
- const EULER = 2.71828182845904523536;
-
- /**
- * Details of the Units of measure that can be used in CONVERTUOM().
*
- * @var mixed[]
+ * @deprecated 1.18.0
+ * Use Engineering\Constants::EULER instead
+ * @see Engineering\Constants::EULER
*/
- private static $conversionUnits = [
- 'g' => ['Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => true],
- 'sg' => ['Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => false],
- 'lbm' => ['Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
- 'u' => ['Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
- 'ozm' => ['Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
- 'm' => ['Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => true],
- 'mi' => ['Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
- 'Nmi' => ['Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
- 'in' => ['Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => false],
- 'ft' => ['Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => false],
- 'yd' => ['Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => false],
- 'ang' => ['Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
- 'Pica' => ['Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
- 'yr' => ['Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => false],
- 'day' => ['Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => false],
- 'hr' => ['Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => false],
- 'mn' => ['Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => false],
- 'sec' => ['Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => true],
- 'Pa' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
- 'p' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
- 'atm' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
- 'at' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
- 'mmHg' => ['Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
- 'N' => ['Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => true],
- 'dyn' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
- 'dy' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
- 'lbf' => ['Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
- 'J' => ['Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => true],
- 'e' => ['Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => true],
- 'c' => ['Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
- 'cal' => ['Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
- 'eV' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
- 'ev' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
- 'HPh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
- 'hh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
- 'Wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
- 'wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
- 'flb' => ['Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
- 'BTU' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
- 'btu' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
- 'HP' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
- 'h' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
- 'W' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
- 'w' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
- 'T' => ['Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
- 'ga' => ['Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
- 'C' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
- 'cel' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
- 'F' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
- 'fah' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
- 'K' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
- 'kel' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
- 'tsp' => ['Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
- 'tbs' => ['Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
- 'oz' => ['Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
- 'cup' => ['Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => false],
- 'pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
- 'us_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
- 'uk_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
- 'qt' => ['Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => false],
- 'gal' => ['Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
- 'l' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
- 'lt' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
- ];
-
- /**
- * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
- *
- * @var mixed[]
- */
- private static $conversionMultipliers = [
- 'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
- 'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
- 'E' => ['multiplier' => 1E18, 'name' => 'exa'],
- 'P' => ['multiplier' => 1E15, 'name' => 'peta'],
- 'T' => ['multiplier' => 1E12, 'name' => 'tera'],
- 'G' => ['multiplier' => 1E9, 'name' => 'giga'],
- 'M' => ['multiplier' => 1E6, 'name' => 'mega'],
- 'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
- 'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
- 'e' => ['multiplier' => 1E1, 'name' => 'deka'],
- 'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
- 'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
- 'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
- 'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
- 'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
- 'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
- 'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
- 'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
- 'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
- 'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
- ];
-
- /**
- * Details of the Units of measure conversion factors, organised by group.
- *
- * @var mixed[]
- */
- private static $unitConversions = [
- 'Mass' => [
- 'g' => [
- 'g' => 1.0,
- 'sg' => 6.85220500053478E-05,
- 'lbm' => 2.20462291469134E-03,
- 'u' => 6.02217000000000E+23,
- 'ozm' => 3.52739718003627E-02,
- ],
- 'sg' => [
- 'g' => 1.45938424189287E+04,
- 'sg' => 1.0,
- 'lbm' => 3.21739194101647E+01,
- 'u' => 8.78866000000000E+27,
- 'ozm' => 5.14782785944229E+02,
- ],
- 'lbm' => [
- 'g' => 4.5359230974881148E+02,
- 'sg' => 3.10810749306493E-02,
- 'lbm' => 1.0,
- 'u' => 2.73161000000000E+26,
- 'ozm' => 1.60000023429410E+01,
- ],
- 'u' => [
- 'g' => 1.66053100460465E-24,
- 'sg' => 1.13782988532950E-28,
- 'lbm' => 3.66084470330684E-27,
- 'u' => 1.0,
- 'ozm' => 5.85735238300524E-26,
- ],
- 'ozm' => [
- 'g' => 2.83495152079732E+01,
- 'sg' => 1.94256689870811E-03,
- 'lbm' => 6.24999908478882E-02,
- 'u' => 1.70725600000000E+25,
- 'ozm' => 1.0,
- ],
- ],
- 'Distance' => [
- 'm' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- 'yr' => [
- 'yr' => 1.0,
- 'day' => 365.25,
- 'hr' => 8766.0,
- 'mn' => 525960.0,
- 'sec' => 31557600.0,
- ],
- 'day' => [
- 'yr' => 2.73785078713210E-03,
- 'day' => 1.0,
- 'hr' => 24.0,
- 'mn' => 1440.0,
- 'sec' => 86400.0,
- ],
- 'hr' => [
- 'yr' => 1.14077116130504E-04,
- 'day' => 4.16666666666667E-02,
- 'hr' => 1.0,
- 'mn' => 60.0,
- 'sec' => 3600.0,
- ],
- 'mn' => [
- 'yr' => 1.90128526884174E-06,
- 'day' => 6.94444444444444E-04,
- 'hr' => 1.66666666666667E-02,
- 'mn' => 1.0,
- 'sec' => 60.0,
- ],
- 'sec' => [
- 'yr' => 3.16880878140289E-08,
- 'day' => 1.15740740740741E-05,
- 'hr' => 2.77777777777778E-04,
- 'mn' => 1.66666666666667E-02,
- 'sec' => 1.0,
- ],
- ],
- 'Pressure' => [
- 'Pa' => [
- 'Pa' => 1.0,
- 'p' => 1.0,
- 'atm' => 9.86923299998193E-06,
- 'at' => 9.86923299998193E-06,
- 'mmHg' => 7.50061707998627E-03,
- ],
- 'p' => [
- 'Pa' => 1.0,
- 'p' => 1.0,
- 'atm' => 9.86923299998193E-06,
- 'at' => 9.86923299998193E-06,
- 'mmHg' => 7.50061707998627E-03,
- ],
- 'atm' => [
- 'Pa' => 1.01324996583000E+05,
- 'p' => 1.01324996583000E+05,
- 'atm' => 1.0,
- 'at' => 1.0,
- 'mmHg' => 760.0,
- ],
- 'at' => [
- 'Pa' => 1.01324996583000E+05,
- 'p' => 1.01324996583000E+05,
- 'atm' => 1.0,
- 'at' => 1.0,
- 'mmHg' => 760.0,
- ],
- 'mmHg' => [
- 'Pa' => 1.33322363925000E+02,
- 'p' => 1.33322363925000E+02,
- 'atm' => 1.31578947368421E-03,
- 'at' => 1.31578947368421E-03,
- 'mmHg' => 1.0,
- ],
- ],
- 'Force' => [
- 'N' => [
- 'N' => 1.0,
- 'dyn' => 1.0E+5,
- 'dy' => 1.0E+5,
- 'lbf' => 2.24808923655339E-01,
- ],
- 'dyn' => [
- 'N' => 1.0E-5,
- 'dyn' => 1.0,
- 'dy' => 1.0,
- 'lbf' => 2.24808923655339E-06,
- ],
- 'dy' => [
- 'N' => 1.0E-5,
- 'dyn' => 1.0,
- 'dy' => 1.0,
- 'lbf' => 2.24808923655339E-06,
- ],
- 'lbf' => [
- 'N' => 4.448222,
- 'dyn' => 4.448222E+5,
- 'dy' => 4.448222E+5,
- 'lbf' => 1.0,
- ],
- ],
- 'Energy' => [
- 'J' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- 'HP' => [
- 'HP' => 1.0,
- 'h' => 1.0,
- 'W' => 7.45701000000000E+02,
- 'w' => 7.45701000000000E+02,
- ],
- 'h' => [
- 'HP' => 1.0,
- 'h' => 1.0,
- 'W' => 7.45701000000000E+02,
- 'w' => 7.45701000000000E+02,
- ],
- 'W' => [
- 'HP' => 1.34102006031908E-03,
- 'h' => 1.34102006031908E-03,
- 'W' => 1.0,
- 'w' => 1.0,
- ],
- 'w' => [
- 'HP' => 1.34102006031908E-03,
- 'h' => 1.34102006031908E-03,
- 'W' => 1.0,
- 'w' => 1.0,
- ],
- ],
- 'Magnetism' => [
- 'T' => [
- 'T' => 1.0,
- 'ga' => 10000.0,
- ],
- 'ga' => [
- 'T' => 0.0001,
- 'ga' => 1.0,
- ],
- ],
- 'Liquid' => [
- 'tsp' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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' => [
- '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,
- ],
- ],
- ];
+ public const EULER = 2.71828182845904523536;
/**
* parseComplex.
*
* Parses a complex number into its real and imaginary parts, and an I or J suffix
*
- * @deprecated 2.0.0 No longer used by internal code. Please use the Complex\Complex class instead
+ * @deprecated 1.12.0 No longer used by internal code. Please use the \Complex\Complex class instead
*
* @param string $complexNumber The complex number
*
@@ -738,35 +42,6 @@ class Engineering
];
}
- /**
- * Formats a number base string value with leading zeroes.
- *
- * @param string $xVal The "number" to pad
- * @param int $places The length that we want to pad this value
- *
- * @return string The padded "number"
- */
- private static function nbrConversionFormat($xVal, $places)
- {
- if ($places !== null) {
- if (is_numeric($places)) {
- $places = (int) $places;
- } else {
- return Functions::VALUE();
- }
- if ($places < 0) {
- return Functions::NAN();
- }
- if (strlen($xVal) <= $places) {
- return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
- }
-
- return Functions::NAN();
- }
-
- return substr($xVal, -10);
- }
-
/**
* BESSELI.
*
@@ -776,7 +51,9 @@ class Engineering
* Excel Function:
* BESSELI(x,ord)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BESSELI() method in the Engineering\BesselI class instead
+ * @see Engineering\BesselI::BESSELI()
*
* @param float $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELI returns the #VALUE! error value.
@@ -785,42 +62,11 @@ class Engineering
* If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
* If $ord < 0, BESSELI returns the #NUM! error value.
*
- * @return float
+ * @return array|float|string Result, or a string containing an error
*/
public static function BESSELI($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- $ord = floor($ord);
- if ($ord < 0) {
- return Functions::NAN();
- }
-
- if (abs($x) <= 30) {
- $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord);
- $ordK = 1;
- $fSqrX = ($x * $x) / 4;
- do {
- $fTerm *= $fSqrX;
- $fTerm /= ($ordK * ($ordK + $ord));
- $fResult += $fTerm;
- } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
- } else {
- $f_2_PI = 2 * M_PI;
-
- $fXAbs = abs($x);
- $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
- if (($ord & 1) && ($x < 0)) {
- $fResult = -$fResult;
- }
- }
-
- return (is_nan($fResult)) ? Functions::NAN() : $fResult;
- }
-
- return Functions::VALUE();
+ return Engineering\BesselI::BESSELI($x, $ord);
}
/**
@@ -831,7 +77,9 @@ class Engineering
* Excel Function:
* BESSELJ(x,ord)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BESSELJ() method in the Engineering\BesselJ class instead
+ * @see Engineering\BesselJ::BESSELJ()
*
* @param float $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELJ returns the #VALUE! error value.
@@ -839,80 +87,11 @@ class Engineering
* If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
* If $ord < 0, BESSELJ returns the #NUM! error value.
*
- * @return float
+ * @return array|float|string Result, or a string containing an error
*/
public static function BESSELJ($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- $ord = floor($ord);
- if ($ord < 0) {
- return Functions::NAN();
- }
-
- $fResult = 0;
- if (abs($x) <= 30) {
- $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord);
- $ordK = 1;
- $fSqrX = ($x * $x) / -4;
- do {
- $fTerm *= $fSqrX;
- $fTerm /= ($ordK * ($ordK + $ord));
- $fResult += $fTerm;
- } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
- } else {
- $f_PI_DIV_2 = M_PI / 2;
- $f_PI_DIV_4 = M_PI / 4;
-
- $fXAbs = abs($x);
- $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
- if (($ord & 1) && ($x < 0)) {
- $fResult = -$fResult;
- }
- }
-
- return (is_nan($fResult)) ? Functions::NAN() : $fResult;
- }
-
- return Functions::VALUE();
- }
-
- 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;
- }
-
- 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;
+ return Engineering\BesselJ::BESSELJ($x, $ord);
}
/**
@@ -924,7 +103,9 @@ class Engineering
* Excel Function:
* BESSELK(x,ord)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BESSELK() method in the Engineering\BesselK class instead
+ * @see Engineering\BesselK::BESSELK()
*
* @param float $x The value at which to evaluate the function.
* If x is nonnumeric, BESSELK returns the #VALUE! error value.
@@ -932,77 +113,11 @@ class Engineering
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
* If $ord < 0, BESSELK returns the #NUM! error value.
*
- * @return float
+ * @return array|float|string Result, or a string containing an error
*/
public static function BESSELK($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- if (($ord < 0) || ($x == 0.0)) {
- return Functions::NAN();
- }
-
- switch (floor($ord)) {
- case 0:
- $fBk = self::besselK0($x);
-
- break;
- case 1:
- $fBk = 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 (is_nan($fBk)) ? Functions::NAN() : $fBk;
- }
-
- return Functions::VALUE();
- }
-
- 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 + 0.636619772 * 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(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
- }
-
- return $fRet;
- }
-
- 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 + 0.636619772 * (self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
- } else {
- $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
- }
-
- return $fRet;
+ return Engineering\BesselK::BESSELK($x, $ord);
}
/**
@@ -1013,50 +128,21 @@ class Engineering
* Excel Function:
* BESSELY(x,ord)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BESSELY() method in the Engineering\BesselY class instead
+ * @see Engineering\BesselY::BESSELY()
*
* @param float $x The value at which to evaluate the function.
- * If x is nonnumeric, BESSELK returns the #VALUE! error value.
+ * If x is nonnumeric, BESSELY returns the #VALUE! error value.
* @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
- * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
- * If $ord < 0, BESSELK returns the #NUM! error value.
+ * If $ord is nonnumeric, BESSELY returns the #VALUE! error value.
+ * If $ord < 0, BESSELY returns the #NUM! error value.
*
- * @return float
+ * @return array|float|string Result, or a string containing an error
*/
public static function BESSELY($x, $ord)
{
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- if (($ord < 0) || ($x == 0.0)) {
- return Functions::NAN();
- }
-
- switch (floor($ord)) {
- case 0:
- $fBy = self::besselY0($x);
-
- break;
- case 1:
- $fBy = 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 (is_nan($fBy)) ? Functions::NAN() : $fBy;
- }
-
- return Functions::VALUE();
+ return Engineering\BesselY::BESSELY($x, $ord);
}
/**
@@ -1067,45 +153,22 @@ class Engineering
* Excel Function:
* BIN2DEC(x)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toDecimal() method in the Engineering\ConvertBinary class instead
+ * @see Engineering\ConvertBinary::toDecimal()
*
- * @param string $x The binary number (as a string) that you want to convert. The number
+ * @param mixed $x The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2DEC returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function BINTODEC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- $x = substr($x, -9);
-
- return '-' . (512 - bindec($x));
- }
-
- return bindec($x);
+ return Engineering\ConvertBinary::toDecimal($x);
}
/**
@@ -1116,52 +179,28 @@ class Engineering
* Excel Function:
* BIN2HEX(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toHex() method in the Engineering\ConvertBinary class instead
+ * @see Engineering\ConvertBinary::toHex()
*
- * @param string $x The binary number (as a string) that you want to convert. The number
+ * @param mixed $x The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+ * @param mixed $places The number of characters to use. If places is omitted, BIN2HEX uses the
* minimum number of characters necessary. Places is useful for padding the
* return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
* If places is negative, BIN2HEX returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function BINTOHEX($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- // Argument X
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } 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);
+ return Engineering\ConvertBinary::toHex($x, $places);
}
/**
@@ -1172,51 +211,28 @@ class Engineering
* Excel Function:
* BIN2OCT(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toOctal() method in the Engineering\ConvertBinary class instead
+ * @see Engineering\ConvertBinary::toOctal()
*
- * @param string $x The binary number (as a string) that you want to convert. The number
+ * @param mixed $x The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+ * @param mixed $places The number of characters to use. If places is omitted, BIN2OCT uses the
* minimum number of characters necessary. Places is useful for padding the
* return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
* If places is negative, BIN2OCT returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function BINTOOCT($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } 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);
+ return Engineering\ConvertBinary::toOctal($x, $places);
}
/**
@@ -1227,9 +243,11 @@ class Engineering
* Excel Function:
* DEC2BIN(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toBinary() method in the Engineering\ConvertDecimal class instead
+ * @see Engineering\ConvertDecimal::toBinary()
*
- * @param string $x The decimal integer you want to convert. If number is negative,
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
* valid place values are ignored and DEC2BIN returns a 10-character
* (10-bit) binary number in which the most significant bit is the sign
* bit. The remaining 9 bits are magnitude bits. Negative numbers are
@@ -1239,45 +257,18 @@ class Engineering
* If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
* If DEC2BIN requires more than places characters, it returns the #NUM!
* error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2BIN uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
* If places is zero or negative, DEC2BIN returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function DECTOBIN($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
-
- $x = (string) floor($x);
- if ($x < -512 || $x > 511) {
- return Functions::NAN();
- }
-
- $r = decbin($x);
- // Two's Complement
- $r = substr($r, -10);
- if (strlen($r) >= 11) {
- return Functions::NAN();
- }
-
- return self::nbrConversionFormat($r, $places);
+ return Engineering\ConvertDecimal::toBinary($x, $places);
}
/**
@@ -1288,9 +279,11 @@ class Engineering
* Excel Function:
* DEC2HEX(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toHex() method in the Engineering\ConvertDecimal class instead
+ * @see Engineering\ConvertDecimal::toHex()
*
- * @param string $x The decimal integer you want to convert. If number is negative,
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
* places is ignored and DEC2HEX returns a 10-character (40-bit)
* hexadecimal number in which the most significant bit is the sign
* bit. The remaining 39 bits are magnitude bits. Negative numbers
@@ -1300,39 +293,18 @@ class Engineering
* If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
* If DEC2HEX requires more than places characters, it returns the
* #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2HEX uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
* If places is zero or negative, DEC2HEX returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function DECTOHEX($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
- $x = (string) floor($x);
- $r = strtoupper(dechex($x));
- if (strlen($r) == 8) {
- // Two's Complement
- $r = 'FF' . $r;
- }
-
- return self::nbrConversionFormat($r, $places);
+ return Engineering\ConvertDecimal::toHex($x, $places);
}
/**
@@ -1343,9 +315,11 @@ class Engineering
* Excel Function:
* DEC2OCT(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toOctal() method in the Engineering\ConvertDecimal class instead
+ * @see Engineering\ConvertDecimal::toOctal()
*
- * @param string $x The decimal integer you want to convert. If number is negative,
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
* places is ignored and DEC2OCT returns a 10-character (30-bit)
* octal number in which the most significant bit is the sign bit.
* The remaining 29 bits are magnitude bits. Negative numbers are
@@ -1355,40 +329,18 @@ class Engineering
* If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
* If DEC2OCT requires more than places characters, it returns the
* #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2OCT uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
* If places is zero or negative, DEC2OCT returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function DECTOOCT($x, $places = null)
{
- $xorig = $x;
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
- $x = (string) floor($x);
- $r = decoct($x);
- if (strlen($r) == 11) {
- // Two's Complement
- $r = substr($r, -10);
- }
-
- return self::nbrConversionFormat($r, $places);
+ return Engineering\ConvertDecimal::toOctal($x, $places);
}
/**
@@ -1399,9 +351,11 @@ class Engineering
* Excel Function:
* HEX2BIN(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toBinary() method in the Engineering\ConvertHex class instead
+ * @see Engineering\ConvertHex::toBinary()
*
- * @param string $x the hexadecimal number you want to convert.
+ * @param mixed $x the hexadecimal number (as a string) that you want to convert.
* Number cannot contain more than 10 characters.
* The most significant bit of number is the sign bit (40th bit from the right).
* The remaining 9 bits are magnitude bits.
@@ -1411,29 +365,18 @@ class Engineering
* and if number is positive, it cannot be greater than 1FF.
* If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
* If HEX2BIN requires more than places characters, it returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted,
+ * @param mixed $places The number of characters to use. If places is omitted,
* HEX2BIN uses the minimum number of characters necessary. Places
* is useful for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
* If places is negative, HEX2BIN returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function HEXTOBIN($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- return self::DECTOBIN(self::HEXTODEC($x), $places);
+ return Engineering\ConvertHex::toBinary($x, $places);
}
/**
@@ -1444,9 +387,11 @@ class Engineering
* Excel Function:
* HEX2DEC(x)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toDecimal() method in the Engineering\ConvertHex class instead
+ * @see Engineering\ConvertHex::toDecimal()
*
- * @param string $x The hexadecimal number you want to convert. This number cannot
+ * @param mixed $x The hexadecimal number (as a string) that you want to convert. This number cannot
* contain more than 10 characters (40 bits). The most significant
* bit of number is the sign bit. The remaining 39 bits are magnitude
* bits. Negative numbers are represented using two's-complement
@@ -1454,37 +399,11 @@ class Engineering
* If number is not a valid hexadecimal number, HEX2DEC returns the
* #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function HEXTODEC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- if (strlen($x) > 10) {
- return Functions::NAN();
- }
-
- $binX = '';
- foreach (str_split($x) as $char) {
- $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
- }
- if (strlen($binX) == 40 && $binX[0] == '1') {
- for ($i = 0; $i < 40; ++$i) {
- $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
- }
-
- return (bindec($binX) + 1) * -1;
- }
-
- return bindec($binX);
+ return Engineering\ConvertHex::toDecimal($x);
}
/**
@@ -1495,9 +414,11 @@ class Engineering
* Excel Function:
* HEX2OCT(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toOctal() method in the Engineering\ConvertHex class instead
+ * @see Engineering\ConvertHex::toOctal()
*
- * @param string $x The hexadecimal number you want to convert. Number cannot
+ * @param mixed $x The hexadecimal number (as a string) that you want to convert. Number cannot
* contain more than 10 characters. The most significant bit of
* number is the sign bit. The remaining 39 bits are magnitude
* bits. Negative numbers are represented using two's-complement
@@ -1510,7 +431,7 @@ class Engineering
* the #NUM! error value.
* If HEX2OCT requires more than places characters, it returns
* the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, HEX2OCT
+ * @param mixed $places The number of characters to use. If places is omitted, HEX2OCT
* uses the minimum number of characters necessary. Places is
* useful for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
@@ -1518,27 +439,11 @@ class Engineering
* value.
* If places is negative, HEX2OCT returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function HEXTOOCT($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- $decimal = self::HEXTODEC($x);
- if ($decimal < -536870912 || $decimal > 536870911) {
- return Functions::NAN();
- }
-
- return self::DECTOOCT($decimal, $places);
+ return Engineering\ConvertHex::toOctal($x, $places);
}
/**
@@ -1549,9 +454,11 @@ class Engineering
* Excel Function:
* OCT2BIN(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toBinary() method in the Engineering\ConvertOctal class instead
+ * @see Engineering\ConvertOctal::toBinary()
*
- * @param string $x The octal number you want to convert. Number may not
+ * @param mixed $x The octal number you want to convert. Number may not
* contain more than 10 characters. The most significant
* bit of number is the sign bit. The remaining 29 bits
* are magnitude bits. Negative numbers are represented
@@ -1564,7 +471,7 @@ class Engineering
* the #NUM! error value.
* If OCT2BIN requires more than places characters, it
* returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted,
+ * @param mixed $places The number of characters to use. If places is omitted,
* OCT2BIN uses the minimum number of characters necessary.
* Places is useful for padding the return value with
* leading 0s (zeros).
@@ -1574,22 +481,11 @@ class Engineering
* If places is negative, OCT2BIN returns the #NUM! error
* value.
*
- * @return string
+ * @return array|string
*/
public static function OCTTOBIN($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
-
- return self::DECTOBIN(self::OCTTODEC($x), $places);
+ return Engineering\ConvertOctal::toBinary($x, $places);
}
/**
@@ -1600,9 +496,11 @@ class Engineering
* Excel Function:
* OCT2DEC(x)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toDecimal() method in the Engineering\ConvertOctal class instead
+ * @see Engineering\ConvertOctal::toDecimal()
*
- * @param string $x The octal number you want to convert. Number may not contain
+ * @param mixed $x The octal number you want to convert. Number may not contain
* more than 10 octal characters (30 bits). The most significant
* bit of number is the sign bit. The remaining 29 bits are
* magnitude bits. Negative numbers are represented using
@@ -1610,32 +508,11 @@ class Engineering
* If number is not a valid octal number, OCT2DEC returns the
* #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function OCTTODEC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
- $binX = '';
- foreach (str_split($x) as $char) {
- $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
- }
- if (strlen($binX) == 30 && $binX[0] == '1') {
- for ($i = 0; $i < 30; ++$i) {
- $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
- }
-
- return (bindec($binX) + 1) * -1;
- }
-
- return bindec($binX);
+ return Engineering\ConvertOctal::toDecimal($x);
}
/**
@@ -1646,9 +523,11 @@ class Engineering
* Excel Function:
* OCT2HEX(x[,places])
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the toHex() method in the Engineering\ConvertOctal class instead
+ * @see Engineering\ConvertOctal::toHex()
*
- * @param string $x The octal number you want to convert. Number may not contain
+ * @param mixed $x The octal number you want to convert. Number may not contain
* more than 10 octal characters (30 bits). The most significant
* bit of number is the sign bit. The remaining 29 bits are
* magnitude bits. Negative numbers are represented using
@@ -1659,30 +538,18 @@ class Engineering
* #NUM! error value.
* If OCT2HEX requires more than places characters, it returns
* the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, OCT2HEX
+ * @param mixed $places The number of characters to use. If places is omitted, OCT2HEX
* uses the minimum number of characters necessary. Places is useful
* for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
* If places is negative, OCT2HEX returns the #NUM! error value.
*
- * @return string
+ * @return array|string
*/
public static function OCTTOHEX($x, $places = null)
{
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
- $hexVal = strtoupper(dechex(self::OCTTODEC($x)));
-
- return self::nbrConversionFormat($hexVal, $places);
+ return Engineering\ConvertOctal::toHex($x, $places);
}
/**
@@ -1693,30 +560,20 @@ class Engineering
* Excel Function:
* COMPLEX(realNumber,imaginary[,suffix])
*
- * @category Engineering Functions
+ * @deprecated 1.18.0
+ * Use the COMPLEX() method in the Engineering\Complex class instead
+ * @see Engineering\Complex::COMPLEX()
*
- * @param float $realNumber the real coefficient of the complex number
- * @param float $imaginary the imaginary coefficient of the complex number
- * @param string $suffix The suffix for the imaginary component of the complex number.
+ * @param array|float $realNumber the real coefficient of the complex number
+ * @param array|float $imaginary the imaginary coefficient of the complex number
+ * @param array|string $suffix The suffix for the imaginary component of the complex number.
* If omitted, the suffix is assumed to be "i".
*
- * @return string
+ * @return array|string
*/
public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
{
- $realNumber = ($realNumber === null) ? 0.0 : Functions::flattenSingleValue($realNumber);
- $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary);
- $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix);
-
- if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
- (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))
- ) {
- $complex = new Complex($realNumber, $imaginary, $suffix);
-
- return (string) $complex;
- }
-
- return Functions::VALUE();
+ return Engineering\Complex::COMPLEX($realNumber, $imaginary, $suffix);
}
/**
@@ -1727,18 +584,18 @@ class Engineering
* Excel Function:
* IMAGINARY(complexNumber)
*
- * @category Engineering Functions
+ * @deprecated 1.18.0
+ * Use the IMAGINARY() method in the Engineering\Complex class instead
+ * @see Engineering\Complex::IMAGINARY()
*
* @param string $complexNumber the complex number for which you want the imaginary
* coefficient
*
- * @return float
+ * @return array|float|string
*/
public static function IMAGINARY($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->getImaginary();
+ return Engineering\Complex::IMAGINARY($complexNumber);
}
/**
@@ -1749,17 +606,17 @@ class Engineering
* Excel Function:
* IMREAL(complexNumber)
*
- * @category Engineering Functions
+ * @deprecated 1.18.0
+ * Use the IMREAL() method in the Engineering\Complex class instead
+ * @see Engineering\Complex::IMREAL()
*
* @param string $complexNumber the complex number for which you want the real coefficient
*
- * @return float
+ * @return array|float|string
*/
public static function IMREAL($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->getReal();
+ return Engineering\Complex::IMREAL($complexNumber);
}
/**
@@ -1770,15 +627,17 @@ class Engineering
* Excel Function:
* IMABS(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMABS() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMABS()
+ *
* @param string $complexNumber the complex number for which you want the absolute value
*
- * @return float
+ * @return array|float|string
*/
public static function IMABS($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->abs();
+ return ComplexFunctions::IMABS($complexNumber);
}
/**
@@ -1790,20 +649,17 @@ class Engineering
* Excel Function:
* IMARGUMENT(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the argument theta
+ * @deprecated 1.18.0
+ * Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMARGUMENT()
*
- * @return float|string
+ * @param array|string $complexNumber the complex number for which you want the argument theta
+ *
+ * @return array|float|string
*/
public static function IMARGUMENT($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::DIV0();
- }
-
- return $complex->argument();
+ return ComplexFunctions::IMARGUMENT($complexNumber);
}
/**
@@ -1814,15 +670,17 @@ class Engineering
* Excel Function:
* IMCONJUGATE(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the conjugate
+ * @deprecated 1.18.0
+ * Use the IMCONJUGATE() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCONJUGATE()
*
- * @return string
+ * @param array|string $complexNumber the complex number for which you want the conjugate
+ *
+ * @return array|string
*/
public static function IMCONJUGATE($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->conjugate();
+ return ComplexFunctions::IMCONJUGATE($complexNumber);
}
/**
@@ -1833,15 +691,17 @@ class Engineering
* Excel Function:
* IMCOS(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the cosine
+ * @deprecated 1.18.0
+ * Use the IMCOS() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCOS()
*
- * @return float|string
+ * @param array|string $complexNumber the complex number for which you want the cosine
+ *
+ * @return array|float|string
*/
public static function IMCOS($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cos();
+ return ComplexFunctions::IMCOS($complexNumber);
}
/**
@@ -1852,15 +712,17 @@ class Engineering
* Excel Function:
* IMCOSH(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the hyperbolic cosine
+ * @deprecated 1.18.0
+ * Use the IMCOSH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCOSH()
*
- * @return float|string
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
+ *
+ * @return array|float|string
*/
public static function IMCOSH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cosh();
+ return ComplexFunctions::IMCOSH($complexNumber);
}
/**
@@ -1871,15 +733,17 @@ class Engineering
* Excel Function:
* IMCOT(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the cotangent
+ * @deprecated 1.18.0
+ * Use the IMCOT() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCOT()
*
- * @return float|string
+ * @param array|string $complexNumber the complex number for which you want the cotangent
+ *
+ * @return array|float|string
*/
public static function IMCOT($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cot();
+ return ComplexFunctions::IMCOT($complexNumber);
}
/**
@@ -1890,15 +754,17 @@ class Engineering
* Excel Function:
* IMCSC(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the cosecant
+ * @deprecated 1.18.0
+ * Use the IMCSC() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCSC()
*
- * @return float|string
+ * @param array|string $complexNumber the complex number for which you want the cosecant
+ *
+ * @return array|float|string
*/
public static function IMCSC($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->csc();
+ return ComplexFunctions::IMCSC($complexNumber);
}
/**
@@ -1909,15 +775,17 @@ class Engineering
* Excel Function:
* IMCSCH(complexNumber)
*
- * @param string $complexNumber the complex number for which you want the hyperbolic cosecant
+ * @deprecated 1.18.0
+ * Use the IMCSCH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCSCH()
*
- * @return float|string
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
+ *
+ * @return array|float|string
*/
public static function IMCSCH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->csch();
+ return ComplexFunctions::IMCSCH($complexNumber);
}
/**
@@ -1928,15 +796,17 @@ class Engineering
* Excel Function:
* IMSIN(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMSIN() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSIN()
+ *
* @param string $complexNumber the complex number for which you want the sine
*
- * @return float|string
+ * @return array|float|string
*/
public static function IMSIN($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sin();
+ return ComplexFunctions::IMSIN($complexNumber);
}
/**
@@ -1947,15 +817,17 @@ class Engineering
* Excel Function:
* IMSINH(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMSINH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSINH()
+ *
* @param string $complexNumber the complex number for which you want the hyperbolic sine
*
- * @return float|string
+ * @return array|float|string
*/
public static function IMSINH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sinh();
+ return ComplexFunctions::IMSINH($complexNumber);
}
/**
@@ -1966,15 +838,17 @@ class Engineering
* Excel Function:
* IMSEC(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMSEC() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSEC()
+ *
* @param string $complexNumber the complex number for which you want the secant
*
- * @return float|string
+ * @return array|float|string
*/
public static function IMSEC($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sec();
+ return ComplexFunctions::IMSEC($complexNumber);
}
/**
@@ -1985,15 +859,17 @@ class Engineering
* Excel Function:
* IMSECH(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMSECH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSECH()
+ *
* @param string $complexNumber the complex number for which you want the hyperbolic secant
*
- * @return float|string
+ * @return array|float|string
*/
public static function IMSECH($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sech();
+ return ComplexFunctions::IMSECH($complexNumber);
}
/**
@@ -2004,15 +880,17 @@ class Engineering
* Excel Function:
* IMTAN(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMTAN() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMTAN()
+ *
* @param string $complexNumber the complex number for which you want the tangent
*
- * @return float|string
+ * @return array|float|string
*/
public static function IMTAN($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->tan();
+ return ComplexFunctions::IMTAN($complexNumber);
}
/**
@@ -2023,20 +901,17 @@ class Engineering
* Excel Function:
* IMSQRT(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMSQRT() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSQRT()
+ *
* @param string $complexNumber the complex number for which you want the square root
*
- * @return string
+ * @return array|string
*/
public static function IMSQRT($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $theta = self::IMARGUMENT($complexNumber);
- if ($theta === Functions::DIV0()) {
- return '0';
- }
-
- return (string) (new Complex($complexNumber))->sqrt();
+ return ComplexFunctions::IMSQRT($complexNumber);
}
/**
@@ -2047,20 +922,17 @@ class Engineering
* Excel Function:
* IMLN(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMLN() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMLN()
+ *
* @param string $complexNumber the complex number for which you want the natural logarithm
*
- * @return string
+ * @return array|string
*/
public static function IMLN($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->ln();
+ return ComplexFunctions::IMLN($complexNumber);
}
/**
@@ -2071,20 +943,17 @@ class Engineering
* Excel Function:
* IMLOG10(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMLOG10() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMLOG10()
+ *
* @param string $complexNumber the complex number for which you want the common logarithm
*
- * @return string
+ * @return array|string
*/
public static function IMLOG10($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->log10();
+ return ComplexFunctions::IMLOG10($complexNumber);
}
/**
@@ -2095,20 +964,17 @@ class Engineering
* Excel Function:
* IMLOG2(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMLOG2() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMLOG2()
+ *
* @param string $complexNumber the complex number for which you want the base-2 logarithm
*
- * @return string
+ * @return array|string
*/
public static function IMLOG2($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->log2();
+ return ComplexFunctions::IMLOG2($complexNumber);
}
/**
@@ -2119,15 +985,17 @@ class Engineering
* Excel Function:
* IMEXP(complexNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMEXP() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMEXP()
+ *
* @param string $complexNumber the complex number for which you want the exponential
*
- * @return string
+ * @return array|string
*/
public static function IMEXP($complexNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->exp();
+ return ComplexFunctions::IMEXP($complexNumber);
}
/**
@@ -2138,21 +1006,18 @@ class Engineering
* Excel Function:
* IMPOWER(complexNumber,realNumber)
*
+ * @deprecated 1.18.0
+ * Use the IMPOWER() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMPOWER()
+ *
* @param string $complexNumber the complex number you want to raise to a power
* @param float $realNumber the power to which you want to raise the complex number
*
- * @return string
+ * @return array|string
*/
public static function IMPOWER($complexNumber, $realNumber)
{
- $complexNumber = Functions::flattenSingleValue($complexNumber);
- $realNumber = Functions::flattenSingleValue($realNumber);
-
- if (!is_numeric($realNumber)) {
- return Functions::VALUE();
- }
-
- return (string) (new Complex($complexNumber))->pow($realNumber);
+ return ComplexFunctions::IMPOWER($complexNumber, $realNumber);
}
/**
@@ -2163,21 +1028,18 @@ class Engineering
* Excel Function:
* IMDIV(complexDividend,complexDivisor)
*
+ * @deprecated 1.18.0
+ * Use the IMDIV() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMDIV()
+ *
* @param string $complexDividend the complex numerator or dividend
* @param string $complexDivisor the complex denominator or divisor
*
- * @return string
+ * @return array|string
*/
public static function IMDIV($complexDividend, $complexDivisor)
{
- $complexDividend = Functions::flattenSingleValue($complexDividend);
- $complexDivisor = Functions::flattenSingleValue($complexDivisor);
-
- try {
- return (string) (new Complex($complexDividend))->divideby(new Complex($complexDivisor));
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
+ return ComplexOperations::IMDIV($complexDividend, $complexDivisor);
}
/**
@@ -2188,21 +1050,18 @@ class Engineering
* Excel Function:
* IMSUB(complexNumber1,complexNumber2)
*
+ * @deprecated 1.18.0
+ * Use the IMSUB() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMSUB()
+ *
* @param string $complexNumber1 the complex number from which to subtract complexNumber2
* @param string $complexNumber2 the complex number to subtract from complexNumber1
*
- * @return string
+ * @return array|string
*/
public static function IMSUB($complexNumber1, $complexNumber2)
{
- $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
- $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
-
- try {
- return (string) (new Complex($complexNumber1))->subtract(new Complex($complexNumber2));
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
+ return ComplexOperations::IMSUB($complexNumber1, $complexNumber2);
}
/**
@@ -2213,26 +1072,17 @@ class Engineering
* Excel Function:
* IMSUM(complexNumber[,complexNumber[,...]])
*
+ * @deprecated 1.18.0
+ * Use the IMSUM() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMSUM()
+ *
* @param string ...$complexNumbers Series of complex numbers to add
*
* @return string
*/
public static function IMSUM(...$complexNumbers)
{
- // Return value
- $returnValue = new Complex(0.0);
- $aArgs = Functions::flattenArray($complexNumbers);
-
- try {
- // Loop through the arguments
- foreach ($aArgs as $complex) {
- $returnValue = $returnValue->add(new Complex($complex));
- }
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
-
- return (string) $returnValue;
+ return ComplexOperations::IMSUM(...$complexNumbers);
}
/**
@@ -2243,50 +1093,42 @@ class Engineering
* Excel Function:
* IMPRODUCT(complexNumber[,complexNumber[,...]])
*
+ * @deprecated 1.18.0
+ * Use the IMPRODUCT() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMPRODUCT()
+ *
* @param string ...$complexNumbers Series of complex numbers to multiply
*
* @return string
*/
public static function IMPRODUCT(...$complexNumbers)
{
- // Return value
- $returnValue = new Complex(1.0);
- $aArgs = Functions::flattenArray($complexNumbers);
-
- try {
- // Loop through the arguments
- foreach ($aArgs as $complex) {
- $returnValue = $returnValue->multiply(new Complex($complex));
- }
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
-
- return (string) $returnValue;
+ return ComplexOperations::IMPRODUCT(...$complexNumbers);
}
/**
* DELTA.
*
* Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
- * Use this function to filter a set of values. For example, by summing several DELTA
- * functions you calculate the count of equal pairs. This function is also known as the
- * Kronecker Delta function.
+ * Use this function to filter a set of values. For example, by summing several DELTA
+ * functions you calculate the count of equal pairs. This function is also known as the
+ * Kronecker Delta function.
*
* Excel Function:
* DELTA(a[,b])
*
+ * @deprecated 1.17.0
+ * Use the DELTA() method in the Engineering\Compare class instead
+ * @see Engineering\Compare::DELTA()
+ *
* @param float $a the first number
* @param float $b The second number. If omitted, b is assumed to be zero.
*
- * @return int
+ * @return array|int|string (string in the event of an error)
*/
public static function DELTA($a, $b = 0)
{
- $a = Functions::flattenSingleValue($a);
- $b = Functions::flattenSingleValue($b);
-
- return (int) ($a == $b);
+ return Engineering\Compare::DELTA($a, $b);
}
/**
@@ -2297,79 +1139,20 @@ class Engineering
*
* Returns 1 if number >= step; returns 0 (zero) otherwise
* Use this function to filter a set of values. For example, by summing several GESTEP
- * functions you calculate the count of values that exceed a threshold.
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @deprecated 1.17.0
+ * Use the GESTEP() method in the Engineering\Compare class instead
+ * @see Engineering\Compare::GESTEP()
*
* @param float $number the value to test against step
- * @param float $step The threshold value.
- * If you omit a value for step, GESTEP uses zero.
+ * @param float $step The threshold value. If you omit a value for step, GESTEP uses zero.
*
- * @return int
+ * @return array|int|string (string in the event of an error)
*/
public static function GESTEP($number, $step = 0)
{
- $number = Functions::flattenSingleValue($number);
- $step = Functions::flattenSingleValue($step);
-
- return (int) ($number >= $step);
- }
-
- //
- // Private method to calculate the erf value
- //
- private static $twoSqrtPi = 1.128379167095512574;
-
- public 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) > Functions::PRECISION);
-
- return self::$twoSqrtPi * $sum;
- }
-
- /**
- * Validate arguments passed to the bitwise functions.
- *
- * @param mixed $value
- *
- * @throws Exception
- *
- * @return int
- */
- private static function validateBitwiseArgument($value)
- {
- $value = Functions::flattenSingleValue($value);
-
- if (is_int($value)) {
- return $value;
- } elseif (is_numeric($value)) {
- if ($value == (int) ($value)) {
- $value = (int) ($value);
- if (($value > pow(2, 48) - 1) || ($value < 0)) {
- throw new Exception(Functions::NAN());
- }
-
- return $value;
- }
-
- throw new Exception(Functions::NAN());
- }
-
- throw new Exception(Functions::VALUE());
+ return Engineering\Compare::GESTEP($number, $step);
}
/**
@@ -2380,23 +1163,18 @@ class Engineering
* Excel Function:
* BITAND(number1, number2)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BITAND() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITAND()
*
* @param int $number1
* @param int $number2
*
- * @return int|string
+ * @return array|int|string
*/
public static function BITAND($number1, $number2)
{
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 & $number2;
+ return Engineering\BitWise::BITAND($number1, $number2);
}
/**
@@ -2407,23 +1185,18 @@ class Engineering
* Excel Function:
* BITOR(number1, number2)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BITOR() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITOR()
*
* @param int $number1
* @param int $number2
*
- * @return int|string
+ * @return array|int|string
*/
public static function BITOR($number1, $number2)
{
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 | $number2;
+ return Engineering\BitWise::BITOR($number1, $number2);
}
/**
@@ -2434,23 +1207,18 @@ class Engineering
* Excel Function:
* BITXOR(number1, number2)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BITXOR() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITXOR()
*
* @param int $number1
* @param int $number2
*
- * @return int|string
+ * @return array|int|string
*/
public static function BITXOR($number1, $number2)
{
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 ^ $number2;
+ return Engineering\BitWise::BITXOR($number1, $number2);
}
/**
@@ -2461,29 +1229,18 @@ class Engineering
* Excel Function:
* BITLSHIFT(number, shift_amount)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BITLSHIFT() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITLSHIFT()
*
* @param int $number
* @param int $shiftAmount
*
- * @return int|string
+ * @return array|float|int|string
*/
public static function BITLSHIFT($number, $shiftAmount)
{
- try {
- $number = self::validateBitwiseArgument($number);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- $shiftAmount = Functions::flattenSingleValue($shiftAmount);
-
- $result = $number << $shiftAmount;
- if ($result > pow(2, 48) - 1) {
- return Functions::NAN();
- }
-
- return $result;
+ return Engineering\BitWise::BITLSHIFT($number, $shiftAmount);
}
/**
@@ -2494,24 +1251,18 @@ class Engineering
* Excel Function:
* BITRSHIFT(number, shift_amount)
*
- * @category Engineering Functions
+ * @deprecated 1.17.0
+ * Use the BITRSHIFT() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITRSHIFT()
*
* @param int $number
* @param int $shiftAmount
*
- * @return int|string
+ * @return array|float|int|string
*/
public static function BITRSHIFT($number, $shiftAmount)
{
- try {
- $number = self::validateBitwiseArgument($number);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- $shiftAmount = Functions::flattenSingleValue($shiftAmount);
-
- return $number >> $shiftAmount;
+ return Engineering\BitWise::BITRSHIFT($number, $shiftAmount);
}
/**
@@ -2527,27 +1278,19 @@ class Engineering
* Excel Function:
* ERF(lower[,upper])
*
+ * @deprecated 1.17.0
+ * Use the ERF() method in the Engineering\Erf class instead
+ * @see Engineering\Erf::ERF()
+ *
* @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 float|string
+ * @return array|float|string
*/
public static function ERF($lower, $upper = null)
{
- $lower = Functions::flattenSingleValue($lower);
- $upper = Functions::flattenSingleValue($upper);
-
- if (is_numeric($lower)) {
- if ($upper === null) {
- return self::erfVal($lower);
- }
- if (is_numeric($upper)) {
- return self::erfVal($upper) - self::erfVal($lower);
- }
- }
-
- return Functions::VALUE();
+ return Engineering\Erf::ERF($lower, $upper);
}
/**
@@ -2558,48 +1301,17 @@ class Engineering
* Excel Function:
* ERF.PRECISE(limit)
*
+ * @deprecated 1.17.0
+ * Use the ERFPRECISE() method in the Engineering\Erf class instead
+ * @see Engineering\Erf::ERFPRECISE()
+ *
* @param float $limit bound for integrating ERF
*
- * @return float|string
+ * @return array|float|string
*/
public static function ERFPRECISE($limit)
{
- $limit = Functions::flattenSingleValue($limit);
-
- return self::ERF($limit);
- }
-
- //
- // Private method to calculate the erfc value
- //
- private static $oneSqrtPi = 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) > Functions::PRECISION);
-
- return self::$oneSqrtPi * exp(-$x * $x) * $q2;
+ return Engineering\Erf::ERFPRECISE($limit);
}
/**
@@ -2615,88 +1327,97 @@ class Engineering
* Excel Function:
* ERFC(x)
*
+ * @deprecated 1.17.0
+ * Use the ERFC() method in the Engineering\ErfC class instead
+ * @see Engineering\ErfC::ERFC()
+ *
* @param float $x The lower bound for integrating ERFC
*
- * @return float|string
+ * @return array|float|string
*/
public static function ERFC($x)
{
- $x = Functions::flattenSingleValue($x);
-
- if (is_numeric($x)) {
- return self::erfcVal($x);
- }
-
- return Functions::VALUE();
+ return Engineering\ErfC::ERFC($x);
}
/**
* getConversionGroups
* Returns a list of the different conversion groups for UOM conversions.
*
+ * @deprecated 1.16.0
+ * Use the getConversionCategories() method in the Engineering\ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionCategories()
+ *
* @return array
*/
public static function getConversionGroups()
{
- $conversionGroups = [];
- foreach (self::$conversionUnits as $conversionUnit) {
- $conversionGroups[] = $conversionUnit['Group'];
- }
-
- return array_merge(array_unique($conversionGroups));
+ return Engineering\ConvertUOM::getConversionCategories();
}
/**
* getConversionGroupUnits
* Returns an array of units of measure, for a specified conversion group, or for all groups.
*
- * @param string $group The group whose units of measure you want to retrieve
+ * @deprecated 1.16.0
+ * Use the getConversionCategoryUnits() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionCategoryUnits()
+ *
+ * @param null|mixed $category
*
* @return array
*/
- public static function getConversionGroupUnits($group = null)
+ public static function getConversionGroupUnits($category = null)
{
- $conversionGroups = [];
- foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
- if (($group === null) || ($conversionGroup['Group'] == $group)) {
- $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
- }
- }
-
- return $conversionGroups;
+ return Engineering\ConvertUOM::getConversionCategoryUnits($category);
}
/**
* getConversionGroupUnitDetails.
*
- * @param string $group The group whose units of measure you want to retrieve
+ * @deprecated 1.16.0
+ * Use the getConversionCategoryUnitDetails() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionCategoryUnitDetails()
+ *
+ * @param null|mixed $category
*
* @return array
*/
- public static function getConversionGroupUnitDetails($group = null)
+ public static function getConversionGroupUnitDetails($category = null)
{
- $conversionGroups = [];
- foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
- if (($group === null) || ($conversionGroup['Group'] == $group)) {
- $conversionGroups[$conversionGroup['Group']][] = [
- 'unit' => $conversionUnit,
- 'description' => $conversionGroup['Unit Name'],
- ];
- }
- }
-
- return $conversionGroups;
+ return Engineering\ConvertUOM::getConversionCategoryUnitDetails($category);
}
/**
* getConversionMultipliers
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
- * @return array of mixed
+ * @deprecated 1.16.0
+ * Use the getConversionMultipliers() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionMultipliers()
+ *
+ * @return mixed[]
*/
public static function getConversionMultipliers()
{
- return self::$conversionMultipliers;
+ return Engineering\ConvertUOM::getConversionMultipliers();
+ }
+
+ /**
+ * getBinaryConversionMultipliers.
+ *
+ * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure
+ * in CONVERTUOM().
+ *
+ * @deprecated 1.16.0
+ * Use the getBinaryConversionMultipliers() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getBinaryConversionMultipliers()
+ *
+ * @return mixed[]
+ */
+ public static function getBinaryConversionMultipliers()
+ {
+ return Engineering\ConvertUOM::getBinaryConversionMultipliers();
}
/**
@@ -2709,99 +1430,18 @@ class Engineering
* Excel Function:
* CONVERT(value,fromUOM,toUOM)
*
- * @param float $value the value in fromUOM to convert
+ * @deprecated 1.16.0
+ * Use the CONVERT() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::CONVERT()
+ *
+ * @param float|int $value the value in fromUOM to convert
* @param string $fromUOM the units for value
* @param string $toUOM the units for the result
*
- * @return float|string
+ * @return array|float|string
*/
public static function CONVERTUOM($value, $fromUOM, $toUOM)
{
- $value = Functions::flattenSingleValue($value);
- $fromUOM = Functions::flattenSingleValue($fromUOM);
- $toUOM = Functions::flattenSingleValue($toUOM);
-
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- $fromMultiplier = 1.0;
- 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 Functions::NA();
- }
- if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) {
- $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
- } else {
- return Functions::NA();
- }
- }
- $value *= $fromMultiplier;
-
- $toMultiplier = 1.0;
- 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 Functions::NA();
- }
- if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) {
- $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
- } else {
- return Functions::NA();
- }
- }
- if ($unitGroup1 != $unitGroup2) {
- return Functions::NA();
- }
-
- if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) {
- // We've already factored $fromMultiplier into the value, so we need
- // to reverse it again
- return $value / $fromMultiplier;
- } elseif ($unitGroup1 == 'Temperature') {
- if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
- if (($toUOM == 'F') || ($toUOM == 'fah')) {
- return $value;
- }
- $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 $value;
- } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
- (($toUOM == 'C') || ($toUOM == 'cel'))
- ) {
- return $value;
- }
- 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;
+ return Engineering\ConvertUOM::CONVERT($value, $fromUOM, $toUOM);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselI.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselI.php
new file mode 100644
index 0000000..1134574
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselI.php
@@ -0,0 +1,152 @@
+getMessage();
+ }
+
+ if ($ord < 0) {
+ return ExcelError::NAN();
+ }
+
+ $fResult = self::calculate($x, $ord);
+
+ return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselI0($x);
+ case 1:
+ return self::besselI1($x);
+ }
+
+ return self::besselI2($x, $ord);
+ }
+
+ private static function besselI0(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 3.75) {
+ $y = $x / 3.75;
+ $y = $y * $y;
+
+ return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
+ + $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
+ }
+
+ $y = 3.75 / $ax;
+
+ return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
+ + $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1 +
+ $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
+ }
+
+ private static function besselI1(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 3.75) {
+ $y = $x / 3.75;
+ $y = $y * $y;
+ $ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1 +
+ $y * (0.301532e-2 + $y * 0.32411e-3))))));
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ $y = 3.75 / $ax;
+ $ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
+ $ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2 +
+ $y * (-0.1031555e-1 + $y * $ans))));
+ $ans *= exp($ax) / sqrt($ax);
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ /**
+ * Sop to Scrutinizer.
+ *
+ * @var float
+ */
+ private static $zeroPointZero = 0.0;
+
+ private static function besselI2(float $x, int $ord): float
+ {
+ if ($x === self::$zeroPointZero) {
+ return 0.0;
+ }
+
+ $tox = 2.0 / abs($x);
+ $bip = 0;
+ $ans = 0.0;
+ $bi = 1.0;
+
+ for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
+ $bim = $bip + $j * $tox * $bi;
+ $bip = $bi;
+ $bi = $bim;
+
+ if (abs($bi) > 1.0e+12) {
+ $ans *= 1.0e-12;
+ $bi *= 1.0e-12;
+ $bip *= 1.0e-12;
+ }
+
+ if ($j === $ord) {
+ $ans = $bip;
+ }
+ }
+
+ $ans *= self::besselI0($x) / $bi;
+
+ return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselJ.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
new file mode 100644
index 0000000..800a8a1
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
@@ -0,0 +1,180 @@
+ 8. This code provides a more accurate calculation
+ *
+ * @param mixed $x A float value at which to evaluate the function.
+ * If x is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * Or can be an array of values
+ * @param mixed $ord The integer order of the Bessel function.
+ * If ord is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * If $ord < 0, BESSELJ returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|float|string Result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BESSELJ($x, $ord)
+ {
+ if (is_array($x) || is_array($ord)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
+ }
+
+ try {
+ $x = EngineeringValidations::validateFloat($x);
+ $ord = EngineeringValidations::validateInt($ord);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($ord < 0) {
+ return ExcelError::NAN();
+ }
+
+ $fResult = self::calculate($x, $ord);
+
+ return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselJ0($x);
+ case 1:
+ return self::besselJ1($x);
+ }
+
+ return self::besselJ2($x, $ord);
+ }
+
+ private static function besselJ0(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 8.0) {
+ $y = $x * $x;
+ $ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y *
+ (77392.33017 + $y * (-184.9052456)))));
+ $ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y *
+ (267.8532712 + $y * 1.0))));
+
+ return $ans1 / $ans2;
+ }
+
+ $z = 8.0 / $ax;
+ $y = $z * $z;
+ $xx = $ax - 0.785398164;
+ $ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y *
+ (0.7621095161e-6 - $y * 0.934935152e-7)));
+
+ return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
+ }
+
+ private static function besselJ1(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 8.0) {
+ $y = $x * $x;
+ $ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y *
+ (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
+ $ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y *
+ (376.9991397 + $y * 1.0))));
+
+ return $ans1 / $ans2;
+ }
+
+ $z = 8.0 / $ax;
+ $y = $z * $z;
+ $xx = $ax - 2.356194491;
+
+ $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
+ $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
+ (-0.88228987e-6 + $y * 0.105787412e-6)));
+ $ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ private static function besselJ2(float $x, int $ord): float
+ {
+ $ax = abs($x);
+ if ($ax === 0.0) {
+ return 0.0;
+ }
+
+ if ($ax > $ord) {
+ return self::besselj2a($ax, $ord, $x);
+ }
+
+ return self::besselj2b($ax, $ord, $x);
+ }
+
+ private static function besselj2a(float $ax, int $ord, float $x)
+ {
+ $tox = 2.0 / $ax;
+ $bjm = self::besselJ0($ax);
+ $bj = self::besselJ1($ax);
+ for ($j = 1; $j < $ord; ++$j) {
+ $bjp = $j * $tox * $bj - $bjm;
+ $bjm = $bj;
+ $bj = $bjp;
+ }
+ $ans = $bj;
+
+ return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
+ }
+
+ private static function besselj2b(float $ax, int $ord, float $x)
+ {
+ $tox = 2.0 / $ax;
+ $jsum = false;
+ $bjp = $ans = $sum = 0.0;
+ $bj = 1.0;
+ for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
+ $bjm = $j * $tox * $bj - $bjp;
+ $bjp = $bj;
+ $bj = $bjm;
+ if (abs($bj) > 1.0e+10) {
+ $bj *= 1.0e-10;
+ $bjp *= 1.0e-10;
+ $ans *= 1.0e-10;
+ $sum *= 1.0e-10;
+ }
+ if ($jsum === true) {
+ $sum += $bj;
+ }
+ $jsum = $jsum === false;
+ if ($j === $ord) {
+ $ans = $bjp;
+ }
+ }
+ $sum = 2.0 * $sum - $bj;
+ $ans /= $sum;
+
+ return ($x < 0.0 && ($ord % 2) === 1) ? -$ans : $ans;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselK.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselK.php
new file mode 100644
index 0000000..2d21e75
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselK.php
@@ -0,0 +1,135 @@
+getMessage();
+ }
+
+ if (($ord < 0) || ($x <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ $fBk = self::calculate($x, $ord);
+
+ return (is_nan($fBk)) ? ExcelError::NAN() : $fBk;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselK0($x);
+ case 1:
+ return self::besselK1($x);
+ }
+
+ return self::besselK2($x, $ord);
+ }
+
+ /**
+ * Mollify Phpstan.
+ *
+ * @codeCoverageIgnore
+ */
+ private static function callBesselI(float $x, int $ord): float
+ {
+ $rslt = BesselI::BESSELI($x, $ord);
+ if (!is_float($rslt)) {
+ throw new Exception('Unexpected array or string');
+ }
+
+ return $rslt;
+ }
+
+ private static function besselK0(float $x): float
+ {
+ if ($x <= 2) {
+ $fNum2 = $x * 0.5;
+ $y = ($fNum2 * $fNum2);
+
+ return -log($fNum2) * self::callBesselI($x, 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))))));
+ }
+
+ $y = 2 / $x;
+
+ return exp(-$x) / sqrt($x) *
+ (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))))));
+ }
+
+ private static function besselK1(float $x): float
+ {
+ if ($x <= 2) {
+ $fNum2 = $x * 0.5;
+ $y = ($fNum2 * $fNum2);
+
+ return log($fNum2) * self::callBesselI($x, 1) +
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
+ (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
+ }
+
+ $y = 2 / $x;
+
+ return exp(-$x) / sqrt($x) *
+ (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)))))));
+ }
+
+ private static function besselK2(float $x, int $ord): float
+ {
+ $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;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselY.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselY.php
new file mode 100644
index 0000000..31d9694
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BesselY.php
@@ -0,0 +1,141 @@
+getMessage();
+ }
+
+ if (($ord < 0) || ($x <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ $fBy = self::calculate($x, $ord);
+
+ return (is_nan($fBy)) ? ExcelError::NAN() : $fBy;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ // special cases
+ switch ($ord) {
+ case 0:
+ return self::besselY0($x);
+ case 1:
+ return self::besselY1($x);
+ }
+
+ return self::besselY2($x, $ord);
+ }
+
+ /**
+ * Mollify Phpstan.
+ *
+ * @codeCoverageIgnore
+ */
+ private static function callBesselJ(float $x, int $ord): float
+ {
+ $rslt = BesselJ::BESSELJ($x, $ord);
+ if (!is_float($rslt)) {
+ throw new Exception('Unexpected array or string');
+ }
+
+ return $rslt;
+ }
+
+ private static function besselY0(float $x): float
+ {
+ if ($x < 8.0) {
+ $y = ($x * $x);
+ $ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y *
+ (-86327.92757 + $y * 228.4622733))));
+ $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
+ (47447.26470 + $y * (226.1030244 + $y))));
+
+ return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
+ }
+
+ $z = 8.0 / $x;
+ $y = ($z * $z);
+ $xx = $x - 0.785398164;
+ $ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y *
+ (-0.934945152e-7))));
+
+ return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
+ }
+
+ private static function besselY1(float $x): float
+ {
+ if ($x < 8.0) {
+ $y = ($x * $x);
+ $ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y *
+ (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
+ $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
+ (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+
+ return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
+ }
+
+ $z = 8.0 / $x;
+ $y = $z * $z;
+ $xx = $x - 2.356194491;
+ $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
+ $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
+ (-0.88228987e-6 + $y * 0.105787412e-6)));
+
+ return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
+ }
+
+ private static function besselY2(float $x, int $ord): float
+ {
+ $fTox = 2.0 / $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;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BitWise.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BitWise.php
new file mode 100644
index 0000000..0362649
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/BitWise.php
@@ -0,0 +1,277 @@
+getMessage();
+ }
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
+ }
+
+ /**
+ * BITOR.
+ *
+ * Returns the bitwise OR of two integer values.
+ *
+ * Excel Function:
+ * BITOR(number1, number2)
+ *
+ * @param array|int $number1
+ * Or can be an array of values
+ * @param array|int $number2
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITOR($number1, $number2)
+ {
+ if (is_array($number1) || is_array($number2)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+ }
+
+ try {
+ $number1 = self::validateBitwiseArgument($number1);
+ $number2 = self::validateBitwiseArgument($number2);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
+ }
+
+ /**
+ * BITXOR.
+ *
+ * Returns the bitwise XOR of two integer values.
+ *
+ * Excel Function:
+ * BITXOR(number1, number2)
+ *
+ * @param array|int $number1
+ * Or can be an array of values
+ * @param array|int $number2
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITXOR($number1, $number2)
+ {
+ if (is_array($number1) || is_array($number2)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+ }
+
+ try {
+ $number1 = self::validateBitwiseArgument($number1);
+ $number2 = self::validateBitwiseArgument($number2);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
+ }
+
+ /**
+ * BITLSHIFT.
+ *
+ * Returns the number value shifted left by shift_amount bits.
+ *
+ * Excel Function:
+ * BITLSHIFT(number, shift_amount)
+ *
+ * @param array|int $number
+ * Or can be an array of values
+ * @param array|int $shiftAmount
+ * Or can be an array of values
+ *
+ * @return array|float|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITLSHIFT($number, $shiftAmount)
+ {
+ if (is_array($number) || is_array($shiftAmount)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
+ }
+
+ try {
+ $number = self::validateBitwiseArgument($number);
+ $shiftAmount = self::validateShiftAmount($shiftAmount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = floor($number * (2 ** $shiftAmount));
+ if ($result > 2 ** 48 - 1) {
+ return ExcelError::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * BITRSHIFT.
+ *
+ * Returns the number value shifted right by shift_amount bits.
+ *
+ * Excel Function:
+ * BITRSHIFT(number, shift_amount)
+ *
+ * @param array|int $number
+ * Or can be an array of values
+ * @param array|int $shiftAmount
+ * Or can be an array of values
+ *
+ * @return array|float|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITRSHIFT($number, $shiftAmount)
+ {
+ if (is_array($number) || is_array($shiftAmount)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
+ }
+
+ try {
+ $number = self::validateBitwiseArgument($number);
+ $shiftAmount = self::validateShiftAmount($shiftAmount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = floor($number / (2 ** $shiftAmount));
+ if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
+ return ExcelError::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validate arguments passed to the bitwise functions.
+ *
+ * @param mixed $value
+ *
+ * @return float
+ */
+ private static function validateBitwiseArgument($value)
+ {
+ $value = self::nullFalseTrueToNumber($value);
+
+ if (is_numeric($value)) {
+ $value = (float) $value;
+ if ($value == floor($value)) {
+ if (($value > 2 ** 48 - 1) || ($value < 0)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return floor($value);
+ }
+
+ throw new Exception(ExcelError::NAN());
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Validate arguments passed to the bitwise functions.
+ *
+ * @param mixed $value
+ *
+ * @return int
+ */
+ private static function validateShiftAmount($value)
+ {
+ $value = self::nullFalseTrueToNumber($value);
+
+ if (is_numeric($value)) {
+ if (abs($value) > 53) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return (int) $value;
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @param mixed $number
+ *
+ * @return mixed
+ */
+ private static function nullFalseTrueToNumber(&$number)
+ {
+ if ($number === null) {
+ $number = 0;
+ } elseif (is_bool($number)) {
+ $number = (int) $number;
+ }
+
+ return $number;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Compare.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Compare.php
new file mode 100644
index 0000000..6aaf1fa
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Compare.php
@@ -0,0 +1,82 @@
+getMessage();
+ }
+
+ return (int) (abs($a - $b) < 1.0e-15);
+ }
+
+ /**
+ * GESTEP.
+ *
+ * Excel Function:
+ * GESTEP(number[,step])
+ *
+ * Returns 1 if number >= step; returns 0 (zero) otherwise
+ * Use this function to filter a set of values. For example, by summing several GESTEP
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @param array|float $number the value to test against step
+ * Or can be an array of values
+ * @param null|array|float $step The threshold value. If you omit a value for step, GESTEP uses zero.
+ * Or can be an array of values
+ *
+ * @return array|int|string (string in the event of an error)
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function GESTEP($number, $step = 0.0)
+ {
+ if (is_array($number) || is_array($step)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);
+ }
+
+ try {
+ $number = EngineeringValidations::validateFloat($number);
+ $step = EngineeringValidations::validateFloat($step ?? 0.0);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (int) ($number >= $step);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Complex.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Complex.php
new file mode 100644
index 0000000..691de8b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Complex.php
@@ -0,0 +1,121 @@
+getMessage();
+ }
+
+ if (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) {
+ $complex = new ComplexObject($realNumber, $imaginary, $suffix);
+
+ return (string) $complex;
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * IMAGINARY.
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMAGINARY(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the imaginary
+ * coefficient
+ * Or can be an array of values
+ *
+ * @return array|float|string (string if an error)
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMAGINARY($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return $complex->getImaginary();
+ }
+
+ /**
+ * IMREAL.
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMREAL(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the real coefficient
+ * Or can be an array of values
+ *
+ * @return array|float|string (string if an error)
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMREAL($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return $complex->getReal();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
new file mode 100644
index 0000000..28a27a0
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
@@ -0,0 +1,611 @@
+abs();
+ }
+
+ /**
+ * 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.
+ *
+ * Excel Function:
+ * IMARGUMENT(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the argument theta
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMARGUMENT($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::DIV0();
+ }
+
+ return $complex->argument();
+ }
+
+ /**
+ * IMCONJUGATE.
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCONJUGATE(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the conjugate
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCONJUGATE($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->conjugate();
+ }
+
+ /**
+ * IMCOS.
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOS(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the cosine
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCOS($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->cos();
+ }
+
+ /**
+ * IMCOSH.
+ *
+ * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOSH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCOSH($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->cosh();
+ }
+
+ /**
+ * IMCOT.
+ *
+ * Returns the cotangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOT(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the cotangent
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCOT($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->cot();
+ }
+
+ /**
+ * IMCSC.
+ *
+ * Returns the cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSC(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the cosecant
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCSC($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->csc();
+ }
+
+ /**
+ * IMCSCH.
+ *
+ * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSCH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCSCH($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->csch();
+ }
+
+ /**
+ * IMSIN.
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSIN(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the sine
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSIN($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sin();
+ }
+
+ /**
+ * IMSINH.
+ *
+ * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSINH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic sine
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSINH($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sinh();
+ }
+
+ /**
+ * IMSEC.
+ *
+ * Returns the secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSEC(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the secant
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSEC($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sec();
+ }
+
+ /**
+ * IMSECH.
+ *
+ * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSECH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic secant
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSECH($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sech();
+ }
+
+ /**
+ * IMTAN.
+ *
+ * Returns the tangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMTAN(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the tangent
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMTAN($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->tan();
+ }
+
+ /**
+ * IMSQRT.
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSQRT(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the square root
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSQRT($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ $theta = self::IMARGUMENT($complexNumber);
+ if ($theta === ExcelError::DIV0()) {
+ return '0';
+ }
+
+ return (string) $complex->sqrt();
+ }
+
+ /**
+ * IMLN.
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLN(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the natural logarithm
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMLN($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->ln();
+ }
+
+ /**
+ * IMLOG10.
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG10(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the common logarithm
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMLOG10($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->log10();
+ }
+
+ /**
+ * IMLOG2.
+ *
+ * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG2(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the base-2 logarithm
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMLOG2($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->log2();
+ }
+
+ /**
+ * IMEXP.
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMEXP(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the exponential
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMEXP($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->exp();
+ }
+
+ /**
+ * IMPOWER.
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * Excel Function:
+ * IMPOWER(complexNumber,realNumber)
+ *
+ * @param array|string $complexNumber the complex number you want to raise to a power
+ * Or can be an array of values
+ * @param array|float|int|string $realNumber the power to which you want to raise the complex number
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMPOWER($complexNumber, $realNumber)
+ {
+ if (is_array($complexNumber) || is_array($realNumber)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ if (!is_numeric($realNumber)) {
+ return ExcelError::VALUE();
+ }
+
+ return (string) $complex->pow((float) $realNumber);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
new file mode 100644
index 0000000..e525b4b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
@@ -0,0 +1,134 @@
+divideby(new ComplexObject($complexDivisor));
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+ }
+
+ /**
+ * IMSUB.
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUB(complexNumber1,complexNumber2)
+ *
+ * @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
+ * Or can be an array of values
+ * @param array|string $complexNumber2 the complex number to subtract from complexNumber1
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSUB($complexNumber1, $complexNumber2)
+ {
+ if (is_array($complexNumber1) || is_array($complexNumber2)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
+ }
+
+ try {
+ return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+ }
+
+ /**
+ * IMSUM.
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUM(complexNumber[,complexNumber[,...]])
+ *
+ * @param string ...$complexNumbers Series of complex numbers to add
+ *
+ * @return string
+ */
+ public static function IMSUM(...$complexNumbers)
+ {
+ // Return value
+ $returnValue = new ComplexObject(0.0);
+ $aArgs = Functions::flattenArray($complexNumbers);
+
+ try {
+ // Loop through the arguments
+ foreach ($aArgs as $complex) {
+ $returnValue = $returnValue->add(new ComplexObject($complex));
+ }
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $returnValue;
+ }
+
+ /**
+ * IMPRODUCT.
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMPRODUCT(complexNumber[,complexNumber[,...]])
+ *
+ * @param string ...$complexNumbers Series of complex numbers to multiply
+ *
+ * @return string
+ */
+ public static function IMPRODUCT(...$complexNumbers)
+ {
+ // Return value
+ $returnValue = new ComplexObject(1.0);
+ $aArgs = Functions::flattenArray($complexNumbers);
+
+ try {
+ // Loop through the arguments
+ foreach ($aArgs as $complex) {
+ $returnValue = $returnValue->multiply(new ComplexObject($complex));
+ }
+ } catch (ComplexException $e) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Constants.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Constants.php
new file mode 100644
index 0000000..a926db6
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/Constants.php
@@ -0,0 +1,11 @@
+ 10) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return (int) $places;
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Formats a number base string value with leading zeroes.
+ *
+ * @param string $value The "number" to pad
+ * @param ?int $places The length that we want to pad this value
+ *
+ * @return string The padded "number"
+ */
+ protected static function nbrConversionFormat(string $value, ?int $places): string
+ {
+ if ($places !== null) {
+ if (strlen($value) <= $places) {
+ return substr(str_pad($value, $places, '0', STR_PAD_LEFT), -10);
+ }
+
+ return ExcelError::NAN();
+ }
+
+ return substr($value, -10);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
new file mode 100644
index 0000000..4741f30
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
@@ -0,0 +1,163 @@
+getMessage();
+ }
+
+ if (strlen($value) == 10) {
+ // Two's Complement
+ $value = substr($value, -9);
+
+ return '-' . (512 - bindec($value));
+ }
+
+ return (string) bindec($value);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return a binary value as hex.
+ *
+ * Excel Function:
+ * BIN2HEX(x[,places])
+ *
+ * @param array|string $value The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
+ * If places is negative, BIN2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toHex($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateBinary($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) == 10) {
+ $high2 = substr($value, 0, 2);
+ $low8 = substr($value, 2);
+ $xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
+
+ return $xarr[$high2] . strtoupper(substr('0' . dechex((int) bindec($low8)), -2));
+ }
+ $hexVal = (string) strtoupper(dechex((int) bindec($value)));
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return a binary value as octal.
+ *
+ * Excel Function:
+ * BIN2OCT(x[,places])
+ *
+ * @param array|string $value The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
+ * If places is negative, BIN2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toOctal($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateBinary($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) == 10 && substr($value, 0, 1) === '1') { // Two's Complement
+ return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
+ }
+ $octVal = (string) decoct((int) bindec($value));
+
+ return self::nbrConversionFormat($octVal, $places);
+ }
+
+ protected static function validateBinary(string $value): string
+ {
+ if ((strlen($value) > preg_match_all('/[01]/', $value)) || (strlen($value) > 10)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
new file mode 100644
index 0000000..9b59d39
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
@@ -0,0 +1,213 @@
+ 511, DEC2BIN returns the #NUM! error
+ * value.
+ * If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If DEC2BIN requires more than places characters, it returns the #NUM!
+ * error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If places is zero or negative, DEC2BIN returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toBinary($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = (int) floor((float) $value);
+ if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
+ return ExcelError::NAN();
+ }
+
+ $r = decbin($value);
+ // Two's Complement
+ $r = substr($r, -10);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return a decimal value as hex.
+ *
+ * Excel Function:
+ * DEC2HEX(x[,places])
+ *
+ * @param array|string $value The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2HEX returns a 10-character (40-bit)
+ * hexadecimal number in which the most significant bit is the sign
+ * bit. The remaining 39 bits are magnitude bits. Negative numbers
+ * are represented using two's-complement notation.
+ * If number < -549,755,813,888 or if number > 549,755,813,887,
+ * DEC2HEX returns the #NUM! error value.
+ * If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If DEC2HEX requires more than places characters, it returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If places is zero or negative, DEC2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toHex($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = floor((float) $value);
+ if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
+ return ExcelError::NAN();
+ }
+ $r = strtoupper(dechex((int) $value));
+ $r = self::hex32bit($value, $r);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ public static function hex32bit(float $value, string $hexstr, bool $force = false): string
+ {
+ if (PHP_INT_SIZE === 4 || $force) {
+ if ($value >= 2 ** 32) {
+ $quotient = (int) ($value / (2 ** 32));
+
+ return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr);
+ }
+ if ($value < -(2 ** 32)) {
+ $quotient = 256 - (int) ceil((-$value) / (2 ** 32));
+
+ return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8));
+ }
+ if ($value < 0) {
+ return "FF$hexstr";
+ }
+ }
+
+ return $hexstr;
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return an decimal value as octal.
+ *
+ * Excel Function:
+ * DEC2OCT(x[,places])
+ *
+ * @param array|string $value The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2OCT returns a 10-character (30-bit)
+ * octal number in which the most significant bit is the sign bit.
+ * The remaining 29 bits are magnitude bits. Negative numbers are
+ * represented using two's-complement notation.
+ * If number < -536,870,912 or if number > 536,870,911, DEC2OCT
+ * returns the #NUM! error value.
+ * If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If DEC2OCT requires more than places characters, it returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If places is zero or negative, DEC2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toOctal($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = (int) floor((float) $value);
+ if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
+ return ExcelError::NAN();
+ }
+ $r = decoct($value);
+ $r = substr($r, -10);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ protected static function validateDecimal(string $value): string
+ {
+ if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $value;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
new file mode 100644
index 0000000..55ce209
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
@@ -0,0 +1,175 @@
+getMessage();
+ }
+
+ $dec = self::toDecimal($value);
+
+ return ConvertDecimal::toBinary($dec, $places);
+ }
+
+ /**
+ * toDecimal.
+ *
+ * Return a hex value as decimal.
+ *
+ * Excel Function:
+ * HEX2DEC(x)
+ *
+ * @param array|string $value The hexadecimal number you want to convert. This number cannot
+ * contain more than 10 characters (40 bits). The most significant
+ * bit of number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is not a valid hexadecimal number, HEX2DEC returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toDecimal($value)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateHex($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) > 10) {
+ return ExcelError::NAN();
+ }
+
+ $binX = '';
+ foreach (str_split($value) as $char) {
+ $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
+ }
+ if (strlen($binX) == 40 && $binX[0] == '1') {
+ for ($i = 0; $i < 40; ++$i) {
+ $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
+ }
+
+ return (string) ((bindec($binX) + 1) * -1);
+ }
+
+ return (string) bindec($binX);
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return a hex value as octal.
+ *
+ * Excel Function:
+ * HEX2OCT(x[,places])
+ *
+ * @param array|string $value The hexadecimal number you want to convert. Number cannot
+ * contain more than 10 characters. The most significant bit of
+ * number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is negative, HEX2OCT ignores places and returns a
+ * 10-character octal number.
+ * If number is negative, it cannot be less than FFE0000000, and
+ * if number is positive, it cannot be greater than 1FFFFFFF.
+ * If number is not a valid hexadecimal number, HEX2OCT returns
+ * the #NUM! error value.
+ * If HEX2OCT requires more than places characters, it returns
+ * the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
+ * uses the minimum number of characters necessary. Places is
+ * useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2OCT returns the #VALUE! error
+ * value.
+ * If places is negative, HEX2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toOctal($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateHex($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $decimal = self::toDecimal($value);
+
+ return ConvertDecimal::toOctal($decimal, $places);
+ }
+
+ protected static function validateHex(string $value): string
+ {
+ if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
new file mode 100644
index 0000000..add7aba
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
@@ -0,0 +1,174 @@
+getMessage();
+ }
+
+ return ConvertDecimal::toBinary(self::toDecimal($value), $places);
+ }
+
+ /**
+ * toDecimal.
+ *
+ * Return an octal value as decimal.
+ *
+ * Excel Function:
+ * OCT2DEC(x)
+ *
+ * @param array|string $value The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is not a valid octal number, OCT2DEC returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toDecimal($value)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateOctal($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $binX = '';
+ foreach (str_split($value) as $char) {
+ $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
+ }
+ if (strlen($binX) == 30 && $binX[0] == '1') {
+ for ($i = 0; $i < 30; ++$i) {
+ $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
+ }
+
+ return (string) ((bindec($binX) + 1) * -1);
+ }
+
+ return (string) bindec($binX);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return an octal value as hex.
+ *
+ * Excel Function:
+ * OCT2HEX(x[,places])
+ *
+ * @param array|string $value The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is negative, OCT2HEX ignores places and returns a
+ * 10-character hexadecimal number.
+ * If number is not a valid octal number, OCT2HEX returns the
+ * #NUM! error value.
+ * If OCT2HEX requires more than places characters, it returns
+ * the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
+ * uses the minimum number of characters necessary. Places is useful
+ * for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
+ * If places is negative, OCT2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toHex($value, $places = null)
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateOctal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $hexVal = strtoupper(dechex((int) self::toDecimal($value)));
+ $hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF{$hexVal}" : $hexVal;
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+ protected static function validateOctal(string $value): string
+ {
+ $numDigits = (int) preg_match_all('/[01234567]/', $value);
+ if (strlen($value) > $numDigits || $numDigits > 10) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
new file mode 100644
index 0000000..8541a6c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
@@ -0,0 +1,694 @@
+ ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true],
+ 'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false],
+ 'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
+ 'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
+ 'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
+ 'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false],
+ 'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
+ 'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
+ 'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
+ 'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
+ 'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
+ 'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false],
+ 'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false],
+ 'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+ 'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+ 'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+ // Distance
+ 'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true],
+ 'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
+ 'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
+ 'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false],
+ 'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false],
+ 'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false],
+ 'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
+ 'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false],
+ 'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false],
+ 'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
+ 'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
+ 'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
+ 'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
+ 'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false],
+ 'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
+ // Time
+ 'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false],
+ 'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
+ 'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
+ 'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false],
+ 'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
+ 'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
+ 'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
+ 's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
+ // Pressure
+ 'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
+ 'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
+ 'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
+ 'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
+ 'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
+ 'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true],
+ 'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true],
+ // Force
+ 'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true],
+ 'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
+ 'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
+ 'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
+ 'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true],
+ // Energy
+ 'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true],
+ 'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true],
+ 'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
+ 'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
+ 'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
+ 'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
+ 'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
+ 'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
+ 'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
+ 'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
+ 'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
+ 'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
+ 'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
+ // Power
+ 'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
+ 'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
+ 'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
+ 'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
+ 'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false],
+ // Magnetism
+ 'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
+ 'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
+ // Temperature
+ 'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
+ 'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
+ 'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
+ 'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
+ 'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
+ 'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
+ 'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false],
+ 'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false],
+ // Volume
+ 'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
+ 'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
+ 'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
+ 'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
+ 'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false],
+ 'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
+ 'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
+ 'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false],
+ 'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
+ 'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
+ 'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
+ 'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false],
+ 'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
+ 'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
+ 'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
+ 'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
+ 'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
+ 'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false],
+ 'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false],
+ 'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
+ 'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
+ 'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
+ 'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
+ 'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
+ 'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
+ 'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
+ 'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
+ 'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
+ 'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
+ 'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
+ 'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
+ 'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
+ 'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
+ 'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
+ 'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
+ 'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
+ // Area
+ 'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true],
+ 'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false],
+ 'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
+ 'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
+ 'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
+ 'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true],
+ 'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
+ 'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
+ 'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
+ 'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
+ 'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
+ 'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
+ 'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
+ 'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
+ 'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false],
+ 'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
+ 'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
+ 'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
+ 'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
+ 'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
+ 'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
+ // Information
+ 'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true],
+ 'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true],
+ // Speed
+ 'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
+ 'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
+ 'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
+ 'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
+ 'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false],
+ 'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false],
+ 'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false],
+ ];
+
+ /**
+ * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @var mixed[]
+ */
+ private static $conversionMultipliers = [
+ 'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
+ 'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
+ 'E' => ['multiplier' => 1E18, 'name' => 'exa'],
+ 'P' => ['multiplier' => 1E15, 'name' => 'peta'],
+ 'T' => ['multiplier' => 1E12, 'name' => 'tera'],
+ 'G' => ['multiplier' => 1E9, 'name' => 'giga'],
+ 'M' => ['multiplier' => 1E6, 'name' => 'mega'],
+ 'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
+ 'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
+ 'e' => ['multiplier' => 1E1, 'name' => 'dekao'],
+ 'da' => ['multiplier' => 1E1, 'name' => 'dekao'],
+ 'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
+ 'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
+ 'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
+ 'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
+ 'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
+ 'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
+ 'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
+ 'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
+ 'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
+ 'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
+ ];
+
+ /**
+ * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @var mixed[]
+ */
+ private static $binaryConversionMultipliers = [
+ 'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
+ 'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'],
+ 'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'],
+ 'Pi' => ['multiplier' => 2 ** 50, 'name' => 'pebi'],
+ 'Ti' => ['multiplier' => 2 ** 40, 'name' => 'tebi'],
+ 'Gi' => ['multiplier' => 2 ** 30, 'name' => 'gibi'],
+ 'Mi' => ['multiplier' => 2 ** 20, 'name' => 'mebi'],
+ 'ki' => ['multiplier' => 2 ** 10, 'name' => 'kibi'],
+ ];
+
+ /**
+ * Details of the Units of measure conversion factors, organised by group.
+ *
+ * @var mixed[]
+ */
+ private static $unitConversions = [
+ // Conversion uses gram (g) as an intermediate unit
+ self::CATEGORY_WEIGHT_AND_MASS => [
+ 'g' => 1.0,
+ 'sg' => 6.85217658567918E-05,
+ 'lbm' => 2.20462262184878E-03,
+ 'u' => 6.02214179421676E+23,
+ 'ozm' => 3.52739619495804E-02,
+ 'grain' => 1.54323583529414E+01,
+ 'cwt' => 2.20462262184878E-05,
+ 'shweight' => 2.20462262184878E-05,
+ 'uk_cwt' => 1.96841305522212E-05,
+ 'lcwt' => 1.96841305522212E-05,
+ 'hweight' => 1.96841305522212E-05,
+ 'stone' => 1.57473044417770E-04,
+ 'ton' => 1.10231131092439E-06,
+ 'uk_ton' => 9.84206527611061E-07,
+ 'LTON' => 9.84206527611061E-07,
+ 'brton' => 9.84206527611061E-07,
+ ],
+ // Conversion uses meter (m) as an intermediate unit
+ self::CATEGORY_DISTANCE => [
+ 'm' => 1.0,
+ 'mi' => 6.21371192237334E-04,
+ 'Nmi' => 5.39956803455724E-04,
+ 'in' => 3.93700787401575E+01,
+ 'ft' => 3.28083989501312E+00,
+ 'yd' => 1.09361329833771E+00,
+ 'ang' => 1.0E+10,
+ 'ell' => 8.74890638670166E-01,
+ 'ly' => 1.05700083402462E-16,
+ 'parsec' => 3.24077928966473E-17,
+ 'pc' => 3.24077928966473E-17,
+ 'Pica' => 2.83464566929134E+03,
+ 'Picapt' => 2.83464566929134E+03,
+ 'pica' => 2.36220472440945E+02,
+ 'survey_mi' => 6.21369949494950E-04,
+ ],
+ // Conversion uses second (s) as an intermediate unit
+ self::CATEGORY_TIME => [
+ 'yr' => 3.16880878140289E-08,
+ 'day' => 1.15740740740741E-05,
+ 'd' => 1.15740740740741E-05,
+ 'hr' => 2.77777777777778E-04,
+ 'mn' => 1.66666666666667E-02,
+ 'min' => 1.66666666666667E-02,
+ 'sec' => 1.0,
+ 's' => 1.0,
+ ],
+ // Conversion uses Pascal (Pa) as an intermediate unit
+ self::CATEGORY_PRESSURE => [
+ 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923266716013E-06,
+ 'at' => 9.86923266716013E-06,
+ 'mmHg' => 7.50063755419211E-03,
+ 'psi' => 1.45037737730209E-04,
+ 'Torr' => 7.50061682704170E-03,
+ ],
+ // Conversion uses Newton (N) as an intermediate unit
+ self::CATEGORY_FORCE => [
+ 'N' => 1.0,
+ 'dyn' => 1.0E+5,
+ 'dy' => 1.0E+5,
+ 'lbf' => 2.24808923655339E-01,
+ 'pond' => 1.01971621297793E+02,
+ ],
+ // Conversion uses Joule (J) as an intermediate unit
+ self::CATEGORY_ENERGY => [
+ '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,
+ ],
+ // Conversion uses Horsepower (HP) as an intermediate unit
+ self::CATEGORY_POWER => [
+ 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45699871582270E+02,
+ 'w' => 7.45699871582270E+02,
+ 'PS' => 1.01386966542400E+00,
+ ],
+ // Conversion uses Tesla (T) as an intermediate unit
+ self::CATEGORY_MAGNETISM => [
+ 'T' => 1.0,
+ 'ga' => 10000.0,
+ ],
+ // Conversion uses litre (l) as an intermediate unit
+ self::CATEGORY_VOLUME => [
+ 'l' => 1.0,
+ 'L' => 1.0,
+ 'lt' => 1.0,
+ 'tsp' => 2.02884136211058E+02,
+ 'tspm' => 2.0E+02,
+ 'tbs' => 6.76280454036860E+01,
+ 'oz' => 3.38140227018430E+01,
+ 'cup' => 4.22675283773038E+00,
+ 'pt' => 2.11337641886519E+00,
+ 'us_pt' => 2.11337641886519E+00,
+ 'uk_pt' => 1.75975398639270E+00,
+ 'qt' => 1.05668820943259E+00,
+ 'uk_qt' => 8.79876993196351E-01,
+ 'gal' => 2.64172052358148E-01,
+ 'uk_gal' => 2.19969248299088E-01,
+ 'ang3' => 1.0E+27,
+ 'ang^3' => 1.0E+27,
+ 'barrel' => 6.28981077043211E-03,
+ 'bushel' => 2.83775932584017E-02,
+ 'in3' => 6.10237440947323E+01,
+ 'in^3' => 6.10237440947323E+01,
+ 'ft3' => 3.53146667214886E-02,
+ 'ft^3' => 3.53146667214886E-02,
+ 'ly3' => 1.18093498844171E-51,
+ 'ly^3' => 1.18093498844171E-51,
+ 'm3' => 1.0E-03,
+ 'm^3' => 1.0E-03,
+ 'mi3' => 2.39912758578928E-13,
+ 'mi^3' => 2.39912758578928E-13,
+ 'yd3' => 1.30795061931439E-03,
+ 'yd^3' => 1.30795061931439E-03,
+ 'Nmi3' => 1.57426214685811E-13,
+ 'Nmi^3' => 1.57426214685811E-13,
+ 'Pica3' => 2.27769904358706E+07,
+ 'Pica^3' => 2.27769904358706E+07,
+ 'Picapt3' => 2.27769904358706E+07,
+ 'Picapt^3' => 2.27769904358706E+07,
+ 'GRT' => 3.53146667214886E-04,
+ 'regton' => 3.53146667214886E-04,
+ 'MTON' => 8.82866668037215E-04,
+ ],
+ // Conversion uses hectare (ha) as an intermediate unit
+ self::CATEGORY_AREA => [
+ 'ha' => 1.0,
+ 'uk_acre' => 2.47105381467165E+00,
+ 'us_acre' => 2.47104393046628E+00,
+ 'ang2' => 1.0E+24,
+ 'ang^2' => 1.0E+24,
+ 'ar' => 1.0E+02,
+ 'ft2' => 1.07639104167097E+05,
+ 'ft^2' => 1.07639104167097E+05,
+ 'in2' => 1.55000310000620E+07,
+ 'in^2' => 1.55000310000620E+07,
+ 'ly2' => 1.11725076312873E-28,
+ 'ly^2' => 1.11725076312873E-28,
+ 'm2' => 1.0E+04,
+ 'm^2' => 1.0E+04,
+ 'Morgen' => 4.0E+00,
+ 'mi2' => 3.86102158542446E-03,
+ 'mi^2' => 3.86102158542446E-03,
+ 'Nmi2' => 2.91553349598123E-03,
+ 'Nmi^2' => 2.91553349598123E-03,
+ 'Pica2' => 8.03521607043214E+10,
+ 'Pica^2' => 8.03521607043214E+10,
+ 'Picapt2' => 8.03521607043214E+10,
+ 'Picapt^2' => 8.03521607043214E+10,
+ 'yd2' => 1.19599004630108E+04,
+ 'yd^2' => 1.19599004630108E+04,
+ ],
+ // Conversion uses bit (bit) as an intermediate unit
+ self::CATEGORY_INFORMATION => [
+ 'bit' => 1.0,
+ 'byte' => 0.125,
+ ],
+ // Conversion uses Meters per Second (m/s) as an intermediate unit
+ self::CATEGORY_SPEED => [
+ 'm/s' => 1.0,
+ 'm/sec' => 1.0,
+ 'm/h' => 3.60E+03,
+ 'm/hr' => 3.60E+03,
+ 'mph' => 2.23693629205440E+00,
+ 'admkn' => 1.94260256941567E+00,
+ 'kn' => 1.94384449244060E+00,
+ ],
+ ];
+
+ /**
+ * getConversionGroups
+ * Returns a list of the different conversion groups for UOM conversions.
+ *
+ * @return array
+ */
+ public static function getConversionCategories()
+ {
+ $conversionGroups = [];
+ foreach (self::$conversionUnits as $conversionUnit) {
+ $conversionGroups[] = $conversionUnit['Group'];
+ }
+
+ return array_merge(array_unique($conversionGroups));
+ }
+
+ /**
+ * getConversionGroupUnits
+ * Returns an array of units of measure, for a specified conversion group, or for all groups.
+ *
+ * @param string $category The group whose units of measure you want to retrieve
+ *
+ * @return array
+ */
+ public static function getConversionCategoryUnits($category = null)
+ {
+ $conversionGroups = [];
+ foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
+ if (($category === null) || ($conversionGroup['Group'] == $category)) {
+ $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
+ }
+ }
+
+ return $conversionGroups;
+ }
+
+ /**
+ * getConversionGroupUnitDetails.
+ *
+ * @param string $category The group whose units of measure you want to retrieve
+ *
+ * @return array
+ */
+ public static function getConversionCategoryUnitDetails($category = null)
+ {
+ $conversionGroups = [];
+ foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
+ if (($category === null) || ($conversionGroup['Group'] == $category)) {
+ $conversionGroups[$conversionGroup['Group']][] = [
+ 'unit' => $conversionUnit,
+ 'description' => $conversionGroup['Unit Name'],
+ ];
+ }
+ }
+
+ return $conversionGroups;
+ }
+
+ /**
+ * getConversionMultipliers
+ * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @return mixed[]
+ */
+ public static function getConversionMultipliers()
+ {
+ return self::$conversionMultipliers;
+ }
+
+ /**
+ * getBinaryConversionMultipliers
+ * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
+ *
+ * @return mixed[]
+ */
+ public static function getBinaryConversionMultipliers()
+ {
+ return self::$binaryConversionMultipliers;
+ }
+
+ /**
+ * CONVERT.
+ *
+ * Converts a number from one measurement system to another.
+ * For example, CONVERT can translate a table of distances in miles to a table of distances
+ * in kilometers.
+ *
+ * Excel Function:
+ * CONVERT(value,fromUOM,toUOM)
+ *
+ * @param array|float|int|string $value the value in fromUOM to convert
+ * Or can be an array of values
+ * @param array|string $fromUOM the units for value
+ * Or can be an array of values
+ * @param array|string $toUOM the units for the result
+ * Or can be an array of values
+ *
+ * @return array|float|string Result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function CONVERT($value, $fromUOM, $toUOM)
+ {
+ if (is_array($value) || is_array($fromUOM) || is_array($toUOM)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $fromUOM, $toUOM);
+ }
+
+ if (!is_numeric($value)) {
+ return ExcelError::VALUE();
+ }
+
+ try {
+ [$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
+ [$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
+ } catch (Exception $e) {
+ return ExcelError::NA();
+ }
+
+ if ($fromCategory !== $toCategory) {
+ return ExcelError::NA();
+ }
+
+ // @var float $value
+ $value *= $fromMultiplier;
+
+ if (($fromUOM === $toUOM) && ($fromMultiplier === $toMultiplier)) {
+ // We've already factored $fromMultiplier into the value, so we need
+ // to reverse it again
+ return $value / $fromMultiplier;
+ } elseif ($fromUOM === $toUOM) {
+ return $value / $toMultiplier;
+ } elseif ($fromCategory === self::CATEGORY_TEMPERATURE) {
+ return self::convertTemperature($fromUOM, $toUOM, /** @scrutinizer ignore-type */ $value);
+ }
+
+ $baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]);
+
+ return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
+ }
+
+ private static function getUOMDetails(string $uom)
+ {
+ if (isset(self::$conversionUnits[$uom])) {
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+
+ return [$uom, $unitCategory, 1.0];
+ }
+
+ // Check 1-character standard metric multiplier prefixes
+ $multiplierType = substr($uom, 0, 1);
+ $uom = substr($uom, 1);
+ if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
+ if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
+ throw new Exception('Prefix not allowed for UoM');
+ }
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+
+ return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
+ }
+
+ $multiplierType .= substr($uom, 0, 1);
+ $uom = substr($uom, 1);
+
+ // Check 2-character standard metric multiplier prefixes
+ if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
+ if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
+ throw new Exception('Prefix not allowed for UoM');
+ }
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+
+ return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
+ }
+
+ // Check 2-character binary multiplier prefixes
+ if (isset(self::$conversionUnits[$uom], self::$binaryConversionMultipliers[$multiplierType])) {
+ if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
+ throw new Exception('Prefix not allowed for UoM');
+ }
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+ if ($unitCategory !== 'Information') {
+ throw new Exception('Binary Prefix is only allowed for Information UoM');
+ }
+
+ return [$uom, $unitCategory, self::$binaryConversionMultipliers[$multiplierType]['multiplier']];
+ }
+
+ throw new Exception('UoM Not Found');
+ }
+
+ /**
+ * @param float|int $value
+ *
+ * @return float|int
+ */
+ protected static function convertTemperature(string $fromUOM, string $toUOM, $value)
+ {
+ $fromUOM = self::resolveTemperatureSynonyms($fromUOM);
+ $toUOM = self::resolveTemperatureSynonyms($toUOM);
+
+ if ($fromUOM === $toUOM) {
+ return $value;
+ }
+
+ // Convert to Kelvin
+ switch ($fromUOM) {
+ case 'F':
+ $value = ($value - 32) / 1.8 + 273.15;
+
+ break;
+ case 'C':
+ $value += 273.15;
+
+ break;
+ case 'Rank':
+ $value /= 1.8;
+
+ break;
+ case 'Reau':
+ $value = $value * 1.25 + 273.15;
+
+ break;
+ }
+
+ // Convert from Kelvin
+ switch ($toUOM) {
+ case 'F':
+ $value = ($value - 273.15) * 1.8 + 32.00;
+
+ break;
+ case 'C':
+ $value -= 273.15;
+
+ break;
+ case 'Rank':
+ $value *= 1.8;
+
+ break;
+ case 'Reau':
+ $value = ($value - 273.15) * 0.80000;
+
+ break;
+ }
+
+ return $value;
+ }
+
+ private static function resolveTemperatureSynonyms(string $uom)
+ {
+ switch ($uom) {
+ case 'fah':
+ return 'F';
+ case 'cel':
+ return 'C';
+ case 'kel':
+ return 'K';
+ }
+
+ return $uom;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
new file mode 100644
index 0000000..c0202ea
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
@@ -0,0 +1,33 @@
+ 2.2) {
+ return 1 - ErfC::ERFC($value);
+ }
+ $sum = $term = $value;
+ $xsqr = ($value * $value);
+ $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) > Functions::PRECISION);
+
+ return self::$twoSqrtPi * $sum;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ErfC.php b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ErfC.php
new file mode 100644
index 0000000..eb834b7
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Engineering/ErfC.php
@@ -0,0 +1,77 @@
+ Functions::PRECISION);
+
+ return self::$oneSqrtPi * exp(-$value * $value) * $q2;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Exception.php b/PhpOffice/PhpSpreadsheet/Calculation/Exception.php
old mode 100755
new mode 100644
index fccf0af..a95a452
--- a/PhpOffice/PhpSpreadsheet/Calculation/Exception.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Exception.php
@@ -6,6 +6,8 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class Exception extends PhpSpreadsheetException
{
+ public const CALCULATION_ENGINE_PUSH_TO_STACK = 1;
+
/**
* Error handler callback.
*
@@ -15,7 +17,7 @@ class Exception extends PhpSpreadsheetException
* @param mixed $line
* @param mixed $context
*/
- public static function errorHandlerCallback($code, $string, $file, $line, $context)
+ public static function errorHandlerCallback($code, $string, $file, $line, /** @scrutinizer ignore-unused */ $context): void
{
$e = new self($string, $code);
$e->line = $line;
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/ExceptionHandler.php b/PhpOffice/PhpSpreadsheet/Calculation/ExceptionHandler.php
old mode 100755
new mode 100644
index 41e51d4..6461961
--- a/PhpOffice/PhpSpreadsheet/Calculation/ExceptionHandler.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/ExceptionHandler.php
@@ -9,7 +9,9 @@ class ExceptionHandler
*/
public function __construct()
{
- set_error_handler([Exception::class, 'errorHandlerCallback'], E_ALL);
+ /** @var callable */
+ $callable = [Exception::class, 'errorHandlerCallback'];
+ set_error_handler($callable, E_ALL);
}
/**
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial.php
old mode 100755
new mode 100644
index dde87c1..43e27c7
--- a/PhpOffice/PhpSpreadsheet/Calculation/Financial.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial.php
@@ -2,177 +2,82 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use PhpOffice\PhpSpreadsheet\Shared\Date;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Amortization;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Depreciation;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Dollar;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\InterestRate;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
+use PhpOffice\PhpSpreadsheet\Calculation\Financial\TreasuryBill;
+/**
+ * @deprecated 1.18.0
+ *
+ * @codeCoverageIgnore
+ */
class Financial
{
- const FINANCIAL_MAX_ITERATIONS = 32;
+ const FINANCIAL_MAX_ITERATIONS = 128;
const FINANCIAL_PRECISION = 1.0e-08;
- /**
- * isLastDayOfMonth.
- *
- * Returns a boolean TRUE/FALSE indicating if this date is the last date of the month
- *
- * @param \DateTime $testDate The date for testing
- *
- * @return bool
- */
- private static function isLastDayOfMonth(\DateTime $testDate)
- {
- return $testDate->format('d') == $testDate->format('t');
- }
-
- private static function couponFirstPeriodDate($settlement, $maturity, $frequency, $next)
- {
- $months = 12 / $frequency;
-
- $result = Date::excelToDateTimeObject($maturity);
- $eom = self::isLastDayOfMonth($result);
-
- while ($settlement < Date::PHPToExcel($result)) {
- $result->modify('-' . $months . ' months');
- }
- if ($next) {
- $result->modify('+' . $months . ' months');
- }
-
- if ($eom) {
- $result->modify('-1 day');
- }
-
- return Date::PHPToExcel($result);
- }
-
- private static function isValidFrequency($frequency)
- {
- if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
- return true;
- }
- if ((Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) &&
- (($frequency == 6) || ($frequency == 12))) {
- return true;
- }
-
- return false;
- }
-
- /**
- * daysPerYear.
- *
- * Returns the number of days in a specified year, as defined by the "basis" value
- *
- * @param int|string $year The year against which we're testing
- * @param int|string $basis The type of day count:
- * 0 or omitted US (NASD) 360
- * 1 Actual (365 or 366 in a leap year)
- * 2 360
- * 3 365
- * 4 European 360
- *
- * @return int
- */
- private static function daysPerYear($year, $basis = 0)
- {
- switch ($basis) {
- case 0:
- case 2:
- case 4:
- $daysPerYear = 360;
-
- break;
- case 3:
- $daysPerYear = 365;
-
- break;
- case 1:
- $daysPerYear = (DateTime::isLeapYear($year)) ? 366 : 365;
-
- break;
- default:
- return Functions::NAN();
- }
-
- return $daysPerYear;
- }
-
- 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 [$interest, $principal];
- }
-
/**
* ACCRINT.
*
* Returns the accrued interest for a security that pays periodic interest.
*
* Excel Function:
- * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis])
+ * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis][,calc_method])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the periodic() method in the Financial\Securities\AccruedInterest class instead
+ * @see Securities\AccruedInterest::periodic()
*
* @param mixed $issue the security's issue date
- * @param mixed $firstinterest the security's first interest date
+ * @param mixed $firstInterest the security's first interest date
* @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 float $rate the security's annual coupon rate
- * @param float $par The security's par value.
- * If you omit par, ACCRINT uses $1,000.
- * @param int $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
- * @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
+ * The security settlement date is the date after the issue date
+ * when the security is traded to the buyer.
+ * @param mixed $rate the security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit par, ACCRINT uses $1,000.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $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
+ * @param mixed $calcMethod
+ * If true, use Issue to Settlement
+ * If false, use FirstInterest to Settlement
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
- public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
- {
- $issue = Functions::flattenSingleValue($issue);
- $firstinterest = Functions::flattenSingleValue($firstinterest);
- $settlement = Functions::flattenSingleValue($settlement);
- $rate = Functions::flattenSingleValue($rate);
- $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par);
- $frequency = ($frequency === null) ? 1 : Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($rate)) && (is_numeric($par))) {
- $rate = (float) $rate;
- $par = (float) $par;
- if (($rate <= 0) || ($par <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
-
- return $par * $rate * $daysBetweenIssueAndSettlement;
- }
-
- return Functions::VALUE();
+ public static function ACCRINT(
+ $issue,
+ $firstInterest,
+ $settlement,
+ $rate,
+ $parValue = 1000,
+ $frequency = 1,
+ $basis = 0,
+ $calcMethod = true
+ ) {
+ return Securities\AccruedInterest::periodic(
+ $issue,
+ $firstInterest,
+ $settlement,
+ $rate,
+ $parValue,
+ $frequency,
+ $basis,
+ $calcMethod
+ );
}
/**
@@ -183,47 +88,27 @@ class Financial
* Excel Function:
* ACCRINTM(issue,settlement,rate[,par[,basis]])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the atMaturity() method in the Financial\Securities\AccruedInterest class instead
+ * @see Financial\Securities\AccruedInterest::atMaturity()
*
* @param mixed $issue The security's issue date
* @param mixed $settlement The security's settlement (or maturity) date
- * @param float $rate The security's annual coupon rate
- * @param float $par The security's par value.
- * If you omit par, ACCRINT uses $1,000.
- * @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
+ * @param mixed $rate The security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit par, ACCRINT uses $1,000.
+ * @param mixed $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|string
+ * @return float|string Result, or a string containing an error
*/
- public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
+ public static function ACCRINTM($issue, $settlement, $rate, $parValue = 1000, $basis = 0)
{
- $issue = Functions::flattenSingleValue($issue);
- $settlement = Functions::flattenSingleValue($settlement);
- $rate = Functions::flattenSingleValue($rate);
- $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par);
- $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($rate)) && (is_numeric($par))) {
- $rate = (float) $rate;
- $par = (float) $par;
- if (($rate <= 0) || ($par <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
-
- return $par * $rate * $daysBetweenIssueAndSettlement;
- }
-
- return Functions::VALUE();
+ return Securities\AccruedInterest::atMaturity($issue, $settlement, $rate, $parValue, $basis);
}
/**
@@ -241,7 +126,9 @@ class Financial
* Excel Function:
* AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the AMORDEGRC() method in the Financial\Amortization class instead
+ * @see Financial\Amortization::AMORDEGRC()
*
* @param float $cost The cost of the asset
* @param mixed $purchased Date of the purchase of the asset
@@ -250,63 +137,17 @@ class Financial
* @param float $period The period
* @param float $rate Rate of depreciation
* @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
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
- * @return float
+ * @return float|string (string containing the error type if there is an error)
*/
public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
{
- $cost = Functions::flattenSingleValue($cost);
- $purchased = Functions::flattenSingleValue($purchased);
- $firstPeriod = Functions::flattenSingleValue($firstPeriod);
- $salvage = Functions::flattenSingleValue($salvage);
- $period = floor(Functions::flattenSingleValue($period));
- $rate = Functions::flattenSingleValue($rate);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- // The depreciation coefficients are:
- // Life of assets (1/rate) Depreciation coefficient
- // Less than 3 years 1
- // Between 3 and 4 years 1.5
- // Between 5 and 6 years 2
- // More than 6 years 2.5
- $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 = round(DateTime::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost, 0);
- $cost -= $fNRate;
- $fRest = $cost - $salvage;
-
- for ($n = 0; $n < $period; ++$n) {
- $fNRate = round($rate * $cost, 0);
- $fRest -= $fNRate;
-
- if ($fRest < 0.0) {
- switch ($period - $n) {
- case 0:
- case 1:
- return round($cost * 0.5, 0);
- default:
- return 0.0;
- }
- }
- $cost -= $fNRate;
- }
-
- return $fNRate;
+ return Amortization::AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
}
/**
@@ -319,7 +160,9 @@ class Financial
* Excel Function:
* AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the AMORLINC() method in the Financial\Amortization class instead
+ * @see Financial\Amortization::AMORLINC()
*
* @param float $cost The cost of the asset
* @param mixed $purchased Date of the purchase of the asset
@@ -328,46 +171,17 @@ class Financial
* @param float $period The period
* @param float $rate Rate of depreciation
* @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
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
- * @return float
+ * @return float|string (string containing the error type if there is an error)
*/
public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
{
- $cost = Functions::flattenSingleValue($cost);
- $purchased = Functions::flattenSingleValue($purchased);
- $firstPeriod = Functions::flattenSingleValue($firstPeriod);
- $salvage = Functions::flattenSingleValue($salvage);
- $period = Functions::flattenSingleValue($period);
- $rate = Functions::flattenSingleValue($rate);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- $fOneRate = $cost * $rate;
- $fCostDelta = $cost - $salvage;
- // Note, quirky variation for leap years on the YEARFRAC for this function
- $purchasedYear = DateTime::YEAR($purchased);
- $yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
-
- if (($basis == 1) && ($yearFrac < 1) && (DateTime::isLeapYear($purchasedYear))) {
- $yearFrac *= 365 / 366;
- }
-
- $f0Rate = $yearFrac * $rate * $cost;
- $nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
-
- if ($period == 0) {
- return $f0Rate;
- } elseif ($period <= $nNumOfFullPeriods) {
- return $fOneRate;
- } elseif ($period == ($nNumOfFullPeriods + 1)) {
- return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
- }
-
- return 0.0;
+ return Amortization::AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
}
/**
@@ -378,7 +192,9 @@ class Financial
* Excel Function:
* COUPDAYBS(settlement,maturity,frequency[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the COUPDAYBS() method in the Financial\Coupons class instead
+ * @see Financial\Coupons::COUPDAYBS()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -390,10 +206,6 @@ class Financial
* 1 Annual
* 2 Semi-Annual
* 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
* @param int $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -405,28 +217,7 @@ class Financial
*/
public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
-
- return DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
+ return Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
}
/**
@@ -437,7 +228,9 @@ class Financial
* Excel Function:
* COUPDAYS(settlement,maturity,frequency[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the COUPDAYS() method in the Financial\Coupons class instead
+ * @see Financial\Coupons::COUPDAYS()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -449,10 +242,6 @@ class Financial
* 1 Annual
* 2 Semi-Annual
* 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
* @param int $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -464,43 +253,7 @@ class Financial
*/
public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- switch ($basis) {
- case 3:
- // Actual/365
- return 365 / $frequency;
- case 1:
- // Actual/actual
- if ($frequency == 1) {
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
-
- return $daysPerYear / $frequency;
- }
- $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
- $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
-
- return $next - $prev;
- default:
- // US (NASD) 30/360, Actual/360 or European 30/360
- return 360 / $frequency;
- }
+ return Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
}
/**
@@ -511,7 +264,9 @@ class Financial
* Excel Function:
* COUPDAYSNC(settlement,maturity,frequency[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the COUPDAYSNC() method in the Financial\Coupons class instead
+ * @see Financial\Coupons::COUPDAYSNC()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -523,10 +278,6 @@ class Financial
* 1 Annual
* 2 Semi-Annual
* 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
* @param int $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -538,28 +289,7 @@ class Financial
*/
public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
-
- return DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
+ return Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
}
/**
@@ -570,7 +300,9 @@ class Financial
* Excel Function:
* COUPNCD(settlement,maturity,frequency[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the COUPNCD() method in the Financial\Coupons class instead
+ * @see Financial\Coupons::COUPNCD()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -582,10 +314,6 @@ class Financial
* 1 Annual
* 2 Semi-Annual
* 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
* @param int $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -598,25 +326,7 @@ class Financial
*/
public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- return self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
+ return Coupons::COUPNCD($settlement, $maturity, $frequency, $basis);
}
/**
@@ -628,7 +338,9 @@ class Financial
* Excel Function:
* COUPNUM(settlement,maturity,frequency[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the COUPNUM() method in the Financial\Coupons class instead
+ * @see Financial\Coupons::COUPNUM()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -640,10 +352,6 @@ class Financial
* 1 Annual
* 2 Semi-Annual
* 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
* @param int $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -655,37 +363,7 @@ class Financial
*/
public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis) * $daysPerYear;
-
- switch ($frequency) {
- case 1: // annual payments
- case 2: // half-yearly
- case 4: // quarterly
- case 6: // bimonthly
- case 12: // monthly
- return ceil($daysBetweenSettlementAndMaturity / $daysPerYear * $frequency);
- }
-
- return Functions::VALUE();
+ return Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
}
/**
@@ -696,7 +374,9 @@ class Financial
* Excel Function:
* COUPPCD(settlement,maturity,frequency[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the COUPPCD() method in the Financial\Coupons class instead
+ * @see Financial\Coupons::COUPPCD()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -708,10 +388,6 @@ class Financial
* 1 Annual
* 2 Semi-Annual
* 4 Quarterly
- * If working in Gnumeric Mode, the following frequency options are
- * also available
- * 6 Bimonthly
- * 12 Monthly
* @param int $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -724,25 +400,7 @@ class Financial
*/
public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- return self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
+ return Coupons::COUPPCD($settlement, $maturity, $frequency, $basis);
}
/**
@@ -753,44 +411,25 @@ class Financial
* Excel Function:
* CUMIPMT(rate,nper,pv,start,end[,type])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the interest() method in the Financial\CashFlow\Constant\Periodic\Cumulative class instead
+ * @see Financial\CashFlow\Constant\Periodic\Cumulative::interest()
*
* @param float $rate The Interest rate
* @param int $nper The total number of payment periods
* @param float $pv Present Value
* @param int $start The first period in the calculation.
- * Payment periods are numbered beginning with 1.
+ * Payment periods are numbered beginning with 1.
* @param int $end the last period in the calculation
* @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
*
* @return float|string
*/
public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $start = (int) Functions::flattenSingleValue($start);
- $end = (int) Functions::flattenSingleValue($end);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($start < 1 || $start > $end) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interest = 0;
- for ($per = $start; $per <= $end; ++$per) {
- $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
- }
-
- return $interest;
+ return Financial\CashFlow\Constant\Periodic\Cumulative::interest($rate, $nper, $pv, $start, $end, $type);
}
/**
@@ -801,44 +440,25 @@ class Financial
* Excel Function:
* CUMPRINC(rate,nper,pv,start,end[,type])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the principal() method in the Financial\CashFlow\Constant\Periodic\Cumulative class instead
+ * @see Financial\CashFlow\Constant\Periodic\Cumulative::principal()
*
* @param float $rate The Interest rate
* @param int $nper The total number of payment periods
* @param float $pv Present Value
* @param int $start The first period in the calculation.
- * Payment periods are numbered beginning with 1.
+ * Payment periods are numbered beginning with 1.
* @param int $end the last period in the calculation
* @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
*
* @return float|string
*/
public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $start = (int) Functions::flattenSingleValue($start);
- $end = (int) Functions::flattenSingleValue($end);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($start < 1 || $start > $end) {
- return Functions::VALUE();
- }
-
- // Calculate
- $principal = 0;
- for ($per = $start; $per <= $end; ++$per) {
- $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
- }
-
- return $principal;
+ return Financial\CashFlow\Constant\Periodic\Cumulative::principal($rate, $nper, $pv, $start, $end, $type);
}
/**
@@ -854,7 +474,9 @@ class Financial
* Excel Function:
* DB(cost,salvage,life,period[,month])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the DB() method in the Financial\Depreciation class instead
+ * @see Financial\Depreciation::DB()
*
* @param float $cost Initial cost of the asset
* @param float $salvage Value at the end of the depreciation.
@@ -870,48 +492,7 @@ class Financial
*/
public static function DB($cost, $salvage, $life, $period, $month = 12)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
- $month = Functions::flattenSingleValue($month);
-
- // Validate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
- $cost = (float) $cost;
- $salvage = (float) $salvage;
- $life = (int) $life;
- $period = (int) $period;
- $month = (int) $month;
- if ($cost == 0) {
- return 0.0;
- } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
- return Functions::NAN();
- }
- // 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 (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $depreciation = round($depreciation, 2);
- }
-
- return $depreciation;
- }
-
- return Functions::VALUE();
+ return Depreciation::DB($cost, $salvage, $life, $period, $month);
}
/**
@@ -923,7 +504,9 @@ class Financial
* Excel Function:
* DDB(cost,salvage,life,period[,factor])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the DDB() method in the Financial\Depreciation class instead
+ * @see Financial\Depreciation::DDB()
*
* @param float $cost Initial cost of the asset
* @param float $salvage Value at the end of the depreciation.
@@ -940,40 +523,7 @@ class Financial
*/
public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
- $factor = Functions::flattenSingleValue($factor);
-
- // Validate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
- $cost = (float) $cost;
- $salvage = (float) $salvage;
- $life = (int) $life;
- $period = (int) $period;
- $factor = (float) $factor;
- if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
- return Functions::NAN();
- }
- // 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 (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $depreciation = round($depreciation, 2);
- }
-
- return $depreciation;
- }
-
- return Functions::VALUE();
+ return Depreciation::DDB($cost, $salvage, $life, $period, $factor);
}
/**
@@ -984,7 +534,9 @@ class Financial
* Excel Function:
* DISC(settlement,maturity,price,redemption[,basis])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the discount() method in the Financial\Securities\Rates class instead
+ * @see Financial\Securities\Rates::discount()
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
@@ -1004,30 +556,7 @@ class Financial
*/
public static function DISC($settlement, $maturity, $price, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- $price = (float) $price;
- $redemption = (float) $redemption;
- $basis = (int) $basis;
- if (($price <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
- }
-
- return Functions::VALUE();
+ return Financial\Securities\Rates::discount($settlement, $maturity, $price, $redemption, $basis);
}
/**
@@ -1040,32 +569,18 @@ class Financial
* Excel Function:
* DOLLARDE(fractional_dollar,fraction)
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the decimal() method in the Financial\Dollar class instead
+ * @see Financial\Dollar::decimal()
*
- * @param float $fractional_dollar Fractional Dollar
- * @param int $fraction Fraction
+ * @param array|float $fractional_dollar Fractional Dollar
+ * @param array|int $fraction Fraction
*
- * @return float|string
+ * @return array|float|string
*/
public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
{
- $fractional_dollar = Functions::flattenSingleValue($fractional_dollar);
- $fraction = (int) Functions::flattenSingleValue($fraction);
-
- // Validate parameters
- if ($fractional_dollar === null || $fraction < 0) {
- return Functions::NAN();
- }
- if ($fraction == 0) {
- return Functions::DIV0();
- }
-
- $dollars = floor($fractional_dollar);
- $cents = fmod($fractional_dollar, 1);
- $cents /= $fraction;
- $cents *= pow(10, ceil(log10($fraction)));
-
- return $dollars + $cents;
+ return Dollar::decimal($fractional_dollar, $fraction);
}
/**
@@ -1078,32 +593,18 @@ class Financial
* Excel Function:
* DOLLARFR(decimal_dollar,fraction)
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the fractional() method in the Financial\Dollar class instead
+ * @see Financial\Dollar::fractional()
*
- * @param float $decimal_dollar Decimal Dollar
- * @param int $fraction Fraction
+ * @param array|float $decimal_dollar Decimal Dollar
+ * @param array|int $fraction Fraction
*
- * @return float|string
+ * @return array|float|string
*/
public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
{
- $decimal_dollar = Functions::flattenSingleValue($decimal_dollar);
- $fraction = (int) Functions::flattenSingleValue($fraction);
-
- // Validate parameters
- if ($decimal_dollar === null || $fraction < 0) {
- return Functions::NAN();
- }
- if ($fraction == 0) {
- return Functions::DIV0();
- }
-
- $dollars = floor($decimal_dollar);
- $cents = fmod($decimal_dollar, 1);
- $cents *= $fraction;
- $cents *= pow(10, -ceil(log10($fraction)));
-
- return $dollars + $cents;
+ return Dollar::fractional($decimal_dollar, $fraction);
}
/**
@@ -1115,24 +616,18 @@ class Financial
* Excel Function:
* EFFECT(nominal_rate,npery)
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the effective() method in the Financial\InterestRate class instead
+ * @see Financial\InterestRate::effective()
*
- * @param float $nominal_rate Nominal interest rate
- * @param int $npery Number of compounding payments per year
+ * @param float $nominalRate Nominal interest rate
+ * @param int $periodsPerYear Number of compounding payments per year
*
* @return float|string
*/
- public static function EFFECT($nominal_rate = 0, $npery = 0)
+ public static function EFFECT($nominalRate = 0, $periodsPerYear = 0)
{
- $nominal_rate = Functions::flattenSingleValue($nominal_rate);
- $npery = (int) Functions::flattenSingleValue($npery);
-
- // Validate parameters
- if ($nominal_rate <= 0 || $npery < 1) {
- return Functions::NAN();
- }
-
- return pow((1 + $nominal_rate / $npery), $npery) - 1;
+ return Financial\InterestRate::effective($nominalRate, $periodsPerYear);
}
/**
@@ -1143,7 +638,9 @@ class Financial
* Excel Function:
* FV(rate,nper,pmt[,pv[,type]])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the futureValue() method in the Financial\CashFlow\Constant\Periodic class instead
+ * @see Financial\CashFlow\Constant\Periodic::futureValue()
*
* @param float $rate The interest rate per period
* @param int $nper Total number of payment periods in an annuity
@@ -1160,23 +657,7 @@ class Financial
*/
public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
- }
-
- return -$pv - $pmt * $nper;
+ return Financial\CashFlow\Constant\Periodic::futureValue($rate, $nper, $pmt, $pv, $type);
}
/**
@@ -1188,21 +669,18 @@ class Financial
* Excel Function:
* FVSCHEDULE(principal,schedule)
*
+ * @deprecated 1.18.0
+ * Use the futureValue() method in the Financial\CashFlow\Single class instead
+ * @see Financial\CashFlow\Single::futureValue()
+ *
* @param float $principal the present value
* @param float[] $schedule an array of interest rates to apply
*
- * @return float
+ * @return float|string
*/
public static function FVSCHEDULE($principal, $schedule)
{
- $principal = Functions::flattenSingleValue($principal);
- $schedule = Functions::flattenArray($schedule);
-
- foreach ($schedule as $rate) {
- $principal *= 1 + $rate;
- }
-
- return $principal;
+ return Financial\CashFlow\Single::futureValue($principal, $schedule);
}
/**
@@ -1213,57 +691,44 @@ class Financial
* Excel Function:
* INTRATE(settlement,maturity,investment,redemption[,basis])
*
+ * @deprecated 1.18.0
+ * Use the interest() method in the Financial\Securities\Rates class instead
+ * @see Financial\Securities\Rates::interest()
+ *
* @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.
+ * 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.
+ * 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
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
* @return float|string
*/
public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $investment = Functions::flattenSingleValue($investment);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- $investment = (float) $investment;
- $redemption = (float) $redemption;
- $basis = (int) $basis;
- if (($investment <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Financial\Securities\Rates::interest($settlement, $maturity, $investment, $redemption, $basis);
}
/**
* IPMT.
*
- * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
*
* Excel Function:
* IPMT(rate,per,nper,pv[,fv][,type])
*
+ * @deprecated 1.18.0
+ * Use the payment() method in the Financial\CashFlow\Constant\Periodic\Interest class instead
+ * @see Financial\CashFlow\Constant\Periodic\Interest::payment()
+ *
* @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
@@ -1275,25 +740,7 @@ class Financial
*/
public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $per = (int) Functions::flattenSingleValue($per);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($per <= 0 || $per > $nper) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
-
- return $interestAndPrincipal[0];
+ return Financial\CashFlow\Constant\Periodic\Interest::payment($rate, $per, $nper, $pv, $fv, $type);
}
/**
@@ -1308,63 +755,21 @@ class Financial
* Excel Function:
* IRR(values[,guess])
*
- * @param float[] $values An array or a reference to cells that contain numbers for which you want
+ * @deprecated 1.18.0
+ * Use the rate() method in the Financial\CashFlow\Variable\Periodic class instead
+ * @see Financial\CashFlow\Variable\Periodic::rate()
+ *
+ * @param mixed $values An array or a reference to cells that contain numbers for which you want
* to calculate the internal rate of return.
* Values must contain at least one positive value and one negative value to
* calculate the internal rate of return.
- * @param float $guess A number that you guess is close to the result of IRR
+ * @param mixed $guess A number that you guess is close to the result of IRR
*
* @return float|string
*/
public static function IRR($values, $guess = 0.1)
{
- if (!is_array($values)) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $guess = Functions::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 < self::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 Functions::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 < self::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) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
- return $x_mid;
- }
- }
-
- return Functions::VALUE();
+ return Financial\CashFlow\Variable\Periodic::rate($values, $guess);
}
/**
@@ -1373,7 +778,11 @@ class Financial
* 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)
+ * =ISPMT(interest_rate, period, number_payments, pv)
+ *
+ * @deprecated 1.18.0
+ * Use the schedulePayment() method in the Financial\CashFlow\Constant\Periodic\Interest class instead
+ * @see Financial\CashFlow\Constant\Periodic\Interest::schedulePayment()
*
* interest_rate is the interest rate for the investment
*
@@ -1381,32 +790,15 @@ class Financial
*
* number_payments is the number of payments for the annuity
*
- * PV is the loan amount or present value of the payments
+ * pv is the loan amount or present value of the payments
+ *
+ * @param array $args
+ *
+ * @return float|string
*/
public static function ISPMT(...$args)
{
- // Return value
- $returnValue = 0;
-
- // Get the parameters
- $aArgs = Functions::flattenArray($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;
+ return Financial\CashFlow\Constant\Periodic\Interest::schedulePayment(...$args);
}
/**
@@ -1418,44 +810,21 @@ class Financial
* Excel Function:
* MIRR(values,finance_rate, reinvestment_rate)
*
- * @param float[] $values An array or a reference to cells that contain a series of payments and
- * income occurring at regular intervals.
- * Payments are negative value, income is positive values.
- * @param float $finance_rate The interest rate you pay on the money used in the cash flows
- * @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
+ * @deprecated 1.18.0
+ * Use the modifiedRate() method in the Financial\CashFlow\Variable\Periodic class instead
+ * @see Financial\CashFlow\Variable\Periodic::modifiedRate()
*
- * @return float|string
+ * @param mixed $values An array or a reference to cells that contain a series of payments and
+ * income occurring at regular intervals.
+ * Payments are negative value, income is positive values.
+ * @param mixed $finance_rate The interest rate you pay on the money used in the cash flows
+ * @param mixed $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
+ *
+ * @return float|string Result, or a string containing an error
*/
public static function MIRR($values, $finance_rate, $reinvestment_rate)
{
- if (!is_array($values)) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $finance_rate = Functions::flattenSingleValue($finance_rate);
- $reinvestment_rate = Functions::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 Functions::VALUE();
- }
-
- $mirr = pow((-$npv_pos * pow($rr, $n))
- / ($npv_neg * ($rr)), (1.0 / ($n - 1))) - 1.0;
-
- return is_finite($mirr) ? $mirr : Functions::VALUE();
+ return Financial\CashFlow\Variable\Periodic::modifiedRate($values, $finance_rate, $reinvestment_rate);
}
/**
@@ -1463,23 +832,21 @@ class Financial
*
* 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
+ * Excel Function:
+ * NOMINAL(effect_rate, npery)
*
- * @return float|string
+ * @deprecated 1.18.0
+ * Use the nominal() method in the Financial\InterestRate class instead
+ * @see Financial\InterestRate::nominal()
+ *
+ * @param float $effectiveRate Effective interest rate
+ * @param int $periodsPerYear Number of compounding payments per year
+ *
+ * @return float|string Result, or a string containing an error
*/
- public static function NOMINAL($effect_rate = 0, $npery = 0)
+ public static function NOMINAL($effectiveRate = 0, $periodsPerYear = 0)
{
- $effect_rate = Functions::flattenSingleValue($effect_rate);
- $npery = (int) Functions::flattenSingleValue($npery);
-
- // Validate parameters
- if ($effect_rate <= 0 || $npery < 1) {
- return Functions::NAN();
- }
-
- // Calculate
- return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
+ return InterestRate::nominal($effectiveRate, $periodsPerYear);
}
/**
@@ -1487,40 +854,21 @@ class Financial
*
* Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
*
+ * @deprecated 1.18.0
+ * Use the periods() method in the Financial\CashFlow\Constant\Periodic class instead
+ * @see Financial\CashFlow\Constant\Periodic::periods()
+ *
* @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|string
+ * @return float|string Result, or a string containing an error
*/
public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- if ($pmt == 0 && $pv == 0) {
- return Functions::NAN();
- }
-
- return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
- }
- if ($pmt == 0) {
- return Functions::NAN();
- }
-
- return (-$pv - $fv) / $pmt;
+ return Financial\CashFlow\Constant\Periodic::periods($rate, $pmt, $pv, $fv, $type);
}
/**
@@ -1528,28 +876,17 @@ class Financial
*
* Returns the Net Present Value of a cash flow series given a discount rate.
*
+ * @deprecated 1.18.0
+ * Use the presentValue() method in the Financial\CashFlow\Variable\Periodic class instead
+ * @see Financial\CashFlow\Variable\Periodic::presentValue()
+ *
+ * @param array $args
+ *
* @return float
*/
public static function NPV(...$args)
{
- // Return value
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $rate = array_shift($aArgs);
- $countArgs = count($aArgs);
- for ($i = 1; $i <= $countArgs; ++$i) {
- // Is it a numeric value?
- if (is_numeric($aArgs[$i - 1])) {
- $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
- }
- }
-
- // Return
- return $returnValue;
+ return Financial\CashFlow\Variable\Periodic::presentValue(...$args);
}
/**
@@ -1557,26 +894,19 @@ class Financial
*
* Calculates the number of periods required for an investment to reach a specified value.
*
+ * @deprecated 1.18.0
+ * Use the periods() method in the Financial\CashFlow\Single class instead
+ * @see Financial\CashFlow\Single::periods()
+ *
* @param float $rate Interest rate per period
* @param float $pv Present Value
* @param float $fv Future Value
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function PDURATION($rate = 0, $pv = 0, $fv = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
-
- // Validate parameters
- if (!is_numeric($rate) || !is_numeric($pv) || !is_numeric($fv)) {
- return Functions::VALUE();
- } elseif ($rate <= 0.0 || $pv <= 0.0 || $fv <= 0.0) {
- return Functions::NAN();
- }
-
- return (log($fv) - log($pv)) / log(1 + $rate);
+ return Financial\CashFlow\Single::periods($rate, $pv, $fv);
}
/**
@@ -1584,39 +914,32 @@ class Financial
*
* Returns the constant payment (annuity) for a cash flow with a constant interest rate.
*
+ * @deprecated 1.18.0
+ * Use the annuity() method in the Financial\CashFlow\Constant\Periodic\Payments class instead
+ * @see Financial\CashFlow\Constant\Periodic\Payments::annuity()
+ *
* @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
+ * @return float|string Result, or a string containing an error
*/
public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
- }
-
- return (-$pv - $fv) / $nper;
+ return Financial\CashFlow\Constant\Periodic\Payments::annuity($rate, $nper, $pv, $fv, $type);
}
/**
* PPMT.
*
- * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
+ *
+ * @deprecated 1.18.0
+ * Use the interestPayment() method in the Financial\CashFlow\Constant\Periodic\Payments class instead
+ * @see Financial\CashFlow\Constant\Periodic\Payments::interestPayment()
*
* @param float $rate Interest rate per period
* @param int $per Period for which we want to find the interest
@@ -1625,70 +948,46 @@ class Financial
* @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
+ * @return float|string Result, or a string containing an error
*/
public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $per = (int) Functions::flattenSingleValue($per);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($per <= 0 || $per > $nper) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
-
- return $interestAndPrincipal[1];
+ return Financial\CashFlow\Constant\Periodic\Payments::interestPayment($rate, $per, $nper, $pv, $fv, $type);
}
+ /**
+ * PRICE.
+ *
+ * Returns the price per $100 face value of a security that pays periodic interest.
+ *
+ * @deprecated 1.18.0
+ * Use the price() method in the Financial\Securities\Price class instead
+ * @see Financial\Securities\Price::price()
+ *
+ * @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 float $rate the security's annual coupon rate
+ * @param float $yield the security's annual yield
+ * @param float $redemption The number of coupon payments per year.
+ * For annual payments, frequency = 1;
+ * for semiannual, frequency = 2;
+ * for quarterly, frequency = 4.
+ * @param int $frequency
+ * @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|string Result, or a string containing an error
+ */
public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $rate = (float) Functions::flattenSingleValue($rate);
- $yield = (float) Functions::flattenSingleValue($yield);
- $redemption = (float) Functions::flattenSingleValue($redemption);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (($settlement > $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))) {
- return Functions::NAN();
- }
-
- $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;
+ return Securities\Price::price($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
}
/**
@@ -1696,10 +995,15 @@ class Financial
*
* Returns the price per $100 face value of a discounted security.
*
+ * @deprecated 1.18.0
+ * Use the priceDiscounted() method in the Financial\Securities\Price class instead
+ * @see Financial\Securities\Price::priceDiscounted()
+ *
* @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.
+ * 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.
+ * 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.
@@ -1709,31 +1013,11 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = (float) Functions::flattenSingleValue($discount);
- $redemption = (float) Functions::flattenSingleValue($redemption);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- if (($discount <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Securities\Price::priceDiscounted($settlement, $maturity, $discount, $redemption, $basis);
}
/**
@@ -1741,10 +1025,15 @@ class Financial
*
* Returns the price per $100 face value of a security that pays interest at maturity.
*
+ * @deprecated 1.18.0
+ * Use the priceAtMaturity() method in the Financial\Securities\Price class instead
+ * @see Financial\Securities\Price::priceAtMaturity()
+ *
* @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.
+ * 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.
+ * 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
@@ -1755,51 +1044,11 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $issue = Functions::flattenSingleValue($issue);
- $rate = Functions::flattenSingleValue($rate);
- $yield = Functions::flattenSingleValue($yield);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($rate) && is_numeric($yield)) {
- if (($rate <= 0) || ($yield <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
- $daysBetweenIssueAndSettlement *= $daysPerYear;
- $daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
- if (!is_numeric($daysBetweenIssueAndMaturity)) {
- // return date error
- return $daysBetweenIssueAndMaturity;
- }
- $daysBetweenIssueAndMaturity *= $daysPerYear;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
- (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
- (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
- }
-
- return Functions::VALUE();
+ return Securities\Price::priceAtMaturity($settlement, $maturity, $issue, $rate, $yield, $basis);
}
/**
@@ -1807,33 +1056,21 @@ class Financial
*
* Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
*
+ * @deprecated 1.18.0
+ * Use the presentValue() method in the Financial\CashFlow\Constant\Periodic class instead
+ * @see Financial\CashFlow\Constant\Periodic::presentValue()
+ *
* @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
+ * @return float|string Result, or a string containing an error
*/
public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
{
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
- }
-
- return -$fv - $pmt * $nper;
+ return Financial\CashFlow\Constant\Periodic::presentValue($rate, $nper, $pmt, $fv, $type);
}
/**
@@ -1847,113 +1084,61 @@ class Financial
* Excel Function:
* RATE(nper,pmt,pv[,fv[,type[,guess]]])
*
- * @category Financial Functions
+ * @deprecated 1.18.0
+ * Use the rate() method in the Financial\CashFlow\Constant\Periodic\Interest class instead
+ * @see Financial\CashFlow\Constant\Periodic\Interest::rate()
*
- * @param float $nper The total number of payment periods in an annuity
- * @param float $pmt The payment made each period and cannot change over the life
+ * @param mixed $nper The total number of payment periods in an annuity
+ * @param mixed $pmt The payment made each period and cannot change over the life
* of the annuity.
* Typically, pmt includes principal and interest but no other
* fees or taxes.
- * @param float $pv The present value - the total amount that a series of future
+ * @param mixed $pv The present value - the total amount that a series of future
* payments is worth now
- * @param float $fv The future value, or a cash balance you want to attain after
+ * @param mixed $fv The future value, or a cash balance you want to attain after
* the last payment is made. If fv is omitted, it is assumed
* to be 0 (the future value of a loan, for example, is 0).
- * @param int $type A number 0 or 1 and indicates when payments are due:
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
* 0 or omitted At the end of the period.
* 1 At the beginning of the period.
- * @param float $guess Your guess for what the rate will be.
+ * @param mixed $guess Your guess for what the rate will be.
* If you omit guess, it is assumed to be 10 percent.
*
- * @return float
+ * @return float|string
*/
public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
{
- $nper = (int) Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $fv = ($fv === null) ? 0.0 : Functions::flattenSingleValue($fv);
- $type = ($type === null) ? 0 : (int) Functions::flattenSingleValue($type);
- $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
-
- $rate = $guess;
- if (abs($rate) < self::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) > self::FINANCIAL_PRECISION) && ($i < self::FINANCIAL_MAX_ITERATIONS)) {
- $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
- $x0 = $x1;
- $x1 = $rate;
- if (($nper * abs($pmt)) > ($pv - $fv)) {
- $x1 = abs($x1);
- }
- if (abs($rate) < self::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;
+ return Financial\CashFlow\Constant\Periodic\Interest::rate($nper, $pmt, $pv, $fv, $type, $guess);
}
/**
* RECEIVED.
*
- * Returns the price per $100 face value of a discounted security.
+ * Returns the amount received at maturity for a fully invested Security.
+ *
+ * @deprecated 1.18.0
+ * Use the received() method in the Financial\Securities\Price class instead
+ * @see Financial\Securities\Price::received()
*
* @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.
+ * 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
+ * The maturity date is the date when the security expires.
+ * @param mixed $investment The amount invested in the security
+ * @param mixed $discount The security's discount rate
+ * @param mixed $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
+ * @return float|string Result, or a string containing an error
*/
public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $investment = (float) Functions::flattenSingleValue($investment);
- $discount = (float) Functions::flattenSingleValue($discount);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
- if (($investment <= 0) || ($discount <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
- }
-
- return Functions::VALUE();
+ return Financial\Securities\Price::received($settlement, $maturity, $investment, $discount, $basis);
}
/**
@@ -1961,26 +1146,19 @@ class Financial
*
* Calculates the interest rate required for an investment to grow to a specified future value .
*
+ * @deprecated 1.18.0
+ * Use the interestRate() method in the Financial\CashFlow\Single class instead
+ * @see Financial\CashFlow\Single::interestRate()
+ *
* @param float $nper The number of periods over which the investment is made
* @param float $pv Present Value
* @param float $fv Future Value
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function RRI($nper = 0, $pv = 0, $fv = 0)
{
- $nper = Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
-
- // Validate parameters
- if (!is_numeric($nper) || !is_numeric($pv) || !is_numeric($fv)) {
- return Functions::VALUE();
- } elseif ($nper <= 0.0 || $pv <= 0.0 || $fv < 0.0) {
- return Functions::NAN();
- }
-
- return pow($fv / $pv, 1 / $nper) - 1;
+ return Financial\CashFlow\Single::interestRate($nper, $pv, $fv);
}
/**
@@ -1988,28 +1166,19 @@ class Financial
*
* Returns the straight-line depreciation of an asset for one period
*
+ * @deprecated 1.18.0
+ * Use the SLN() method in the Financial\Depreciation class instead
+ * @see Financial\Depreciation::SLN()
+ *
* @param mixed $cost Initial cost of the asset
* @param mixed $salvage Value at the end of the depreciation
* @param mixed $life Number of periods over which the asset is depreciated
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function SLN($cost, $salvage, $life)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
-
- // Calculate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
- if ($life < 0) {
- return Functions::NAN();
- }
-
- return ($cost - $salvage) / $life;
- }
-
- return Functions::VALUE();
+ return Depreciation::SLN($cost, $salvage, $life);
}
/**
@@ -2017,30 +1186,20 @@ class Financial
*
* Returns the sum-of-years' digits depreciation of an asset for a specified period.
*
+ * @deprecated 1.18.0
+ * Use the SYD() method in the Financial\Depreciation class instead
+ * @see Financial\Depreciation::SYD()
+ *
* @param mixed $cost Initial cost of the asset
* @param mixed $salvage Value at the end of the depreciation
* @param mixed $life Number of periods over which the asset is depreciated
* @param mixed $period Period
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function SYD($cost, $salvage, $life, $period)
{
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
-
- // Calculate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
- if (($life < 1) || ($period > $life)) {
- return Functions::NAN();
- }
-
- return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
- }
-
- return Functions::VALUE();
+ return Depreciation::SYD($cost, $salvage, $life, $period);
}
/**
@@ -2048,93 +1207,45 @@ class Financial
*
* Returns the bond-equivalent yield for a Treasury bill.
*
+ * @deprecated 1.18.0
+ * Use the bondEquivalentYield() method in the Financial\TreasuryBill class instead
+ * @see Financial\TreasuryBill::bondEquivalentYield()
+ *
* @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.
+ * 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
+ * @return float|string Result, or a string containing an error
*/
public static function TBILLEQ($settlement, $maturity, $discount)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = Functions::flattenSingleValue($discount);
-
- // Use TBILLPRICE for validation
- $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
- if (is_string($testValue)) {
- return $testValue;
- }
-
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ return TreasuryBill::bondEquivalentYield($settlement, $maturity, $discount);
}
/**
* TBILLPRICE.
*
- * Returns the yield for a Treasury bill.
+ * Returns the price per $100 face value for a Treasury bill.
+ *
+ * @deprecated 1.18.0
+ * Use the price() method in the Financial\TreasuryBill class instead
+ * @see Financial\TreasuryBill::price()
*
* @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.
+ * 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
+ * @return float|string Result, or a string containing an error
*/
public static function TBILLPRICE($settlement, $maturity, $discount)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = Functions::flattenSingleValue($discount);
-
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- // Validate
- if (is_numeric($discount)) {
- if ($discount <= 0) {
- return Functions::NAN();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- if ($daysBetweenSettlementAndMaturity > 360) {
- return Functions::NAN();
- }
-
- $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
- if ($price <= 0) {
- return Functions::NAN();
- }
-
- return $price;
- }
-
- return Functions::VALUE();
+ return TreasuryBill::price($settlement, $maturity, $discount);
}
/**
@@ -2142,99 +1253,48 @@ class Financial
*
* Returns the yield for a Treasury bill.
*
+ * @deprecated 1.18.0
+ * Use the yield() method in the Financial\TreasuryBill class instead
+ * @see Financial\TreasuryBill::yield()
+ *
* @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.
+ * 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
+ * @return float|mixed|string
*/
public static function TBILLYIELD($settlement, $maturity, $price)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
-
- // Validate
- if (is_numeric($price)) {
- if ($price <= 0) {
- return Functions::NAN();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- if ($daysBetweenSettlementAndMaturity > 360) {
- return Functions::NAN();
- }
-
- return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return TreasuryBill::yield($settlement, $maturity, $price);
}
+ /**
+ * XIRR.
+ *
+ * Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic.
+ *
+ * Excel Function:
+ * =XIRR(values,dates,guess)
+ *
+ * @deprecated 1.18.0
+ * Use the rate() method in the Financial\CashFlow\Variable\NonPeriodic class instead
+ * @see Financial\CashFlow\Variable\NonPeriodic::rate()
+ *
+ * @param float[] $values A series of cash flow payments
+ * The series of values must contain at least one positive value & one negative value
+ * @param mixed[] $dates A series of payment dates
+ * The first payment date indicates the beginning of the schedule of payments
+ * All other dates must be later than this date, but they may occur in any order
+ * @param float $guess An optional guess at the expected answer
+ *
+ * @return float|mixed|string
+ */
public static function XIRR($values, $dates, $guess = 0.1)
{
- if ((!is_array($values)) && (!is_array($dates))) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $dates = Functions::flattenArray($dates);
- $guess = Functions::flattenSingleValue($guess);
- if (count($values) != count($dates)) {
- return Functions::NAN();
- }
-
- // 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 < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- if (($f1 * $f2) < 0.0) {
- break;
- } elseif (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 Functions::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 < self::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) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
- return $x_mid;
- }
- }
-
- return Functions::VALUE();
+ return Financial\CashFlow\Variable\NonPeriodic::rate($values, $dates, $guess);
}
/**
@@ -2246,45 +1306,26 @@ class Financial
* Excel Function:
* =XNPV(rate,values,dates)
*
- * @param float $rate the discount rate to apply to the cash flows
- * @param array of float $values A series of cash flows that corresponds to a schedule of payments in dates.
- * The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
- * If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
- * The series of values must contain at least one positive value and one negative value.
- * @param array of mixed $dates A schedule of payment dates that corresponds to the cash flow payments.
- * The first payment date indicates the beginning of the schedule of payments.
- * All other dates must be later than this date, but they may occur in any order.
+ * @deprecated 1.18.0
+ * Use the presentValue() method in the Financial\CashFlow\Variable\NonPeriodic class instead
+ * @see Financial\CashFlow\Variable\NonPeriodic::presentValue()
*
- * @return float
+ * @param float $rate the discount rate to apply to the cash flows
+ * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
+ * The first payment is optional and corresponds to a cost or payment that occurs
+ * at the beginning of the investment.
+ * If the first value is a cost or payment, it must be a negative value.
+ * All succeeding payments are discounted based on a 365-day year.
+ * The series of values must contain at least one positive value and one negative value.
+ * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
+ * The first payment date indicates the beginning of the schedule of payments.
+ * All other dates must be later than this date, but they may occur in any order.
+ *
+ * @return float|mixed|string
*/
public static function XNPV($rate, $values, $dates)
{
- $rate = Functions::flattenSingleValue($rate);
- if (!is_numeric($rate)) {
- return Functions::VALUE();
- }
- if ((!is_array($values)) || (!is_array($dates))) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $dates = Functions::flattenArray($dates);
- $valCount = count($values);
- if ($valCount != count($dates)) {
- return Functions::NAN();
- }
- if ((min($values) > 0) || (max($values) < 0)) {
- return Functions::VALUE();
- }
-
- $xnpv = 0.0;
- for ($i = 0; $i < $valCount; ++$i) {
- if (!is_numeric($values[$i])) {
- return Functions::VALUE();
- }
- $xnpv += $values[$i] / pow(1 + $rate, DateTime::DATEDIF($dates[0], $dates[$i], 'd') / 365);
- }
-
- return (is_finite($xnpv)) ? $xnpv : Functions::VALUE();
+ return Financial\CashFlow\Variable\NonPeriodic::presentValue($rate, $values, $dates);
}
/**
@@ -2292,10 +1333,15 @@ class Financial
*
* Returns the annual yield of a security that pays interest at maturity.
*
+ * @deprecated 1.18.0
+ * Use the yieldDiscounted() method in the Financial\Securities\Yields class instead
+ * @see Financial\Securities\Yields::yieldDiscounted()
+ *
* @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.
+ * 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.
+ * 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.
@@ -2305,36 +1351,11 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($price) && is_numeric($redemption)) {
- if (($price <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Securities\Yields::yieldDiscounted($settlement, $maturity, $price, $redemption, $basis);
}
/**
@@ -2342,64 +1363,29 @@ class Financial
*
* Returns the annual yield of a security that pays interest at maturity.
*
+ * @deprecated 1.18.0
+ * Use the yieldAtMaturity() method in the Financial\Securities\Yields class instead
+ * @see Financial\Securities\Yields::yieldAtMaturity()
+ *
* @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.
+ * 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.
+ * 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
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
{
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $issue = Functions::flattenSingleValue($issue);
- $rate = Functions::flattenSingleValue($rate);
- $price = Functions::flattenSingleValue($price);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($rate) && is_numeric($price)) {
- if (($rate <= 0) || ($price <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
- $daysBetweenIssueAndSettlement *= $daysPerYear;
- $daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
- if (!is_numeric($daysBetweenIssueAndMaturity)) {
- // return date error
- return $daysBetweenIssueAndMaturity;
- }
- $daysBetweenIssueAndMaturity *= $daysPerYear;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
- (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
- ($daysPerYear / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
+ return Securities\Yields::yieldAtMaturity($settlement, $maturity, $issue, $rate, $price, $basis);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Amortization.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Amortization.php
new file mode 100644
index 0000000..691ba40
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Amortization.php
@@ -0,0 +1,214 @@
+getMessage();
+ }
+
+ $yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+ if (is_string($yearFracx)) {
+ return $yearFracx;
+ }
+ /** @var float */
+ $yearFrac = $yearFracx;
+
+ $amortiseCoeff = self::getAmortizationCoefficient($rate);
+
+ $rate *= $amortiseCoeff;
+ $fNRate = round($yearFrac * $rate * $cost, 0);
+ $cost -= $fNRate;
+ $fRest = $cost - $salvage;
+
+ for ($n = 0; $n < $period; ++$n) {
+ $fNRate = round($rate * $cost, 0);
+ $fRest -= $fNRate;
+
+ if ($fRest < 0.0) {
+ switch ($period - $n) {
+ case 0:
+ case 1:
+ return round($cost * 0.5, 0);
+ default:
+ return 0.0;
+ }
+ }
+ $cost -= $fNRate;
+ }
+
+ return $fNRate;
+ }
+
+ /**
+ * AMORLINC.
+ *
+ * Returns the depreciation for each accounting period.
+ * This function is provided for the French accounting system. If an asset is purchased in
+ * the middle of the accounting period, the prorated depreciation is taken into account.
+ *
+ * Excel Function:
+ * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
+ *
+ * @param mixed $cost The cost of the asset as a float
+ * @param mixed $purchased Date of the purchase of the asset
+ * @param mixed $firstPeriod Date of the end of the first period
+ * @param mixed $salvage The salvage value at the end of the life of the asset
+ * @param mixed $period The period as a float
+ * @param mixed $rate Rate of depreciation as float
+ * @param mixed $basis Integer indicating 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|string (string containing the error type if there is an error)
+ */
+ public static function AMORLINC(
+ $cost,
+ $purchased,
+ $firstPeriod,
+ $salvage,
+ $period,
+ $rate,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $cost = Functions::flattenSingleValue($cost);
+ $purchased = Functions::flattenSingleValue($purchased);
+ $firstPeriod = Functions::flattenSingleValue($firstPeriod);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $period = Functions::flattenSingleValue($period);
+ $rate = Functions::flattenSingleValue($rate);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $cost = FinancialValidations::validateFloat($cost);
+ $purchased = FinancialValidations::validateDate($purchased);
+ $firstPeriod = FinancialValidations::validateDate($firstPeriod);
+ $salvage = FinancialValidations::validateFloat($salvage);
+ $period = FinancialValidations::validateFloat($period);
+ $rate = FinancialValidations::validateFloat($rate);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $fOneRate = $cost * $rate;
+ $fCostDelta = $cost - $salvage;
+ // Note, quirky variation for leap years on the YEARFRAC for this function
+ $purchasedYear = DateTimeExcel\DateParts::year($purchased);
+ $yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+ if (is_string($yearFracx)) {
+ return $yearFracx;
+ }
+ /** @var float */
+ $yearFrac = $yearFracx;
+
+ if (
+ ($basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) &&
+ ($yearFrac < 1) && (Functions::scalar(DateTimeExcel\Helpers::isLeapYear($purchasedYear)))
+ ) {
+ $yearFrac *= 365 / 366;
+ }
+
+ $f0Rate = $yearFrac * $rate * $cost;
+ $nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
+
+ if ($period == 0) {
+ return $f0Rate;
+ } elseif ($period <= $nNumOfFullPeriods) {
+ return $fOneRate;
+ } elseif ($period == ($nNumOfFullPeriods + 1)) {
+ return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
+ }
+
+ return 0.0;
+ }
+
+ private static function getAmortizationCoefficient(float $rate): float
+ {
+ // The depreciation coefficients are:
+ // Life of assets (1/rate) Depreciation coefficient
+ // Less than 3 years 1
+ // Between 3 and 4 years 1.5
+ // Between 5 and 6 years 2
+ // More than 6 years 2.5
+ $fUsePer = 1.0 / $rate;
+
+ if ($fUsePer < 3.0) {
+ return 1.0;
+ } elseif ($fUsePer < 4.0) {
+ return 1.5;
+ } elseif ($fUsePer <= 6.0) {
+ return 2.0;
+ }
+
+ return 2.5;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
new file mode 100644
index 0000000..8ebe9ed
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
@@ -0,0 +1,53 @@
+getMessage();
+ }
+
+ return self::calculateFutureValue($rate, $numberOfPeriods, $payment, $presentValue, $type);
+ }
+
+ /**
+ * PV.
+ *
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $numberOfPeriods Number of periods as an integer
+ * @param mixed $payment Periodic payment (annuity)
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function presentValue(
+ $rate,
+ $numberOfPeriods,
+ $payment = 0.0,
+ $futureValue = 0.0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($numberOfPeriods < 0) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculatePresentValue($rate, $numberOfPeriods, $payment, $futureValue, $type);
+ }
+
+ /**
+ * NPER.
+ *
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $payment Periodic payment (annuity)
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function periods(
+ $rate,
+ $payment,
+ $presentValue,
+ $futureValue = 0.0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $payment = Functions::flattenSingleValue($payment);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($payment == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculatePeriods($rate, $payment, $presentValue, $futureValue, $type);
+ }
+
+ private static function calculateFutureValue(
+ float $rate,
+ int $numberOfPeriods,
+ float $payment,
+ float $presentValue,
+ int $type
+ ): float {
+ if ($rate !== null && $rate != 0) {
+ return -$presentValue *
+ (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
+ / $rate;
+ }
+
+ return -$presentValue - $payment * $numberOfPeriods;
+ }
+
+ private static function calculatePresentValue(
+ float $rate,
+ int $numberOfPeriods,
+ float $payment,
+ float $futureValue,
+ int $type
+ ): float {
+ if ($rate != 0.0) {
+ return (-$payment * (1 + $rate * $type)
+ * (((1 + $rate) ** $numberOfPeriods - 1) / $rate) - $futureValue) / (1 + $rate) ** $numberOfPeriods;
+ }
+
+ return -$futureValue - $payment * $numberOfPeriods;
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function calculatePeriods(
+ float $rate,
+ float $payment,
+ float $presentValue,
+ float $futureValue,
+ int $type
+ ) {
+ if ($rate != 0.0) {
+ if ($presentValue == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return log(($payment * (1 + $rate * $type) / $rate - $futureValue) /
+ ($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+ }
+
+ return (-$presentValue - $futureValue) / $payment;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
new file mode 100644
index 0000000..b7aaffd
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
@@ -0,0 +1,142 @@
+getMessage();
+ }
+
+ // Validate parameters
+ if ($start < 1 || $start > $end) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ $interest = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $ipmt = Interest::payment($rate, $per, $periods, $presentValue, 0, $type);
+ if (is_string($ipmt)) {
+ return $ipmt;
+ }
+
+ $interest += $ipmt;
+ }
+
+ return $interest;
+ }
+
+ /**
+ * CUMPRINC.
+ *
+ * Returns the cumulative principal paid on a loan between the start and end periods.
+ *
+ * Excel Function:
+ * CUMPRINC(rate,nper,pv,start,end[,type])
+ *
+ * @param mixed $rate The Interest rate
+ * @param mixed $periods The total number of payment periods as an integer
+ * @param mixed $presentValue Present Value
+ * @param mixed $start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param mixed $end the last period in the calculation
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ *
+ * @return float|string
+ */
+ public static function principal(
+ $rate,
+ $periods,
+ $presentValue,
+ $start,
+ $end,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $periods = Functions::flattenSingleValue($periods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $start = Functions::flattenSingleValue($start);
+ $end = Functions::flattenSingleValue($end);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $periods = CashFlowValidations::validateInt($periods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $start = CashFlowValidations::validateInt($start);
+ $end = CashFlowValidations::validateInt($end);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($start < 1 || $start > $end) {
+ return ExcelError::VALUE();
+ }
+
+ // Calculate
+ $principal = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $ppmt = Payments::interestPayment($rate, $per, $periods, $presentValue, 0, $type);
+ if (is_string($ppmt)) {
+ return $ppmt;
+ }
+
+ $principal += $ppmt;
+ }
+
+ return $principal;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
new file mode 100644
index 0000000..4a82514
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
@@ -0,0 +1,219 @@
+getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ $interestAndPrincipal = new InterestAndPrincipal(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue,
+ $type
+ );
+
+ return $interestAndPrincipal->interest();
+ }
+
+ /**
+ * 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)
+ *
+ * @param mixed $interestRate is the interest rate for the investment
+ * @param mixed $period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
+ * @param mixed $numberOfPeriods is the number of payments for the annuity
+ * @param mixed $principleRemaining is the loan amount or present value of the payments
+ *
+ * @return float|string
+ */
+ public static function schedulePayment($interestRate, $period, $numberOfPeriods, $principleRemaining)
+ {
+ $interestRate = Functions::flattenSingleValue($interestRate);
+ $period = Functions::flattenSingleValue($period);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $principleRemaining = Functions::flattenSingleValue($principleRemaining);
+
+ try {
+ $interestRate = CashFlowValidations::validateRate($interestRate);
+ $period = CashFlowValidations::validateInt($period);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $principleRemaining = CashFlowValidations::validateFloat($principleRemaining);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return ExcelError::NAN();
+ }
+
+ // Return value
+ $returnValue = 0;
+
+ // Calculate
+ $principlePayment = ($principleRemaining * 1.0) / ($numberOfPeriods * 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 == $numberOfPeriods) {
+ $returnValue = 0.0;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * RATE.
+ *
+ * Returns the interest rate per period of an annuity.
+ * RATE is calculated by iteration and can have zero or more solutions.
+ * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
+ * RATE returns the #NUM! error value.
+ *
+ * Excel Function:
+ * RATE(nper,pmt,pv[,fv[,type[,guess]]])
+ *
+ * @param mixed $numberOfPeriods The total number of payment periods in an annuity
+ * @param mixed $payment The payment made each period and cannot change over the life of the annuity.
+ * Typically, pmt includes principal and interest but no other fees or taxes.
+ * @param mixed $presentValue The present value - the total amount that a series of future payments is worth now
+ * @param mixed $futureValue The future value, or a cash balance you want to attain after the last payment is made.
+ * If fv is omitted, it is assumed to be 0 (the future value of a loan,
+ * for example, is 0).
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @param mixed $guess Your guess for what the rate will be.
+ * If you omit guess, it is assumed to be 10 percent.
+ *
+ * @return float|string
+ */
+ public static function rate(
+ $numberOfPeriods,
+ $payment,
+ $presentValue,
+ $futureValue = 0.0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD,
+ $guess = 0.1
+ ) {
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $payment = Functions::flattenSingleValue($payment);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+ $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
+
+ try {
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ $guess = CashFlowValidations::validateFloat($guess);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $rate = $guess;
+ // rest of code adapted from python/numpy
+ $close = false;
+ $iter = 0;
+ while (!$close && $iter < self::FINANCIAL_MAX_ITERATIONS) {
+ $nextdiff = self::rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type);
+ if (!is_numeric($nextdiff)) {
+ break;
+ }
+ $rate1 = $rate - $nextdiff;
+ $close = abs($rate1 - $rate) < self::FINANCIAL_PRECISION;
+ ++$iter;
+ $rate = $rate1;
+ }
+
+ return $close ? $rate : ExcelError::NAN();
+ }
+
+ private static function rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type)
+ {
+ if ($rate == 0.0) {
+ return ExcelError::NAN();
+ }
+ $tt1 = ($rate + 1) ** $numberOfPeriods;
+ $tt2 = ($rate + 1) ** ($numberOfPeriods - 1);
+ $numerator = $futureValue + $tt1 * $presentValue + $payment * ($tt1 - 1) * ($rate * $type + 1) / $rate;
+ $denominator = $numberOfPeriods * $tt2 * $presentValue - $payment * ($tt1 - 1)
+ * ($rate * $type + 1) / ($rate * $rate) + $numberOfPeriods
+ * $payment * $tt2 * ($rate * $type + 1) / $rate + $payment * ($tt1 - 1) * $type / $rate;
+ if ($denominator == 0) {
+ return ExcelError::NAN();
+ }
+
+ return $numerator / $denominator;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
new file mode 100644
index 0000000..ca989e0
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
@@ -0,0 +1,44 @@
+interest = $interest;
+ $this->principal = $principal;
+ }
+
+ public function interest(): float
+ {
+ return $this->interest;
+ }
+
+ public function principal(): float
+ {
+ return $this->principal;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
new file mode 100644
index 0000000..83965f9
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ // Calculate
+ if ($interestRate != 0.0) {
+ return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods) /
+ (1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
+ }
+
+ return (-$presentValue - $futureValue) / $numberOfPeriods;
+ }
+
+ /**
+ * PPMT.
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
+ *
+ * @param mixed $interestRate Interest rate per period
+ * @param mixed $period Period for which we want to find the interest
+ * @param mixed $numberOfPeriods Number of periods
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function interestPayment(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue = 0,
+ $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $interestRate = Functions::flattenSingleValue($interestRate);
+ $period = Functions::flattenSingleValue($period);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $interestRate = CashFlowValidations::validateRate($interestRate);
+ $period = CashFlowValidations::validateInt($period);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ $interestAndPrincipal = new InterestAndPrincipal(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue,
+ $type
+ );
+
+ return $interestAndPrincipal->principal();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
new file mode 100644
index 0000000..058e89c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
@@ -0,0 +1,109 @@
+getMessage();
+ }
+
+ return $principal;
+ }
+
+ /**
+ * PDURATION.
+ *
+ * Calculates the number of periods required for an investment to reach a specified value.
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function periods($rate, $presentValue, $futureValue)
+ {
+ $rate = Functions::flattenSingleValue($rate);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = Functions::flattenSingleValue($futureValue);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($rate <= 0.0 || $presentValue <= 0.0 || $futureValue <= 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (log($futureValue) - log($presentValue)) / log(1 + $rate);
+ }
+
+ /**
+ * RRI.
+ *
+ * Calculates the interest rate required for an investment to grow to a specified future value .
+ *
+ * @param float $periods The number of periods over which the investment is made
+ * @param float $presentValue Present Value
+ * @param float $futureValue Future Value
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function interestRate($periods = 0.0, $presentValue = 0.0, $futureValue = 0.0)
+ {
+ $periods = Functions::flattenSingleValue($periods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = Functions::flattenSingleValue($futureValue);
+
+ try {
+ $periods = CashFlowValidations::validateFloat($periods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($periods <= 0.0 || $presentValue <= 0.0 || $futureValue < 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return ($futureValue / $presentValue) ** (1 / $periods) - 1;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
new file mode 100644
index 0000000..4441356
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
@@ -0,0 +1,325 @@
+ 1;
+ $datesIsArray = count($dates) > 1;
+ if (!$valuesIsArray && !$datesIsArray) {
+ return ExcelError::NA();
+ }
+ if (count($values) != count($dates)) {
+ return ExcelError::NAN();
+ }
+
+ $datesCount = count($dates);
+ for ($i = 0; $i < $datesCount; ++$i) {
+ try {
+ $dates[$i] = DateTimeExcel\Helpers::getDateValue($dates[$i]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ return self::xirrPart2($values);
+ }
+
+ private static function xirrPart2(array &$values): string
+ {
+ $valCount = count($values);
+ $foundpos = false;
+ $foundneg = false;
+ for ($i = 0; $i < $valCount; ++$i) {
+ $fld = $values[$i];
+ if (!is_numeric($fld)) {
+ return ExcelError::VALUE();
+ } elseif ($fld > 0) {
+ $foundpos = true;
+ } elseif ($fld < 0) {
+ $foundneg = true;
+ }
+ }
+ if (!self::bothNegAndPos($foundneg, $foundpos)) {
+ return ExcelError::NAN();
+ }
+
+ return '';
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function xirrPart3(array $values, array $dates, float $x1, float $x2)
+ {
+ $f = self::xnpvOrdered($x1, $values, $dates, false);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ $rslt = ExcelError::VALUE();
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = (float) self::xnpvOrdered($x_mid, $values, $dates, false);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
+ $rslt = $x_mid;
+
+ break;
+ }
+ }
+
+ return $rslt;
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function xirrBisection(array $values, array $dates, float $x1, float $x2)
+ {
+ $rslt = ExcelError::NAN();
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $rslt = ExcelError::NAN();
+ $f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
+ $f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
+ if (!is_numeric($f1) || !is_numeric($f2)) {
+ break;
+ }
+ $f1 = (float) $f1;
+ $f2 = (float) $f2;
+ if (abs($f1) < self::FINANCIAL_PRECISION && abs($f2) < self::FINANCIAL_PRECISION) {
+ break;
+ }
+ if ($f1 * $f2 > 0) {
+ break;
+ }
+ $rslt = ($x1 + $x2) / 2;
+ $f3 = self::xnpvOrdered($rslt, $values, $dates, false, true);
+ if (!is_float($f3)) {
+ break;
+ }
+ if ($f3 * $f1 < 0) {
+ $x2 = $rslt;
+ } else {
+ $x1 = $rslt;
+ }
+ if (abs($f3) < self::FINANCIAL_PRECISION) {
+ break;
+ }
+ }
+
+ return $rslt;
+ }
+
+ /**
+ * @param mixed $rate
+ * @param mixed $values
+ * @param mixed $dates
+ *
+ * @return float|string
+ */
+ private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true, bool $capAtNegative1 = false)
+ {
+ $rate = Functions::flattenSingleValue($rate);
+ $values = Functions::flattenArray($values);
+ $dates = Functions::flattenArray($dates);
+ $valCount = count($values);
+
+ try {
+ self::validateXnpv($rate, $values, $dates);
+ if ($capAtNegative1 && $rate <= -1) {
+ $rate = -1.0 + 1.0E-10;
+ }
+ $date0 = DateTimeExcel\Helpers::getDateValue($dates[0]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $xnpv = 0.0;
+ for ($i = 0; $i < $valCount; ++$i) {
+ if (!is_numeric($values[$i])) {
+ return ExcelError::VALUE();
+ }
+
+ try {
+ $datei = DateTimeExcel\Helpers::getDateValue($dates[$i]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ if ($date0 > $datei) {
+ $dif = $ordered ? ExcelError::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
+ } else {
+ $dif = DateTimeExcel\Difference::interval($date0, $datei, 'd');
+ }
+ if (!is_numeric($dif)) {
+ return $dif;
+ }
+ if ($rate <= -1.0) {
+ $xnpv += -abs($values[$i]) / (-1 - $rate) ** ($dif / 365);
+ } else {
+ $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
+ }
+ }
+
+ return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
+ }
+
+ /**
+ * @param mixed $rate
+ */
+ private static function validateXnpv($rate, array $values, array $dates): void
+ {
+ if (!is_numeric($rate)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ $valCount = count($values);
+ if ($valCount != count($dates)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
new file mode 100644
index 0000000..545102f
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
@@ -0,0 +1,168 @@
+ 0.0) {
+ return ExcelError::VALUE();
+ }
+
+ $f = self::presentValue($x1, $values);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::presentValue($x_mid, $values);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
+ return $x_mid;
+ }
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * MIRR.
+ *
+ * Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
+ * the cost of the investment and the interest received on reinvestment of cash.
+ *
+ * Excel Function:
+ * MIRR(values,finance_rate, reinvestment_rate)
+ *
+ * @param mixed $values An array or a reference to cells that contain a series of payments and
+ * income occurring at regular intervals.
+ * Payments are negative value, income is positive values.
+ * @param mixed $financeRate The interest rate you pay on the money used in the cash flows
+ * @param mixed $reinvestmentRate The interest rate you receive on the cash flows as you reinvest them
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function modifiedRate($values, $financeRate, $reinvestmentRate)
+ {
+ if (!is_array($values)) {
+ return ExcelError::DIV0();
+ }
+ $values = Functions::flattenArray($values);
+ $financeRate = Functions::flattenSingleValue($financeRate);
+ $reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate);
+ $n = count($values);
+
+ $rr = 1.0 + $reinvestmentRate;
+ $fr = 1.0 + $financeRate;
+
+ $npvPos = $npvNeg = self::$zeroPointZero;
+ foreach ($values as $i => $v) {
+ if ($v >= 0) {
+ $npvPos += $v / $rr ** $i;
+ } else {
+ $npvNeg += $v / $fr ** $i;
+ }
+ }
+
+ if ($npvNeg === self::$zeroPointZero || $npvPos === self::$zeroPointZero) {
+ return ExcelError::DIV0();
+ }
+
+ $mirr = ((-$npvPos * $rr ** $n)
+ / ($npvNeg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
+
+ return is_finite($mirr) ? $mirr : ExcelError::NAN();
+ }
+
+ /**
+ * Sop to Scrutinizer.
+ *
+ * @var float
+ */
+ private static $zeroPointZero = 0.0;
+
+ /**
+ * NPV.
+ *
+ * Returns the Net Present Value of a cash flow series given a discount rate.
+ *
+ * @param mixed $rate
+ *
+ * @return float
+ */
+ public static function presentValue($rate, ...$args)
+ {
+ $returnValue = 0;
+
+ $rate = Functions::flattenSingleValue($rate);
+ $aArgs = Functions::flattenArray($args);
+
+ // Calculate
+ $countArgs = count($aArgs);
+ for ($i = 1; $i <= $countArgs; ++$i) {
+ // Is it a numeric value?
+ if (is_numeric($aArgs[$i - 1])) {
+ $returnValue += $aArgs[$i - 1] / (1 + $rate) ** $i;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Constants.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Constants.php
new file mode 100644
index 0000000..17740b0
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Constants.php
@@ -0,0 +1,19 @@
+getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+ if (is_string($daysPerYear)) {
+ return ExcelError::VALUE();
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+
+ if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) {
+ return abs((float) DateTimeExcel\Days::between($prev, $settlement));
+ }
+
+ return (float) DateTimeExcel\YearFrac::fraction($prev, $settlement, $basis) * $daysPerYear;
+ }
+
+ /**
+ * COUPDAYS.
+ *
+ * Returns the number of days in the coupon period that contains the settlement date.
+ *
+ * Excel Function:
+ * COUPDAYS(settlement,maturity,frequency[,basis])
+ *
+ * @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 mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function COUPDAYS(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ switch ($basis) {
+ case FinancialConstants::BASIS_DAYS_PER_YEAR_365:
+ // Actual/365
+ return 365 / $frequency;
+ case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL:
+ // Actual/actual
+ if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) {
+ $daysPerYear = (int) Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+
+ return $daysPerYear / $frequency;
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+
+ return $next - $prev;
+ default:
+ // US (NASD) 30/360, Actual/360 or European 30/360
+ return 360 / $frequency;
+ }
+ }
+
+ /**
+ * COUPDAYSNC.
+ *
+ * Returns the number of days from the settlement date to the next coupon date.
+ *
+ * Excel Function:
+ * COUPDAYSNC(settlement,maturity,frequency[,basis])
+ *
+ * @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 mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int) .
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function COUPDAYSNC(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ /** @var int */
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+
+ if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) {
+ $settlementDate = Date::excelToDateTimeObject($settlement);
+ $settlementEoM = Helpers::isLastDayOfMonth($settlementDate);
+ if ($settlementEoM) {
+ ++$settlement;
+ }
+ }
+
+ return (float) DateTimeExcel\YearFrac::fraction($settlement, $next, $basis) * $daysPerYear;
+ }
+
+ /**
+ * COUPNCD.
+ *
+ * Returns the next coupon date after the settlement date.
+ *
+ * Excel Function:
+ * COUPNCD(settlement,maturity,frequency[,basis])
+ *
+ * @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 mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @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 COUPNCD(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ self::doNothing($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+ }
+
+ /**
+ * COUPNUM.
+ *
+ * Returns the number of coupons payable between the settlement date and maturity date,
+ * rounded up to the nearest whole coupon.
+ *
+ * Excel Function:
+ * COUPNUM(settlement,maturity,frequency[,basis])
+ *
+ * @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 mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return int|string
+ */
+ public static function COUPNUM(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ self::doNothing($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction(
+ $settlement,
+ $maturity,
+ FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ );
+
+ return (int) ceil((float) $yearsBetweenSettlementAndMaturity * $frequency);
+ }
+
+ /**
+ * COUPPCD.
+ *
+ * Returns the previous coupon date before the settlement date.
+ *
+ * Excel Function:
+ * COUPPCD(settlement,maturity,frequency[,basis])
+ *
+ * @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 mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @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 COUPPCD(
+ $settlement,
+ $maturity,
+ $frequency,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ self::doNothing($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+ }
+
+ private static function monthsDiff(DateTime $result, int $months, string $plusOrMinus, int $day, bool $lastDayFlag): void
+ {
+ $result->setDate((int) $result->format('Y'), (int) $result->format('m'), 1);
+ $result->modify("$plusOrMinus $months months");
+ $daysInMonth = (int) $result->format('t');
+ $result->setDate((int) $result->format('Y'), (int) $result->format('m'), $lastDayFlag ? $daysInMonth : min($day, $daysInMonth));
+ }
+
+ private static function couponFirstPeriodDate(float $settlement, float $maturity, int $frequency, bool $next): float
+ {
+ $months = 12 / $frequency;
+
+ $result = Date::excelToDateTimeObject($maturity);
+ $day = (int) $result->format('d');
+ $lastDayFlag = Helpers::isLastDayOfMonth($result);
+
+ while ($settlement < Date::PHPToExcel($result)) {
+ self::monthsDiff($result, $months, '-', $day, $lastDayFlag);
+ }
+ if ($next === true) {
+ self::monthsDiff($result, $months, '+', $day, $lastDayFlag);
+ }
+
+ return (float) Date::PHPToExcel($result);
+ }
+
+ private static function validateCouponPeriod(float $settlement, float $maturity): void
+ {
+ if ($settlement >= $maturity) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+
+ /** @param mixed $basis */
+ private static function doNothing($basis): bool
+ {
+ return $basis;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Depreciation.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Depreciation.php
new file mode 100644
index 0000000..8e1a2fc
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Depreciation.php
@@ -0,0 +1,270 @@
+getMessage();
+ }
+
+ if ($cost === self::$zeroPointZero) {
+ return 0.0;
+ }
+
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ // TODO Handle period value between 0 and 1 (e.g. 0.5)
+ $previousDepreciation = 0;
+ $depreciation = 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;
+ }
+
+ return $depreciation;
+ }
+
+ /**
+ * DDB.
+ *
+ * Returns the depreciation of an asset for a specified period using the
+ * double-declining balance method or some other method you specify.
+ *
+ * Excel Function:
+ * DDB(cost,salvage,life,period[,factor])
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation.
+ * (Sometimes called the salvage value of the asset)
+ * @param mixed $life Number of periods over which the asset is depreciated.
+ * (Sometimes called the useful life of the asset)
+ * @param mixed $period The period for which you want to calculate the
+ * depreciation. Period must use the same units as life.
+ * @param mixed $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|string
+ */
+ public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+ $period = Functions::flattenSingleValue($period);
+ $factor = Functions::flattenSingleValue($factor);
+
+ try {
+ $cost = self::validateCost($cost);
+ $salvage = self::validateSalvage($salvage);
+ $life = self::validateLife($life);
+ $period = self::validatePeriod($period);
+ $factor = self::validateFactor($factor);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($period > $life) {
+ return ExcelError::NAN();
+ }
+
+ // Loop through each period calculating the depreciation
+ // TODO Handling for fractional $period values
+ $previousDepreciation = 0;
+ $depreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ $depreciation = min(
+ ($cost - $previousDepreciation) * ($factor / $life),
+ ($cost - $salvage - $previousDepreciation)
+ );
+ $previousDepreciation += $depreciation;
+ }
+
+ return $depreciation;
+ }
+
+ /**
+ * SLN.
+ *
+ * Returns the straight-line depreciation of an asset for one period
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation
+ * @param mixed $life Number of periods over which the asset is depreciated
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function SLN($cost, $salvage, $life)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+
+ try {
+ $cost = self::validateCost($cost, true);
+ $salvage = self::validateSalvage($salvage, true);
+ $life = self::validateLife($life, true);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($life === self::$zeroPointZero) {
+ return ExcelError::DIV0();
+ }
+
+ return ($cost - $salvage) / $life;
+ }
+
+ /**
+ * SYD.
+ *
+ * Returns the sum-of-years' digits depreciation of an asset for a specified period.
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation
+ * @param mixed $life Number of periods over which the asset is depreciated
+ * @param mixed $period Period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function SYD($cost, $salvage, $life, $period)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+ $period = Functions::flattenSingleValue($period);
+
+ try {
+ $cost = self::validateCost($cost, true);
+ $salvage = self::validateSalvage($salvage);
+ $life = self::validateLife($life);
+ $period = self::validatePeriod($period);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($period > $life) {
+ return ExcelError::NAN();
+ }
+
+ $syd = (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
+
+ return $syd;
+ }
+
+ private static function validateCost($cost, bool $negativeValueAllowed = false): float
+ {
+ $cost = FinancialValidations::validateFloat($cost);
+ if ($cost < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $cost;
+ }
+
+ private static function validateSalvage($salvage, bool $negativeValueAllowed = false): float
+ {
+ $salvage = FinancialValidations::validateFloat($salvage);
+ if ($salvage < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $salvage;
+ }
+
+ private static function validateLife($life, bool $negativeValueAllowed = false): float
+ {
+ $life = FinancialValidations::validateFloat($life);
+ if ($life < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $life;
+ }
+
+ private static function validatePeriod($period, bool $negativeValueAllowed = false): float
+ {
+ $period = FinancialValidations::validateFloat($period);
+ if ($period <= 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $period;
+ }
+
+ private static function validateMonth($month): int
+ {
+ $month = FinancialValidations::validateInt($month);
+ if ($month < 1) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $month;
+ }
+
+ private static function validateFactor($factor): float
+ {
+ $factor = FinancialValidations::validateFloat($factor);
+ if ($factor <= 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $factor;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Dollar.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Dollar.php
new file mode 100644
index 0000000..b1f0d25
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Dollar.php
@@ -0,0 +1,132 @@
+getMessage();
+ }
+
+ // Additional parameter validations
+ if ($fraction < 0) {
+ return ExcelError::NAN();
+ }
+ if ($fraction == 0) {
+ return ExcelError::DIV0();
+ }
+
+ $dollars = ($fractionalDollar < 0) ? ceil($fractionalDollar) : floor($fractionalDollar);
+ $cents = fmod($fractionalDollar, 1.0);
+ $cents /= $fraction;
+ $cents *= 10 ** ceil(log10($fraction));
+
+ return $dollars + $cents;
+ }
+
+ /**
+ * 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.
+ *
+ * Excel Function:
+ * DOLLARFR(decimal_dollar,fraction)
+ *
+ * @param mixed $decimalDollar Decimal Dollar
+ * Or can be an array of values
+ * @param mixed $fraction Fraction
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ */
+ public static function fractional($decimalDollar = null, $fraction = 0)
+ {
+ if (is_array($decimalDollar) || is_array($fraction)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $decimalDollar, $fraction);
+ }
+
+ try {
+ $decimalDollar = FinancialValidations::validateFloat(
+ Functions::flattenSingleValue($decimalDollar) ?? 0.0
+ );
+ $fraction = FinancialValidations::validateInt(Functions::flattenSingleValue($fraction));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Additional parameter validations
+ if ($fraction < 0) {
+ return ExcelError::NAN();
+ }
+ if ($fraction == 0) {
+ return ExcelError::DIV0();
+ }
+
+ $dollars = ($decimalDollar < 0.0) ? ceil($decimalDollar) : floor($decimalDollar);
+ $cents = fmod($decimalDollar, 1);
+ $cents *= $fraction;
+ $cents *= 10 ** (-ceil(log10($fraction)));
+
+ return $dollars + $cents;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
new file mode 100644
index 0000000..1b04419
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
@@ -0,0 +1,158 @@
+ 4)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $basis;
+ }
+
+ /**
+ * @param mixed $price
+ */
+ public static function validatePrice($price): float
+ {
+ $price = self::validateFloat($price);
+ if ($price < 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $price;
+ }
+
+ /**
+ * @param mixed $parValue
+ */
+ public static function validateParValue($parValue): float
+ {
+ $parValue = self::validateFloat($parValue);
+ if ($parValue < 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $parValue;
+ }
+
+ /**
+ * @param mixed $yield
+ */
+ public static function validateYield($yield): float
+ {
+ $yield = self::validateFloat($yield);
+ if ($yield < 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $yield;
+ }
+
+ /**
+ * @param mixed $discount
+ */
+ public static function validateDiscount($discount): float
+ {
+ $discount = self::validateFloat($discount);
+ if ($discount <= 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $discount;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Helpers.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Helpers.php
new file mode 100644
index 0000000..c7f1f46
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Helpers.php
@@ -0,0 +1,58 @@
+format('d') === $date->format('t');
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/InterestRate.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/InterestRate.php
new file mode 100644
index 0000000..1cbe265
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/InterestRate.php
@@ -0,0 +1,73 @@
+getMessage();
+ }
+
+ if ($nominalRate <= 0 || $periodsPerYear < 1) {
+ return ExcelError::NAN();
+ }
+
+ return ((1 + $nominalRate / $periodsPerYear) ** $periodsPerYear) - 1;
+ }
+
+ /**
+ * NOMINAL.
+ *
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
+ *
+ * @param mixed $effectiveRate Effective interest rate as a float
+ * @param mixed $periodsPerYear Integer number of compounding payments per year
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function nominal($effectiveRate = 0, $periodsPerYear = 0)
+ {
+ $effectiveRate = Functions::flattenSingleValue($effectiveRate);
+ $periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
+
+ try {
+ $effectiveRate = FinancialValidations::validateFloat($effectiveRate);
+ $periodsPerYear = FinancialValidations::validateInt($periodsPerYear);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($effectiveRate <= 0 || $periodsPerYear < 1) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ return $periodsPerYear * (($effectiveRate + 1) ** (1 / $periodsPerYear) - 1);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
new file mode 100644
index 0000000..e1bf04b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
@@ -0,0 +1,159 @@
+getMessage();
+ }
+
+ $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenFirstInterestAndSettlement = Functions::scalar(YearFrac::fraction($firstInterest, $settlement, $basis));
+ if (!is_numeric($daysBetweenFirstInterestAndSettlement)) {
+ // return date error
+ return $daysBetweenFirstInterestAndSettlement;
+ }
+
+ return $parValue * $rate * $daysBetweenIssueAndSettlement;
+ }
+
+ /**
+ * ACCRINTM.
+ *
+ * Returns the accrued interest for a security that pays interest at maturity.
+ *
+ * Excel Function:
+ * ACCRINTM(issue,settlement,rate[,par[,basis]])
+ *
+ * @param mixed $issue The security's issue date
+ * @param mixed $settlement The security's settlement (or maturity) date
+ * @param mixed $rate The security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit parValue, ACCRINT uses $1,000.
+ * @param mixed $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|string Result, or a string containing an error
+ */
+ public static function atMaturity(
+ $issue,
+ $settlement,
+ $rate,
+ $parValue = 1000,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $issue = Functions::flattenSingleValue($issue);
+ $settlement = Functions::flattenSingleValue($settlement);
+ $rate = Functions::flattenSingleValue($rate);
+ $parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ SecurityValidations::validateSecurityPeriod($issue, $settlement);
+ $rate = SecurityValidations::validateRate($rate);
+ $parValue = SecurityValidations::validateParValue($parValue);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+
+ return $parValue * $rate * $daysBetweenIssueAndSettlement;
+ }
+
+ /** @param mixed $arg */
+ private static function doNothing($arg): bool
+ {
+ return (bool) $arg;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
new file mode 100644
index 0000000..de1748a
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
@@ -0,0 +1,284 @@
+getMessage();
+ }
+
+ $dsc = Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
+ $e = Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
+ $n = Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
+ $a = Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
+
+ $baseYF = 1.0 + ($yield / $frequency);
+ $rfp = 100 * ($rate / $frequency);
+ $de = $dsc / $e;
+
+ $result = $redemption / $baseYF ** (--$n + $de);
+ for ($k = 0; $k <= $n; ++$k) {
+ $result += $rfp / ($baseYF ** ($k + $de));
+ }
+ $result -= $rfp * ($a / $e);
+
+ return $result;
+ }
+
+ /**
+ * 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 mixed $discount The security's discount rate
+ * @param mixed $redemption The security's redemption value per $100 face value
+ * @param mixed $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|string Result, or a string containing an error
+ */
+ public static function priceDiscounted(
+ $settlement,
+ $maturity,
+ $discount,
+ $redemption,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $discount = Functions::flattenSingleValue($discount);
+ $redemption = Functions::flattenSingleValue($redemption);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $discount = SecurityValidations::validateDiscount($discount);
+ $redemption = SecurityValidations::validateRedemption($redemption);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * 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 mixed $rate The security's interest rate at date of issue
+ * @param mixed $yield The security's annual yield
+ * @param mixed $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|string Result, or a string containing an error
+ */
+ public static function priceAtMaturity(
+ $settlement,
+ $maturity,
+ $issue,
+ $rate,
+ $yield,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $issue = Functions::flattenSingleValue($issue);
+ $rate = Functions::flattenSingleValue($rate);
+ $yield = Functions::flattenSingleValue($yield);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $rate = SecurityValidations::validateRate($rate);
+ $yield = SecurityValidations::validateYield($yield);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Functions::scalar(Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis));
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
+ (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
+ (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
+ }
+
+ /**
+ * RECEIVED.
+ *
+ * Returns the amount received at maturity 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 mixed $investment The amount invested in the security
+ * @param mixed $discount The security's discount rate
+ * @param mixed $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|string Result, or a string containing an error
+ */
+ public static function received(
+ $settlement,
+ $maturity,
+ $investment,
+ $discount,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $investment = Functions::flattenSingleValue($investment);
+ $discount = Functions::flattenSingleValue($discount);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $investment = SecurityValidations::validateFloat($investment);
+ $discount = SecurityValidations::validateDiscount($discount);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($investment <= 0) {
+ return ExcelError::NAN();
+ }
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
new file mode 100644
index 0000000..f8d8673
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
@@ -0,0 +1,138 @@
+getMessage();
+ }
+
+ if ($price <= 0.0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
+ }
+
+ /**
+ * INTRATE.
+ *
+ * Returns the interest rate for a fully invested security.
+ *
+ * Excel Function:
+ * INTRATE(settlement,maturity,investment,redemption[,basis])
+ *
+ * @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 mixed $investment the amount invested in the security
+ * @param mixed $redemption the amount to be received at maturity
+ * @param mixed $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|string
+ */
+ public static function interest(
+ $settlement,
+ $maturity,
+ $investment,
+ $redemption,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $investment = Functions::flattenSingleValue($investment);
+ $redemption = Functions::flattenSingleValue($redemption);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $investment = SecurityValidations::validateFloat($investment);
+ $redemption = SecurityValidations::validateRedemption($redemption);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($investment <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
new file mode 100644
index 0000000..d3196f0
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
@@ -0,0 +1,42 @@
+= $maturity) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+
+ /**
+ * @param mixed $redemption
+ */
+ public static function validateRedemption($redemption): float
+ {
+ $redemption = self::validateFloat($redemption);
+ if ($redemption <= 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $redemption;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
new file mode 100644
index 0000000..bb2e8ae
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
@@ -0,0 +1,153 @@
+getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * 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 mixed $rate The security's interest rate at date of issue
+ * @param mixed $price The security's price per $100 face value
+ * @param mixed $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|string Result, or a string containing an error
+ */
+ public static function yieldAtMaturity(
+ $settlement,
+ $maturity,
+ $issue,
+ $rate,
+ $price,
+ $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $issue = Functions::flattenSingleValue($issue);
+ $rate = Functions::flattenSingleValue($rate);
+ $price = Functions::flattenSingleValue($price);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $rate = SecurityValidations::validateRate($rate);
+ $price = SecurityValidations::validatePrice($price);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) -
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
+ ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/PhpOffice/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
new file mode 100644
index 0000000..7ee34f7
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
@@ -0,0 +1,148 @@
+getMessage();
+ }
+
+ if ($discount <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return ExcelError::NAN();
+ }
+
+ return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * TBILLPRICE.
+ *
+ * Returns the price per $100 face value 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 mixed $discount The Treasury bill's discount rate
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function price($settlement, $maturity, $discount)
+ {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $discount = Functions::flattenSingleValue($discount);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ $discount = FinancialValidations::validateFloat($discount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($discount <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return ExcelError::NAN();
+ }
+
+ $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
+ if ($price < 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return $price;
+ }
+
+ /**
+ * 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 mixed $price The Treasury bill's price per $100 face value
+ *
+ * @return float|string
+ */
+ public static function yield($settlement, $maturity, $price)
+ {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $price = Functions::flattenSingleValue($price);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ $price = FinancialValidations::validatePrice($price);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return ExcelError::NAN();
+ }
+
+ return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/FormulaParser.php b/PhpOffice/PhpSpreadsheet/Calculation/FormulaParser.php
old mode 100755
new mode 100644
index 9b3c66e..14c5ae5
--- a/PhpOffice/PhpSpreadsheet/Calculation/FormulaParser.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/FormulaParser.php
@@ -61,19 +61,17 @@ class FormulaParser
/**
* Create a new FormulaParser.
*
- * @param string $pFormula Formula to parse
- *
- * @throws Exception
+ * @param ?string $formula Formula to parse
*/
- public function __construct($pFormula = '')
+ public function __construct($formula = '')
{
// Check parameters
- if ($pFormula === null) {
+ if ($formula === null) {
throw new Exception('Invalid parameter passed: formula');
}
// Initialise values
- $this->formula = trim($pFormula);
+ $this->formula = trim($formula);
// Parse!
$this->parseToTokens();
}
@@ -91,19 +89,15 @@ class FormulaParser
/**
* Get Token.
*
- * @param int $pId Token id
- *
- * @throws Exception
- *
- * @return string
+ * @param int $id Token id
*/
- public function getToken($pId = 0)
+ public function getToken(int $id = 0): FormulaToken
{
- if (isset($this->tokens[$pId])) {
- return $this->tokens[$pId];
+ if (isset($this->tokens[$id])) {
+ return $this->tokens[$id];
}
- throw new Exception("Token with id $pId does not exist.");
+ throw new Exception("Token with id $id does not exist.");
}
/**
@@ -129,7 +123,7 @@ class FormulaParser
/**
* Parse to tokens.
*/
- private function parseToTokens()
+ private function parseToTokens(): void
{
// 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.
@@ -143,7 +137,8 @@ class FormulaParser
// Helper variables
$tokens1 = $tokens2 = $stack = [];
$inString = $inPath = $inRange = $inError = false;
- $token = $previousToken = $nextToken = null;
+ $nextToken = null;
+ //$token = $previousToken = null;
$index = 1;
$value = '';
@@ -494,11 +489,13 @@ class FormulaParser
continue;
}
- if (!(
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ if (
+ !(
+ (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
- )) {
+ )
+ ) {
continue;
}
@@ -506,11 +503,13 @@ class FormulaParser
continue;
}
- if (!(
- (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
+ if (
+ !(
+ (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
(($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
- )) {
+ )
+ ) {
continue;
}
@@ -529,11 +528,11 @@ class FormulaParser
} else {
$previousToken = null;
}
- if (isset($tokens2[$i + 1])) {
- $nextToken = $tokens2[$i + 1];
- } else {
- $nextToken = null;
- }
+ //if (isset($tokens2[$i + 1])) {
+ // $nextToken = $tokens2[$i + 1];
+ //} else {
+ // $nextToken = null;
+ //}
if ($token === null) {
continue;
@@ -542,12 +541,14 @@ class FormulaParser
if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') {
if ($i == 0) {
$token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
- } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
+ } elseif (
+ (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) {
+ ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
} else {
$token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
@@ -561,12 +562,14 @@ class FormulaParser
if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '+') {
if ($i == 0) {
continue;
- } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
+ } elseif (
+ (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) {
+ ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
} else {
continue;
@@ -577,8 +580,10 @@ class FormulaParser
continue;
}
- if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
- $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (
+ $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
+ $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
+ ) {
if (strpos('<>=', substr($token->getValue(), 0, 1)) !== false) {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
} elseif ($token->getValue() == '&') {
@@ -592,8 +597,10 @@ class FormulaParser
continue;
}
- if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
- $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (
+ $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
+ $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
+ ) {
if (!is_numeric($token->getValue())) {
if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/FormulaToken.php b/PhpOffice/PhpSpreadsheet/Calculation/FormulaToken.php
old mode 100755
new mode 100644
index 66618d4..68e5eea
--- a/PhpOffice/PhpSpreadsheet/Calculation/FormulaToken.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/FormulaToken.php
@@ -76,16 +76,16 @@ class FormulaToken
/**
* Create a new FormulaToken.
*
- * @param string $pValue
- * @param string $pTokenType Token type (represented by TOKEN_TYPE_*)
- * @param string $pTokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
+ * @param string $value
+ * @param string $tokenType Token type (represented by TOKEN_TYPE_*)
+ * @param string $tokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
*/
- public function __construct($pValue, $pTokenType = self::TOKEN_TYPE_UNKNOWN, $pTokenSubType = self::TOKEN_SUBTYPE_NOTHING)
+ public function __construct($value, $tokenType = self::TOKEN_TYPE_UNKNOWN, $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
{
// Initialise values
- $this->value = $pValue;
- $this->tokenType = $pTokenType;
- $this->tokenSubType = $pTokenSubType;
+ $this->value = $value;
+ $this->tokenType = $tokenType;
+ $this->tokenSubType = $tokenSubType;
}
/**
@@ -103,7 +103,7 @@ class FormulaToken
*
* @param string $value
*/
- public function setValue($value)
+ public function setValue($value): void
{
$this->value = $value;
}
@@ -123,7 +123,7 @@ class FormulaToken
*
* @param string $value
*/
- public function setTokenType($value)
+ public function setTokenType($value): void
{
$this->tokenType = $value;
}
@@ -143,7 +143,7 @@ class FormulaToken
*
* @param string $value
*/
- public function setTokenSubType($value)
+ public function setTokenSubType($value): void
{
$this->tokenSubType = $value;
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Functions.php b/PhpOffice/PhpSpreadsheet/Calculation/Functions.php
old mode 100755
new mode 100644
index 40e7d96..747a1f4
--- a/PhpOffice/PhpSpreadsheet/Calculation/Functions.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Functions.php
@@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
class Functions
{
@@ -13,12 +14,13 @@ class Functions
*/
const M_2DIVPI = 0.63661977236758134307553505349006;
- /** constants */
const COMPATIBILITY_EXCEL = 'Excel';
const COMPATIBILITY_GNUMERIC = 'Gnumeric';
const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
+ /** Use of RETURNDATE_PHP_NUMERIC is discouraged - not 32-bit Y2038-safe, no timezone. */
const RETURNDATE_PHP_NUMERIC = 'P';
+ /** Use of RETURNDATE_UNIX_TIMESTAMP is discouraged - not 32-bit Y2038-safe, no timezone. */
const RETURNDATE_UNIX_TIMESTAMP = 'P';
const RETURNDATE_PHP_OBJECT = 'O';
const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
@@ -38,38 +40,21 @@ class Functions
*/
protected static $returnDateType = self::RETURNDATE_EXCEL;
- /**
- * List of error codes.
- *
- * @var array
- */
- protected static $errorCodes = [
- 'null' => '#NULL!',
- 'divisionbyzero' => '#DIV/0!',
- 'value' => '#VALUE!',
- 'reference' => '#REF!',
- 'name' => '#NAME?',
- 'num' => '#NUM!',
- 'na' => '#N/A',
- 'gettingdata' => '#GETTING_DATA',
- ];
-
/**
* Set the Compatibility Mode.
*
- * @category Function Configuration
- *
* @param string $compatibilityMode Compatibility Mode
- * Permitted values are:
- * Functions::COMPATIBILITY_EXCEL 'Excel'
- * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
- * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ * Permitted values are:
+ * Functions::COMPATIBILITY_EXCEL 'Excel'
+ * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
*
* @return bool (Success or Failure)
*/
public static function setCompatibilityMode($compatibilityMode)
{
- if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
+ if (
+ ($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
) {
@@ -84,13 +69,11 @@ class Functions
/**
* Return the current Compatibility Mode.
*
- * @category Function Configuration
- *
* @return string Compatibility Mode
- * Possible Return values are:
- * Functions::COMPATIBILITY_EXCEL 'Excel'
- * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
- * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ * Possible Return values are:
+ * Functions::COMPATIBILITY_EXCEL 'Excel'
+ * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
*/
public static function getCompatibilityMode()
{
@@ -98,21 +81,20 @@ class Functions
}
/**
- * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
- *
- * @category Function Configuration
+ * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP DateTime Object).
*
* @param string $returnDateType Return Date Format
- * Permitted values are:
- * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
- * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
- * Functions::RETURNDATE_EXCEL 'E'
+ * Permitted values are:
+ * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
+ * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
+ * Functions::RETURNDATE_EXCEL 'E'
*
* @return bool Success or failure
*/
public static function setReturnDateType($returnDateType)
{
- if (($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
+ if (
+ ($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
($returnDateType == self::RETURNDATE_EXCEL)
) {
@@ -127,13 +109,11 @@ class Functions
/**
* Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
*
- * @category Function Configuration
- *
* @return string Return Date Format
- * Possible Return values are:
- * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
- * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
- * Functions::RETURNDATE_EXCEL 'E'
+ * Possible Return values are:
+ * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
+ * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
+ * Functions::RETURNDATE_EXCEL ' 'E'
*/
public static function getReturnDateType()
{
@@ -143,8 +123,6 @@ class Functions
/**
* DUMMY.
*
- * @category Error Returns
- *
* @return string #Not Yet Implemented
*/
public static function DUMMY()
@@ -152,16 +130,120 @@ class Functions
return '#Not Yet Implemented';
}
- /**
- * DIV0.
- *
- * @category Error Returns
- *
- * @return string #Not Yet Implemented
- */
- public static function DIV0()
+ public static function isMatrixValue($idx)
{
- return self::$errorCodes['divisionbyzero'];
+ return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
+ }
+
+ public static function isValue($idx)
+ {
+ return substr_count($idx, '.') === 0;
+ }
+
+ public static function isCellValue($idx)
+ {
+ return substr_count($idx, '.') > 1;
+ }
+
+ public static function ifCondition($condition)
+ {
+ $condition = self::flattenSingleValue($condition);
+
+ if ($condition === '') {
+ return '=""';
+ }
+ if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
+ $condition = self::operandSpecialHandling($condition);
+ if (is_bool($condition)) {
+ return '=' . ($condition ? 'TRUE' : 'FALSE');
+ } elseif (!is_numeric($condition)) {
+ if ($condition !== '""') { // Not an empty string
+ // Escape any quotes in the string value
+ $condition = (string) preg_replace('/"/ui', '""', $condition);
+ }
+ $condition = Calculation::wrapResult(strtoupper($condition));
+ }
+
+ return str_replace('""""', '""', '=' . $condition);
+ }
+ preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
+ [, $operator, $operand] = $matches;
+
+ $operand = self::operandSpecialHandling($operand);
+ if (is_numeric(trim($operand, '"'))) {
+ $operand = trim($operand, '"');
+ } elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
+ $operand = str_replace('"', '""', $operand);
+ $operand = Calculation::wrapResult(strtoupper($operand));
+ }
+
+ return str_replace('""""', '""', $operator . $operand);
+ }
+
+ private static function operandSpecialHandling($operand)
+ {
+ if (is_numeric($operand) || is_bool($operand)) {
+ return $operand;
+ } elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
+ return strtoupper($operand);
+ }
+
+ // Check for percentage
+ if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $operand)) {
+ return ((float) rtrim($operand, '%')) / 100;
+ }
+
+ // Check for dates
+ if (($dateValueOperand = Date::stringToExcel($operand)) !== false) {
+ return $dateValueOperand;
+ }
+
+ return $operand;
+ }
+
+ /**
+ * NULL.
+ *
+ * Returns the error value #NULL!
+ *
+ * @deprecated 1.23.0 Use the null() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::null()
+ *
+ * @return string #NULL!
+ */
+ public static function null()
+ {
+ return Information\ExcelError::null();
+ }
+
+ /**
+ * NaN.
+ *
+ * Returns the error value #NUM!
+ *
+ * @deprecated 1.23.0 Use the NAN() method in the Information\Error class instead
+ * @see Information\ExcelError::NAN()
+ *
+ * @return string #NUM!
+ */
+ public static function NAN()
+ {
+ return Information\ExcelError::NAN();
+ }
+
+ /**
+ * REF.
+ *
+ * Returns the error value #REF!
+ *
+ * @deprecated 1.23.0 Use the REF() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::REF()
+ *
+ * @return string #REF!
+ */
+ public static function REF()
+ {
+ return Information\ExcelError::REF();
}
/**
@@ -173,69 +255,14 @@ class Functions
* Returns the error value #N/A
* #N/A is the error value that means "no value is available."
*
- * @category Logical Functions
+ * @deprecated 1.23.0 Use the NA() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::NA()
*
* @return string #N/A!
*/
public static function NA()
{
- return self::$errorCodes['na'];
- }
-
- /**
- * NaN.
- *
- * Returns the error value #NUM!
- *
- * @category Error Returns
- *
- * @return string #NUM!
- */
- public static function NAN()
- {
- return self::$errorCodes['num'];
- }
-
- /**
- * NAME.
- *
- * Returns the error value #NAME?
- *
- * @category Error Returns
- *
- * @return string #NAME?
- */
- public static function NAME()
- {
- return self::$errorCodes['name'];
- }
-
- /**
- * REF.
- *
- * Returns the error value #REF!
- *
- * @category Error Returns
- *
- * @return string #REF!
- */
- public static function REF()
- {
- return self::$errorCodes['reference'];
- }
-
- /**
- * NULL.
- *
- * Returns the error value #NULL!
- *
- * @category Error Returns
- *
- * @return string #NULL!
- */
- public static function null()
- {
- return self::$errorCodes['null'];
+ return Information\ExcelError::NA();
}
/**
@@ -243,54 +270,42 @@ class Functions
*
* Returns the error value #VALUE!
*
- * @category Error Returns
+ * @deprecated 1.23.0 Use the VALUE() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::VALUE()
*
* @return string #VALUE!
*/
public static function VALUE()
{
- return self::$errorCodes['value'];
+ return Information\ExcelError::VALUE();
}
- public static function isMatrixValue($idx)
+ /**
+ * NAME.
+ *
+ * Returns the error value #NAME?
+ *
+ * @deprecated 1.23.0 Use the NAME() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::NAME()
+ *
+ * @return string #NAME?
+ */
+ public static function NAME()
{
- return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
+ return Information\ExcelError::NAME();
}
- public static function isValue($idx)
+ /**
+ * DIV0.
+ *
+ * @deprecated 1.23.0 Use the DIV0() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::DIV0()
+ *
+ * @return string #Not Yet Implemented
+ */
+ public static function DIV0()
{
- return substr_count($idx, '.') == 0;
- }
-
- public static function isCellValue($idx)
- {
- return substr_count($idx, '.') > 1;
- }
-
- public static function ifCondition($condition)
- {
- $condition = self::flattenSingleValue($condition);
- if (!isset($condition[0]) && !is_numeric($condition)) {
- $condition = '=""';
- }
- if (!in_array($condition[0], ['>', '<', '='])) {
- if (!is_numeric($condition)) {
- $condition = Calculation::wrapResult(strtoupper($condition));
- }
-
- return '=' . $condition;
- }
- preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
- [, $operator, $operand] = $matches;
-
- if (is_numeric(trim($operand, '"'))) {
- $operand = trim($operand, '"');
- } elseif (!is_numeric($operand)) {
- $operand = str_replace('"', '""', $operand);
- $operand = Calculation::wrapResult(strtoupper($operand));
- }
-
- return $operator . $operand;
+ return Information\ExcelError::DIV0();
}
/**
@@ -298,21 +313,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the type() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::type()
+ *
+ * @return array|int|string
*/
public static function errorType($value = '')
{
- $value = self::flattenSingleValue($value);
-
- $i = 1;
- foreach (self::$errorCodes as $errorCode) {
- if ($value === $errorCode) {
- return $i;
- }
- ++$i;
- }
-
- return self::NA();
+ return Information\ExcelError::type($value);
}
/**
@@ -320,15 +328,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isBlank() method in the Information\Value class instead
+ * @see Information\Value::isBlank()
+ *
+ * @return array|bool
*/
public static function isBlank($value = null)
{
- if ($value !== null) {
- $value = self::flattenSingleValue($value);
- }
-
- return $value === null;
+ return Information\Value::isBlank($value);
}
/**
@@ -336,13 +343,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isErr() method in the Information\ErrorValue class instead
+ * @see Information\ErrorValue::isErr()
+ *
+ * @return array|bool
*/
public static function isErr($value = '')
{
- $value = self::flattenSingleValue($value);
-
- return self::isError($value) && (!self::isNa(($value)));
+ return Information\ErrorValue::isErr($value);
}
/**
@@ -350,17 +358,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isError() method in the Information\ErrorValue class instead
+ * @see Information\ErrorValue::isError()
+ *
+ * @return array|bool
*/
public static function isError($value = '')
{
- $value = self::flattenSingleValue($value);
-
- if (!is_string($value)) {
- return false;
- }
-
- return in_array($value, self::$errorCodes);
+ return Information\ErrorValue::isError($value);
}
/**
@@ -368,13 +373,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isNa() method in the Information\ErrorValue class instead
+ * @see Information\ErrorValue::isNa()
+ *
+ * @return array|bool
*/
public static function isNa($value = '')
{
- $value = self::flattenSingleValue($value);
-
- return $value === self::NA();
+ return Information\ErrorValue::isNa($value);
}
/**
@@ -382,19 +388,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool|string
+ * @deprecated 1.23.0 Use the isEven() method in the Information\Value class instead
+ * @see Information\Value::isEven()
+ *
+ * @return array|bool|string
*/
public static function isEven($value = null)
{
- $value = self::flattenSingleValue($value);
-
- if ($value === null) {
- return self::NAME();
- } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
- return self::VALUE();
- }
-
- return $value % 2 == 0;
+ return Information\Value::isEven($value);
}
/**
@@ -402,19 +403,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool|string
+ * @deprecated 1.23.0 Use the isOdd() method in the Information\Value class instead
+ * @see Information\Value::isOdd()
+ *
+ * @return array|bool|string
*/
public static function isOdd($value = null)
{
- $value = self::flattenSingleValue($value);
-
- if ($value === null) {
- return self::NAME();
- } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
- return self::VALUE();
- }
-
- return abs($value) % 2 == 1;
+ return Information\Value::isOdd($value);
}
/**
@@ -422,17 +418,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isNumber() method in the Information\Value class instead
+ * @see Information\Value::isNumber()
+ *
+ * @return array|bool
*/
public static function isNumber($value = null)
{
- $value = self::flattenSingleValue($value);
-
- if (is_string($value)) {
- return false;
- }
-
- return is_numeric($value);
+ return Information\Value::isNumber($value);
}
/**
@@ -440,13 +433,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isLogical() method in the Information\Value class instead
+ * @see Information\Value::isLogical()
+ *
+ * @return array|bool
*/
public static function isLogical($value = null)
{
- $value = self::flattenSingleValue($value);
-
- return is_bool($value);
+ return Information\Value::isLogical($value);
}
/**
@@ -454,13 +448,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isText() method in the Information\Value class instead
+ * @see Information\Value::isText()
+ *
+ * @return array|bool
*/
public static function isText($value = null)
{
- $value = self::flattenSingleValue($value);
-
- return is_string($value) && !self::isError($value);
+ return Information\Value::isText($value);
}
/**
@@ -468,11 +463,14 @@ class Functions
*
* @param mixed $value Value to check
*
- * @return bool
+ * @deprecated 1.23.0 Use the isNonText() method in the Information\Value class instead
+ * @see Information\Value::isNonText()
+ *
+ * @return array|bool
*/
public static function isNonText($value = null)
{
- return !self::isText($value);
+ return Information\Value::isNonText($value);
}
/**
@@ -480,9 +478,12 @@ class Functions
*
* Returns a value converted to a number
*
+ * @deprecated 1.23.0 Use the asNumber() method in the Information\Value class instead
+ * @see Information\Value::asNumber()
+ *
* @param null|mixed $value The value you want converted
*
- * @return number N converts values listed in the following table
+ * @return number|string 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
@@ -493,27 +494,7 @@ class Functions
*/
public static function n($value = null)
{
- while (is_array($value)) {
- $value = array_shift($value);
- }
-
- switch (gettype($value)) {
- case 'double':
- case 'float':
- case 'integer':
- return $value;
- case 'boolean':
- return (int) $value;
- case 'string':
- // Errors
- if ((strlen($value) > 0) && ($value[0] == '#')) {
- return $value;
- }
-
- break;
- }
-
- return 0;
+ return Information\Value::asNumber($value);
}
/**
@@ -521,6 +502,9 @@ class Functions
*
* Returns a number that identifies the type of a value
*
+ * @deprecated 1.23.0 Use the type() method in the Information\Value class instead
+ * @see Information\Value::type()
+ *
* @param null|mixed $value The value you want tested
*
* @return number N converts values listed in the following table
@@ -533,45 +517,13 @@ class Functions
*/
public static function TYPE($value = null)
{
- $value = self::flattenArrayIndexed($value);
- if (is_array($value) && (count($value) > 1)) {
- end($value);
- $a = key($value);
- // Range of cells is an error
- if (self::isCellValue($a)) {
- return 16;
- // Test for Matrix
- } elseif (self::isMatrixValue($a)) {
- return 64;
- }
- } elseif (empty($value)) {
- // Empty Cell
- return 1;
- }
- $value = self::flattenSingleValue($value);
-
- if (($value === null) || (is_float($value)) || (is_int($value))) {
- return 1;
- } elseif (is_bool($value)) {
- return 4;
- } elseif (is_array($value)) {
- return 64;
- } elseif (is_string($value)) {
- // Errors
- if ((strlen($value) > 0) && ($value[0] == '#')) {
- return 16;
- }
-
- return 2;
- }
-
- return 0;
+ return Information\Value::type($value);
}
/**
* Convert a multi-dimensional array to a simple 1-dimensional array.
*
- * @param array $array Array to be flattened
+ * @param array|mixed $array Array to be flattened
*
* @return array Flattened array
*/
@@ -581,30 +533,44 @@ class Functions
return (array) $array;
}
- $arrayValues = [];
- foreach ($array as $value) {
+ $flattened = [];
+ $stack = array_values($array);
+
+ while (!empty($stack)) {
+ $value = array_shift($stack);
+
if (is_array($value)) {
- foreach ($value as $val) {
- if (is_array($val)) {
- foreach ($val as $v) {
- $arrayValues[] = $v;
- }
- } else {
- $arrayValues[] = $val;
- }
- }
+ array_unshift($stack, ...array_values($value));
} else {
- $arrayValues[] = $value;
+ $flattened[] = $value;
}
}
- return $arrayValues;
+ return $flattened;
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return null|mixed
+ */
+ public static function scalar($value)
+ {
+ if (!is_array($value)) {
+ return $value;
+ }
+
+ do {
+ $value = array_pop($value);
+ } while (is_array($value));
+
+ return $value;
}
/**
* Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
*
- * @param array $array Array to be flattened
+ * @param array|mixed $array Array to be flattened
*
* @return array Flattened array
*/
@@ -644,7 +610,7 @@ class Functions
public static function flattenSingleValue($value = '')
{
while (is_array($value)) {
- $value = array_pop($value);
+ $value = array_shift($value);
}
return $value;
@@ -653,26 +619,50 @@ class Functions
/**
* ISFORMULA.
*
- * @param mixed $cellReference The cell to check
- * @param Cell $pCell The current cell (containing this formula)
+ * @deprecated 1.23.0 Use the isFormula() method in the Information\Value class instead
+ * @see Information\Value::isFormula()
*
- * @return bool|string
+ * @param mixed $cellReference The cell to check
+ * @param ?Cell $cell The current cell (containing this formula)
+ *
+ * @return array|bool|string
*/
- public static function isFormula($cellReference = '', Cell $pCell = null)
+ public static function isFormula($cellReference = '', ?Cell $cell = null)
{
- if ($pCell === null) {
- return self::REF();
+ return Information\Value::isFormula($cellReference, $cell);
+ }
+
+ public static function expandDefinedName(string $coordinate, Cell $cell): string
+ {
+ $worksheet = $cell->getWorksheet();
+ $spreadsheet = $worksheet->getParent();
+ // Uppercase coordinate
+ $pCoordinatex = strtoupper($coordinate);
+ // Eliminate leading equal sign
+ $pCoordinatex = (string) preg_replace('/^=/', '', $pCoordinatex);
+ $defined = $spreadsheet->getDefinedName($pCoordinatex, $worksheet);
+ if ($defined !== null) {
+ $worksheet2 = $defined->getWorkSheet();
+ if (!$defined->isFormula() && $worksheet2 !== null) {
+ $coordinate = "'" . $worksheet2->getTitle() . "'!" .
+ (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
+ }
}
- preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
+ return $coordinate;
+ }
- $cellReference = $matches[6] . $matches[7];
- $worksheetName = trim($matches[3], "'");
+ public static function trimTrailingRange(string $coordinate): string
+ {
+ return (string) preg_replace('/:[\\w\$]+$/', '', $coordinate);
+ }
- $worksheet = (!empty($worksheetName))
- ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
- : $pCell->getWorksheet();
+ public static function trimSheetFromCellReference(string $coordinate): string
+ {
+ if (strpos($coordinate, '!') !== false) {
+ $coordinate = substr($coordinate, strrpos($coordinate, '!') + 1);
+ }
- return $worksheet->getCell($cellReference)->isFormula();
+ return $coordinate;
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Information/ErrorValue.php b/PhpOffice/PhpSpreadsheet/Calculation/Information/ErrorValue.php
new file mode 100644
index 0000000..4b9f818
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Information/ErrorValue.php
@@ -0,0 +1,71 @@
+
+ */
+ public const ERROR_CODES = [
+ 'null' => '#NULL!', // 1
+ 'divisionbyzero' => '#DIV/0!', // 2
+ 'value' => '#VALUE!', // 3
+ 'reference' => '#REF!', // 4
+ 'name' => '#NAME?', // 5
+ 'num' => '#NUM!', // 6
+ 'na' => '#N/A', // 7
+ 'gettingdata' => '#GETTING_DATA', // 8
+ 'spill' => '#SPILL!', // 9
+ 'connect' => '#CONNECT!', //10
+ 'blocked' => '#BLOCKED!', //11
+ 'unknown' => '#UNKNOWN!', //12
+ 'field' => '#FIELD!', //13
+ 'calculation' => '#CALC!', //14
+ ];
+
+ /**
+ * List of error codes. Replaced by constant;
+ * previously it was public and updateable, allowing
+ * user to make inappropriate alterations.
+ *
+ * @deprecated 1.25.0 Use ERROR_CODES constant instead.
+ *
+ * @var array
+ */
+ public static $errorCodes = self::ERROR_CODES;
+
+ /**
+ * @param mixed $value
+ */
+ public static function throwError($value): string
+ {
+ return in_array($value, self::ERROR_CODES, true) ? $value : self::ERROR_CODES['value'];
+ }
+
+ /**
+ * ERROR_TYPE.
+ *
+ * @param mixed $value Value to check
+ *
+ * @return array|int|string
+ */
+ public static function type($value = '')
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ $i = 1;
+ foreach (self::ERROR_CODES as $errorCode) {
+ if ($value === $errorCode) {
+ return $i;
+ }
+ ++$i;
+ }
+
+ return self::NA();
+ }
+
+ /**
+ * NULL.
+ *
+ * Returns the error value #NULL!
+ *
+ * @return string #NULL!
+ */
+ public static function null(): string
+ {
+ return self::ERROR_CODES['null'];
+ }
+
+ /**
+ * NaN.
+ *
+ * Returns the error value #NUM!
+ *
+ * @return string #NUM!
+ */
+ public static function NAN(): string
+ {
+ return self::ERROR_CODES['num'];
+ }
+
+ /**
+ * REF.
+ *
+ * Returns the error value #REF!
+ *
+ * @return string #REF!
+ */
+ public static function REF(): string
+ {
+ return self::ERROR_CODES['reference'];
+ }
+
+ /**
+ * NA.
+ *
+ * Excel Function:
+ * =NA()
+ *
+ * Returns the error value #N/A
+ * #N/A is the error value that means "no value is available."
+ *
+ * @return string #N/A!
+ */
+ public static function NA(): string
+ {
+ return self::ERROR_CODES['na'];
+ }
+
+ /**
+ * VALUE.
+ *
+ * Returns the error value #VALUE!
+ *
+ * @return string #VALUE!
+ */
+ public static function VALUE(): string
+ {
+ return self::ERROR_CODES['value'];
+ }
+
+ /**
+ * NAME.
+ *
+ * Returns the error value #NAME?
+ *
+ * @return string #NAME?
+ */
+ public static function NAME(): string
+ {
+ return self::ERROR_CODES['name'];
+ }
+
+ /**
+ * DIV0.
+ *
+ * @return string #DIV/0!
+ */
+ public static function DIV0(): string
+ {
+ return self::ERROR_CODES['divisionbyzero'];
+ }
+
+ /**
+ * CALC.
+ *
+ * @return string #CALC!
+ */
+ public static function CALC(): string
+ {
+ return self::ERROR_CODES['calculation'];
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Information/Value.php b/PhpOffice/PhpSpreadsheet/Calculation/Information/Value.php
new file mode 100644
index 0000000..2e524db
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Information/Value.php
@@ -0,0 +1,328 @@
+getCoordinate()) {
+ return false;
+ }
+
+ $cellValue = Functions::trimTrailingRange($value);
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) {
+ [$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true);
+ if (!empty($worksheet) && $cell->getWorksheet()->getParent()->getSheetByName($worksheet) === null) {
+ return false;
+ }
+ [$column, $row] = Coordinate::indexesFromString($cellValue);
+ if ($column > 16384 || $row > 1048576) {
+ return false;
+ }
+
+ return true;
+ }
+
+ $namedRange = $cell->getWorksheet()->getParent()->getNamedRange($value);
+
+ return $namedRange instanceof NamedRange;
+ }
+
+ /**
+ * IS_EVEN.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isEven($value = null)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ if ($value === null) {
+ return ExcelError::NAME();
+ } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return ExcelError::VALUE();
+ }
+
+ return ((int) fmod($value, 2)) === 0;
+ }
+
+ /**
+ * IS_ODD.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isOdd($value = null)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ if ($value === null) {
+ return ExcelError::NAME();
+ } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return ExcelError::VALUE();
+ }
+
+ return ((int) fmod($value, 2)) !== 0;
+ }
+
+ /**
+ * IS_NUMBER.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isNumber($value = null)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ if (is_string($value)) {
+ return false;
+ }
+
+ return is_numeric($value);
+ }
+
+ /**
+ * IS_LOGICAL.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isLogical($value = null)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ return is_bool($value);
+ }
+
+ /**
+ * IS_TEXT.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isText($value = null)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ return is_string($value) && !ErrorValue::isError($value);
+ }
+
+ /**
+ * IS_NONTEXT.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isNonText($value = null)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ return !self::isText($value);
+ }
+
+ /**
+ * ISFORMULA.
+ *
+ * @param mixed $cellReference The cell to check
+ * @param ?Cell $cell The current cell (containing this formula)
+ *
+ * @return array|bool|string
+ */
+ public static function isFormula($cellReference = '', ?Cell $cell = null)
+ {
+ if ($cell === null) {
+ return ExcelError::REF();
+ }
+
+ $fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
+
+ if (strpos($cellReference, '!') !== false) {
+ $cellReference = Functions::trimSheetFromCellReference($cellReference);
+ $cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
+ if (count($cellReferences) > 1) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $cellReferences, $cell);
+ }
+ }
+
+ $fullCellReference = Functions::trimTrailingRange($fullCellReference);
+
+ preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
+
+ $fullCellReference = $matches[6] . $matches[7];
+ $worksheetName = str_replace("''", "'", trim($matches[2], "'"));
+
+ $worksheet = (!empty($worksheetName))
+ ? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
+ : $cell->getWorksheet();
+
+ return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
+ }
+
+ /**
+ * N.
+ *
+ * Returns a value converted to a number
+ *
+ * @param null|mixed $value The value you want converted
+ *
+ * @return number|string N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number That number value
+ * A date The Excel serialized number of that date
+ * TRUE 1
+ * FALSE 0
+ * An error value The error value
+ * Anything else 0
+ */
+ public static function asNumber($value = null)
+ {
+ while (is_array($value)) {
+ $value = array_shift($value);
+ }
+
+ switch (gettype($value)) {
+ case 'double':
+ case 'float':
+ case 'integer':
+ return $value;
+ case 'boolean':
+ return (int) $value;
+ case 'string':
+ // Errors
+ if ((strlen($value) > 0) && ($value[0] == '#')) {
+ return $value;
+ }
+
+ break;
+ }
+
+ return 0;
+ }
+
+ /**
+ * TYPE.
+ *
+ * Returns a number that identifies the type of a value
+ *
+ * @param null|mixed $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 = null)
+ {
+ $value = Functions::flattenArrayIndexed($value);
+ if (is_array($value) && (count($value) > 1)) {
+ end($value);
+ $a = key($value);
+ // Range of cells is an error
+ if (Functions::isCellValue($a)) {
+ return 16;
+ // Test for Matrix
+ } elseif (Functions::isMatrixValue($a)) {
+ return 64;
+ }
+ } elseif (empty($value)) {
+ // Empty Cell
+ return 1;
+ }
+
+ $value = Functions::flattenSingleValue($value);
+ if (($value === null) || (is_float($value)) || (is_int($value))) {
+ return 1;
+ } elseif (is_bool($value)) {
+ return 4;
+ } elseif (is_array($value)) {
+ return 64;
+ } elseif (is_string($value)) {
+ // Errors
+ if ((strlen($value) > 0) && ($value[0] == '#')) {
+ return 16;
+ }
+
+ return 2;
+ }
+
+ return 0;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php b/PhpOffice/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
new file mode 100644
index 0000000..8b53464
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
@@ -0,0 +1,11 @@
+ 0) && ($returnValue == $argCount);
+ return Logical\Operations::logicalAnd(...$args);
}
/**
@@ -120,10 +89,12 @@ class Logical
*
* 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
+ * 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
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the logicalOr() method in the Logical\Operations class instead
+ * @see Logical\Operations::logicalOr()
*
* @param mixed $args Data values
*
@@ -131,29 +102,15 @@ class Logical
*/
public static function logicalOr(...$args)
{
- $args = Functions::flattenArray($args);
-
- if (count($args) == 0) {
- return Functions::VALUE();
- }
-
- $args = array_filter($args, function ($value) {
- return $value !== null || (is_string($value) && trim($value) == '');
- });
-
- $returnValue = self::countTrueValues($args);
- if (is_string($returnValue)) {
- return $returnValue;
- }
-
- return $returnValue > 0;
+ return Logical\Operations::logicalOr(...$args);
}
/**
* LOGICAL_XOR.
*
* Returns the Exclusive Or logical operation for one or more supplied conditions.
- * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE, and FALSE otherwise.
+ * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
+ * and FALSE otherwise.
*
* Excel Function:
* =XOR(logical1[,logical2[, ...]])
@@ -163,10 +120,12 @@ class Logical
*
* 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
+ * 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
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the logicalXor() method in the Logical\Operations class instead
+ * @see Logical\Operations::logicalXor()
*
* @param mixed $args Data values
*
@@ -174,22 +133,7 @@ class Logical
*/
public static function logicalXor(...$args)
{
- $args = Functions::flattenArray($args);
-
- if (count($args) == 0) {
- return Functions::VALUE();
- }
-
- $args = array_filter($args, function ($value) {
- return $value !== null || (is_string($value) && trim($value) == '');
- });
-
- $returnValue = self::countTrueValues($args);
- if (is_string($returnValue)) {
- return $returnValue;
- }
-
- return $returnValue % 2 == 1;
+ return Logical\Operations::logicalXor(...$args);
}
/**
@@ -204,31 +148,20 @@ class Logical
*
* 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
+ * 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
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the NOT() method in the Logical\Operations class instead
+ * @see Logical\Operations::NOT()
*
* @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
*
- * @return bool|string the boolean inverse of the argument
+ * @return array|bool|string the boolean inverse of the argument
*/
public static function NOT($logical = false)
{
- $logical = Functions::flattenSingleValue($logical);
-
- if (is_string($logical)) {
- $logical = strtoupper($logical);
- if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
- return false;
- } elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
- return true;
- }
-
- return Functions::VALUE();
- }
-
- return !$logical;
+ return Logical\Operations::NOT($logical);
}
/**
@@ -244,19 +177,21 @@ class Logical
* 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.
+ * 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".
+ * 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.
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the statementIf() method in the Logical\Conditional class instead
+ * @see Logical\Conditional::statementIf()
*
* @param mixed $condition Condition to evaluate
* @param mixed $returnIfTrue Value to return when condition is true
@@ -266,11 +201,7 @@ class Logical
*/
public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
{
- $condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition);
- $returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue);
- $returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse);
-
- return ($condition) ? $returnIfTrue : $returnIfFalse;
+ return Logical\Conditional::statementIf($condition, $returnIfTrue, $returnIfFalse);
}
/**
@@ -284,13 +215,18 @@ class Logical
* Expression
* The expression to compare to a list of values.
* value1, value2, ... value_n
- * A list of values that are compared to expression. The SWITCH function is looking for the first value that matches the expression.
+ * A list of values that are compared to expression.
+ * The SWITCH function is looking for the first value that matches the expression.
* result1, result2, ... result_n
- * A list of results. The SWITCH function returns the corresponding result when a value matches expression.
+ * A list of results. The SWITCH function returns the corresponding result when a value
+ * matches expression.
* default
- * Optional. It is the default to return if expression does not match any of the values (value1, value2, ... value_n).
+ * Optional. It is the default to return if expression does not match any of the values
+ * (value1, value2, ... value_n).
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the statementSwitch() method in the Logical\Conditional class instead
+ * @see Logical\Conditional::statementSwitch()
*
* @param mixed $arguments Statement arguments
*
@@ -298,33 +234,7 @@ class Logical
*/
public static function statementSwitch(...$arguments)
{
- $result = Functions::VALUE();
-
- if (count($arguments) > 0) {
- $targetValue = Functions::flattenSingleValue($arguments[0]);
- $argc = count($arguments) - 1;
- $switchCount = floor($argc / 2);
- $switchSatisfied = false;
- $hasDefaultClause = $argc % 2 !== 0;
- $defaultClause = $argc % 2 === 0 ? null : $arguments[count($arguments) - 1];
-
- if ($switchCount) {
- for ($index = 0; $index < $switchCount; ++$index) {
- if ($targetValue == $arguments[$index * 2 + 1]) {
- $result = $arguments[$index * 2 + 2];
- $switchSatisfied = true;
-
- break;
- }
- }
- }
-
- if (!$switchSatisfied) {
- $result = $hasDefaultClause ? $defaultClause : Functions::NA();
- }
- }
-
- return $result;
+ return Logical\Conditional::statementSwitch(...$arguments);
}
/**
@@ -333,7 +243,9 @@ class Logical
* Excel Function:
* =IFERROR(testValue,errorpart)
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the IFERROR() method in the Logical\Conditional class instead
+ * @see Logical\Conditional::IFERROR()
*
* @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
@@ -342,10 +254,7 @@ class Logical
*/
public static function IFERROR($testValue = '', $errorpart = '')
{
- $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
- $errorpart = ($errorpart === null) ? '' : Functions::flattenSingleValue($errorpart);
-
- return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
+ return Logical\Conditional::IFERROR($testValue, $errorpart);
}
/**
@@ -354,7 +263,9 @@ class Logical
* Excel Function:
* =IFNA(testValue,napart)
*
- * @category Logical Functions
+ * @deprecated 1.17.0
+ * Use the IFNA() method in the Logical\Conditional class instead
+ * @see Logical\Conditional::IFNA()
*
* @param mixed $testValue Value to check, is also the value returned when not an NA
* @param mixed $napart Value to return when testValue is an NA condition
@@ -363,9 +274,30 @@ class Logical
*/
public static function IFNA($testValue = '', $napart = '')
{
- $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
- $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
+ return Logical\Conditional::IFNA($testValue, $napart);
+ }
- return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
+ /**
+ * IFS.
+ *
+ * Excel Function:
+ * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
+ *
+ * testValue1 ... testValue_n
+ * Conditions to Evaluate
+ * returnIfTrue1 ... returnIfTrue_n
+ * Value returned if corresponding testValue (nth) was true
+ *
+ * @deprecated 1.17.0
+ * Use the IFS() method in the Logical\Conditional class instead
+ * @see Logical\Conditional::IFS()
+ *
+ * @param mixed ...$arguments Statement arguments
+ *
+ * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
+ */
+ public static function IFS(...$arguments)
+ {
+ return Logical\Conditional::IFS(...$arguments);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Logical/Boolean.php b/PhpOffice/PhpSpreadsheet/Calculation/Logical/Boolean.php
new file mode 100644
index 0000000..8f1e935
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Logical/Boolean.php
@@ -0,0 +1,36 @@
+ 0) {
+ $targetValue = Functions::flattenSingleValue($arguments[0]);
+ $argc = count($arguments) - 1;
+ $switchCount = floor($argc / 2);
+ $hasDefaultClause = $argc % 2 !== 0;
+ $defaultClause = $argc % 2 === 0 ? null : $arguments[$argc];
+
+ $switchSatisfied = false;
+ if ($switchCount > 0) {
+ for ($index = 0; $index < $switchCount; ++$index) {
+ if ($targetValue == Functions::flattenSingleValue($arguments[$index * 2 + 1])) {
+ $result = $arguments[$index * 2 + 2];
+ $switchSatisfied = true;
+
+ break;
+ }
+ }
+ }
+
+ if ($switchSatisfied !== true) {
+ $result = $hasDefaultClause ? $defaultClause : ExcelError::NA();
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * IFERROR.
+ *
+ * Excel Function:
+ * =IFERROR(testValue,errorpart)
+ *
+ * @param mixed $testValue Value to check, is also the value returned when no error
+ * Or can be an array of values
+ * @param mixed $errorpart Value to return when testValue is an error condition
+ * Note that this can be an array value to be returned
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ * If an array of values is passed as the $testValue argument, then the returned result will also be
+ * an array with the same dimensions
+ */
+ public static function IFERROR($testValue = '', $errorpart = '')
+ {
+ if (is_array($testValue)) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $errorpart);
+ }
+
+ $errorpart = $errorpart ?? '';
+ $testValue = $testValue ?? 0; // this is how Excel handles empty cell
+
+ return self::statementIf(ErrorValue::isError($testValue), $errorpart, $testValue);
+ }
+
+ /**
+ * IFNA.
+ *
+ * Excel Function:
+ * =IFNA(testValue,napart)
+ *
+ * @param mixed $testValue Value to check, is also the value returned when not an NA
+ * Or can be an array of values
+ * @param mixed $napart Value to return when testValue is an NA condition
+ * Note that this can be an array value to be returned
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ * If an array of values is passed as the $testValue argument, then the returned result will also be
+ * an array with the same dimensions
+ */
+ public static function IFNA($testValue = '', $napart = '')
+ {
+ if (is_array($testValue)) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $napart);
+ }
+
+ $napart = $napart ?? '';
+ $testValue = $testValue ?? 0; // this is how Excel handles empty cell
+
+ return self::statementIf(ErrorValue::isNa($testValue), $napart, $testValue);
+ }
+
+ /**
+ * IFS.
+ *
+ * Excel Function:
+ * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
+ *
+ * testValue1 ... testValue_n
+ * Conditions to Evaluate
+ * returnIfTrue1 ... returnIfTrue_n
+ * Value returned if corresponding testValue (nth) was true
+ *
+ * @param mixed ...$arguments Statement arguments
+ * Note that this can be an array value to be returned
+ *
+ * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
+ */
+ public static function IFS(...$arguments)
+ {
+ $argumentCount = count($arguments);
+
+ if ($argumentCount % 2 != 0) {
+ return ExcelError::NA();
+ }
+ // We use instance of Exception as a falseValue in order to prevent string collision with value in cell
+ $falseValueException = new Exception();
+ for ($i = 0; $i < $argumentCount; $i += 2) {
+ $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
+ $returnIfTrue = ($arguments[$i + 1] === null) ? '' : $arguments[$i + 1];
+ $result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
+
+ if ($result !== $falseValueException) {
+ return $result;
+ }
+ }
+
+ return ExcelError::NA();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Logical/Operations.php b/PhpOffice/PhpSpreadsheet/Calculation/Logical/Operations.php
new file mode 100644
index 0000000..2e2faa1
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Logical/Operations.php
@@ -0,0 +1,207 @@
+ 0) && ($returnValue == $argCount);
+ }
+
+ /**
+ * 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
+ *
+ * @param mixed $args Data values
+ *
+ * @return bool|string the logical OR of the arguments
+ */
+ public static function logicalOr(...$args)
+ {
+ $args = Functions::flattenArray($args);
+
+ if (count($args) == 0) {
+ return ExcelError::VALUE();
+ }
+
+ $args = array_filter($args, function ($value) {
+ return $value !== null || (is_string($value) && trim($value) == '');
+ });
+
+ $returnValue = self::countTrueValues($args);
+ if (is_string($returnValue)) {
+ return $returnValue;
+ }
+
+ return $returnValue > 0;
+ }
+
+ /**
+ * LOGICAL_XOR.
+ *
+ * Returns the Exclusive Or logical operation for one or more supplied conditions.
+ * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
+ * and FALSE otherwise.
+ *
+ * Excel Function:
+ * =XOR(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
+ *
+ * @param mixed $args Data values
+ *
+ * @return bool|string the logical XOR of the arguments
+ */
+ public static function logicalXor(...$args)
+ {
+ $args = Functions::flattenArray($args);
+
+ if (count($args) == 0) {
+ return ExcelError::VALUE();
+ }
+
+ $args = array_filter($args, function ($value) {
+ return $value !== null || (is_string($value) && trim($value) == '');
+ });
+
+ $returnValue = self::countTrueValues($args);
+ if (is_string($returnValue)) {
+ return $returnValue;
+ }
+
+ return $returnValue % 2 == 1;
+ }
+
+ /**
+ * 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
+ *
+ * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+ * Or can be an array of values
+ *
+ * @return array|bool|string the boolean inverse of the argument
+ * If an array of values is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function NOT($logical = false)
+ {
+ if (is_array($logical)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $logical);
+ }
+
+ if (is_string($logical)) {
+ $logical = mb_strtoupper($logical, 'UTF-8');
+ if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
+ return false;
+ } elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
+ return true;
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ return !$logical;
+ }
+
+ /**
+ * @return int|string
+ */
+ private static function countTrueValues(array $args)
+ {
+ $trueValueCount = 0;
+
+ foreach ($args as $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $trueValueCount += $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $trueValueCount += ((int) $arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = mb_strtoupper($arg, 'UTF-8');
+ if (($arg == 'TRUE') || ($arg == Calculation::getTRUE())) {
+ $arg = true;
+ } elseif (($arg == 'FALSE') || ($arg == Calculation::getFALSE())) {
+ $arg = false;
+ } else {
+ return ExcelError::VALUE();
+ }
+ $trueValueCount += ($arg != 0);
+ }
+ }
+
+ return $trueValueCount;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef.php
old mode 100755
new mode 100644
index 4843430..354cb83
--- a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef.php
@@ -2,11 +2,20 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Address;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Indirect;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Lookup;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Offset;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\RowColumnInformation;
+use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
-use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
-use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+/**
+ * @deprecated 1.18.0
+ */
class LookupRef
{
/**
@@ -17,103 +26,53 @@ class LookupRef
* Excel Function:
* =ADDRESS(row, column, [relativity], [referenceStyle], [sheetText])
*
+ * @deprecated 1.18.0
+ * Use the cell() method in the LookupRef\Address class instead
+ * @see LookupRef\Address::cell()
+ *
* @param mixed $row Row number to use in the cell reference
* @param mixed $column Column number to use in the cell reference
* @param int $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
+ * 2 Absolute row; relative column
+ * 3 Relative row; absolute column
+ * 4 Relative
* @param bool $referenceStyle A logical value that specifies the A1 or R1C1 reference style.
- * TRUE or omitted CELL_ADDRESS returns an A1-style reference
+ * TRUE or omitted CELL_ADDRESS returns an A1-style reference
* FALSE CELL_ADDRESS returns an R1C1-style reference
- * @param string $sheetText Optional Name of worksheet to use
+ * @param array|string $sheetText Optional Name of worksheet to use
*
- * @return string
+ * @return array|string
*/
public static function cellAddress($row, $column, $relativity = 1, $referenceStyle = true, $sheetText = '')
{
- $row = Functions::flattenSingleValue($row);
- $column = Functions::flattenSingleValue($column);
- $relativity = Functions::flattenSingleValue($relativity);
- $sheetText = Functions::flattenSingleValue($sheetText);
-
- if (($row < 1) || ($column < 1)) {
- return Functions::VALUE();
- }
-
- if ($sheetText > '') {
- if (strpos($sheetText, ' ') !== false) {
- $sheetText = "'" . $sheetText . "'";
- }
- $sheetText .= '!';
- }
- if ((!is_bool($referenceStyle)) || $referenceStyle) {
- $rowRelative = $columnRelative = '$';
- $column = Coordinate::stringFromColumnIndex($column);
- if (($relativity == 2) || ($relativity == 4)) {
- $columnRelative = '';
- }
- if (($relativity == 3) || ($relativity == 4)) {
- $rowRelative = '';
- }
-
- return $sheetText . $columnRelative . $column . $rowRelative . $row;
- }
- if (($relativity == 2) || ($relativity == 4)) {
- $column = '[' . $column . ']';
- }
- if (($relativity == 3) || ($relativity == 4)) {
- $row = '[' . $row . ']';
- }
-
- return $sheetText . 'R' . $row . 'C' . $column;
+ return Address::cell($row, $column, $relativity, $referenceStyle, $sheetText);
}
/**
* 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.
+ * 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 1.
*
* Excel Function:
* =COLUMN([cellAddress])
*
+ * @deprecated 1.18.0
+ * Use the COLUMN() method in the LookupRef\RowColumnInformation class instead
+ * @see LookupRef\RowColumnInformation::COLUMN()
+ *
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
*
- * @return int|int[]
+ * @return int|int[]|string
*/
- public static function COLUMN($cellAddress = null)
+ public static function COLUMN($cellAddress = null, ?Cell $cell = null)
{
- if ($cellAddress === null || trim($cellAddress) === '') {
- return 0;
- }
-
- if (is_array($cellAddress)) {
- foreach ($cellAddress as $columnKey => $value) {
- $columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
-
- return (int) Coordinate::columnIndexFromString($columnKey);
- }
- } else {
- [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- if (strpos($cellAddress, ':') !== false) {
- [$startAddress, $endAddress] = explode(':', $cellAddress);
- $startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
- $endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
- $returnValue = [];
- do {
- $returnValue[] = (int) Coordinate::columnIndexFromString($startAddress);
- } while ($startAddress++ != $endAddress);
-
- return $returnValue;
- }
- $cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
-
- return (int) Coordinate::columnIndexFromString($cellAddress);
- }
+ return RowColumnInformation::COLUMN($cellAddress, $cell);
}
/**
@@ -124,73 +83,44 @@ class LookupRef
* Excel Function:
* =COLUMNS(cellAddress)
*
- * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
+ * @deprecated 1.18.0
+ * Use the COLUMNS() method in the LookupRef\RowColumnInformation class instead
+ * @see LookupRef\RowColumnInformation::COLUMNS()
*
- * @return int The number of columns in cellAddress
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of columns
+ *
+ * @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/
public static function COLUMNS($cellAddress = null)
{
- if ($cellAddress === null || $cellAddress === '') {
- return 1;
- } elseif (!is_array($cellAddress)) {
- return Functions::VALUE();
- }
-
- reset($cellAddress);
- $isMatrix = (is_numeric(key($cellAddress)));
- [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
-
- if ($isMatrix) {
- return $rows;
- }
-
- return $columns;
+ return RowColumnInformation::COLUMNS($cellAddress);
}
/**
* 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.
+ * 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 1.
*
* Excel Function:
* =ROW([cellAddress])
*
+ * @deprecated 1.18.0
+ * Use the ROW() method in the LookupRef\RowColumnInformation class instead
+ * @see LookupRef\RowColumnInformation::ROW()
+ *
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
*
- * @return int or array of integer
+ * @return int|mixed[]|string
*/
- public static function ROW($cellAddress = null)
+ public static function ROW($cellAddress = null, ?Cell $cell = null)
{
- if ($cellAddress === null || trim($cellAddress) === '') {
- return 0;
- }
-
- if (is_array($cellAddress)) {
- foreach ($cellAddress as $columnKey => $rowValue) {
- foreach ($rowValue as $rowKey => $cellValue) {
- return (int) preg_replace('/\D/', '', $rowKey);
- }
- }
- } else {
- [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- if (strpos($cellAddress, ':') !== false) {
- [$startAddress, $endAddress] = explode(':', $cellAddress);
- $startAddress = preg_replace('/\D/', '', $startAddress);
- $endAddress = preg_replace('/\D/', '', $endAddress);
- $returnValue = [];
- do {
- $returnValue[][] = (int) $startAddress;
- } while ($startAddress++ != $endAddress);
-
- return $returnValue;
- }
- [$cellAddress] = explode(':', $cellAddress);
-
- return (int) preg_replace('/\D/', '', $cellAddress);
- }
+ return RowColumnInformation::ROW($cellAddress, $cell);
}
/**
@@ -201,27 +131,18 @@ class LookupRef
* Excel Function:
* =ROWS(cellAddress)
*
- * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @deprecated 1.18.0
+ * Use the ROWS() method in the LookupRef\RowColumnInformation class instead
+ * @see LookupRef\RowColumnInformation::ROWS()
*
- * @return int The number of rows in cellAddress
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of rows
+ *
+ * @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/
public static function ROWS($cellAddress = null)
{
- if ($cellAddress === null || $cellAddress === '') {
- return 1;
- } elseif (!is_array($cellAddress)) {
- return Functions::VALUE();
- }
-
- reset($cellAddress);
- $isMatrix = (is_numeric(key($cellAddress)));
- [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
-
- if ($isMatrix) {
- return $columns;
- }
-
- return $rows;
+ return RowColumnInformation::ROWS($cellAddress);
}
/**
@@ -230,31 +151,19 @@ class LookupRef
* Excel Function:
* =HYPERLINK(linkURL,displayName)
*
- * @category Logical Functions
+ * @deprecated 1.18.0
+ * Use the set() method in the LookupRef\Hyperlink class instead
+ * @see LookupRef\Hyperlink::set()
*
- * @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
- * @param Cell $pCell The cell to set the hyperlink in
+ * @param mixed $linkURL Expect string. Value to check, is also the value returned when no error
+ * @param mixed $displayName Expect string. Value to return when testValue is an error condition
+ * @param Cell $cell The cell to set the hyperlink in
*
- * @return mixed The value of $displayName (or $linkURL if $displayName was blank)
+ * @return string The value of $displayName (or $linkURL if $displayName was blank)
*/
- public static function HYPERLINK($linkURL = '', $displayName = null, Cell $pCell = null)
+ public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $cell = null)
{
- $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
- $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
-
- if ((!is_object($pCell)) || (trim($linkURL) == '')) {
- return Functions::REF();
- }
-
- if ((is_object($displayName)) || trim($displayName) == '') {
- $displayName = $linkURL;
- }
-
- $pCell->getHyperlink()->setUrl($linkURL);
- $pCell->getHyperlink()->setTooltip($displayName);
-
- return $displayName;
+ return LookupRef\Hyperlink::set($linkURL, $displayName, $cell);
}
/**
@@ -266,54 +175,20 @@ class LookupRef
* Excel Function:
* =INDIRECT(cellAddress)
*
+ * @deprecated 1.18.0
+ * Use the INDIRECT() method in the LookupRef\Indirect class instead
+ * @see LookupRef\Indirect::INDIRECT()
+ *
+ * @param array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
+ * @param Cell $cell The current cell (containing this formula)
+ *
+ * @return array|string An array containing a cell or range of cells, or a string on error
+ *
* NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
- *
- * @param null|array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
- * @param Cell $pCell The current cell (containing this formula)
- *
- * @return mixed The cells referenced by cellAddress
- *
- * @todo Support for the optional a1 parameter introduced in Excel 2010
*/
- public static function INDIRECT($cellAddress = null, Cell $pCell = null)
+ public static function INDIRECT($cellAddress, Cell $cell)
{
- $cellAddress = Functions::flattenSingleValue($cellAddress);
- if ($cellAddress === null || $cellAddress === '') {
- return Functions::REF();
- }
-
- $cellAddress1 = $cellAddress;
- $cellAddress2 = null;
- if (strpos($cellAddress, ':') !== false) {
- [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
- }
-
- if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
- (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))) {
- if (!preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $cellAddress1, $matches)) {
- return Functions::REF();
- }
-
- if (strpos($cellAddress, '!') !== false) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractNamedRange($cellAddress, $pSheet, false);
- }
-
- if (strpos($cellAddress, '!') !== false) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
+ return Indirect::INDIRECT($cellAddress, true, $cell);
}
/**
@@ -326,88 +201,33 @@ class LookupRef
* Excel Function:
* =OFFSET(cellAddress, rows, cols, [height], [width])
*
- * @param null|string $cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
- * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
- * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
- * Using 5 as the rows argument specifies that the upper-left cell in the reference is
- * five rows below reference. Rows can be positive (which means below the starting reference)
- * or negative (which means above the starting reference).
- * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell of the result
- * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
- * reference is five columns to the right of reference. Cols can be positive (which means
- * to the right of the starting reference) or negative (which means to the left of the
- * starting reference).
- * @param mixed $height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
- * @param mixed $width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
- * @param null|Cell $pCell
+ * @deprecated 1.18.0
+ * Use the OFFSET() method in the LookupRef\Offset class instead
+ * @see LookupRef\Offset::OFFSET()
*
- * @return string A reference to a cell or range of cells
+ * @param null|string $cellAddress The reference from which you want to base the offset.
+ * Reference must refer to a cell or range of adjacent cells;
+ * otherwise, OFFSET returns the #VALUE! error value.
+ * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
+ * Using 5 as the rows argument specifies that the upper-left cell in the
+ * reference is five rows below reference. Rows can be positive (which means
+ * below the starting reference) or negative (which means above the starting
+ * reference).
+ * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell
+ * of the result to refer to. Using 5 as the cols argument specifies that the
+ * upper-left cell in the reference is five columns to the right of reference.
+ * Cols can be positive (which means to the right of the starting reference)
+ * or negative (which means to the left of the starting reference).
+ * @param mixed $height The height, in number of rows, that you want the returned reference to be.
+ * Height must be a positive number.
+ * @param mixed $width The width, in number of columns, that you want the returned reference to be.
+ * Width must be a positive number.
+ *
+ * @return array|string An array containing a cell or range of cells, or a string on error
*/
- public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, Cell $pCell = null)
+ public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null)
{
- $rows = Functions::flattenSingleValue($rows);
- $columns = Functions::flattenSingleValue($columns);
- $height = Functions::flattenSingleValue($height);
- $width = Functions::flattenSingleValue($width);
- if ($cellAddress === null) {
- return 0;
- }
-
- if (!is_object($pCell)) {
- return Functions::REF();
- }
-
- $sheetName = null;
- if (strpos($cellAddress, '!')) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- }
- if (strpos($cellAddress, ':')) {
- [$startCell, $endCell] = explode(':', $cellAddress);
- } else {
- $startCell = $endCell = $cellAddress;
- }
- [$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
- [$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
-
- $startCellRow += $rows;
- $startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
- $startCellColumn += $columns;
-
- if (($startCellRow <= 0) || ($startCellColumn < 0)) {
- return Functions::REF();
- }
- $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
- if (($width != null) && (!is_object($width))) {
- $endCellColumn = $startCellColumn + $width - 1;
- } else {
- $endCellColumn += $columns;
- }
- $startCellColumn = Coordinate::stringFromColumnIndex($startCellColumn + 1);
-
- if (($height != null) && (!is_object($height))) {
- $endCellRow = $startCellRow + $height - 1;
- } else {
- $endCellRow += $rows;
- }
-
- if (($endCellRow <= 0) || ($endCellColumn < 0)) {
- return Functions::REF();
- }
- $endCellColumn = Coordinate::stringFromColumnIndex($endCellColumn + 1);
-
- $cellAddress = $startCellColumn . $startCellRow;
- if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
- $cellAddress .= ':' . $endCellColumn . $endCellRow;
- }
-
- if ($sheetName !== null) {
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
+ return Offset::OFFSET($cellAddress, $rows, $columns, $height, $width, $cell);
}
/**
@@ -419,39 +239,15 @@ class LookupRef
* Excel Function:
* =CHOOSE(index_num, value1, [value2], ...)
*
- * @param mixed $index_num Specifies which value argument is selected.
- * Index_num must be a number between 1 and 254, or a formula or reference to a cell containing a number
- * between 1 and 254.
- * @param mixed $value1 ... Value1 is required, subsequent values are optional.
- * Between 1 to 254 value arguments from which CHOOSE selects a value or an action to perform based on
- * index_num. The arguments can be numbers, cell references, defined names, formulas, functions, or
- * text.
+ * @deprecated 1.18.0
+ * Use the choose() method in the LookupRef\Selection class instead
+ * @see LookupRef\Selection::choose()
*
* @return mixed The selected value
*/
public static function CHOOSE(...$chooseArgs)
{
- $chosenEntry = Functions::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 Functions::VALUE();
- }
- $chosenEntry = floor($chosenEntry);
- if (($chosenEntry < 0) || ($chosenEntry > $entryCount)) {
- return Functions::VALUE();
- }
-
- if (is_array($chooseArgs[$chosenEntry])) {
- return Functions::flattenArray($chooseArgs[$chosenEntry]);
- }
-
- return $chooseArgs[$chosenEntry];
+ return LookupRef\Selection::choose(...$chooseArgs);
}
/**
@@ -462,153 +258,20 @@ class LookupRef
* Excel Function:
* =MATCH(lookup_value, lookup_array, [match_type])
*
+ * @deprecated 1.18.0
+ * Use the MATCH() method in the LookupRef\ExcelMatch class instead
+ * @see LookupRef\ExcelMatch::MATCH()
+ *
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupArray The range of cells being searched
* @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
* If match_type is 1 or -1, the list has to be ordered.
*
- * @return int|string The relative position of the found item
+ * @return array|int|string The relative position of the found item
*/
public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
{
- $lookupArray = Functions::flattenArray($lookupArray);
- $lookupValue = Functions::flattenSingleValue($lookupValue);
- $matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
-
- // MATCH is not case sensitive, so we convert lookup value to be lower cased in case it's string type.
- if (is_string($lookupValue)) {
- $lookupValue = StringHelper::strToLower($lookupValue);
- }
-
- // Lookup_value type has to be number, text, or logical values
- if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
- return Functions::NA();
- }
-
- // Match_type is 0, 1 or -1
- if (($matchType !== 0) && ($matchType !== -1) && ($matchType !== 1)) {
- return Functions::NA();
- }
-
- // Lookup_array should not be empty
- $lookupArraySize = count($lookupArray);
- if ($lookupArraySize <= 0) {
- return Functions::NA();
- }
-
- // Lookup_array should contain only number, text, or logical values, or empty (null) cells
- foreach ($lookupArray as $i => $lookupArrayValue) {
- // check the type of the value
- if ((!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) &&
- (!is_bool($lookupArrayValue)) && ($lookupArrayValue !== null)
- ) {
- return Functions::NA();
- }
- // Convert strings to lowercase for case-insensitive testing
- if (is_string($lookupArrayValue)) {
- $lookupArray[$i] = StringHelper::strToLower($lookupArrayValue);
- }
- if (($lookupArrayValue === null) && (($matchType == 1) || ($matchType == -1))) {
- $lookupArray = array_slice($lookupArray, 0, $i - 1);
- }
- }
-
- if ($matchType == 1) {
- // If match_type is 1 the list has to be processed from last to first
-
- $lookupArray = array_reverse($lookupArray);
- $keySet = array_reverse(array_keys($lookupArray));
- }
-
- // **
- // find the match
- // **
-
- if ($matchType === 0 || $matchType === 1) {
- foreach ($lookupArray as $i => $lookupArrayValue) {
- $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
- $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
- $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
- $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
-
- if ($matchType === 0) {
- if ($typeMatch && is_string($lookupValue) && (bool) preg_match('/([\?\*])/', $lookupValue)) {
- $splitString = $lookupValue;
- $chars = array_map(function ($i) use ($splitString) {
- return mb_substr($splitString, $i, 1);
- }, range(0, mb_strlen($splitString) - 1));
-
- $length = count($chars);
- $pattern = '/^';
- for ($j = 0; $j < $length; ++$j) {
- if ($chars[$j] === '~') {
- if (isset($chars[$j + 1])) {
- if ($chars[$j + 1] === '*') {
- $pattern .= preg_quote($chars[$j + 1], '/');
- ++$j;
- } elseif ($chars[$j + 1] === '?') {
- $pattern .= preg_quote($chars[$j + 1], '/');
- ++$j;
- }
- } else {
- $pattern .= preg_quote($chars[$j], '/');
- }
- } elseif ($chars[$j] === '*') {
- $pattern .= '.*';
- } elseif ($chars[$j] === '?') {
- $pattern .= '.{1}';
- } else {
- $pattern .= preg_quote($chars[$j], '/');
- }
- }
-
- $pattern .= '$/';
- if ((bool) preg_match($pattern, $lookupArrayValue)) {
- // exact match
- return $i + 1;
- }
- } elseif ($exactMatch) {
- // exact match
- return $i + 1;
- }
- } elseif (($matchType === 1) && $typeMatch && ($lookupArrayValue <= $lookupValue)) {
- $i = array_search($i, $keySet);
-
- // The current value is the (first) match
- return $i + 1;
- }
- }
- } else {
- $maxValueKey = null;
-
- // The basic algorithm is:
- // Iterate and keep the highest match until the next element is smaller than the searched value.
- // Return immediately if perfect match is found
- foreach ($lookupArray as $i => $lookupArrayValue) {
- $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
- $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
- $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
- $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
-
- if ($exactMatch) {
- // Another "special" case. If a perfect match is found,
- // the algorithm gives up immediately
- return $i + 1;
- } elseif ($typeMatch & $lookupArrayValue >= $lookupValue) {
- $maxValueKey = $i + 1;
- } elseif ($typeMatch & $lookupArrayValue < $lookupValue) {
- //Excel algorithm gives up immediately if the first element is smaller than the searched value
- break;
- }
- }
-
- if ($maxValueKey !== null) {
- return $maxValueKey;
- }
- }
-
- // Unsuccessful in finding a match, return #N/A error value
- return Functions::NA();
+ return LookupRef\ExcelMatch::MATCH($lookupValue, $lookupArray, $matchType);
}
/**
@@ -619,254 +282,94 @@ class LookupRef
* Excel Function:
* =INDEX(range_array, row_num, [column_num])
*
- * @param mixed $arrayValues A range of cells or an array constant
- * @param mixed $rowNum The row in array from which to return a value. If row_num is omitted, column_num is required.
- * @param mixed $columnNum The column in array from which to return a value. If column_num is omitted, row_num is required.
+ * @deprecated 1.18.0
+ * Use the index() method in the LookupRef\Matrix class instead
+ * @see LookupRef\Matrix::index()
+ *
+ * @param mixed $rowNum The row in the array or range from which to return a value.
+ * If row_num is omitted, column_num is required.
+ * @param mixed $columnNum The column in the array or range from which to return a value.
+ * If column_num is omitted, row_num is required.
+ * @param mixed $matrix
*
* @return mixed the value of a specified cell or array of cells
*/
- public static function INDEX($arrayValues, $rowNum = 0, $columnNum = 0)
+ public static function INDEX($matrix, $rowNum = 0, $columnNum = 0)
{
- $rowNum = Functions::flattenSingleValue($rowNum);
- $columnNum = Functions::flattenSingleValue($columnNum);
-
- if (($rowNum < 0) || ($columnNum < 0)) {
- return Functions::VALUE();
- }
-
- if (!is_array($arrayValues) || ($rowNum > count($arrayValues))) {
- return Functions::REF();
- }
-
- $rowKeys = array_keys($arrayValues);
- $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
-
- if ($columnNum > count($columnKeys)) {
- return Functions::VALUE();
- } elseif ($columnNum == 0) {
- if ($rowNum == 0) {
- return $arrayValues;
- }
- $rowNum = $rowKeys[--$rowNum];
- $returnArray = [];
- foreach ($arrayValues as $arrayColumn) {
- if (is_array($arrayColumn)) {
- if (isset($arrayColumn[$rowNum])) {
- $returnArray[] = $arrayColumn[$rowNum];
- } else {
- return [$rowNum => $arrayValues[$rowNum]];
- }
- } else {
- return $arrayValues[$rowNum];
- }
- }
-
- return $returnArray;
- }
- $columnNum = $columnKeys[--$columnNum];
- if ($rowNum > count($rowKeys)) {
- return Functions::VALUE();
- } elseif ($rowNum == 0) {
- return $arrayValues[$columnNum];
- }
- $rowNum = $rowKeys[--$rowNum];
-
- return $arrayValues[$rowNum][$columnNum];
+ return Matrix::index($matrix, $rowNum, $columnNum);
}
/**
* TRANSPOSE.
*
+ * @deprecated 1.18.0
+ * Use the transpose() method in the LookupRef\Matrix class instead
+ * @see LookupRef\Matrix::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
+ * 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 = [];
- if (!is_array($matrixData)) {
- $matrixData = [[$matrixData]];
- }
-
- $column = 0;
- foreach ($matrixData as $matrixRow) {
- $row = 0;
- foreach ($matrixRow as $matrixCell) {
- $returnMatrix[$row][$column] = $matrixCell;
- ++$row;
- }
- ++$column;
- }
-
- return $returnMatrix;
- }
-
- private static function vlookupSort($a, $b)
- {
- reset($a);
- $firstColumn = key($a);
- $aLower = StringHelper::strToLower($a[$firstColumn]);
- $bLower = StringHelper::strToLower($b[$firstColumn]);
- if ($aLower == $bLower) {
- return 0;
- }
-
- return ($aLower < $bLower) ? -1 : 1;
+ return Matrix::transpose($matrixData);
}
/**
* 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.
+ * 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.
+ *
+ * @deprecated 1.18.0
+ * Use the lookup() method in the LookupRef\VLookup class instead
+ * @see LookupRef\VLookup::lookup()
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_array The range of cells being searched
- * @param mixed $index_number The column number in table_array from which the matching value must be returned. The first column is 1.
+ * @param mixed $index_number The column number in table_array from which the matching value must be returned.
+ * The first column is 1.
* @param mixed $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 = Functions::flattenSingleValue($lookup_value);
- $index_number = Functions::flattenSingleValue($index_number);
- $not_exact_match = Functions::flattenSingleValue($not_exact_match);
-
- // index_number must be greater than or equal to 1
- if ($index_number < 1) {
- return Functions::VALUE();
- }
-
- // index_number must be less than or equal to the number of columns in lookup_array
- if ((!is_array($lookup_array)) || (empty($lookup_array))) {
- return Functions::REF();
- }
- $f = array_keys($lookup_array);
- $firstRow = array_pop($f);
- if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
- return Functions::REF();
- }
- $columnKeys = array_keys($lookup_array[$firstRow]);
- $returnColumn = $columnKeys[--$index_number];
- $firstColumn = array_shift($columnKeys);
-
- if (!$not_exact_match) {
- uasort($lookup_array, ['self', 'vlookupSort']);
- }
-
- $lookupLower = StringHelper::strToLower($lookup_value);
- $rowNumber = $rowValue = false;
- foreach ($lookup_array as $rowKey => $rowData) {
- $firstLower = StringHelper::strToLower($rowData[$firstColumn]);
-
- // break if we have passed possible keys
- if ((is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) ||
- (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))) {
- break;
- }
- // remember the last key, but only if datatypes match
- if ((is_numeric($lookup_value) && is_numeric($rowData[$firstColumn])) ||
- (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))) {
- if ($not_exact_match) {
- $rowNumber = $rowKey;
-
- continue;
- } elseif (($firstLower == $lookupLower)
- // Spreadsheets software returns first exact match,
- // we have sorted and we might have broken key orders
- // we want the first one (by its initial index)
- && (($rowNumber == false) || ($rowKey < $rowNumber))
- ) {
- $rowNumber = $rowKey;
- }
- }
- }
-
- if ($rowNumber !== false) {
- // return the appropriate value
- return $lookup_array[$rowNumber][$returnColumn];
- }
-
- return Functions::NA();
+ return VLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
}
/**
* HLOOKUP
- * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value in the same column based on the index_number.
+ * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
+ * in the same column based on the index_number.
+ *
+ * @deprecated 1.18.0
+ * Use the lookup() method in the LookupRef\HLookup class instead
+ * @see LookupRef\HLookup::lookup()
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_array The range of cells being searched
- * @param mixed $index_number The row number in table_array from which the matching value must be returned. The first row is 1.
+ * @param mixed $index_number The row number in table_array from which the matching value must be returned.
+ * The first row is 1.
* @param mixed $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 HLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
{
- $lookup_value = Functions::flattenSingleValue($lookup_value);
- $index_number = Functions::flattenSingleValue($index_number);
- $not_exact_match = Functions::flattenSingleValue($not_exact_match);
-
- // index_number must be greater than or equal to 1
- if ($index_number < 1) {
- return Functions::VALUE();
- }
-
- // index_number must be less than or equal to the number of columns in lookup_array
- if ((!is_array($lookup_array)) || (empty($lookup_array))) {
- return Functions::REF();
- }
- $f = array_keys($lookup_array);
- $firstRow = array_pop($f);
- if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array))) {
- return Functions::REF();
- }
-
- $firstkey = $f[0] - 1;
- $returnColumn = $firstkey + $index_number;
- $firstColumn = array_shift($f);
- $rowNumber = null;
- foreach ($lookup_array[$firstColumn] as $rowKey => $rowData) {
- // break if we have passed possible keys
- $bothNumeric = is_numeric($lookup_value) && is_numeric($rowData);
- $bothNotNumeric = !is_numeric($lookup_value) && !is_numeric($rowData);
- $lookupLower = StringHelper::strToLower($lookup_value);
- $rowDataLower = StringHelper::strToLower($rowData);
-
- if ($not_exact_match && (
- ($bothNumeric && $rowData > $lookup_value) ||
- ($bothNotNumeric && $rowDataLower > $lookupLower)
- )) {
- break;
- }
-
- // Remember the last key, but only if datatypes match (as in VLOOKUP)
- if ($bothNumeric || $bothNotNumeric) {
- if ($not_exact_match) {
- $rowNumber = $rowKey;
-
- continue;
- } elseif ($rowDataLower === $lookupLower
- && ($rowNumber === null || $rowKey < $rowNumber)
- ) {
- $rowNumber = $rowKey;
- }
- }
- }
-
- if ($rowNumber !== null) {
- // otherwise return the appropriate value
- return $lookup_array[$returnColumn][$rowNumber];
- }
-
- return Functions::NA();
+ return HLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
}
/**
* LOOKUP
* The LOOKUP function searches for value either from a one-row or one-column range or from an array.
*
+ * @deprecated 1.18.0
+ * Use the lookup() method in the LookupRef\Lookup class instead
+ * @see LookupRef\Lookup::lookup()
+ *
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_vector The range of cells being searched
* @param null|mixed $result_vector The column from which the matching value must be returned
@@ -875,94 +378,23 @@ class LookupRef
*/
public static function LOOKUP($lookup_value, $lookup_vector, $result_vector = null)
{
- $lookup_value = Functions::flattenSingleValue($lookup_value);
-
- if (!is_array($lookup_vector)) {
- return Functions::NA();
- }
- $hasResultVector = isset($result_vector);
- $lookupRows = count($lookup_vector);
- $l = array_keys($lookup_vector);
- $l = array_shift($l);
- $lookupColumns = count($lookup_vector[$l]);
- // we correctly orient our results
- if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $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 ($result_vector === null) {
- $result_vector = $lookup_vector;
- }
- $resultRows = count($result_vector);
- $l = array_keys($result_vector);
- $l = array_shift($l);
- $resultColumns = count($result_vector[$l]);
- // we correctly orient our results
- if ($resultRows === 1 && $resultColumns > 1) {
- $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 && !$hasResultVector) {
- $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 = [$key1 => $dataValue1, $key2 => $dataValue2];
- }
- unset($value);
- }
-
- return self::VLOOKUP($lookup_value, $lookup_vector, 2);
+ return Lookup::lookup($lookup_value, $lookup_vector, $result_vector);
}
/**
* FORMULATEXT.
*
+ * @deprecated 1.18.0
+ * Use the text() method in the LookupRef\Formula class instead
+ * @see LookupRef\Formula::text()
+ *
* @param mixed $cellReference The cell to check
- * @param Cell $pCell The current cell (containing this formula)
+ * @param Cell $cell The current cell (containing this formula)
*
* @return string
*/
- public static function FORMULATEXT($cellReference = '', Cell $pCell = null)
+ public static function FORMULATEXT($cellReference = '', ?Cell $cell = null)
{
- if ($pCell === null) {
- return Functions::REF();
- }
-
- preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
-
- $cellReference = $matches[6] . $matches[7];
- $worksheetName = trim($matches[3], "'");
- $worksheet = (!empty($worksheetName))
- ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
- : $pCell->getWorksheet();
-
- if (!$worksheet->getCell($cellReference)->isFormula()) {
- return Functions::NA();
- }
-
- return $worksheet->getCell($cellReference)->getValue();
+ return LookupRef\Formula::text($cellReference, $cell);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Address.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Address.php
new file mode 100644
index 0000000..0d2db8b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Address.php
@@ -0,0 +1,124 @@
+ '') {
+ if (strpos($sheetName, ' ') !== false || strpos($sheetName, '[') !== false) {
+ $sheetName = "'{$sheetName}'";
+ }
+ $sheetName .= '!';
+ }
+
+ return $sheetName;
+ }
+
+ private static function formatAsA1(int $row, int $column, int $relativity, string $sheetName): string
+ {
+ $rowRelative = $columnRelative = '$';
+ if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $columnRelative = '';
+ }
+ if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $rowRelative = '';
+ }
+ $column = Coordinate::stringFromColumnIndex($column);
+
+ return "{$sheetName}{$columnRelative}{$column}{$rowRelative}{$row}";
+ }
+
+ private static function formatAsR1C1(int $row, int $column, int $relativity, string $sheetName): string
+ {
+ if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $column = "[{$column}]";
+ }
+ if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $row = "[{$row}]";
+ }
+ [$rowChar, $colChar] = AddressHelper::getRowAndColumnChars();
+
+ return "{$sheetName}$rowChar{$row}$colChar{$column}";
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
new file mode 100644
index 0000000..e09477c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
@@ -0,0 +1,282 @@
+getMessage();
+ }
+
+ // MATCH() is not case sensitive, so we convert lookup value to be lower cased if it's a string type.
+ if (is_string($lookupValue)) {
+ $lookupValue = StringHelper::strToLower($lookupValue);
+ }
+
+ $valueKey = null;
+ switch ($matchType) {
+ case self::MATCHTYPE_LARGEST_VALUE:
+ $valueKey = self::matchLargestValue($lookupArray, $lookupValue, $keySet);
+
+ break;
+ case self::MATCHTYPE_FIRST_VALUE:
+ $valueKey = self::matchFirstValue($lookupArray, $lookupValue);
+
+ break;
+ case self::MATCHTYPE_SMALLEST_VALUE:
+ default:
+ $valueKey = self::matchSmallestValue($lookupArray, $lookupValue);
+ }
+
+ if ($valueKey !== null) {
+ return ++$valueKey;
+ }
+
+ // Unsuccessful in finding a match, return #N/A error value
+ return ExcelError::NA();
+ }
+
+ /**
+ * @param mixed $lookupValue
+ *
+ * @return mixed
+ */
+ private static function matchFirstValue(array $lookupArray, $lookupValue)
+ {
+ if (is_string($lookupValue)) {
+ $valueIsString = true;
+ $wildcard = WildcardMatch::wildcard($lookupValue);
+ } else {
+ $valueIsString = false;
+ $wildcard = '';
+ }
+
+ $valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if (
+ $valueIsString
+ && is_string($lookupArrayValue)
+ ) {
+ if (WildcardMatch::compare($lookupArrayValue, $wildcard)) {
+ return $i; // wildcard match
+ }
+ } else {
+ if ($lookupArrayValue === $lookupValue) {
+ return $i; // exact match
+ }
+ if (
+ $valueIsNumeric
+ && (is_float($lookupArrayValue) || is_int($lookupArrayValue))
+ && $lookupArrayValue == $lookupValue
+ ) {
+ return $i; // exact match
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param mixed $lookupValue
+ *
+ * @return mixed
+ */
+ private static function matchLargestValue(array $lookupArray, $lookupValue, array $keySet)
+ {
+ if (is_string($lookupValue)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $wildcard = WildcardMatch::wildcard($lookupValue);
+ foreach (array_reverse($lookupArray) as $i => $lookupArrayValue) {
+ if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
+ return $i;
+ }
+ }
+ } else {
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if ($lookupArrayValue === $lookupValue) {
+ return $keySet[$i];
+ }
+ }
+ }
+ }
+ $valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if ($valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue))) {
+ if ($lookupArrayValue <= $lookupValue) {
+ return array_search($i, $keySet);
+ }
+ }
+ $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
+ if ($typeMatch && ($lookupArrayValue <= $lookupValue)) {
+ return array_search($i, $keySet);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param mixed $lookupValue
+ *
+ * @return mixed
+ */
+ private static function matchSmallestValue(array $lookupArray, $lookupValue)
+ {
+ $valueKey = null;
+ if (is_string($lookupValue)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $wildcard = WildcardMatch::wildcard($lookupValue);
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
+ return $i;
+ }
+ }
+ }
+ }
+
+ $valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
+ // The basic algorithm is:
+ // Iterate and keep the highest match until the next element is smaller than the searched value.
+ // Return immediately if perfect match is found
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
+ $bothNumeric = $valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue));
+
+ if ($lookupArrayValue === $lookupValue) {
+ // Another "special" case. If a perfect match is found,
+ // the algorithm gives up immediately
+ return $i;
+ }
+ if ($bothNumeric && $lookupValue == $lookupArrayValue) {
+ return $i; // exact match, as above
+ }
+ if (($typeMatch || $bothNumeric) && $lookupArrayValue >= $lookupValue) {
+ $valueKey = $i;
+ } elseif ($typeMatch && $lookupArrayValue < $lookupValue) {
+ //Excel algorithm gives up immediately if the first element is smaller than the searched value
+ break;
+ }
+ }
+
+ return $valueKey;
+ }
+
+ /**
+ * @param mixed $lookupValue
+ */
+ private static function validateLookupValue($lookupValue): void
+ {
+ // Lookup_value type has to be number, text, or logical values
+ if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
+ throw new Exception(ExcelError::NA());
+ }
+ }
+
+ /**
+ * @param mixed $matchType
+ */
+ private static function validateMatchType($matchType): int
+ {
+ // Match_type is 0, 1 or -1
+ // However Excel accepts other numeric values,
+ // including numeric strings and floats.
+ // It seems to just be interested in the sign.
+ if (!is_numeric($matchType)) {
+ throw new Exception(ExcelError::Value());
+ }
+ if ($matchType > 0) {
+ return self::MATCHTYPE_LARGEST_VALUE;
+ }
+ if ($matchType < 0) {
+ return self::MATCHTYPE_SMALLEST_VALUE;
+ }
+
+ return self::MATCHTYPE_FIRST_VALUE;
+ }
+
+ private static function validateLookupArray(array $lookupArray): void
+ {
+ // Lookup_array should not be empty
+ $lookupArraySize = count($lookupArray);
+ if ($lookupArraySize <= 0) {
+ throw new Exception(ExcelError::NA());
+ }
+ }
+
+ /**
+ * @param mixed $matchType
+ */
+ private static function prepareLookupArray(array $lookupArray, $matchType): array
+ {
+ // Lookup_array should contain only number, text, or logical values, or empty (null) cells
+ foreach ($lookupArray as $i => $value) {
+ // check the type of the value
+ if ((!is_numeric($value)) && (!is_string($value)) && (!is_bool($value)) && ($value !== null)) {
+ throw new Exception(ExcelError::NA());
+ }
+ // Convert strings to lowercase for case-insensitive testing
+ if (is_string($value)) {
+ $lookupArray[$i] = StringHelper::strToLower($value);
+ }
+ if (
+ ($value === null) &&
+ (($matchType == self::MATCHTYPE_LARGEST_VALUE) || ($matchType == self::MATCHTYPE_SMALLEST_VALUE))
+ ) {
+ unset($lookupArray[$i]);
+ }
+ }
+
+ return $lookupArray;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Filter.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Filter.php
new file mode 100644
index 0000000..74fa832
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Filter.php
@@ -0,0 +1,81 @@
+getWorksheet()->getParent()->getSheetByName($worksheetName)
+ : $cell->getWorksheet();
+
+ if (
+ $worksheet === null ||
+ !$worksheet->cellExists($cellReference) ||
+ !$worksheet->getCell($cellReference)->isFormula()
+ ) {
+ return ExcelError::NA();
+ }
+
+ return $worksheet->getCell($cellReference)->getValue();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/HLookup.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
new file mode 100644
index 0000000..e2d27bd
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
@@ -0,0 +1,121 @@
+getMessage();
+ }
+
+ $f = array_keys($lookupArray);
+ $firstRow = reset($f);
+ if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray))) {
+ return ExcelError::REF();
+ }
+
+ $firstkey = $f[0] - 1;
+ $returnColumn = $firstkey + $indexNumber;
+ $firstColumn = array_shift($f) ?? 1;
+ $rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
+
+ if ($rowNumber !== null) {
+ // otherwise return the appropriate value
+ return $lookupArray[$returnColumn][Coordinate::stringFromColumnIndex($rowNumber)];
+ }
+
+ return ExcelError::NA();
+ }
+
+ /**
+ * @param mixed $lookupValue The value that you want to match in lookup_array
+ * @param int|string $column
+ */
+ private static function hLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
+ {
+ $lookupLower = StringHelper::strToLower((string) $lookupValue);
+
+ $rowNumber = null;
+ foreach ($lookupArray[$column] as $rowKey => $rowData) {
+ // break if we have passed possible keys
+ $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
+ $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
+ $cellDataLower = StringHelper::strToLower((string) $rowData);
+
+ if (
+ $notExactMatch &&
+ (($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
+ ) {
+ break;
+ }
+
+ $rowNumber = self::checkMatch(
+ $bothNumeric,
+ $bothNotNumeric,
+ $notExactMatch,
+ Coordinate::columnIndexFromString($rowKey),
+ $cellDataLower,
+ $lookupLower,
+ $rowNumber
+ );
+ }
+
+ return $rowNumber;
+ }
+
+ private static function convertLiteralArray(array $lookupArray): array
+ {
+ if (array_key_exists(0, $lookupArray)) {
+ $lookupArray2 = [];
+ $row = 0;
+ foreach ($lookupArray as $arrayVal) {
+ ++$row;
+ if (!is_array($arrayVal)) {
+ $arrayVal = [$arrayVal];
+ }
+ $arrayVal2 = [];
+ foreach ($arrayVal as $key2 => $val2) {
+ $index = Coordinate::stringFromColumnIndex($key2 + 1);
+ $arrayVal2[$index] = $val2;
+ }
+ $lookupArray2[$row] = $arrayVal2;
+ }
+ $lookupArray = $lookupArray2;
+ }
+
+ return $lookupArray;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Helpers.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
new file mode 100644
index 0000000..76a194b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
@@ -0,0 +1,74 @@
+getWorkSheet();
+ $sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle();
+ $value = (string) preg_replace('/^=/', '', $namedRange->getValue());
+ self::adjustSheetTitle($sheetTitle, $value);
+ $cellAddress1 = $sheetTitle . $value;
+ $cellAddress = $cellAddress1;
+ $a1 = self::CELLADDRESS_USE_A1;
+ }
+ if (strpos($cellAddress, ':') !== false) {
+ [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
+ }
+ $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1, $baseRow, $baseCol);
+
+ return [$cellAddress1, $cellAddress2, $cellAddress];
+ }
+
+ public static function extractWorksheet(string $cellAddress, Cell $cell): array
+ {
+ $sheetName = '';
+ if (strpos($cellAddress, '!') !== false) {
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $sheetName = trim($sheetName, "'");
+ }
+
+ $worksheet = ($sheetName !== '')
+ ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+ : $cell->getWorksheet();
+
+ return [$cellAddress, $worksheet, $sheetName];
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
new file mode 100644
index 0000000..5387833
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
@@ -0,0 +1,41 @@
+getHyperlink()->setUrl($linkURL);
+ $cell->getHyperlink()->setTooltip($displayName);
+
+ return $displayName;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
new file mode 100644
index 0000000..bd76ce9
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
@@ -0,0 +1,130 @@
+getCoordinate());
+
+ try {
+ $a1 = self::a1Format($a1fmt);
+ $cellAddress = self::validateAddress($cellAddress);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ [$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+ $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+ } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+ $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+ }
+
+ try {
+ [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName, $baseRow, $baseCol);
+ } catch (Exception $e) {
+ return ExcelError::REF();
+ }
+
+ if (
+ (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) ||
+ (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
+ ) {
+ return ExcelError::REF();
+ }
+
+ return self::extractRequiredCells($worksheet, $cellAddress);
+ }
+
+ /**
+ * Extract range values.
+ *
+ * @return mixed Array of values in range if range contains more than one element.
+ * Otherwise, a single value is returned.
+ */
+ private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress)
+ {
+ return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
+ ->extractCellRange($cellAddress, $worksheet, false);
+ }
+
+ private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
+ {
+ // Being lazy, we're only checking a single row/column to get the max
+ if (ctype_digit($start) && $start <= 1048576) {
+ // Max 16,384 columns for Excel2007
+ $endColRef = ($worksheet !== null) ? $worksheet->getHighestDataColumn((int) $start) : AddressRange::MAX_COLUMN;
+
+ return "A{$start}:{$endColRef}{$end}";
+ } elseif (ctype_alpha($start) && strlen($start) <= 3) {
+ // Max 1,048,576 rows for Excel2007
+ $endRowRef = ($worksheet !== null) ? $worksheet->getHighestDataRow($start) : AddressRange::MAX_ROW;
+
+ return "{$start}1:{$end}{$endRowRef}";
+ }
+
+ return "{$start}:{$end}";
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Lookup.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
new file mode 100644
index 0000000..76a360b
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
@@ -0,0 +1,110 @@
+ 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
+ $lookupVector = LookupRef\Matrix::transpose($lookupVector);
+ $lookupRows = self::rowCount($lookupVector);
+ $lookupColumns = self::columnCount($lookupVector);
+ }
+
+ $resultVector = self::verifyResultVector($lookupVector, $resultVector);
+
+ if ($lookupRows === 2 && !$hasResultVector) {
+ $resultVector = array_pop($lookupVector);
+ $lookupVector = array_shift($lookupVector);
+ }
+
+ if ($lookupColumns !== 2) {
+ $lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
+ }
+
+ return VLookup::lookup($lookupValue, $lookupVector, 2);
+ }
+
+ private static function verifyLookupValues(array $lookupVector, array $resultVector): array
+ {
+ foreach ($lookupVector 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($resultVector);
+ if (is_array($dataValue2)) {
+ $dataValue2 = array_shift($dataValue2);
+ }
+ $value = [$key1 => $dataValue1, $key2 => $dataValue2];
+ }
+ unset($value);
+
+ return $lookupVector;
+ }
+
+ private static function verifyResultVector(array $lookupVector, $resultVector)
+ {
+ if ($resultVector === null) {
+ $resultVector = $lookupVector;
+ }
+
+ $resultRows = self::rowCount($resultVector);
+ $resultColumns = self::columnCount($resultVector);
+
+ // we correctly orient our results
+ if ($resultRows === 1 && $resultColumns > 1) {
+ $resultVector = LookupRef\Matrix::transpose($resultVector);
+ }
+
+ return $resultVector;
+ }
+
+ private static function rowCount(array $dataArray): int
+ {
+ return count($dataArray);
+ }
+
+ private static function columnCount(array $dataArray): int
+ {
+ $rowKeys = array_keys($dataArray);
+ $row = array_shift($rowKeys);
+
+ return count($dataArray[$row]);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
new file mode 100644
index 0000000..5fe1676
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
@@ -0,0 +1,66 @@
+ 1 &&
+ (count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
+ }
+
+ /**
+ * TRANSPOSE.
+ *
+ * @param array|mixed $matrixData A matrix of values
+ *
+ * @return array
+ */
+ public static function transpose($matrixData)
+ {
+ $returnMatrix = [];
+ if (!is_array($matrixData)) {
+ $matrixData = [[$matrixData]];
+ }
+
+ $column = 0;
+ foreach ($matrixData as $matrixRow) {
+ $row = 0;
+ foreach ($matrixRow as $matrixCell) {
+ $returnMatrix[$row][$column] = $matrixCell;
+ ++$row;
+ }
+ ++$column;
+ }
+
+ return $returnMatrix;
+ }
+
+ /**
+ * INDEX.
+ *
+ * Uses an index to choose a value from a reference or array
+ *
+ * Excel Function:
+ * =INDEX(range_array, row_num, [column_num], [area_num])
+ *
+ * @param mixed $matrix A range of cells or an array constant
+ * @param mixed $rowNum The row in the array or range from which to return a value.
+ * If row_num is omitted, column_num is required.
+ * Or can be an array of values
+ * @param mixed $columnNum The column in the array or range from which to return a value.
+ * If column_num is omitted, row_num is required.
+ * Or can be an array of values
+ *
+ * TODO Provide support for area_num, currently not supported
+ *
+ * @return mixed the value of a specified cell or array of cells
+ * If an array of values is passed as the $rowNum and/or $columnNum arguments, then the returned result
+ * will also be an array with the same dimensions
+ */
+ public static function index($matrix, $rowNum = 0, $columnNum = null)
+ {
+ if (is_array($rowNum) || is_array($columnNum)) {
+ return self::evaluateArrayArgumentsSubsetFrom([self::class, __FUNCTION__], 1, $matrix, $rowNum, $columnNum);
+ }
+
+ $rowNum = $rowNum ?? 0;
+ $originalColumnNum = $columnNum;
+ $columnNum = $columnNum ?? 0;
+
+ try {
+ $rowNum = LookupRefValidations::validatePositiveInt($rowNum);
+ $columnNum = LookupRefValidations::validatePositiveInt($columnNum);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (!is_array($matrix) || ($rowNum > count($matrix))) {
+ return ExcelError::REF();
+ }
+
+ $rowKeys = array_keys($matrix);
+ $columnKeys = @array_keys($matrix[$rowKeys[0]]);
+
+ if ($columnNum > count($columnKeys)) {
+ return ExcelError::REF();
+ }
+ if ($originalColumnNum === null && 1 < count($columnKeys)) {
+ return ExcelError::REF();
+ }
+
+ if ($columnNum === 0) {
+ return self::extractRowValue($matrix, $rowKeys, $rowNum);
+ }
+
+ $columnNum = $columnKeys[--$columnNum];
+ if ($rowNum === 0) {
+ return array_map(
+ function ($value) {
+ return [$value];
+ },
+ array_column($matrix, $columnNum)
+ );
+ }
+ $rowNum = $rowKeys[--$rowNum];
+
+ return $matrix[$rowNum][$columnNum];
+ }
+
+ private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum)
+ {
+ if ($rowNum === 0) {
+ return $matrix;
+ }
+
+ $rowNum = $rowKeys[--$rowNum];
+ $row = $matrix[$rowNum];
+ if (is_array($row)) {
+ return [$rowNum => $row];
+ }
+
+ return $row;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Offset.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Offset.php
new file mode 100644
index 0000000..02a2558
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Offset.php
@@ -0,0 +1,148 @@
+getParent() : null)
+ ->extractCellRange($cellAddress, $worksheet, false);
+ }
+
+ private static function extractWorksheet($cellAddress, Cell $cell): array
+ {
+ $cellAddress = self::assessCellAddress($cellAddress, $cell);
+
+ $sheetName = '';
+ if (strpos($cellAddress, '!') !== false) {
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $sheetName = trim($sheetName, "'");
+ }
+
+ $worksheet = ($sheetName !== '')
+ ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+ : $cell->getWorksheet();
+
+ return [$cellAddress, $worksheet];
+ }
+
+ private static function assessCellAddress(string $cellAddress, Cell $cell): string
+ {
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $cellAddress) !== false) {
+ $cellAddress = Functions::expandDefinedName($cellAddress, $cell);
+ }
+
+ return $cellAddress;
+ }
+
+ private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns)
+ {
+ $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
+ if (($width !== null) && (!is_object($width))) {
+ $endCellColumn = $startCellColumn + (int) $width - 1;
+ } else {
+ $endCellColumn += (int) $columns;
+ }
+
+ return $endCellColumn;
+ }
+
+ private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, $endCellRow): int
+ {
+ if (($height !== null) && (!is_object($height))) {
+ $endCellRow = $startCellRow + (int) $height - 1;
+ } else {
+ $endCellRow += (int) $rows;
+ }
+
+ return $endCellRow;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
new file mode 100644
index 0000000..8bce07e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
@@ -0,0 +1,209 @@
+getColumn()) : 1;
+ }
+
+ /**
+ * 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 1.
+ *
+ * Excel Function:
+ * =COLUMN([cellAddress])
+ *
+ * @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
+ *
+ * @return int|int[]
+ */
+ public static function COLUMN($cellAddress = null, ?Cell $cell = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return self::cellColumn($cell);
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $columnKey => $value) {
+ $columnKey = (string) preg_replace('/[^a-z]/i', '', $columnKey);
+
+ return (int) Coordinate::columnIndexFromString($columnKey);
+ }
+
+ return self::cellColumn($cell);
+ }
+
+ $cellAddress = $cellAddress ?? '';
+ if ($cell != null) {
+ [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
+ }
+ [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ if (strpos($cellAddress, ':') !== false) {
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
+ $startAddress = (string) preg_replace('/[^a-z]/i', '', $startAddress);
+ $endAddress = (string) preg_replace('/[^a-z]/i', '', $endAddress);
+
+ return range(
+ (int) Coordinate::columnIndexFromString($startAddress),
+ (int) Coordinate::columnIndexFromString($endAddress)
+ );
+ }
+
+ $cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
+
+ return (int) Coordinate::columnIndexFromString($cellAddress);
+ }
+
+ /**
+ * COLUMNS.
+ *
+ * Returns the number of columns in an array or reference.
+ *
+ * Excel Function:
+ * =COLUMNS(cellAddress)
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of columns
+ *
+ * @return int|string The number of columns in cellAddress, or a string if arguments are invalid
+ */
+ public static function COLUMNS($cellAddress = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return 1;
+ }
+ if (!is_array($cellAddress)) {
+ return ExcelError::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $rows;
+ }
+
+ return $columns;
+ }
+
+ private static function cellRow(?Cell $cell): int
+ {
+ return ($cell !== null) ? $cell->getRow() : 1;
+ }
+
+ /**
+ * 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 1.
+ *
+ * Excel Function:
+ * =ROW([cellAddress])
+ *
+ * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
+ *
+ * @return int|mixed[]|string
+ */
+ public static function ROW($cellAddress = null, ?Cell $cell = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return self::cellRow($cell);
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $rowKey => $rowValue) {
+ foreach ($rowValue as $columnKey => $cellValue) {
+ return (int) preg_replace('/\D/', '', $rowKey);
+ }
+ }
+
+ return self::cellRow($cell);
+ }
+
+ $cellAddress = $cellAddress ?? '';
+ if ($cell !== null) {
+ [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
+ }
+ [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ if (strpos($cellAddress, ':') !== false) {
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
+ $startAddress = (string) preg_replace('/\D/', '', $startAddress);
+ $endAddress = (string) preg_replace('/\D/', '', $endAddress);
+
+ return array_map(
+ function ($value) {
+ return [$value];
+ },
+ range($startAddress, $endAddress)
+ );
+ }
+ [$cellAddress] = explode(':', $cellAddress);
+
+ return (int) preg_replace('/\D/', '', $cellAddress);
+ }
+
+ /**
+ * ROWS.
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * Excel Function:
+ * =ROWS(cellAddress)
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of rows
+ *
+ * @return int|string The number of rows in cellAddress, or a string if arguments are invalid
+ */
+ public static function ROWS($cellAddress = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return 1;
+ }
+ if (!is_array($cellAddress)) {
+ return ExcelError::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $columns;
+ }
+
+ return $rows;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Selection.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Selection.php
new file mode 100644
index 0000000..0ac9177
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Selection.php
@@ -0,0 +1,51 @@
+ $entryCount)) {
+ return ExcelError::VALUE();
+ }
+
+ if (is_array($chooseArgs[$chosenEntry])) {
+ return Functions::flattenArray($chooseArgs[$chosenEntry]);
+ }
+
+ return $chooseArgs[$chosenEntry];
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Sort.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Sort.php
new file mode 100644
index 0000000..ff78fbe
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Sort.php
@@ -0,0 +1,342 @@
+getMessage();
+ }
+
+ // We want a simple, enumrated array of arrays where we can reference column by its index number.
+ $sortArray = array_values(array_map('array_values', $sortArray));
+
+ return ($byColumn === true)
+ ? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
+ : self::sortByRow($sortArray, $sortIndex, $sortOrder);
+ }
+
+ /**
+ * SORTBY
+ * The SORTBY function sorts the contents of a range or array based on the values in a corresponding range or array.
+ * The returned array is the same shape as the provided array argument.
+ * Both $sortIndex and $sortOrder can be arrays, to provide multi-level sorting.
+ *
+ * @param mixed $sortArray The range of cells being sorted
+ * @param mixed $args
+ * At least one additional argument must be provided, The vector or range to sort on
+ * After that, arguments are passed as pairs:
+ * sort order: ascending or descending
+ * Ascending = 1 (self::ORDER_ASCENDING)
+ * Descending = -1 (self::ORDER_DESCENDING)
+ * additional arrays or ranges for multi-level sorting
+ *
+ * @return mixed The sorted values from the sort range
+ */
+ public static function sortBy($sortArray, ...$args)
+ {
+ if (!is_array($sortArray)) {
+ // Scalars are always returned "as is"
+ return $sortArray;
+ }
+
+ $sortArray = self::enumerateArrayKeys($sortArray);
+
+ $lookupArraySize = count($sortArray);
+ $argumentCount = count($args);
+
+ try {
+ $sortBy = $sortOrder = [];
+ for ($i = 0; $i < $argumentCount; $i += 2) {
+ $sortBy[] = self::validateSortVector($args[$i], $lookupArraySize);
+ $sortOrder[] = self::validateSortOrder($args[$i + 1] ?? self::ORDER_ASCENDING);
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::processSortBy($sortArray, $sortBy, $sortOrder);
+ }
+
+ private static function enumerateArrayKeys(array $sortArray): array
+ {
+ array_walk(
+ $sortArray,
+ function (&$columns): void {
+ if (is_array($columns)) {
+ $columns = array_values($columns);
+ }
+ }
+ );
+
+ return array_values($sortArray);
+ }
+
+ /**
+ * @param mixed $sortIndex
+ * @param mixed $sortOrder
+ */
+ private static function validateScalarArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+ {
+ if (is_array($sortIndex) || is_array($sortOrder)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $sortIndex = self::validatePositiveInt($sortIndex, false);
+
+ if ($sortIndex > $sortArraySize) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $sortOrder = self::validateSortOrder($sortOrder);
+ }
+
+ /**
+ * @param mixed $sortVector
+ */
+ private static function validateSortVector($sortVector, int $sortArraySize): array
+ {
+ if (!is_array($sortVector)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ // It doesn't matter if it's a row or a column vectors, it works either way
+ $sortVector = Functions::flattenArray($sortVector);
+ if (count($sortVector) !== $sortArraySize) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $sortVector;
+ }
+
+ /**
+ * @param mixed $sortOrder
+ */
+ private static function validateSortOrder($sortOrder): int
+ {
+ $sortOrder = self::validateInt($sortOrder);
+ if (($sortOrder == self::ORDER_ASCENDING || $sortOrder === self::ORDER_DESCENDING) === false) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $sortOrder;
+ }
+
+ /**
+ * @param array $sortIndex
+ * @param mixed $sortOrder
+ */
+ private static function validateArrayArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+ {
+ // It doesn't matter if they're row or column vectors, it works either way
+ $sortIndex = Functions::flattenArray($sortIndex);
+ $sortOrder = Functions::flattenArray($sortOrder);
+
+ if (
+ count($sortOrder) === 0 || count($sortOrder) > $sortArraySize ||
+ (count($sortOrder) > count($sortIndex))
+ ) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ if (count($sortIndex) > count($sortOrder)) {
+ // If $sortOrder has fewer elements than $sortIndex, then the last order element is repeated.
+ $sortOrder = array_merge(
+ $sortOrder,
+ array_fill(0, count($sortIndex) - count($sortOrder), array_pop($sortOrder))
+ );
+ }
+
+ foreach ($sortIndex as $key => &$value) {
+ self::validateScalarArgumentsForSort($value, $sortOrder[$key], $sortArraySize);
+ }
+ }
+
+ private static function prepareSortVectorValues(array $sortVector): array
+ {
+ // Strings should be sorted case-insensitive; with booleans converted to locale-strings
+ return array_map(
+ function ($value) {
+ if (is_bool($value)) {
+ return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
+ } elseif (is_string($value)) {
+ return StringHelper::strToLower($value);
+ }
+
+ return $value;
+ },
+ $sortVector
+ );
+ }
+
+ /**
+ * @param array[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function processSortBy(array $sortArray, array $sortIndex, $sortOrder): array
+ {
+ $sortArguments = [];
+ $sortData = [];
+ foreach ($sortIndex as $index => $sortValues) {
+ $sortData[] = $sortValues;
+ $sortArguments[] = self::prepareSortVectorValues($sortValues);
+ $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+ }
+ $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
+
+ $sortVector = self::executeVectorSortQuery($sortData, $sortArguments);
+
+ return self::sortLookupArrayFromVector($sortArray, $sortVector);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortVector = self::buildVectorForSort($sortArray, $sortIndex, $sortOrder);
+
+ return self::sortLookupArrayFromVector($sortArray, $sortVector);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArray = Matrix::transpose($sortArray);
+ $result = self::sortByRow($sortArray, $sortIndex, $sortOrder);
+
+ return Matrix::transpose($result);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArguments = [];
+ $sortData = [];
+ foreach ($sortIndex as $index => $sortIndexValue) {
+ $sortValues = array_column($sortArray, $sortIndexValue - 1);
+ $sortData[] = $sortValues;
+ $sortArguments[] = self::prepareSortVectorValues($sortValues);
+ $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+ }
+ $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
+
+ $sortData = self::executeVectorSortQuery($sortData, $sortArguments);
+
+ return $sortData;
+ }
+
+ private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
+ {
+ $sortData = Matrix::transpose($sortData);
+
+ // We need to set an index that can be retained, as array_multisort doesn't maintain numeric keys.
+ $sortDataIndexed = [];
+ foreach ($sortData as $key => $value) {
+ $sortDataIndexed[Coordinate::stringFromColumnIndex($key + 1)] = $value;
+ }
+ unset($sortData);
+
+ $sortArguments[] = &$sortDataIndexed;
+
+ array_multisort(...$sortArguments);
+
+ // After the sort, we restore the numeric keys that will now be in the correct, sorted order
+ $sortedData = [];
+ foreach (array_keys($sortDataIndexed) as $key) {
+ $sortedData[] = Coordinate::columnIndexFromString($key) - 1;
+ }
+
+ return $sortedData;
+ }
+
+ private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
+ {
+ // Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
+ $sortedArray = [];
+ foreach ($sortVector as $index) {
+ $sortedArray[] = $sortArray[$index];
+ }
+
+ return $sortedArray;
+
+// uksort(
+// $lookupArray,
+// function (int $a, int $b) use (array $sortVector) {
+// return $sortVector[$a] <=> $sortVector[$b];
+// }
+// );
+//
+// return $lookupArray;
+ }
+
+ /**
+ * Hack to handle PHP 7:
+ * From PHP 8.0.0, If two members compare as equal in a sort, they retain their original order;
+ * but prior to PHP 8.0.0, their relative order in the sorted array was undefined.
+ * MS Excel replicates the PHP 8.0.0 behaviour, retaining the original order of matching elements.
+ * To replicate that behaviour with PHP 7, we add an extra sort based on the row index.
+ */
+ private static function applyPHP7Patch(array $sortArray, array $sortArguments): array
+ {
+ if (PHP_VERSION_ID < 80000) {
+ $sortArguments[] = range(1, count($sortArray));
+ $sortArguments[] = SORT_ASC;
+ }
+
+ return $sortArguments;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Unique.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Unique.php
new file mode 100644
index 0000000..2ba5128
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/Unique.php
@@ -0,0 +1,141 @@
+ count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
+ // We're looking at a full column check (multiple rows)
+ $transpose = Matrix::transpose($lookupVector);
+ $result = self::uniqueByRow($transpose, $exactlyOnce);
+
+ return (is_array($result)) ? Matrix::transpose($result) : $result;
+ }
+
+ $result = self::countValuesCaseInsensitive($flattenedLookupVector);
+
+ if ($exactlyOnce === true) {
+ $result = self::exactlyOnceFilter($result);
+ }
+
+ if (count($result) === 0) {
+ return ExcelError::CALC();
+ }
+
+ $result = array_keys($result);
+
+ return $result;
+ }
+
+ private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
+ {
+ $caseInsensitiveCounts = array_count_values(
+ array_map(
+ function (string $value) {
+ return StringHelper::strToUpper($value);
+ },
+ $caseSensitiveLookupValues
+ )
+ );
+
+ $caseSensitiveCounts = [];
+ foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
+ if (is_numeric($caseInsensitiveKey)) {
+ $caseSensitiveCounts[$caseInsensitiveKey] = $count;
+ } else {
+ foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
+ if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
+ $caseSensitiveCounts[$caseSensitiveValue] = $count;
+
+ break;
+ }
+ }
+ }
+ }
+
+ return $caseSensitiveCounts;
+ }
+
+ private static function exactlyOnceFilter(array $values): array
+ {
+ return array_filter(
+ $values,
+ function ($value) {
+ return $value === 1;
+ }
+ );
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/VLookup.php b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
new file mode 100644
index 0000000..edeb1aa
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
@@ -0,0 +1,117 @@
+getMessage();
+ }
+
+ $f = array_keys($lookupArray);
+ $firstRow = array_pop($f);
+ if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
+ return ExcelError::REF();
+ }
+ $columnKeys = array_keys($lookupArray[$firstRow]);
+ $returnColumn = $columnKeys[--$indexNumber];
+ $firstColumn = array_shift($columnKeys) ?? 1;
+
+ if (!$notExactMatch) {
+ /** @var callable */
+ $callable = [self::class, 'vlookupSort'];
+ uasort($lookupArray, $callable);
+ }
+
+ $rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
+
+ if ($rowNumber !== null) {
+ // return the appropriate value
+ return $lookupArray[$rowNumber][$returnColumn];
+ }
+
+ return ExcelError::NA();
+ }
+
+ private static function vlookupSort(array $a, array $b): int
+ {
+ reset($a);
+ $firstColumn = key($a);
+ $aLower = StringHelper::strToLower((string) $a[$firstColumn]);
+ $bLower = StringHelper::strToLower((string) $b[$firstColumn]);
+
+ if ($aLower == $bLower) {
+ return 0;
+ }
+
+ return ($aLower < $bLower) ? -1 : 1;
+ }
+
+ /**
+ * @param mixed $lookupValue The value that you want to match in lookup_array
+ * @param int|string $column
+ */
+ private static function vLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
+ {
+ $lookupLower = StringHelper::strToLower((string) $lookupValue);
+
+ $rowNumber = null;
+ foreach ($lookupArray as $rowKey => $rowData) {
+ $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
+ $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
+ $cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
+
+ // break if we have passed possible keys
+ if (
+ $notExactMatch &&
+ (($bothNumeric && ($rowData[$column] > $lookupValue)) ||
+ ($bothNotNumeric && ($cellDataLower > $lookupLower)))
+ ) {
+ break;
+ }
+
+ $rowNumber = self::checkMatch(
+ $bothNumeric,
+ $bothNotNumeric,
+ $notExactMatch,
+ $rowKey,
+ $cellDataLower,
+ $lookupLower,
+ $rowNumber
+ );
+ }
+
+ return $rowNumber;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig.php
old mode 100755
new mode 100644
index 4e384ea..1790b21
--- a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig.php
@@ -2,40 +2,30 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use Matrix\Exception as MatrixException;
-use Matrix\Matrix;
-
+/**
+ * @deprecated 1.18.0
+ */
class MathTrig
{
- //
- // Private method to return an array of the factors of the input value
- //
- private static function factors($value)
+ /**
+ * ARABIC.
+ *
+ * Converts a Roman numeral to an Arabic numeral.
+ *
+ * Excel Function:
+ * ARABIC(text)
+ *
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Arabic class instead
+ * @see MathTrig\Arabic::evaluate()
+ *
+ * @param array|string $roman
+ *
+ * @return array|int|string the arabic numberal contrived from the roman numeral
+ */
+ public static function ARABIC($roman)
{
- $startVal = floor(sqrt($value));
-
- $factorArray = [];
- 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 (!empty($factorArray)) {
- rsort($factorArray);
-
- return $factorArray;
- }
-
- return [(int) $value];
- }
-
- private static function romanCut($num, $n)
- {
- return ($num - ($num % $n)) / $n;
+ return MathTrig\Arabic::evaluate($roman);
}
/**
@@ -54,34 +44,41 @@ class MathTrig
* Excel Function:
* ATAN2(xCoordinate,yCoordinate)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the atan2 method in the MathTrig\Trig\Tangent class instead
+ * @see MathTrig\Trig\Tangent::atan2()
*
- * @param float $xCoordinate the x-coordinate of the point
- * @param float $yCoordinate the y-coordinate of the point
+ * @param array|float $xCoordinate the x-coordinate of the point
+ * @param array|float $yCoordinate the y-coordinate of the point
*
- * @return float the inverse tangent of the specified x- and y-coordinates
+ * @return array|float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
*/
public static function ATAN2($xCoordinate = null, $yCoordinate = null)
{
- $xCoordinate = Functions::flattenSingleValue($xCoordinate);
- $yCoordinate = Functions::flattenSingleValue($yCoordinate);
+ return MathTrig\Trig\Tangent::atan2($xCoordinate, $yCoordinate);
+ }
- $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
- $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
-
- if (((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
- ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))) {
- $xCoordinate = (float) $xCoordinate;
- $yCoordinate = (float) $yCoordinate;
-
- if (($xCoordinate == 0) && ($yCoordinate == 0)) {
- return Functions::DIV0();
- }
-
- return atan2($yCoordinate, $xCoordinate);
- }
-
- return Functions::VALUE();
+ /**
+ * BASE.
+ *
+ * Converts a number into a text representation with the given radix (base).
+ *
+ * Excel Function:
+ * BASE(Number, Radix [Min_length])
+ *
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Base class instead
+ * @see MathTrig\Base::evaluate()
+ *
+ * @param float $number
+ * @param float $radix
+ * @param int $minLength
+ *
+ * @return array|string the text representation with the given radix (base)
+ */
+ public static function BASE($number, $radix, $minLength = null)
+ {
+ return MathTrig\Base::evaluate($number, $radix, $minLength);
}
/**
@@ -95,34 +92,18 @@ class MathTrig
* Excel Function:
* CEILING(number[,significance])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.17.0
+ * Use the ceiling() method in the MathTrig\Ceiling class instead
+ * @see MathTrig\Ceiling::ceiling()
*
* @param float $number the number you want to round
* @param float $significance the multiple to which you want to round
*
- * @return float Rounded Number
+ * @return array|float|string Rounded Number, or a string containing an error
*/
public static function CEILING($number, $significance = null)
{
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if (($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
- $significance = $number / abs($number);
- }
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if (($number == 0.0) || ($significance == 0.0)) {
- return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
- return ceil($number / $significance) * $significance;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
+ return MathTrig\Ceiling::ceiling($number, $significance);
}
/**
@@ -134,29 +115,18 @@ class MathTrig
* Excel Function:
* COMBIN(numObjs,numInSet)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the withoutRepetition() method in the MathTrig\Combinations class instead
+ * @see MathTrig\Combinations::withoutRepetition()
*
- * @param int $numObjs Number of different objects
- * @param int $numInSet Number of objects in each combination
+ * @param array|int $numObjs Number of different objects
+ * @param array|int $numInSet Number of objects in each combination
*
- * @return int Number of combinations
+ * @return array|float|int|string Number of combinations, or a string containing an error
*/
public static function COMBIN($numObjs, $numInSet)
{
- $numObjs = Functions::flattenSingleValue($numObjs);
- $numInSet = Functions::flattenSingleValue($numInSet);
-
- if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
- if ($numObjs < $numInSet) {
- return Functions::NAN();
- } elseif ($numInSet < 0) {
- return Functions::NAN();
- }
-
- return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
- }
-
- return Functions::VALUE();
+ return MathTrig\Combinations::withoutRepetition($numObjs, $numInSet);
}
/**
@@ -171,29 +141,29 @@ class MathTrig
* Excel Function:
* EVEN(number)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the even() method in the MathTrig\Round class instead
+ * @see MathTrig\Round::even()
*
- * @param float $number Number to round
+ * @param array|float $number Number to round
*
- * @return int Rounded Number
+ * @return array|float|int|string Rounded Number, or a string containing an error
*/
public static function EVEN($number)
{
- $number = Functions::flattenSingleValue($number);
+ return MathTrig\Round::even($number);
+ }
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- $number = (int) $number;
- }
-
- if (is_numeric($number)) {
- $significance = 2 * self::SIGN($number);
-
- return (int) self::CEILING($number, $significance);
- }
-
- return Functions::VALUE();
+ /**
+ * Helper function for Even.
+ *
+ * @deprecated 1.18.0
+ * Use the evaluate() method in the MathTrig\Helpers class instead
+ * @see MathTrig\Helpers::getEven()
+ */
+ public static function getEven(float $number): int
+ {
+ return (int) MathTrig\Helpers::getEven($number);
}
/**
@@ -205,36 +175,17 @@ class MathTrig
* Excel Function:
* FACT(factVal)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the fact() method in the MathTrig\Factorial class instead
+ * @see MathTrig\Factorial::fact()
*
- * @param float $factVal Factorial Value
+ * @param array|float $factVal Factorial Value
*
- * @return int Factorial
+ * @return array|float|int|string Factorial, or a string containing an error
*/
public static function FACT($factVal)
{
- $factVal = Functions::flattenSingleValue($factVal);
-
- if (is_numeric($factVal)) {
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factLoop = floor($factVal);
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- if ($factVal > $factLoop) {
- return Functions::NAN();
- }
- }
-
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- }
-
- return $factorial;
- }
-
- return Functions::VALUE();
+ return MathTrig\Factorial::fact($factVal);
}
/**
@@ -245,31 +196,17 @@ class MathTrig
* Excel Function:
* FACTDOUBLE(factVal)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the factDouble() method in the MathTrig\Factorial class instead
+ * @see MathTrig\Factorial::factDouble()
*
- * @param float $factVal Factorial Value
+ * @param array|float $factVal Factorial Value
*
- * @return int Double Factorial
+ * @return array|float|int|string Double Factorial, or a string containing an error
*/
public static function FACTDOUBLE($factVal)
{
- $factLoop = Functions::flattenSingleValue($factVal);
-
- if (is_numeric($factLoop)) {
- $factLoop = floor($factLoop);
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- --$factLoop;
- }
-
- return $factorial;
- }
-
- return Functions::VALUE();
+ return MathTrig\Factorial::factDouble($factVal);
}
/**
@@ -280,41 +217,84 @@ class MathTrig
* Excel Function:
* FLOOR(number[,significance])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.17.0
+ * Use the floor() method in the MathTrig\Floor class instead
+ * @see MathTrig\Floor::floor()
*
* @param float $number Number to round
* @param float $significance Significance
*
- * @return float Rounded Number
+ * @return array|float|string Rounded Number, or a string containing an error
*/
public static function FLOOR($number, $significance = null)
{
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if (($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
- $significance = $number / abs($number);
- }
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
- return floor($number / $significance) * $significance;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
+ return MathTrig\Floor::floor($number, $significance);
}
- private static function evaluateGCD($a, $b)
+ /**
+ * FLOOR.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.MATH(number[,significance[,mode]])
+ *
+ * @deprecated 1.17.0
+ * Use the math() method in the MathTrig\Floor class instead
+ * @see MathTrig\Floor::math()
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @param int $mode direction to round negative numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ */
+ public static function FLOORMATH($number, $significance = null, $mode = 0)
{
- return $b ? self::evaluateGCD($b, $a % $b) : $a;
+ return MathTrig\Floor::math($number, $significance, $mode);
+ }
+
+ /**
+ * FLOOR.PRECISE.
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.PRECISE(number[,significance])
+ *
+ * @deprecated 1.17.0
+ * Use the precise() method in the MathTrig\Floor class instead
+ * @see MathTrig\Floor::precise()
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ */
+ public static function FLOORPRECISE($number, $significance = 1)
+ {
+ return MathTrig\Floor::precise($number, $significance);
+ }
+
+ /**
+ * INT.
+ *
+ * Casts a floating point value to an integer
+ *
+ * Excel Function:
+ * INT(number)
+ *
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the MathTrig\IntClass class instead
+ * @see MathTrig\IntClass::evaluate()
+ *
+ * @param array|float $number Number to cast to an integer
+ *
+ * @return array|int|string Integer value, or a string containing an error
+ */
+ public static function INT($number)
+ {
+ return MathTrig\IntClass::evaluate($number);
}
/**
@@ -327,60 +307,17 @@ class MathTrig
* Excel Function:
* GCD(number1[,number2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the evaluate() method in the MathTrig\Gcd class instead
+ * @see MathTrig\Gcd::evaluate()
*
* @param mixed ...$args Data values
*
- * @return int Greatest Common Divisor
+ * @return int|mixed|string Greatest Common Divisor, or a string containing an error
*/
public static function GCD(...$args)
{
- $args = Functions::flattenArray($args);
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- }
-
- $gcd = (int) array_pop($args);
- do {
- $gcd = self::evaluateGCD($gcd, (int) array_pop($args));
- } while (!empty($args));
-
- return $gcd;
- }
-
- /**
- * INT.
- *
- * Casts a floating point value to an integer
- *
- * Excel Function:
- * INT(number)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $number Number to cast to an integer
- *
- * @return int Integer value
- */
- public static function INT($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- return (int) floor($number);
- }
-
- return Functions::VALUE();
+ return MathTrig\Gcd::evaluate(...$args);
}
/**
@@ -394,47 +331,17 @@ class MathTrig
* Excel Function:
* LCM(number1[,number2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the evaluate() method in the MathTrig\Lcm class instead
+ * @see MathTrig\Lcm::evaluate()
*
* @param mixed ...$args Data values
*
- * @return int Lowest Common Multiplier
+ * @return int|string Lowest Common Multiplier, or a string containing an error
*/
public static function LCM(...$args)
{
- $returnValue = 1;
- $allPoweredFactors = [];
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- if ($value == 0) {
- return 0;
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- $myFactors = self::factors(floor($value));
- $myCountedFactors = array_count_values($myFactors);
- $myPoweredFactors = [];
- foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
- $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor, $myCountedPower);
- }
- foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
- if (isset($allPoweredFactors[$myPoweredValue])) {
- if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- } else {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- }
- }
- foreach ($allPoweredFactors as $allPoweredFactor) {
- $returnValue *= (int) $allPoweredFactor;
- }
-
- return $returnValue;
+ return MathTrig\Lcm::evaluate(...$args);
}
/**
@@ -445,26 +352,18 @@ class MathTrig
* Excel Function:
* LOG(number[,base])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the withBase() method in the MathTrig\Logarithms class instead
+ * @see MathTrig\Logarithms::withBase()
*
* @param float $number 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
+ * @return array|float|string The result, or a string containing an error
*/
- public static function logBase($number = null, $base = 10)
+ public static function logBase($number, $base = 10)
{
- $number = Functions::flattenSingleValue($number);
- $base = ($base === null) ? 10 : (float) Functions::flattenSingleValue($base);
-
- if ((!is_numeric($base)) || (!is_numeric($number))) {
- return Functions::VALUE();
- }
- if (($base <= 0) || ($number <= 0)) {
- return Functions::NAN();
- }
-
- return log($number, $base);
+ return MathTrig\Logarithms::withBase($number, $base);
}
/**
@@ -475,48 +374,17 @@ class MathTrig
* Excel Function:
* MDETERM(array)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the determinant() method in the MathTrig\MatrixFunctions class instead
+ * @see MathTrig\MatrixFunctions::determinant()
*
* @param array $matrixValues A matrix of values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MDETERM($matrixValues)
{
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
-
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
-
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
-
- try {
- return $matrix->determinant();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
+ return MathTrig\MatrixFunctions::determinant($matrixValues);
}
/**
@@ -527,138 +395,51 @@ class MathTrig
* Excel Function:
* MINVERSE(array)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the inverse() method in the MathTrig\MatrixFunctions class instead
+ * @see MathTrig\MatrixFunctions::inverse()
*
* @param array $matrixValues A matrix of values
*
- * @return array
+ * @return array|string The result, or a string containing an error
*/
public static function MINVERSE($matrixValues)
{
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
-
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
-
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
-
- if ($matrix->determinant() == 0.0) {
- return Functions::NAN();
- }
-
- try {
- return $matrix->inverse()->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
+ return MathTrig\MatrixFunctions::inverse($matrixValues);
}
/**
* MMULT.
*
+ * @deprecated 1.18.0
+ * Use the multiply() method in the MathTrig\MatrixFunctions class instead
+ * @see MathTrig\MatrixFunctions::multiply()
+ *
* @param array $matrixData1 A matrix of values
* @param array $matrixData2 A matrix of values
*
- * @return array
+ * @return array|string The result, or a string containing an error
*/
public static function MMULT($matrixData1, $matrixData2)
{
- $matrixAData = $matrixBData = [];
- if (!is_array($matrixData1)) {
- $matrixData1 = [[$matrixData1]];
- }
- if (!is_array($matrixData2)) {
- $matrixData2 = [[$matrixData2]];
- }
-
- try {
- $rowA = 0;
- foreach ($matrixData1 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnA = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixAData[$rowA][$columnA] = $matrixCell;
- ++$columnA;
- }
- ++$rowA;
- }
- $matrixA = new Matrix($matrixAData);
- $rowB = 0;
- foreach ($matrixData2 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnB = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixBData[$rowB][$columnB] = $matrixCell;
- ++$columnB;
- }
- ++$rowB;
- }
- $matrixB = new Matrix($matrixBData);
-
- if ($columnA != $rowB) {
- return Functions::VALUE();
- }
-
- return $matrixA->multiply($matrixB)->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
+ return MathTrig\MatrixFunctions::multiply($matrixData1, $matrixData2);
}
/**
* MOD.
*
+ * @deprecated 1.18.0
+ * Use the mod() method in the MathTrig\Operations class instead
+ * @see MathTrig\Operations::mod()
+ *
* @param int $a Dividend
* @param int $b Divisor
*
- * @return int Remainder
+ * @return array|float|int|string Remainder, or a string containing an error
*/
public static function MOD($a = 1, $b = 1)
{
- $a = (float) Functions::flattenSingleValue($a);
- $b = (float) Functions::flattenSingleValue($b);
-
- if ($b == 0.0) {
- return Functions::DIV0();
- } 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);
+ return MathTrig\Operations::mod($a, $b);
}
/**
@@ -666,30 +447,18 @@ class MathTrig
*
* 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
+ * @deprecated 1.17.0
+ * Use the multiple() method in the MathTrig\Mround class instead
+ * @see MathTrig\Round::multiple()
*
- * @return float Rounded Number
+ * @param float $number Number to round
+ * @param array|int $multiple Multiple to which you want to round $number
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
*/
public static function MROUND($number, $multiple)
{
- $number = Functions::flattenSingleValue($number);
- $multiple = Functions::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 Functions::NAN();
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::multiple($number, $multiple);
}
/**
@@ -697,36 +466,17 @@ class MathTrig
*
* Returns the ratio of the factorial of a sum of values to the product of factorials.
*
- * @param array of mixed Data Series
+ * @deprecated 1.18.0
+ * Use the multinomial method in the MathTrig\Factorial class instead
+ * @see MathTrig\Factorial::multinomial()
*
- * @return float
+ * @param mixed[] $args An array of mixed values for the Data Series
+ *
+ * @return float|string The result, or a string containing an error
*/
public static function MULTINOMIAL(...$args)
{
- $summer = 0;
- $divisor = 1;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if (is_numeric($arg)) {
- if ($arg < 1) {
- return Functions::NAN();
- }
- $summer += floor($arg);
- $divisor *= self::FACT($arg);
- } else {
- return Functions::VALUE();
- }
- }
-
- // Return
- if ($summer > 0) {
- $summer = self::FACT($summer);
-
- return $summer / $divisor;
- }
-
- return 0;
+ return MathTrig\Factorial::multinomial(...$args);
}
/**
@@ -734,33 +484,17 @@ class MathTrig
*
* Returns number rounded up to the nearest odd integer.
*
- * @param float $number Number to round
+ * @deprecated 1.18.0
+ * Use the odd method in the MathTrig\Round class instead
+ * @see MathTrig\Round::odd()
*
- * @return int Rounded Number
+ * @param array|float $number Number to round
+ *
+ * @return array|float|int|string Rounded Number, or a string containing an error
*/
public static function ODD($number)
{
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 1;
- } elseif (is_bool($number)) {
- return 1;
- } elseif (is_numeric($number)) {
- $significance = self::SIGN($number);
- if ($significance == 0) {
- return 1;
- }
-
- $result = self::CEILING($number, $significance);
- if ($result == self::EVEN($result)) {
- $result += $significance;
- }
-
- return (int) $result;
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::odd($number);
}
/**
@@ -768,27 +502,18 @@ class MathTrig
*
* Computes x raised to the power y.
*
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Power class instead
+ * @see MathTrig\Operations::power()
+ *
* @param float $x
* @param float $y
*
- * @return float
+ * @return array|float|int|string The result, or a string containing an error
*/
public static function POWER($x = 0, $y = 2)
{
- $x = Functions::flattenSingleValue($x);
- $y = Functions::flattenSingleValue($y);
-
- // Validate parameters
- if ($x == 0.0 && $y == 0.0) {
- return Functions::NAN();
- } elseif ($x == 0.0 && $y < 0.0) {
- return Functions::DIV0();
- }
-
- // Return
- $result = pow($x, $y);
-
- return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN();
+ return MathTrig\Operations::power($x, $y);
}
/**
@@ -796,38 +521,20 @@ class MathTrig
*
* PRODUCT returns the product of all the values and cells referenced in the argument list.
*
+ * @deprecated 1.18.0
+ * Use the product method in the MathTrig\Operations class instead
+ * @see MathTrig\Operations::product()
+ *
* Excel Function:
* PRODUCT(value1[,value2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
- *
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function PRODUCT(...$args)
{
- // Return value
- $returnValue = null;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = $arg;
- } else {
- $returnValue *= $arg;
- }
- }
- }
-
- // Return
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return MathTrig\Operations::product(...$args);
}
/**
@@ -836,90 +543,57 @@ class MathTrig
* QUOTIENT function returns the integer portion of a division. Numerator is the divided number
* and denominator is the divisor.
*
+ * @deprecated 1.18.0
+ * Use the quotient method in the MathTrig\Operations class instead
+ * @see MathTrig\Operations::quotient()
+ *
* Excel Function:
* QUOTIENT(value1[,value2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
+ * @param mixed $numerator
+ * @param mixed $denominator
*
- * @param mixed ...$args Data values
- *
- * @return float
+ * @return array|int|string
*/
- public static function QUOTIENT(...$args)
+ public static function QUOTIENT($numerator, $denominator)
{
- // Return value
- $returnValue = null;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg == 0) ? 0 : $arg;
- } else {
- if (($returnValue == 0) || ($arg == 0)) {
- $returnValue = 0;
- } else {
- $returnValue /= $arg;
- }
- }
- }
- }
-
- // Return
- return (int) $returnValue;
+ return MathTrig\Operations::quotient($numerator, $denominator);
}
/**
- * RAND.
+ * RAND/RANDBETWEEN.
+ *
+ * @deprecated 1.18.0
+ * Use the randBetween or randBetween method in the MathTrig\Random class instead
+ * @see MathTrig\Random::randBetween()
*
* @param int $min Minimal value
* @param int $max Maximal value
*
- * @return int Random number
+ * @return array|float|int|string Random number
*/
public static function RAND($min = 0, $max = 0)
{
- $min = Functions::flattenSingleValue($min);
- $max = Functions::flattenSingleValue($max);
-
- if ($min == 0 && $max == 0) {
- return (mt_rand(0, 10000000)) / 10000000;
- }
-
- return mt_rand($min, $max);
+ return MathTrig\Random::randBetween($min, $max);
}
+ /**
+ * ROMAN.
+ *
+ * Converts a number to Roman numeral
+ *
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the MathTrig\Roman class instead
+ * @see MathTrig\Roman::evaluate()
+ *
+ * @param mixed $aValue Number to convert
+ * @param mixed $style Number indicating one of five possible forms
+ *
+ * @return array|string Roman numeral, or a string containing an error
+ */
public static function ROMAN($aValue, $style = 0)
{
- $aValue = Functions::flattenSingleValue($aValue);
- $style = ($style === null) ? 0 : (int) Functions::flattenSingleValue($style);
- if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
- return Functions::VALUE();
- }
- $aValue = (int) $aValue;
- if ($aValue == 0) {
- return '';
- }
-
- $mill = ['', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM'];
- $cent = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
- $tens = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
- $ones = ['', '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];
+ return MathTrig\Roman::evaluate($aValue, $style);
}
/**
@@ -927,26 +601,18 @@ class MathTrig
*
* 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
+ * @deprecated 1.17.0
+ * Use the up() method in the MathTrig\Round class instead
+ * @see MathTrig\Round::up()
*
- * @return float Rounded Number
+ * @param array|float $number Number to round
+ * @param array|int $digits Number of digits to which you want to round $number
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
*/
public static function ROUNDUP($number, $digits)
{
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
-
- if ((is_numeric($number)) && (is_numeric($digits))) {
- $significance = pow(10, (int) $digits);
- if ($number < 0.0) {
- return floor($number * $significance) / $significance;
- }
-
- return ceil($number * $significance) / $significance;
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::up($number, $digits);
}
/**
@@ -954,26 +620,18 @@ class MathTrig
*
* 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
+ * @deprecated 1.17.0
+ * Use the down() method in the MathTrig\Round class instead
+ * @see MathTrig\Round::down()
*
- * @return float Rounded Number
+ * @param array|float $number Number to round
+ * @param array|int $digits Number of digits to which you want to round $number
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
*/
public static function ROUNDDOWN($number, $digits)
{
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
-
- if ((is_numeric($number)) && (is_numeric($digits))) {
- $significance = pow(10, (int) $digits);
- if ($number < 0.0) {
- return ceil($number * $significance) / $significance;
- }
-
- return floor($number * $significance) / $significance;
- }
-
- return Functions::VALUE();
+ return MathTrig\Round::down($number, $digits);
}
/**
@@ -981,40 +639,20 @@ class MathTrig
*
* 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
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\SeriesSum class instead
+ * @see MathTrig\SeriesSum::evaluate()
*
- * @return float
+ * @param mixed $x Input value
+ * @param mixed $n Initial power
+ * @param mixed $m Step
+ * @param mixed[] $args An array of coefficients for the Data Series
+ *
+ * @return array|float|string The result, or a string containing an error
*/
- public static function SERIESSUM(...$args)
+ public static function SERIESSUM($x, $n, $m, ...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($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 Functions::VALUE();
- }
- }
-
- return $returnValue;
- }
-
- return Functions::VALUE();
+ return MathTrig\SeriesSum::evaluate($x, $n, $m, ...$args);
}
/**
@@ -1023,26 +661,29 @@ class MathTrig
* 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
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Sign class instead
+ * @see MathTrig\Sign::evaluate()
*
- * @return int sign value
+ * @param array|float $number Number to round
+ *
+ * @return array|int|string sign value, or a string containing an error
*/
public static function SIGN($number)
{
- $number = Functions::flattenSingleValue($number);
+ return MathTrig\Sign::evaluate($number);
+ }
- if (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- if ($number == 0.0) {
- return 0;
- }
-
- return $number / abs($number);
- }
-
- return Functions::VALUE();
+ /**
+ * returnSign = returns 0/-1/+1.
+ *
+ * @deprecated 1.18.0
+ * Use the returnSign method in the MathTrig\Helpers class instead
+ * @see MathTrig\Helpers::returnSign()
+ */
+ public static function returnSign(float $number): int
+ {
+ return MathTrig\Helpers::returnSign($number);
}
/**
@@ -1050,57 +691,17 @@ class MathTrig
*
* Returns the square root of (number * pi).
*
- * @param float $number Number
+ * @deprecated 1.18.0
+ * Use the pi method in the MathTrig\Sqrt class instead
+ * @see MathTrig\Sqrt::sqrt()
*
- * @return float Square Root of Number * Pi
+ * @param array|float $number Number
+ *
+ * @return array|float|string Square Root of Number * Pi, or a string containing an error
*/
public static function SQRTPI($number)
{
- $number = Functions::flattenSingleValue($number);
-
- if (is_numeric($number)) {
- if ($number < 0) {
- return Functions::NAN();
- }
-
- return sqrt($number * M_PI);
- }
-
- return Functions::VALUE();
- }
-
- protected static function filterHiddenArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- [, $row, $column] = explode('.', $index);
-
- return $cellReference->getWorksheet()->getRowDimension($row)->getVisible() &&
- $cellReference->getWorksheet()->getColumnDimension($column)->getVisible();
- },
- ARRAY_FILTER_USE_KEY
- );
- }
-
- protected static function filterFormulaArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- [, $row, $column] = explode('.', $index);
- if ($cellReference->getWorksheet()->cellExists($column . $row)) {
- //take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
- $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
- $cellFormula = !preg_match('/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i', $cellReference->getWorksheet()->getCell($column . $row)->getValue());
-
- return !$isFormula || $cellFormula;
- }
-
- return true;
- },
- ARRAY_FILTER_USE_KEY
- );
+ return MathTrig\Sqrt::pi($number);
}
/**
@@ -1108,57 +709,24 @@ class MathTrig
*
* Returns a subtotal in a list or database.
*
- * @param int the number 1 to 11 that specifies which function to
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Subtotal class instead
+ * @see MathTrig\Subtotal::evaluate()
+ *
+ * @param int $functionType
+ * A number 1 to 11 that specifies which function to
* use in calculating subtotals within a range
* list
* Numbers 101 to 111 shadow the functions of 1 to 11
* but ignore any values in the range that are
* in hidden rows or columns
- * @param array of mixed Data Series
+ * @param mixed[] $args A mixed data series of values
*
* @return float|string
*/
- public static function SUBTOTAL(...$args)
+ public static function SUBTOTAL($functionType, ...$args)
{
- $cellReference = array_pop($args);
- $aArgs = Functions::flattenArrayIndexed($args);
- $subtotal = array_shift($aArgs);
-
- // Calculate
- if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
- if ($subtotal > 100) {
- $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
- $subtotal -= 100;
- }
-
- $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
- switch ($subtotal) {
- case 1:
- return Statistical::AVERAGE($aArgs);
- case 2:
- return Statistical::COUNT($aArgs);
- case 3:
- return Statistical::COUNTA($aArgs);
- case 4:
- return Statistical::MAX($aArgs);
- case 5:
- return Statistical::MIN($aArgs);
- case 6:
- return self::PRODUCT($aArgs);
- case 7:
- return Statistical::STDEV($aArgs);
- case 8:
- return Statistical::STDEVP($aArgs);
- case 9:
- return self::SUM($aArgs);
- case 10:
- return Statistical::VARFunc($aArgs);
- case 11:
- return Statistical::VARP($aArgs);
- }
- }
-
- return Functions::VALUE();
+ return MathTrig\Subtotal::evaluate($functionType, ...$args);
}
/**
@@ -1166,134 +734,64 @@ class MathTrig
*
* SUM computes the sum of all the values and cells referenced in the argument list.
*
+ * @deprecated 1.18.0
+ * Use the sumErroringStrings method in the MathTrig\Sum class instead
+ * @see MathTrig\Sum::sumErroringStrings()
+ *
* Excel Function:
* SUM(value1[,value2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
- *
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function SUM(...$args)
{
- $returnValue = 0;
-
- // Loop through the arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg;
- }
- }
-
- return $returnValue;
+ return MathTrig\Sum::sumIgnoringStrings(...$args);
}
/**
* SUMIF.
*
- * Counts the number of cells that contain numbers within the list of arguments
+ * Totals the values of cells that contain numbers within the list of arguments
*
* Excel Function:
- * SUMIF(value1[,value2[, ...]],condition)
+ * SUMIF(range, criteria, [sum_range])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.17.0
+ * Use the SUMIF() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::SUMIF()
*
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be summed
- * @param mixed $sumArgs
+ * @param mixed $range Data values
+ * @param string $criteria the criteria that defines which cells will be summed
+ * @param mixed $sumRange
*
- * @return float
+ * @return float|string
*/
- public static function SUMIF($aArgs, $condition, $sumArgs = [])
+ public static function SUMIF($range, $criteria, $sumRange = [])
{
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $sumArgs = Functions::flattenArray($sumArgs);
- if (empty($sumArgs)) {
- $sumArgs = $aArgs;
- }
- $condition = Functions::ifCondition($condition);
- // Loop through arguments
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- $arg = str_replace('"', '""', $arg);
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
-
- $testCondition = '=' . $arg . $condition;
- $sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0;
-
- if (is_numeric($sumValue) &&
- Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is it a value within our criteria and only numeric can be added to the result
- $returnValue += $sumValue;
- }
- }
-
- return $returnValue;
+ return Statistical\Conditional::SUMIF($range, $criteria, $sumRange);
}
/**
* SUMIFS.
*
- * Counts the number of cells that contain numbers within the list of arguments
+ * Totals the values of cells that contain numbers within the list of arguments
*
* Excel Function:
- * SUMIFS(value1[,value2[, ...]],condition)
+ * SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.17.0
+ * Use the SUMIFS() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::SUMIFS()
*
* @param mixed $args Data values
- * @param string $condition the criteria that defines which cells will be summed
*
- * @return float
+ * @return null|float|string
*/
public static function SUMIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = 0;
-
- $sumArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each sum and see if arguments and conditions are true
- foreach ($sumArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue += $value;
- }
- }
-
- // Return
- return $returnValue;
+ return Statistical\Conditional::SUMIFS(...$args);
}
/**
@@ -1302,41 +800,17 @@ class MathTrig
* Excel Function:
* SUMPRODUCT(value1[,value2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.18.0
+ * Use the product method in the MathTrig\Sum class instead
+ * @see MathTrig\Sum::product()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SUMPRODUCT(...$args)
{
- $arrayList = $args;
-
- $wrkArray = Functions::flattenArray(array_shift($arrayList));
- $wrkCellCount = count($wrkArray);
-
- for ($i = 0; $i < $wrkCellCount; ++$i) {
- if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
- $wrkArray[$i] = 0;
- }
- }
-
- foreach ($arrayList as $matrixData) {
- $array2 = Functions::flattenArray($matrixData);
- $count = count($array2);
- if ($wrkCellCount != $count) {
- return Functions::VALUE();
- }
-
- foreach ($array2 as $i => $val) {
- if ((!is_numeric($val)) || (is_string($val))) {
- $val = 0;
- }
- $wrkArray[$i] *= $val;
- }
- }
-
- return array_sum($wrkArray);
+ return MathTrig\Sum::product(...$args);
}
/**
@@ -1344,103 +818,71 @@ class MathTrig
*
* SUMSQ returns the sum of the squares of the arguments
*
+ * @deprecated 1.18.0
+ * Use the sumSquare method in the MathTrig\SumSquares class instead
+ * @see MathTrig\SumSquares::sumSquare()
+ *
* Excel Function:
* SUMSQ(value1[,value2[, ...]])
*
- * @category Mathematical and Trigonometric Functions
- *
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function SUMSQ(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += ($arg * $arg);
- }
- }
-
- return $returnValue;
+ return MathTrig\SumSquares::sumSquare(...$args);
}
/**
* SUMX2MY2.
*
+ * @deprecated 1.18.0
+ * Use the sumXSquaredMinusYSquared method in the MathTrig\SumSquares class instead
+ * @see MathTrig\SumSquares::sumXSquaredMinusYSquared()
+ *
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
- * @return float
+ * @return float|string
*/
public static function SUMX2MY2($matrixData1, $matrixData2)
{
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $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;
+ return MathTrig\SumSquares::sumXSquaredMinusYSquared($matrixData1, $matrixData2);
}
/**
* SUMX2PY2.
*
+ * @deprecated 1.18.0
+ * Use the sumXSquaredPlusYSquared method in the MathTrig\SumSquares class instead
+ * @see MathTrig\SumSquares::sumXSquaredPlusYSquared()
+ *
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
- * @return float
+ * @return float|string
*/
public static function SUMX2PY2($matrixData1, $matrixData2)
{
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $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;
+ return MathTrig\SumSquares::sumXSquaredPlusYSquared($matrixData1, $matrixData2);
}
/**
* SUMXMY2.
*
+ * @deprecated 1.18.0
+ * Use the sumXMinusYSquared method in the MathTrig\SumSquares class instead
+ * @see MathTrig\SumSquares::sumXMinusYSquared()
+ *
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
- * @return float
+ * @return float|string
*/
public static function SUMXMY2($matrixData1, $matrixData2)
{
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $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;
+ return MathTrig\SumSquares::sumXMinusYSquared($matrixData1, $matrixData2);
}
/**
@@ -1448,30 +890,18 @@ class MathTrig
*
* Truncates value to the number of fractional digits by number_digits.
*
+ * @deprecated 1.17.0
+ * Use the evaluate() method in the MathTrig\Trunc class instead
+ * @see MathTrig\Trunc::evaluate()
+ *
* @param float $value
* @param int $digits
*
- * @return float Truncated value
+ * @return array|float|string Truncated value, or a string containing an error
*/
public static function TRUNC($value = 0, $digits = 0)
{
- $value = Functions::flattenSingleValue($value);
- $digits = Functions::flattenSingleValue($digits);
-
- // Validate parameters
- if ((!is_numeric($value)) || (!is_numeric($digits))) {
- return Functions::VALUE();
- }
- $digits = floor($digits);
-
- // Truncate
- $adjust = pow(10, $digits);
-
- if (($digits > 0) && (rtrim((int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
- return $value;
- }
-
- return ((int) ($value * $adjust)) / $adjust;
+ return MathTrig\Trunc::evaluate($value, $digits);
}
/**
@@ -1479,21 +909,17 @@ class MathTrig
*
* Returns the secant of an angle.
*
- * @param float $angle Number
+ * @deprecated 1.18.0
+ * Use the sec method in the MathTrig\Trig\Secant class instead
+ * @see MathTrig\Trig\Secant::sec()
*
- * @return float|string The secant of the angle
+ * @param array|float $angle Number
+ *
+ * @return array|float|string The secant of the angle
*/
public static function SEC($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = cos($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Secant::sec($angle);
}
/**
@@ -1501,21 +927,17 @@ class MathTrig
*
* Returns the hyperbolic secant of an angle.
*
- * @param float $angle Number
+ * @deprecated 1.18.0
+ * Use the sech method in the MathTrig\Trig\Secant class instead
+ * @see MathTrig\Trig\Secant::sech()
*
- * @return float|string The hyperbolic secant of the angle
+ * @param array|float $angle Number
+ *
+ * @return array|float|string The hyperbolic secant of the angle
*/
public static function SECH($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = cosh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Secant::sech($angle);
}
/**
@@ -1523,21 +945,17 @@ class MathTrig
*
* Returns the cosecant of an angle.
*
- * @param float $angle Number
+ * @deprecated 1.18.0
+ * Use the csc method in the MathTrig\Trig\Cosecant class instead
+ * @see MathTrig\Trig\Cosecant::csc()
*
- * @return float|string The cosecant of the angle
+ * @param array|float $angle Number
+ *
+ * @return array|float|string The cosecant of the angle
*/
public static function CSC($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = sin($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cosecant::csc($angle);
}
/**
@@ -1545,21 +963,17 @@ class MathTrig
*
* Returns the hyperbolic cosecant of an angle.
*
- * @param float $angle Number
+ * @deprecated 1.18.0
+ * Use the csch method in the MathTrig\Trig\Cosecant class instead
+ * @see MathTrig\Trig\Cosecant::csch()
*
- * @return float|string The hyperbolic cosecant of the angle
+ * @param array|float $angle Number
+ *
+ * @return array|float|string The hyperbolic cosecant of the angle
*/
public static function CSCH($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = sinh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cosecant::csch($angle);
}
/**
@@ -1567,21 +981,17 @@ class MathTrig
*
* Returns the cotangent of an angle.
*
- * @param float $angle Number
+ * @deprecated 1.18.0
+ * Use the cot method in the MathTrig\Trig\Cotangent class instead
+ * @see MathTrig\Trig\Cotangent::cot()
*
- * @return float|string The cotangent of the angle
+ * @param array|float $angle Number
+ *
+ * @return array|float|string The cotangent of the angle
*/
public static function COT($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = tan($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cotangent::cot($angle);
}
/**
@@ -1589,21 +999,17 @@ class MathTrig
*
* Returns the hyperbolic cotangent of an angle.
*
- * @param float $angle Number
+ * @deprecated 1.18.0
+ * Use the coth method in the MathTrig\Trig\Cotangent class instead
+ * @see MathTrig\Trig\Cotangent::coth()
*
- * @return float|string The hyperbolic cotangent of the angle
+ * @param array|float $angle Number
+ *
+ * @return array|float|string The hyperbolic cotangent of the angle
*/
public static function COTH($angle)
{
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = tanh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
+ return MathTrig\Trig\Cotangent::coth($angle);
}
/**
@@ -1611,19 +1017,33 @@ class MathTrig
*
* Returns the arccotangent of a number.
*
- * @param float $number Number
+ * @deprecated 1.18.0
+ * Use the acot method in the MathTrig\Trig\Cotangent class instead
+ * @see MathTrig\Trig\Cotangent::acot()
*
- * @return float|string The arccotangent of the number
+ * @param array|float $number Number
+ *
+ * @return array|float|string The arccotangent of the number
*/
public static function ACOT($number)
{
- $number = Functions::flattenSingleValue($number);
+ return MathTrig\Trig\Cotangent::acot($number);
+ }
- if (!is_numeric($number)) {
- return Functions::VALUE();
- }
-
- return (M_PI / 2) - atan($number);
+ /**
+ * Return NAN or value depending on argument.
+ *
+ * @deprecated 1.18.0
+ * Use the numberOrNan method in the MathTrig\Helpers class instead
+ * @see MathTrig\Helpers::numberOrNan()
+ *
+ * @param float $result Number
+ *
+ * @return float|string
+ */
+ public static function numberOrNan($result)
+ {
+ return MathTrig\Helpers::numberOrNan($result);
}
/**
@@ -1631,20 +1051,396 @@ class MathTrig
*
* Returns the hyperbolic arccotangent of a number.
*
- * @param float $number Number
+ * @deprecated 1.18.0
+ * Use the acoth method in the MathTrig\Trig\Cotangent class instead
+ * @see MathTrig\Trig\Cotangent::acoth()
*
- * @return float|string The hyperbolic arccotangent of the number
+ * @param array|float $number Number
+ *
+ * @return array|float|string The hyperbolic arccotangent of the number
*/
public static function ACOTH($number)
+ {
+ return MathTrig\Trig\Cotangent::acoth($number);
+ }
+
+ /**
+ * ROUND.
+ *
+ * Returns the result of builtin function round after validating args.
+ *
+ * @deprecated 1.17.0
+ * Use the round() method in the MathTrig\Round class instead
+ * @see MathTrig\Round::round()
+ *
+ * @param array|mixed $number Should be numeric
+ * @param array|mixed $precision Should be int
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinROUND($number, $precision)
+ {
+ return MathTrig\Round::round($number, $precision);
+ }
+
+ /**
+ * ABS.
+ *
+ * Returns the result of builtin function abs after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Absolute class instead
+ * @see MathTrig\Absolute::evaluate()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|int|string Rounded number
+ */
+ public static function builtinABS($number)
+ {
+ return MathTrig\Absolute::evaluate($number);
+ }
+
+ /**
+ * ACOS.
+ *
+ * @deprecated 1.18.0
+ * Use the acos method in the MathTrig\Trig\Cosine class instead
+ * @see MathTrig\Trig\Cosine::acos()
+ *
+ * Returns the result of builtin function acos after validating args.
+ *
+ * @param array|float $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinACOS($number)
+ {
+ return MathTrig\Trig\Cosine::acos($number);
+ }
+
+ /**
+ * ACOSH.
+ *
+ * Returns the result of builtin function acosh after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the acosh method in the MathTrig\Trig\Cosine class instead
+ * @see MathTrig\Trig\Cosine::acosh()
+ *
+ * @param array|float $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinACOSH($number)
+ {
+ return MathTrig\Trig\Cosine::acosh($number);
+ }
+
+ /**
+ * ASIN.
+ *
+ * Returns the result of builtin function asin after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the asin method in the MathTrig\Trig\Sine class instead
+ * @see MathTrig\Trig\Sine::asin()
+ *
+ * @param array|float $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinASIN($number)
+ {
+ return MathTrig\Trig\Sine::asin($number);
+ }
+
+ /**
+ * ASINH.
+ *
+ * Returns the result of builtin function asinh after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the asinh method in the MathTrig\Trig\Sine class instead
+ * @see MathTrig\Trig\Sine::asinh()
+ *
+ * @param array|float $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinASINH($number)
+ {
+ return MathTrig\Trig\Sine::asinh($number);
+ }
+
+ /**
+ * ATAN.
+ *
+ * Returns the result of builtin function atan after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the atan method in the MathTrig\Trig\Tangent class instead
+ * @see MathTrig\Trig\Tangent::atan()
+ *
+ * @param array|float $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinATAN($number)
+ {
+ return MathTrig\Trig\Tangent::atan($number);
+ }
+
+ /**
+ * ATANH.
+ *
+ * Returns the result of builtin function atanh after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the atanh method in the MathTrig\Trig\Tangent class instead
+ * @see MathTrig\Trig\Tangent::atanh()
+ *
+ * @param array|float $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinATANH($number)
+ {
+ return MathTrig\Trig\Tangent::atanh($number);
+ }
+
+ /**
+ * COS.
+ *
+ * Returns the result of builtin function cos after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the cos method in the MathTrig\Trig\Cosine class instead
+ * @see MathTrig\Trig\Cosine::cos()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinCOS($number)
+ {
+ return MathTrig\Trig\Cosine::cos($number);
+ }
+
+ /**
+ * COSH.
+ *
+ * Returns the result of builtin function cos after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the cosh method in the MathTrig\Trig\Cosine class instead
+ * @see MathTrig\Trig\Cosine::cosh()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinCOSH($number)
+ {
+ return MathTrig\Trig\Cosine::cosh($number);
+ }
+
+ /**
+ * DEGREES.
+ *
+ * Returns the result of builtin function rad2deg after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the toDegrees method in the MathTrig\Angle class instead
+ * @see MathTrig\Angle::toDegrees()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinDEGREES($number)
+ {
+ return MathTrig\Angle::toDegrees($number);
+ }
+
+ /**
+ * EXP.
+ *
+ * Returns the result of builtin function exp after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the evaluate method in the MathTrig\Exp class instead
+ * @see MathTrig\Exp::evaluate()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinEXP($number)
+ {
+ return MathTrig\Exp::evaluate($number);
+ }
+
+ /**
+ * LN.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the natural method in the MathTrig\Logarithms class instead
+ * @see MathTrig\Logarithms::natural()
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinLN($number)
+ {
+ return MathTrig\Logarithms::natural($number);
+ }
+
+ /**
+ * LOG10.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the natural method in the MathTrig\Logarithms class instead
+ * @see MathTrig\Logarithms::base10()
+ *
+ * @param mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinLOG10($number)
+ {
+ return MathTrig\Logarithms::base10($number);
+ }
+
+ /**
+ * RADIANS.
+ *
+ * Returns the result of builtin function deg2rad after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the toRadians method in the MathTrig\Angle class instead
+ * @see MathTrig\Angle::toRadians()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinRADIANS($number)
+ {
+ return MathTrig\Angle::toRadians($number);
+ }
+
+ /**
+ * SIN.
+ *
+ * Returns the result of builtin function sin after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the sin method in the MathTrig\Trig\Sine class instead
+ * @see MathTrig\Trig\Sine::evaluate()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string sine
+ */
+ public static function builtinSIN($number)
+ {
+ return MathTrig\Trig\Sine::sin($number);
+ }
+
+ /**
+ * SINH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the sinh method in the MathTrig\Trig\Sine class instead
+ * @see MathTrig\Trig\Sine::sinh()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinSINH($number)
+ {
+ return MathTrig\Trig\Sine::sinh($number);
+ }
+
+ /**
+ * SQRT.
+ *
+ * Returns the result of builtin function sqrt after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the sqrt method in the MathTrig\Sqrt class instead
+ * @see MathTrig\Sqrt::sqrt()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinSQRT($number)
+ {
+ return MathTrig\Sqrt::sqrt($number);
+ }
+
+ /**
+ * TAN.
+ *
+ * Returns the result of builtin function tan after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the tan method in the MathTrig\Trig\Tangent class instead
+ * @see MathTrig\Trig\Tangent::tan()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinTAN($number)
+ {
+ return MathTrig\Trig\Tangent::tan($number);
+ }
+
+ /**
+ * TANH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the tanh method in the MathTrig\Trig\Tangent class instead
+ * @see MathTrig\Trig\Tangent::tanh()
+ *
+ * @param array|mixed $number Should be numeric
+ *
+ * @return array|float|string Rounded number
+ */
+ public static function builtinTANH($number)
+ {
+ return MathTrig\Trig\Tangent::tanh($number);
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @deprecated 1.18.0
+ * Use the validateNumericNullBool method in the MathTrig\Helpers class instead
+ * @see MathTrig\Helpers::validateNumericNullBool()
+ *
+ * @param mixed $number
+ */
+ public static function nullFalseTrueToNumber(&$number): void
{
$number = Functions::flattenSingleValue($number);
-
- if (!is_numeric($number)) {
- return Functions::VALUE();
+ if ($number === null) {
+ $number = 0;
+ } elseif (is_bool($number)) {
+ $number = (int) $number;
}
-
- $result = log(($number + 1) / ($number - 1)) / 2;
-
- return is_nan($result) ? Functions::NAN() : $result;
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Absolute.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
new file mode 100644
index 0000000..f21c6b7
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
@@ -0,0 +1,37 @@
+getMessage();
+ }
+
+ return abs($number);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Angle.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Angle.php
new file mode 100644
index 0000000..cbeec6f
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Angle.php
@@ -0,0 +1,63 @@
+getMessage();
+ }
+
+ return rad2deg($number);
+ }
+
+ /**
+ * RADIANS.
+ *
+ * Returns the result of builtin function deg2rad after validating args.
+ *
+ * @param mixed $number Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string Rounded number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toRadians($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return deg2rad($number);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Arabic.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
new file mode 100644
index 0000000..ee48850
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
@@ -0,0 +1,112 @@
+ 1000,
+ 'D' => 500,
+ 'C' => 100,
+ 'L' => 50,
+ 'X' => 10,
+ 'V' => 5,
+ 'I' => 1,
+ ];
+
+ /**
+ * Recursively calculate the arabic value of a roman numeral.
+ *
+ * @param int $sum
+ * @param int $subtract
+ *
+ * @return int
+ */
+ private static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
+ {
+ $numeral = array_shift($roman);
+ if (!isset(self::ROMAN_LOOKUP[$numeral])) {
+ throw new Exception('Invalid character detected');
+ }
+
+ $arabic = self::ROMAN_LOOKUP[$numeral];
+ if (count($roman) > 0 && isset(self::ROMAN_LOOKUP[$roman[0]]) && $arabic < self::ROMAN_LOOKUP[$roman[0]]) {
+ $subtract += $arabic;
+ } else {
+ $sum += ($arabic - $subtract);
+ $subtract = 0;
+ }
+
+ if (count($roman) > 0) {
+ self::calculateArabic($roman, $sum, $subtract);
+ }
+
+ return $sum;
+ }
+
+ /**
+ * @param mixed $value
+ */
+ private static function mollifyScrutinizer($value): array
+ {
+ return is_array($value) ? $value : [];
+ }
+
+ private static function strSplit(string $roman): array
+ {
+ $rslt = str_split($roman);
+
+ return self::mollifyScrutinizer($rslt);
+ }
+
+ /**
+ * ARABIC.
+ *
+ * Converts a Roman numeral to an Arabic numeral.
+ *
+ * Excel Function:
+ * ARABIC(text)
+ *
+ * @param mixed $roman Should be a string, or can be an array of strings
+ *
+ * @return array|int|string the arabic numberal contrived from the roman numeral
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function evaluate($roman)
+ {
+ if (is_array($roman)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $roman);
+ }
+
+ // An empty string should return 0
+ $roman = substr(trim(strtoupper((string) $roman)), 0, 255);
+ if ($roman === '') {
+ return 0;
+ }
+
+ // Convert the roman numeral to an arabic number
+ $negativeNumber = $roman[0] === '-';
+ if ($negativeNumber) {
+ $roman = substr($roman, 1);
+ }
+
+ try {
+ $arabic = self::calculateArabic(self::strSplit($roman));
+ } catch (Exception $e) {
+ return ExcelError::VALUE(); // Invalid character detected
+ }
+
+ if ($negativeNumber) {
+ $arabic *= -1; // The number should be negative
+ }
+
+ return $arabic;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Base.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Base.php
new file mode 100644
index 0000000..2fec947
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Base.php
@@ -0,0 +1,68 @@
+getMessage();
+ }
+
+ return self::calculate($number, $radix, $minLength);
+ }
+
+ /**
+ * @param mixed $minLength
+ */
+ private static function calculate(float $number, int $radix, $minLength): string
+ {
+ if ($minLength === null || is_numeric($minLength)) {
+ if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
+ return ExcelError::NAN(); // Numeric range constraints
+ }
+
+ $outcome = strtoupper((string) base_convert("$number", 10, $radix));
+ if ($minLength !== null) {
+ $outcome = str_pad($outcome, (int) $minLength, '0', STR_PAD_LEFT); // String padding
+ }
+
+ return $outcome;
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
new file mode 100644
index 0000000..635f1bb
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
@@ -0,0 +1,167 @@
+getMessage();
+ }
+
+ return self::argumentsOk((float) $number, (float) $significance);
+ }
+
+ /**
+ * CEILING.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * CEILING.MATH(number[,significance[,mode]])
+ *
+ * @param mixed $number Number to round
+ * Or can be an array of values
+ * @param mixed $significance Significance
+ * Or can be an array of values
+ * @param array|int $mode direction to round negative numbers
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function math($number, $significance = null, $mode = 0)
+ {
+ if (is_array($number) || is_array($significance) || is_array($mode)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
+ $mode = Helpers::validateNumericNullSubstitution($mode, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (empty($significance * $number)) {
+ return 0.0;
+ }
+ if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
+ return floor($number / $significance) * $significance;
+ }
+
+ return ceil($number / $significance) * $significance;
+ }
+
+ /**
+ * CEILING.PRECISE.
+ *
+ * Rounds number up, away from zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * CEILING.PRECISE(number[,significance])
+ *
+ * @param mixed $number the number you want to round
+ * Or can be an array of values
+ * @param array|float $significance the multiple to which you want to round
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function precise($number, $significance = 1)
+ {
+ if (is_array($number) || is_array($significance)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (!$significance) {
+ return 0.0;
+ }
+ $result = $number / abs($significance);
+
+ return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
+ }
+
+ /**
+ * Let CEILINGMATH complexity pass Scrutinizer.
+ */
+ private static function ceilingMathTest(float $significance, float $number, int $mode): bool
+ {
+ return ((float) $significance < 0) || ((float) $number < 0 && !empty($mode));
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ *
+ * @return float|string
+ */
+ private static function argumentsOk(float $number, float $significance)
+ {
+ if (empty($number * $significance)) {
+ return 0.0;
+ }
+ if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ private static function floorCheck1Arg(): void
+ {
+ $compatibility = Functions::getCompatibilityMode();
+ if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
+ throw new Exception('Excel requires 2 arguments for CEILING');
+ }
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Combinations.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
new file mode 100644
index 0000000..5a652da
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
@@ -0,0 +1,91 @@
+getMessage();
+ }
+
+ return round(Factorial::fact($numObjs) / Factorial::fact($numObjs - $numInSet)) / Factorial::fact($numInSet);
+ }
+
+ /**
+ * COMBINA.
+ *
+ * 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.
+ *
+ * Excel Function:
+ * COMBINA(numObjs,numInSet)
+ *
+ * @param mixed $numObjs Number of different objects, or can be an array of numbers
+ * @param mixed $numInSet Number of objects in each combination, or can be an array of numbers
+ *
+ * @return array|float|int|string Number of combinations, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function withRepetition($numObjs, $numInSet)
+ {
+ if (is_array($numObjs) || is_array($numInSet)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
+ }
+
+ try {
+ $numObjs = Helpers::validateNumericNullSubstitution($numObjs, null);
+ $numInSet = Helpers::validateNumericNullSubstitution($numInSet, null);
+ Helpers::validateNotNegative($numInSet);
+ Helpers::validateNotNegative($numObjs);
+ $numObjs = (int) $numObjs;
+ $numInSet = (int) $numInSet;
+ // Microsoft documentation says following is true, but Excel
+ // does not enforce this restriction.
+ //Helpers::validateNotNegative($numObjs - $numInSet);
+ if ($numObjs === 0) {
+ Helpers::validateNotNegative(-$numInSet);
+
+ return 1;
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return round(
+ Factorial::fact($numObjs + $numInSet - 1) / Factorial::fact($numObjs - 1)
+ ) / Factorial::fact($numInSet);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Exp.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Exp.php
new file mode 100644
index 0000000..f65c2c1
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Exp.php
@@ -0,0 +1,37 @@
+getMessage();
+ }
+
+ return exp($number);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Factorial.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
new file mode 100644
index 0000000..b6883e2
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
@@ -0,0 +1,125 @@
+getMessage();
+ }
+
+ $factLoop = floor($factVal);
+ if ($factVal > $factLoop) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return Statistical\Distributions\Gamma::gammaValue($factVal + 1);
+ }
+ }
+
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ }
+
+ return $factorial;
+ }
+
+ /**
+ * FACTDOUBLE.
+ *
+ * Returns the double factorial of a number.
+ *
+ * Excel Function:
+ * FACTDOUBLE(factVal)
+ *
+ * @param array|float $factVal Factorial Value, or can be an array of numbers
+ *
+ * @return array|float|int|string Double Factorial, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function factDouble($factVal)
+ {
+ if (is_array($factVal)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $factVal);
+ }
+
+ try {
+ $factVal = Helpers::validateNumericNullSubstitution($factVal, 0);
+ Helpers::validateNotNegative($factVal);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $factLoop = floor($factVal);
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop;
+ $factLoop -= 2;
+ }
+
+ return $factorial;
+ }
+
+ /**
+ * MULTINOMIAL.
+ *
+ * Returns the ratio of the factorial of a sum of values to the product of factorials.
+ *
+ * @param mixed[] $args An array of mixed values for the Data Series
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function multinomial(...$args)
+ {
+ $summer = 0;
+ $divisor = 1;
+
+ try {
+ // Loop through arguments
+ foreach (Functions::flattenArray($args) as $argx) {
+ $arg = Helpers::validateNumericNullSubstitution($argx, null);
+ Helpers::validateNotNegative($arg);
+ $arg = (int) $arg;
+ $summer += $arg;
+ $divisor *= self::fact($arg);
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $summer = self::fact($summer);
+
+ return $summer / $divisor;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Floor.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Floor.php
new file mode 100644
index 0000000..2199dda
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Floor.php
@@ -0,0 +1,195 @@
+getMessage();
+ }
+
+ return self::argumentsOk((float) $number, (float) $significance);
+ }
+
+ /**
+ * FLOOR.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.MATH(number[,significance[,mode]])
+ *
+ * @param mixed $number Number to round
+ * Or can be an array of values
+ * @param mixed $significance Significance
+ * Or can be an array of values
+ * @param mixed $mode direction to round negative numbers
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function math($number, $significance = null, $mode = 0)
+ {
+ if (is_array($number) || is_array($significance) || is_array($mode)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
+ $mode = Helpers::validateNumericNullSubstitution($mode, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::argsOk((float) $number, (float) $significance, (int) $mode);
+ }
+
+ /**
+ * FLOOR.PRECISE.
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.PRECISE(number[,significance])
+ *
+ * @param array|float $number Number to round
+ * Or can be an array of values
+ * @param array|float $significance Significance
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function precise($number, $significance = 1)
+ {
+ if (is_array($number) || is_array($significance)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::argumentsOkPrecise((float) $number, (float) $significance);
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ *
+ * @return float|string
+ */
+ private static function argumentsOkPrecise(float $number, float $significance)
+ {
+ if ($significance == 0.0) {
+ return ExcelError::DIV0();
+ }
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ return floor($number / abs($significance)) * abs($significance);
+ }
+
+ /**
+ * Avoid Scrutinizer complexity problems.
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ private static function argsOk(float $number, float $significance, int $mode)
+ {
+ if (!$significance) {
+ return ExcelError::DIV0();
+ }
+ if (!$number) {
+ return 0.0;
+ }
+ if (self::floorMathTest($number, $significance, $mode)) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return floor($number / $significance) * $significance;
+ }
+
+ /**
+ * Let FLOORMATH complexity pass Scrutinizer.
+ */
+ private static function floorMathTest(float $number, float $significance, int $mode): bool
+ {
+ return Helpers::returnSign($significance) == -1 || (Helpers::returnSign($number) == -1 && !empty($mode));
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ *
+ * @return float|string
+ */
+ private static function argumentsOk(float $number, float $significance)
+ {
+ if ($significance == 0.0) {
+ return ExcelError::DIV0();
+ }
+ if ($number == 0.0) {
+ return 0.0;
+ }
+ if (Helpers::returnSign($significance) == 1) {
+ return floor($number / $significance) * $significance;
+ }
+ if (Helpers::returnSign($number) == -1 && Helpers::returnSign($significance) == -1) {
+ return floor($number / $significance) * $significance;
+ }
+
+ return ExcelError::NAN();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Gcd.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
new file mode 100644
index 0000000..f703599
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
@@ -0,0 +1,70 @@
+getMessage();
+ }
+
+ if (count($arrayArgs) <= 0) {
+ return ExcelError::VALUE();
+ }
+ $gcd = (int) array_pop($arrayArgs);
+ do {
+ $gcd = self::evaluateGCD($gcd, (int) array_pop($arrayArgs));
+ } while (!empty($arrayArgs));
+
+ return $gcd;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Helpers.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
new file mode 100644
index 0000000..f34f159
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
@@ -0,0 +1,130 @@
+= 0.
+ *
+ * @param float|int $number
+ */
+ public static function validateNotNegative($number, ?string $except = null): void
+ {
+ if ($number >= 0) {
+ return;
+ }
+
+ throw new Exception($except ?? ExcelError::NAN());
+ }
+
+ /**
+ * Confirm number > 0.
+ *
+ * @param float|int $number
+ */
+ public static function validatePositive($number, ?string $except = null): void
+ {
+ if ($number > 0) {
+ return;
+ }
+
+ throw new Exception($except ?? ExcelError::NAN());
+ }
+
+ /**
+ * Confirm number != 0.
+ *
+ * @param float|int $number
+ */
+ public static function validateNotZero($number): void
+ {
+ if ($number) {
+ return;
+ }
+
+ throw new Exception(ExcelError::DIV0());
+ }
+
+ public static function returnSign(float $number): int
+ {
+ return $number ? (($number > 0) ? 1 : -1) : 0;
+ }
+
+ public static function getEven(float $number): float
+ {
+ $significance = 2 * self::returnSign($number);
+
+ return $significance ? (ceil($number / $significance) * $significance) : 0;
+ }
+
+ /**
+ * Return NAN or value depending on argument.
+ *
+ * @param float $result Number
+ *
+ * @return float|string
+ */
+ public static function numberOrNan($result)
+ {
+ return is_nan($result) ? ExcelError::NAN() : $result;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/IntClass.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
new file mode 100644
index 0000000..f7f7764
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
@@ -0,0 +1,40 @@
+getMessage();
+ }
+
+ return (int) floor($number);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Lcm.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
new file mode 100644
index 0000000..3b23c1d
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
@@ -0,0 +1,111 @@
+ 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 (!empty($factorArray)) {
+ rsort($factorArray);
+
+ return $factorArray;
+ }
+
+ return [(int) $value];
+ }
+
+ /**
+ * LCM.
+ *
+ * Returns the lowest common multiplier of a series of numbers
+ * The least common multiple is the smallest positive integer that is a multiple
+ * of all integer arguments number1, number2, and so on. Use LCM to add fractions
+ * with different denominators.
+ *
+ * Excel Function:
+ * LCM(number1[,number2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return int|string Lowest Common Multiplier, or a string containing an error
+ */
+ public static function evaluate(...$args)
+ {
+ try {
+ $arrayArgs = [];
+ $anyZeros = 0;
+ $anyNonNulls = 0;
+ foreach (Functions::flattenArray($args) as $value1) {
+ $anyNonNulls += (int) ($value1 !== null);
+ $value = Helpers::validateNumericNullSubstitution($value1, 1);
+ Helpers::validateNotNegative($value);
+ $arrayArgs[] = (int) $value;
+ $anyZeros += (int) !((bool) $value);
+ }
+ self::testNonNulls($anyNonNulls);
+ if ($anyZeros) {
+ return 0;
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $returnValue = 1;
+ $allPoweredFactors = [];
+ // Loop through arguments
+ foreach ($arrayArgs as $value) {
+ $myFactors = self::factors(floor($value));
+ $myCountedFactors = array_count_values($myFactors);
+ $myPoweredFactors = [];
+ foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
+ $myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
+ }
+ self::processPoweredFactors($allPoweredFactors, $myPoweredFactors);
+ }
+ foreach ($allPoweredFactors as $allPoweredFactor) {
+ $returnValue *= (int) $allPoweredFactor;
+ }
+
+ return $returnValue;
+ }
+
+ private static function processPoweredFactors(array &$allPoweredFactors, array &$myPoweredFactors): void
+ {
+ foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
+ if (isset($allPoweredFactors[$myPoweredValue])) {
+ if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ } else {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ }
+ }
+
+ private static function testNonNulls(int $anyNonNulls): void
+ {
+ if (!$anyNonNulls) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
new file mode 100644
index 0000000..7b07f09
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
@@ -0,0 +1,102 @@
+getMessage();
+ }
+
+ return log($number, $base);
+ }
+
+ /**
+ * LOG10.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @param mixed $number Should be numeric
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded number
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function base10($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ Helpers::validatePositive($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return log10($number);
+ }
+
+ /**
+ * LN.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @param mixed $number Should be numeric
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded number
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function natural($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ Helpers::validatePositive($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return log($number);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
new file mode 100644
index 0000000..5a5125a
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
@@ -0,0 +1,179 @@
+getMessage();
+ }
+
+ if ($step === 0) {
+ return array_chunk(
+ array_fill(0, $rows * $columns, $start),
+ max($columns, 1)
+ );
+ }
+
+ return array_chunk(
+ range($start, $start + (($rows * $columns - 1) * $step), $step),
+ max($columns, 1)
+ );
+ }
+
+ /**
+ * MDETERM.
+ *
+ * Returns the matrix determinant of an array.
+ *
+ * Excel Function:
+ * MDETERM(array)
+ *
+ * @param mixed $matrixValues A matrix of values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function determinant($matrixValues)
+ {
+ try {
+ $matrix = self::getMatrix($matrixValues);
+
+ return $matrix->determinant();
+ } catch (MatrixException $ex) {
+ return ExcelError::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MINVERSE.
+ *
+ * Returns the inverse matrix for the matrix stored in an array.
+ *
+ * Excel Function:
+ * MINVERSE(array)
+ *
+ * @param mixed $matrixValues A matrix of values
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function inverse($matrixValues)
+ {
+ try {
+ $matrix = self::getMatrix($matrixValues);
+
+ return $matrix->inverse()->toArray();
+ } catch (MatrixDiv0Exception $e) {
+ return ExcelError::NAN();
+ } catch (MatrixException $e) {
+ return ExcelError::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MMULT.
+ *
+ * @param mixed $matrixData1 A matrix of values
+ * @param mixed $matrixData2 A matrix of values
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function multiply($matrixData1, $matrixData2)
+ {
+ try {
+ $matrixA = self::getMatrix($matrixData1);
+ $matrixB = self::getMatrix($matrixData2);
+
+ return $matrixA->multiply($matrixB)->toArray();
+ } catch (MatrixException $ex) {
+ return ExcelError::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MUnit.
+ *
+ * @param mixed $dimension Number of rows and columns
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function identity($dimension)
+ {
+ try {
+ $dimension = (int) Helpers::validateNumericNullBool($dimension);
+ Helpers::validatePositive($dimension, ExcelError::VALUE());
+ $matrix = Builder::createIdentityMatrix($dimension, 0)->toArray();
+
+ return $matrix;
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Operations.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Operations.php
new file mode 100644
index 0000000..0625845
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Operations.php
@@ -0,0 +1,162 @@
+getMessage();
+ }
+
+ if (($dividend < 0.0) && ($divisor > 0.0)) {
+ return $divisor - fmod(abs($dividend), $divisor);
+ }
+ if (($dividend > 0.0) && ($divisor < 0.0)) {
+ return $divisor + fmod($dividend, abs($divisor));
+ }
+
+ return fmod($dividend, $divisor);
+ }
+
+ /**
+ * POWER.
+ *
+ * Computes x raised to the power y.
+ *
+ * @param array|float|int $x
+ * Or can be an array of values
+ * @param array|float|int $y
+ * Or can be an array of values
+ *
+ * @return array|float|int|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function power($x, $y)
+ {
+ if (is_array($x) || is_array($y)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $y);
+ }
+
+ try {
+ $x = Helpers::validateNumericNullBool($x);
+ $y = Helpers::validateNumericNullBool($y);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if (!$x && !$y) {
+ return ExcelError::NAN();
+ }
+ if (!$x && $y < 0.0) {
+ return ExcelError::DIV0();
+ }
+
+ // Return
+ $result = $x ** $y;
+
+ return Helpers::numberOrNan($result);
+ }
+
+ /**
+ * PRODUCT.
+ *
+ * PRODUCT returns the product of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * PRODUCT(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string
+ */
+ public static function product(...$args)
+ {
+ $args = array_filter(
+ Functions::flattenArray($args),
+ function ($value) {
+ return $value !== null;
+ }
+ );
+
+ // Return value
+ $returnValue = (count($args) === 0) ? 0.0 : 1.0;
+
+ // Loop through arguments
+ foreach ($args as $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ $returnValue *= $arg;
+ } else {
+ return ExcelError::throwError($arg);
+ }
+ }
+
+ return (float) $returnValue;
+ }
+
+ /**
+ * 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)
+ *
+ * @param mixed $numerator Expect float|int
+ * Or can be an array of values
+ * @param mixed $denominator Expect float|int
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function quotient($numerator, $denominator)
+ {
+ if (is_array($numerator) || is_array($denominator)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $numerator, $denominator);
+ }
+
+ try {
+ $numerator = Helpers::validateNumericNullSubstitution($numerator, 0);
+ $denominator = Helpers::validateNumericNullSubstitution($denominator, 0);
+ Helpers::validateNotZero($denominator);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (int) ($numerator / $denominator);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Random.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Random.php
new file mode 100644
index 0000000..22cad2c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Random.php
@@ -0,0 +1,99 @@
+getMessage();
+ }
+
+ return mt_rand($min, $max);
+ }
+
+ /**
+ * RANDARRAY.
+ *
+ * Generates a list of sequential numbers in an array.
+ *
+ * Excel Function:
+ * RANDARRAY([rows],[columns],[start],[step])
+ *
+ * @param mixed $rows the number of rows to return, defaults to 1
+ * @param mixed $columns the number of columns to return, defaults to 1
+ * @param mixed $min the minimum number to be returned, defaults to 0
+ * @param mixed $max the maximum number to be returned, defaults to 1
+ * @param bool $wholeNumber the type of numbers to return:
+ * False - Decimal numbers to 15 decimal places. (default)
+ * True - Whole (integer) numbers
+ *
+ * @return array|string The resulting array, or a string containing an error
+ */
+ public static function randArray($rows = 1, $columns = 1, $min = 0, $max = 1, $wholeNumber = false)
+ {
+ try {
+ $rows = (int) Helpers::validateNumericNullSubstitution($rows, 1);
+ Helpers::validatePositive($rows);
+ $columns = (int) Helpers::validateNumericNullSubstitution($columns, 1);
+ Helpers::validatePositive($columns);
+ $min = Helpers::validateNumericNullSubstitution($min, 1);
+ $max = Helpers::validateNumericNullSubstitution($max, 1);
+
+ if ($max <= $min) {
+ return ExcelError::VALUE();
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return array_chunk(
+ array_map(
+ function () use ($min, $max, $wholeNumber) {
+ return $wholeNumber
+ ? mt_rand((int) $min, (int) $max)
+ : (mt_rand() / mt_getrandmax()) * ($max - $min) + $min;
+ },
+ array_fill(0, $rows * $columns, $min)
+ ),
+ max($columns, 1)
+ );
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Roman.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Roman.php
new file mode 100644
index 0000000..0541548
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Roman.php
@@ -0,0 +1,846 @@
+ ['VL'],
+ 46 => ['VLI'],
+ 47 => ['VLII'],
+ 48 => ['VLIII'],
+ 49 => ['VLIV', 'IL'],
+ 95 => ['VC'],
+ 96 => ['VCI'],
+ 97 => ['VCII'],
+ 98 => ['VCIII'],
+ 99 => ['VCIV', 'IC'],
+ 145 => ['CVL'],
+ 146 => ['CVLI'],
+ 147 => ['CVLII'],
+ 148 => ['CVLIII'],
+ 149 => ['CVLIV', 'CIL'],
+ 195 => ['CVC'],
+ 196 => ['CVCI'],
+ 197 => ['CVCII'],
+ 198 => ['CVCIII'],
+ 199 => ['CVCIV', 'CIC'],
+ 245 => ['CCVL'],
+ 246 => ['CCVLI'],
+ 247 => ['CCVLII'],
+ 248 => ['CCVLIII'],
+ 249 => ['CCVLIV', 'CCIL'],
+ 295 => ['CCVC'],
+ 296 => ['CCVCI'],
+ 297 => ['CCVCII'],
+ 298 => ['CCVCIII'],
+ 299 => ['CCVCIV', 'CCIC'],
+ 345 => ['CCCVL'],
+ 346 => ['CCCVLI'],
+ 347 => ['CCCVLII'],
+ 348 => ['CCCVLIII'],
+ 349 => ['CCCVLIV', 'CCCIL'],
+ 395 => ['CCCVC'],
+ 396 => ['CCCVCI'],
+ 397 => ['CCCVCII'],
+ 398 => ['CCCVCIII'],
+ 399 => ['CCCVCIV', 'CCCIC'],
+ 445 => ['CDVL'],
+ 446 => ['CDVLI'],
+ 447 => ['CDVLII'],
+ 448 => ['CDVLIII'],
+ 449 => ['CDVLIV', 'CDIL'],
+ 450 => ['LD'],
+ 451 => ['LDI'],
+ 452 => ['LDII'],
+ 453 => ['LDIII'],
+ 454 => ['LDIV'],
+ 455 => ['LDV'],
+ 456 => ['LDVI'],
+ 457 => ['LDVII'],
+ 458 => ['LDVIII'],
+ 459 => ['LDIX'],
+ 460 => ['LDX'],
+ 461 => ['LDXI'],
+ 462 => ['LDXII'],
+ 463 => ['LDXIII'],
+ 464 => ['LDXIV'],
+ 465 => ['LDXV'],
+ 466 => ['LDXVI'],
+ 467 => ['LDXVII'],
+ 468 => ['LDXVIII'],
+ 469 => ['LDXIX'],
+ 470 => ['LDXX'],
+ 471 => ['LDXXI'],
+ 472 => ['LDXXII'],
+ 473 => ['LDXXIII'],
+ 474 => ['LDXXIV'],
+ 475 => ['LDXXV'],
+ 476 => ['LDXXVI'],
+ 477 => ['LDXXVII'],
+ 478 => ['LDXXVIII'],
+ 479 => ['LDXXIX'],
+ 480 => ['LDXXX'],
+ 481 => ['LDXXXI'],
+ 482 => ['LDXXXII'],
+ 483 => ['LDXXXIII'],
+ 484 => ['LDXXXIV'],
+ 485 => ['LDXXXV'],
+ 486 => ['LDXXXVI'],
+ 487 => ['LDXXXVII'],
+ 488 => ['LDXXXVIII'],
+ 489 => ['LDXXXIX'],
+ 490 => ['LDXL', 'XD'],
+ 491 => ['LDXLI', 'XDI'],
+ 492 => ['LDXLII', 'XDII'],
+ 493 => ['LDXLIII', 'XDIII'],
+ 494 => ['LDXLIV', 'XDIV'],
+ 495 => ['LDVL', 'XDV', 'VD'],
+ 496 => ['LDVLI', 'XDVI', 'VDI'],
+ 497 => ['LDVLII', 'XDVII', 'VDII'],
+ 498 => ['LDVLIII', 'XDVIII', 'VDIII'],
+ 499 => ['LDVLIV', 'XDIX', 'VDIV', 'ID'],
+ 545 => ['DVL'],
+ 546 => ['DVLI'],
+ 547 => ['DVLII'],
+ 548 => ['DVLIII'],
+ 549 => ['DVLIV', 'DIL'],
+ 595 => ['DVC'],
+ 596 => ['DVCI'],
+ 597 => ['DVCII'],
+ 598 => ['DVCIII'],
+ 599 => ['DVCIV', 'DIC'],
+ 645 => ['DCVL'],
+ 646 => ['DCVLI'],
+ 647 => ['DCVLII'],
+ 648 => ['DCVLIII'],
+ 649 => ['DCVLIV', 'DCIL'],
+ 695 => ['DCVC'],
+ 696 => ['DCVCI'],
+ 697 => ['DCVCII'],
+ 698 => ['DCVCIII'],
+ 699 => ['DCVCIV', 'DCIC'],
+ 745 => ['DCCVL'],
+ 746 => ['DCCVLI'],
+ 747 => ['DCCVLII'],
+ 748 => ['DCCVLIII'],
+ 749 => ['DCCVLIV', 'DCCIL'],
+ 795 => ['DCCVC'],
+ 796 => ['DCCVCI'],
+ 797 => ['DCCVCII'],
+ 798 => ['DCCVCIII'],
+ 799 => ['DCCVCIV', 'DCCIC'],
+ 845 => ['DCCCVL'],
+ 846 => ['DCCCVLI'],
+ 847 => ['DCCCVLII'],
+ 848 => ['DCCCVLIII'],
+ 849 => ['DCCCVLIV', 'DCCCIL'],
+ 895 => ['DCCCVC'],
+ 896 => ['DCCCVCI'],
+ 897 => ['DCCCVCII'],
+ 898 => ['DCCCVCIII'],
+ 899 => ['DCCCVCIV', 'DCCCIC'],
+ 945 => ['CMVL'],
+ 946 => ['CMVLI'],
+ 947 => ['CMVLII'],
+ 948 => ['CMVLIII'],
+ 949 => ['CMVLIV', 'CMIL'],
+ 950 => ['LM'],
+ 951 => ['LMI'],
+ 952 => ['LMII'],
+ 953 => ['LMIII'],
+ 954 => ['LMIV'],
+ 955 => ['LMV'],
+ 956 => ['LMVI'],
+ 957 => ['LMVII'],
+ 958 => ['LMVIII'],
+ 959 => ['LMIX'],
+ 960 => ['LMX'],
+ 961 => ['LMXI'],
+ 962 => ['LMXII'],
+ 963 => ['LMXIII'],
+ 964 => ['LMXIV'],
+ 965 => ['LMXV'],
+ 966 => ['LMXVI'],
+ 967 => ['LMXVII'],
+ 968 => ['LMXVIII'],
+ 969 => ['LMXIX'],
+ 970 => ['LMXX'],
+ 971 => ['LMXXI'],
+ 972 => ['LMXXII'],
+ 973 => ['LMXXIII'],
+ 974 => ['LMXXIV'],
+ 975 => ['LMXXV'],
+ 976 => ['LMXXVI'],
+ 977 => ['LMXXVII'],
+ 978 => ['LMXXVIII'],
+ 979 => ['LMXXIX'],
+ 980 => ['LMXXX'],
+ 981 => ['LMXXXI'],
+ 982 => ['LMXXXII'],
+ 983 => ['LMXXXIII'],
+ 984 => ['LMXXXIV'],
+ 985 => ['LMXXXV'],
+ 986 => ['LMXXXVI'],
+ 987 => ['LMXXXVII'],
+ 988 => ['LMXXXVIII'],
+ 989 => ['LMXXXIX'],
+ 990 => ['LMXL', 'XM'],
+ 991 => ['LMXLI', 'XMI'],
+ 992 => ['LMXLII', 'XMII'],
+ 993 => ['LMXLIII', 'XMIII'],
+ 994 => ['LMXLIV', 'XMIV'],
+ 995 => ['LMVL', 'XMV', 'VM'],
+ 996 => ['LMVLI', 'XMVI', 'VMI'],
+ 997 => ['LMVLII', 'XMVII', 'VMII'],
+ 998 => ['LMVLIII', 'XMVIII', 'VMIII'],
+ 999 => ['LMVLIV', 'XMIX', 'VMIV', 'IM'],
+ 1045 => ['MVL'],
+ 1046 => ['MVLI'],
+ 1047 => ['MVLII'],
+ 1048 => ['MVLIII'],
+ 1049 => ['MVLIV', 'MIL'],
+ 1095 => ['MVC'],
+ 1096 => ['MVCI'],
+ 1097 => ['MVCII'],
+ 1098 => ['MVCIII'],
+ 1099 => ['MVCIV', 'MIC'],
+ 1145 => ['MCVL'],
+ 1146 => ['MCVLI'],
+ 1147 => ['MCVLII'],
+ 1148 => ['MCVLIII'],
+ 1149 => ['MCVLIV', 'MCIL'],
+ 1195 => ['MCVC'],
+ 1196 => ['MCVCI'],
+ 1197 => ['MCVCII'],
+ 1198 => ['MCVCIII'],
+ 1199 => ['MCVCIV', 'MCIC'],
+ 1245 => ['MCCVL'],
+ 1246 => ['MCCVLI'],
+ 1247 => ['MCCVLII'],
+ 1248 => ['MCCVLIII'],
+ 1249 => ['MCCVLIV', 'MCCIL'],
+ 1295 => ['MCCVC'],
+ 1296 => ['MCCVCI'],
+ 1297 => ['MCCVCII'],
+ 1298 => ['MCCVCIII'],
+ 1299 => ['MCCVCIV', 'MCCIC'],
+ 1345 => ['MCCCVL'],
+ 1346 => ['MCCCVLI'],
+ 1347 => ['MCCCVLII'],
+ 1348 => ['MCCCVLIII'],
+ 1349 => ['MCCCVLIV', 'MCCCIL'],
+ 1395 => ['MCCCVC'],
+ 1396 => ['MCCCVCI'],
+ 1397 => ['MCCCVCII'],
+ 1398 => ['MCCCVCIII'],
+ 1399 => ['MCCCVCIV', 'MCCCIC'],
+ 1445 => ['MCDVL'],
+ 1446 => ['MCDVLI'],
+ 1447 => ['MCDVLII'],
+ 1448 => ['MCDVLIII'],
+ 1449 => ['MCDVLIV', 'MCDIL'],
+ 1450 => ['MLD'],
+ 1451 => ['MLDI'],
+ 1452 => ['MLDII'],
+ 1453 => ['MLDIII'],
+ 1454 => ['MLDIV'],
+ 1455 => ['MLDV'],
+ 1456 => ['MLDVI'],
+ 1457 => ['MLDVII'],
+ 1458 => ['MLDVIII'],
+ 1459 => ['MLDIX'],
+ 1460 => ['MLDX'],
+ 1461 => ['MLDXI'],
+ 1462 => ['MLDXII'],
+ 1463 => ['MLDXIII'],
+ 1464 => ['MLDXIV'],
+ 1465 => ['MLDXV'],
+ 1466 => ['MLDXVI'],
+ 1467 => ['MLDXVII'],
+ 1468 => ['MLDXVIII'],
+ 1469 => ['MLDXIX'],
+ 1470 => ['MLDXX'],
+ 1471 => ['MLDXXI'],
+ 1472 => ['MLDXXII'],
+ 1473 => ['MLDXXIII'],
+ 1474 => ['MLDXXIV'],
+ 1475 => ['MLDXXV'],
+ 1476 => ['MLDXXVI'],
+ 1477 => ['MLDXXVII'],
+ 1478 => ['MLDXXVIII'],
+ 1479 => ['MLDXXIX'],
+ 1480 => ['MLDXXX'],
+ 1481 => ['MLDXXXI'],
+ 1482 => ['MLDXXXII'],
+ 1483 => ['MLDXXXIII'],
+ 1484 => ['MLDXXXIV'],
+ 1485 => ['MLDXXXV'],
+ 1486 => ['MLDXXXVI'],
+ 1487 => ['MLDXXXVII'],
+ 1488 => ['MLDXXXVIII'],
+ 1489 => ['MLDXXXIX'],
+ 1490 => ['MLDXL', 'MXD'],
+ 1491 => ['MLDXLI', 'MXDI'],
+ 1492 => ['MLDXLII', 'MXDII'],
+ 1493 => ['MLDXLIII', 'MXDIII'],
+ 1494 => ['MLDXLIV', 'MXDIV'],
+ 1495 => ['MLDVL', 'MXDV', 'MVD'],
+ 1496 => ['MLDVLI', 'MXDVI', 'MVDI'],
+ 1497 => ['MLDVLII', 'MXDVII', 'MVDII'],
+ 1498 => ['MLDVLIII', 'MXDVIII', 'MVDIII'],
+ 1499 => ['MLDVLIV', 'MXDIX', 'MVDIV', 'MID'],
+ 1545 => ['MDVL'],
+ 1546 => ['MDVLI'],
+ 1547 => ['MDVLII'],
+ 1548 => ['MDVLIII'],
+ 1549 => ['MDVLIV', 'MDIL'],
+ 1595 => ['MDVC'],
+ 1596 => ['MDVCI'],
+ 1597 => ['MDVCII'],
+ 1598 => ['MDVCIII'],
+ 1599 => ['MDVCIV', 'MDIC'],
+ 1645 => ['MDCVL'],
+ 1646 => ['MDCVLI'],
+ 1647 => ['MDCVLII'],
+ 1648 => ['MDCVLIII'],
+ 1649 => ['MDCVLIV', 'MDCIL'],
+ 1695 => ['MDCVC'],
+ 1696 => ['MDCVCI'],
+ 1697 => ['MDCVCII'],
+ 1698 => ['MDCVCIII'],
+ 1699 => ['MDCVCIV', 'MDCIC'],
+ 1745 => ['MDCCVL'],
+ 1746 => ['MDCCVLI'],
+ 1747 => ['MDCCVLII'],
+ 1748 => ['MDCCVLIII'],
+ 1749 => ['MDCCVLIV', 'MDCCIL'],
+ 1795 => ['MDCCVC'],
+ 1796 => ['MDCCVCI'],
+ 1797 => ['MDCCVCII'],
+ 1798 => ['MDCCVCIII'],
+ 1799 => ['MDCCVCIV', 'MDCCIC'],
+ 1845 => ['MDCCCVL'],
+ 1846 => ['MDCCCVLI'],
+ 1847 => ['MDCCCVLII'],
+ 1848 => ['MDCCCVLIII'],
+ 1849 => ['MDCCCVLIV', 'MDCCCIL'],
+ 1895 => ['MDCCCVC'],
+ 1896 => ['MDCCCVCI'],
+ 1897 => ['MDCCCVCII'],
+ 1898 => ['MDCCCVCIII'],
+ 1899 => ['MDCCCVCIV', 'MDCCCIC'],
+ 1945 => ['MCMVL'],
+ 1946 => ['MCMVLI'],
+ 1947 => ['MCMVLII'],
+ 1948 => ['MCMVLIII'],
+ 1949 => ['MCMVLIV', 'MCMIL'],
+ 1950 => ['MLM'],
+ 1951 => ['MLMI'],
+ 1952 => ['MLMII'],
+ 1953 => ['MLMIII'],
+ 1954 => ['MLMIV'],
+ 1955 => ['MLMV'],
+ 1956 => ['MLMVI'],
+ 1957 => ['MLMVII'],
+ 1958 => ['MLMVIII'],
+ 1959 => ['MLMIX'],
+ 1960 => ['MLMX'],
+ 1961 => ['MLMXI'],
+ 1962 => ['MLMXII'],
+ 1963 => ['MLMXIII'],
+ 1964 => ['MLMXIV'],
+ 1965 => ['MLMXV'],
+ 1966 => ['MLMXVI'],
+ 1967 => ['MLMXVII'],
+ 1968 => ['MLMXVIII'],
+ 1969 => ['MLMXIX'],
+ 1970 => ['MLMXX'],
+ 1971 => ['MLMXXI'],
+ 1972 => ['MLMXXII'],
+ 1973 => ['MLMXXIII'],
+ 1974 => ['MLMXXIV'],
+ 1975 => ['MLMXXV'],
+ 1976 => ['MLMXXVI'],
+ 1977 => ['MLMXXVII'],
+ 1978 => ['MLMXXVIII'],
+ 1979 => ['MLMXXIX'],
+ 1980 => ['MLMXXX'],
+ 1981 => ['MLMXXXI'],
+ 1982 => ['MLMXXXII'],
+ 1983 => ['MLMXXXIII'],
+ 1984 => ['MLMXXXIV'],
+ 1985 => ['MLMXXXV'],
+ 1986 => ['MLMXXXVI'],
+ 1987 => ['MLMXXXVII'],
+ 1988 => ['MLMXXXVIII'],
+ 1989 => ['MLMXXXIX'],
+ 1990 => ['MLMXL', 'MXM'],
+ 1991 => ['MLMXLI', 'MXMI'],
+ 1992 => ['MLMXLII', 'MXMII'],
+ 1993 => ['MLMXLIII', 'MXMIII'],
+ 1994 => ['MLMXLIV', 'MXMIV'],
+ 1995 => ['MLMVL', 'MXMV', 'MVM'],
+ 1996 => ['MLMVLI', 'MXMVI', 'MVMI'],
+ 1997 => ['MLMVLII', 'MXMVII', 'MVMII'],
+ 1998 => ['MLMVLIII', 'MXMVIII', 'MVMIII'],
+ 1999 => ['MLMVLIV', 'MXMIX', 'MVMIV', 'MIM'],
+ 2045 => ['MMVL'],
+ 2046 => ['MMVLI'],
+ 2047 => ['MMVLII'],
+ 2048 => ['MMVLIII'],
+ 2049 => ['MMVLIV', 'MMIL'],
+ 2095 => ['MMVC'],
+ 2096 => ['MMVCI'],
+ 2097 => ['MMVCII'],
+ 2098 => ['MMVCIII'],
+ 2099 => ['MMVCIV', 'MMIC'],
+ 2145 => ['MMCVL'],
+ 2146 => ['MMCVLI'],
+ 2147 => ['MMCVLII'],
+ 2148 => ['MMCVLIII'],
+ 2149 => ['MMCVLIV', 'MMCIL'],
+ 2195 => ['MMCVC'],
+ 2196 => ['MMCVCI'],
+ 2197 => ['MMCVCII'],
+ 2198 => ['MMCVCIII'],
+ 2199 => ['MMCVCIV', 'MMCIC'],
+ 2245 => ['MMCCVL'],
+ 2246 => ['MMCCVLI'],
+ 2247 => ['MMCCVLII'],
+ 2248 => ['MMCCVLIII'],
+ 2249 => ['MMCCVLIV', 'MMCCIL'],
+ 2295 => ['MMCCVC'],
+ 2296 => ['MMCCVCI'],
+ 2297 => ['MMCCVCII'],
+ 2298 => ['MMCCVCIII'],
+ 2299 => ['MMCCVCIV', 'MMCCIC'],
+ 2345 => ['MMCCCVL'],
+ 2346 => ['MMCCCVLI'],
+ 2347 => ['MMCCCVLII'],
+ 2348 => ['MMCCCVLIII'],
+ 2349 => ['MMCCCVLIV', 'MMCCCIL'],
+ 2395 => ['MMCCCVC'],
+ 2396 => ['MMCCCVCI'],
+ 2397 => ['MMCCCVCII'],
+ 2398 => ['MMCCCVCIII'],
+ 2399 => ['MMCCCVCIV', 'MMCCCIC'],
+ 2445 => ['MMCDVL'],
+ 2446 => ['MMCDVLI'],
+ 2447 => ['MMCDVLII'],
+ 2448 => ['MMCDVLIII'],
+ 2449 => ['MMCDVLIV', 'MMCDIL'],
+ 2450 => ['MMLD'],
+ 2451 => ['MMLDI'],
+ 2452 => ['MMLDII'],
+ 2453 => ['MMLDIII'],
+ 2454 => ['MMLDIV'],
+ 2455 => ['MMLDV'],
+ 2456 => ['MMLDVI'],
+ 2457 => ['MMLDVII'],
+ 2458 => ['MMLDVIII'],
+ 2459 => ['MMLDIX'],
+ 2460 => ['MMLDX'],
+ 2461 => ['MMLDXI'],
+ 2462 => ['MMLDXII'],
+ 2463 => ['MMLDXIII'],
+ 2464 => ['MMLDXIV'],
+ 2465 => ['MMLDXV'],
+ 2466 => ['MMLDXVI'],
+ 2467 => ['MMLDXVII'],
+ 2468 => ['MMLDXVIII'],
+ 2469 => ['MMLDXIX'],
+ 2470 => ['MMLDXX'],
+ 2471 => ['MMLDXXI'],
+ 2472 => ['MMLDXXII'],
+ 2473 => ['MMLDXXIII'],
+ 2474 => ['MMLDXXIV'],
+ 2475 => ['MMLDXXV'],
+ 2476 => ['MMLDXXVI'],
+ 2477 => ['MMLDXXVII'],
+ 2478 => ['MMLDXXVIII'],
+ 2479 => ['MMLDXXIX'],
+ 2480 => ['MMLDXXX'],
+ 2481 => ['MMLDXXXI'],
+ 2482 => ['MMLDXXXII'],
+ 2483 => ['MMLDXXXIII'],
+ 2484 => ['MMLDXXXIV'],
+ 2485 => ['MMLDXXXV'],
+ 2486 => ['MMLDXXXVI'],
+ 2487 => ['MMLDXXXVII'],
+ 2488 => ['MMLDXXXVIII'],
+ 2489 => ['MMLDXXXIX'],
+ 2490 => ['MMLDXL', 'MMXD'],
+ 2491 => ['MMLDXLI', 'MMXDI'],
+ 2492 => ['MMLDXLII', 'MMXDII'],
+ 2493 => ['MMLDXLIII', 'MMXDIII'],
+ 2494 => ['MMLDXLIV', 'MMXDIV'],
+ 2495 => ['MMLDVL', 'MMXDV', 'MMVD'],
+ 2496 => ['MMLDVLI', 'MMXDVI', 'MMVDI'],
+ 2497 => ['MMLDVLII', 'MMXDVII', 'MMVDII'],
+ 2498 => ['MMLDVLIII', 'MMXDVIII', 'MMVDIII'],
+ 2499 => ['MMLDVLIV', 'MMXDIX', 'MMVDIV', 'MMID'],
+ 2545 => ['MMDVL'],
+ 2546 => ['MMDVLI'],
+ 2547 => ['MMDVLII'],
+ 2548 => ['MMDVLIII'],
+ 2549 => ['MMDVLIV', 'MMDIL'],
+ 2595 => ['MMDVC'],
+ 2596 => ['MMDVCI'],
+ 2597 => ['MMDVCII'],
+ 2598 => ['MMDVCIII'],
+ 2599 => ['MMDVCIV', 'MMDIC'],
+ 2645 => ['MMDCVL'],
+ 2646 => ['MMDCVLI'],
+ 2647 => ['MMDCVLII'],
+ 2648 => ['MMDCVLIII'],
+ 2649 => ['MMDCVLIV', 'MMDCIL'],
+ 2695 => ['MMDCVC'],
+ 2696 => ['MMDCVCI'],
+ 2697 => ['MMDCVCII'],
+ 2698 => ['MMDCVCIII'],
+ 2699 => ['MMDCVCIV', 'MMDCIC'],
+ 2745 => ['MMDCCVL'],
+ 2746 => ['MMDCCVLI'],
+ 2747 => ['MMDCCVLII'],
+ 2748 => ['MMDCCVLIII'],
+ 2749 => ['MMDCCVLIV', 'MMDCCIL'],
+ 2795 => ['MMDCCVC'],
+ 2796 => ['MMDCCVCI'],
+ 2797 => ['MMDCCVCII'],
+ 2798 => ['MMDCCVCIII'],
+ 2799 => ['MMDCCVCIV', 'MMDCCIC'],
+ 2845 => ['MMDCCCVL'],
+ 2846 => ['MMDCCCVLI'],
+ 2847 => ['MMDCCCVLII'],
+ 2848 => ['MMDCCCVLIII'],
+ 2849 => ['MMDCCCVLIV', 'MMDCCCIL'],
+ 2895 => ['MMDCCCVC'],
+ 2896 => ['MMDCCCVCI'],
+ 2897 => ['MMDCCCVCII'],
+ 2898 => ['MMDCCCVCIII'],
+ 2899 => ['MMDCCCVCIV', 'MMDCCCIC'],
+ 2945 => ['MMCMVL'],
+ 2946 => ['MMCMVLI'],
+ 2947 => ['MMCMVLII'],
+ 2948 => ['MMCMVLIII'],
+ 2949 => ['MMCMVLIV', 'MMCMIL'],
+ 2950 => ['MMLM'],
+ 2951 => ['MMLMI'],
+ 2952 => ['MMLMII'],
+ 2953 => ['MMLMIII'],
+ 2954 => ['MMLMIV'],
+ 2955 => ['MMLMV'],
+ 2956 => ['MMLMVI'],
+ 2957 => ['MMLMVII'],
+ 2958 => ['MMLMVIII'],
+ 2959 => ['MMLMIX'],
+ 2960 => ['MMLMX'],
+ 2961 => ['MMLMXI'],
+ 2962 => ['MMLMXII'],
+ 2963 => ['MMLMXIII'],
+ 2964 => ['MMLMXIV'],
+ 2965 => ['MMLMXV'],
+ 2966 => ['MMLMXVI'],
+ 2967 => ['MMLMXVII'],
+ 2968 => ['MMLMXVIII'],
+ 2969 => ['MMLMXIX'],
+ 2970 => ['MMLMXX'],
+ 2971 => ['MMLMXXI'],
+ 2972 => ['MMLMXXII'],
+ 2973 => ['MMLMXXIII'],
+ 2974 => ['MMLMXXIV'],
+ 2975 => ['MMLMXXV'],
+ 2976 => ['MMLMXXVI'],
+ 2977 => ['MMLMXXVII'],
+ 2978 => ['MMLMXXVIII'],
+ 2979 => ['MMLMXXIX'],
+ 2980 => ['MMLMXXX'],
+ 2981 => ['MMLMXXXI'],
+ 2982 => ['MMLMXXXII'],
+ 2983 => ['MMLMXXXIII'],
+ 2984 => ['MMLMXXXIV'],
+ 2985 => ['MMLMXXXV'],
+ 2986 => ['MMLMXXXVI'],
+ 2987 => ['MMLMXXXVII'],
+ 2988 => ['MMLMXXXVIII'],
+ 2989 => ['MMLMXXXIX'],
+ 2990 => ['MMLMXL', 'MMXM'],
+ 2991 => ['MMLMXLI', 'MMXMI'],
+ 2992 => ['MMLMXLII', 'MMXMII'],
+ 2993 => ['MMLMXLIII', 'MMXMIII'],
+ 2994 => ['MMLMXLIV', 'MMXMIV'],
+ 2995 => ['MMLMVL', 'MMXMV', 'MMVM'],
+ 2996 => ['MMLMVLI', 'MMXMVI', 'MMVMI'],
+ 2997 => ['MMLMVLII', 'MMXMVII', 'MMVMII'],
+ 2998 => ['MMLMVLIII', 'MMXMVIII', 'MMVMIII'],
+ 2999 => ['MMLMVLIV', 'MMXMIX', 'MMVMIV', 'MMIM'],
+ 3045 => ['MMMVL'],
+ 3046 => ['MMMVLI'],
+ 3047 => ['MMMVLII'],
+ 3048 => ['MMMVLIII'],
+ 3049 => ['MMMVLIV', 'MMMIL'],
+ 3095 => ['MMMVC'],
+ 3096 => ['MMMVCI'],
+ 3097 => ['MMMVCII'],
+ 3098 => ['MMMVCIII'],
+ 3099 => ['MMMVCIV', 'MMMIC'],
+ 3145 => ['MMMCVL'],
+ 3146 => ['MMMCVLI'],
+ 3147 => ['MMMCVLII'],
+ 3148 => ['MMMCVLIII'],
+ 3149 => ['MMMCVLIV', 'MMMCIL'],
+ 3195 => ['MMMCVC'],
+ 3196 => ['MMMCVCI'],
+ 3197 => ['MMMCVCII'],
+ 3198 => ['MMMCVCIII'],
+ 3199 => ['MMMCVCIV', 'MMMCIC'],
+ 3245 => ['MMMCCVL'],
+ 3246 => ['MMMCCVLI'],
+ 3247 => ['MMMCCVLII'],
+ 3248 => ['MMMCCVLIII'],
+ 3249 => ['MMMCCVLIV', 'MMMCCIL'],
+ 3295 => ['MMMCCVC'],
+ 3296 => ['MMMCCVCI'],
+ 3297 => ['MMMCCVCII'],
+ 3298 => ['MMMCCVCIII'],
+ 3299 => ['MMMCCVCIV', 'MMMCCIC'],
+ 3345 => ['MMMCCCVL'],
+ 3346 => ['MMMCCCVLI'],
+ 3347 => ['MMMCCCVLII'],
+ 3348 => ['MMMCCCVLIII'],
+ 3349 => ['MMMCCCVLIV', 'MMMCCCIL'],
+ 3395 => ['MMMCCCVC'],
+ 3396 => ['MMMCCCVCI'],
+ 3397 => ['MMMCCCVCII'],
+ 3398 => ['MMMCCCVCIII'],
+ 3399 => ['MMMCCCVCIV', 'MMMCCCIC'],
+ 3445 => ['MMMCDVL'],
+ 3446 => ['MMMCDVLI'],
+ 3447 => ['MMMCDVLII'],
+ 3448 => ['MMMCDVLIII'],
+ 3449 => ['MMMCDVLIV', 'MMMCDIL'],
+ 3450 => ['MMMLD'],
+ 3451 => ['MMMLDI'],
+ 3452 => ['MMMLDII'],
+ 3453 => ['MMMLDIII'],
+ 3454 => ['MMMLDIV'],
+ 3455 => ['MMMLDV'],
+ 3456 => ['MMMLDVI'],
+ 3457 => ['MMMLDVII'],
+ 3458 => ['MMMLDVIII'],
+ 3459 => ['MMMLDIX'],
+ 3460 => ['MMMLDX'],
+ 3461 => ['MMMLDXI'],
+ 3462 => ['MMMLDXII'],
+ 3463 => ['MMMLDXIII'],
+ 3464 => ['MMMLDXIV'],
+ 3465 => ['MMMLDXV'],
+ 3466 => ['MMMLDXVI'],
+ 3467 => ['MMMLDXVII'],
+ 3468 => ['MMMLDXVIII'],
+ 3469 => ['MMMLDXIX'],
+ 3470 => ['MMMLDXX'],
+ 3471 => ['MMMLDXXI'],
+ 3472 => ['MMMLDXXII'],
+ 3473 => ['MMMLDXXIII'],
+ 3474 => ['MMMLDXXIV'],
+ 3475 => ['MMMLDXXV'],
+ 3476 => ['MMMLDXXVI'],
+ 3477 => ['MMMLDXXVII'],
+ 3478 => ['MMMLDXXVIII'],
+ 3479 => ['MMMLDXXIX'],
+ 3480 => ['MMMLDXXX'],
+ 3481 => ['MMMLDXXXI'],
+ 3482 => ['MMMLDXXXII'],
+ 3483 => ['MMMLDXXXIII'],
+ 3484 => ['MMMLDXXXIV'],
+ 3485 => ['MMMLDXXXV'],
+ 3486 => ['MMMLDXXXVI'],
+ 3487 => ['MMMLDXXXVII'],
+ 3488 => ['MMMLDXXXVIII'],
+ 3489 => ['MMMLDXXXIX'],
+ 3490 => ['MMMLDXL', 'MMMXD'],
+ 3491 => ['MMMLDXLI', 'MMMXDI'],
+ 3492 => ['MMMLDXLII', 'MMMXDII'],
+ 3493 => ['MMMLDXLIII', 'MMMXDIII'],
+ 3494 => ['MMMLDXLIV', 'MMMXDIV'],
+ 3495 => ['MMMLDVL', 'MMMXDV', 'MMMVD'],
+ 3496 => ['MMMLDVLI', 'MMMXDVI', 'MMMVDI'],
+ 3497 => ['MMMLDVLII', 'MMMXDVII', 'MMMVDII'],
+ 3498 => ['MMMLDVLIII', 'MMMXDVIII', 'MMMVDIII'],
+ 3499 => ['MMMLDVLIV', 'MMMXDIX', 'MMMVDIV', 'MMMID'],
+ 3545 => ['MMMDVL'],
+ 3546 => ['MMMDVLI'],
+ 3547 => ['MMMDVLII'],
+ 3548 => ['MMMDVLIII'],
+ 3549 => ['MMMDVLIV', 'MMMDIL'],
+ 3595 => ['MMMDVC'],
+ 3596 => ['MMMDVCI'],
+ 3597 => ['MMMDVCII'],
+ 3598 => ['MMMDVCIII'],
+ 3599 => ['MMMDVCIV', 'MMMDIC'],
+ 3645 => ['MMMDCVL'],
+ 3646 => ['MMMDCVLI'],
+ 3647 => ['MMMDCVLII'],
+ 3648 => ['MMMDCVLIII'],
+ 3649 => ['MMMDCVLIV', 'MMMDCIL'],
+ 3695 => ['MMMDCVC'],
+ 3696 => ['MMMDCVCI'],
+ 3697 => ['MMMDCVCII'],
+ 3698 => ['MMMDCVCIII'],
+ 3699 => ['MMMDCVCIV', 'MMMDCIC'],
+ 3745 => ['MMMDCCVL'],
+ 3746 => ['MMMDCCVLI'],
+ 3747 => ['MMMDCCVLII'],
+ 3748 => ['MMMDCCVLIII'],
+ 3749 => ['MMMDCCVLIV', 'MMMDCCIL'],
+ 3795 => ['MMMDCCVC'],
+ 3796 => ['MMMDCCVCI'],
+ 3797 => ['MMMDCCVCII'],
+ 3798 => ['MMMDCCVCIII'],
+ 3799 => ['MMMDCCVCIV', 'MMMDCCIC'],
+ 3845 => ['MMMDCCCVL'],
+ 3846 => ['MMMDCCCVLI'],
+ 3847 => ['MMMDCCCVLII'],
+ 3848 => ['MMMDCCCVLIII'],
+ 3849 => ['MMMDCCCVLIV', 'MMMDCCCIL'],
+ 3895 => ['MMMDCCCVC'],
+ 3896 => ['MMMDCCCVCI'],
+ 3897 => ['MMMDCCCVCII'],
+ 3898 => ['MMMDCCCVCIII'],
+ 3899 => ['MMMDCCCVCIV', 'MMMDCCCIC'],
+ 3945 => ['MMMCMVL'],
+ 3946 => ['MMMCMVLI'],
+ 3947 => ['MMMCMVLII'],
+ 3948 => ['MMMCMVLIII'],
+ 3949 => ['MMMCMVLIV', 'MMMCMIL'],
+ 3950 => ['MMMLM'],
+ 3951 => ['MMMLMI'],
+ 3952 => ['MMMLMII'],
+ 3953 => ['MMMLMIII'],
+ 3954 => ['MMMLMIV'],
+ 3955 => ['MMMLMV'],
+ 3956 => ['MMMLMVI'],
+ 3957 => ['MMMLMVII'],
+ 3958 => ['MMMLMVIII'],
+ 3959 => ['MMMLMIX'],
+ 3960 => ['MMMLMX'],
+ 3961 => ['MMMLMXI'],
+ 3962 => ['MMMLMXII'],
+ 3963 => ['MMMLMXIII'],
+ 3964 => ['MMMLMXIV'],
+ 3965 => ['MMMLMXV'],
+ 3966 => ['MMMLMXVI'],
+ 3967 => ['MMMLMXVII'],
+ 3968 => ['MMMLMXVIII'],
+ 3969 => ['MMMLMXIX'],
+ 3970 => ['MMMLMXX'],
+ 3971 => ['MMMLMXXI'],
+ 3972 => ['MMMLMXXII'],
+ 3973 => ['MMMLMXXIII'],
+ 3974 => ['MMMLMXXIV'],
+ 3975 => ['MMMLMXXV'],
+ 3976 => ['MMMLMXXVI'],
+ 3977 => ['MMMLMXXVII'],
+ 3978 => ['MMMLMXXVIII'],
+ 3979 => ['MMMLMXXIX'],
+ 3980 => ['MMMLMXXX'],
+ 3981 => ['MMMLMXXXI'],
+ 3982 => ['MMMLMXXXII'],
+ 3983 => ['MMMLMXXXIII'],
+ 3984 => ['MMMLMXXXIV'],
+ 3985 => ['MMMLMXXXV'],
+ 3986 => ['MMMLMXXXVI'],
+ 3987 => ['MMMLMXXXVII'],
+ 3988 => ['MMMLMXXXVIII'],
+ 3989 => ['MMMLMXXXIX'],
+ 3990 => ['MMMLMXL', 'MMMXM'],
+ 3991 => ['MMMLMXLI', 'MMMXMI'],
+ 3992 => ['MMMLMXLII', 'MMMXMII'],
+ 3993 => ['MMMLMXLIII', 'MMMXMIII'],
+ 3994 => ['MMMLMXLIV', 'MMMXMIV'],
+ 3995 => ['MMMLMVL', 'MMMXMV', 'MMMVM'],
+ 3996 => ['MMMLMVLI', 'MMMXMVI', 'MMMVMI'],
+ 3997 => ['MMMLMVLII', 'MMMXMVII', 'MMMVMII'],
+ 3998 => ['MMMLMVLIII', 'MMMXMVIII', 'MMMVMIII'],
+ 3999 => ['MMMLMVLIV', 'MMMXMIX', 'MMMVMIV', 'MMMIM'],
+ ];
+
+ private const THOUSANDS = ['', 'M', 'MM', 'MMM'];
+ private const HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
+ private const TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
+ private const ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
+ const MAX_ROMAN_VALUE = 3999;
+ const MAX_ROMAN_STYLE = 4;
+
+ private static function valueOk(int $aValue, int $style): string
+ {
+ $origValue = $aValue;
+ $m = \intdiv($aValue, 1000);
+ $aValue %= 1000;
+ $c = \intdiv($aValue, 100);
+ $aValue %= 100;
+ $t = \intdiv($aValue, 10);
+ $aValue %= 10;
+ $result = self::THOUSANDS[$m] . self::HUNDREDS[$c] . self::TENS[$t] . self::ONES[$aValue];
+ if ($style > 0) {
+ if (array_key_exists($origValue, self::VALUES)) {
+ $arr = self::VALUES[$origValue];
+ $idx = min($style, count($arr)) - 1;
+ $result = $arr[$idx];
+ }
+ }
+
+ return $result;
+ }
+
+ private static function styleOk(int $aValue, int $style): string
+ {
+ return ($aValue < 0 || $aValue > self::MAX_ROMAN_VALUE) ? ExcelError::VALUE() : self::valueOk($aValue, $style);
+ }
+
+ public static function calculateRoman(int $aValue, int $style): string
+ {
+ return ($style < 0 || $style > self::MAX_ROMAN_STYLE) ? ExcelError::VALUE() : self::styleOk($aValue, $style);
+ }
+
+ /**
+ * ROMAN.
+ *
+ * Converts a number to Roman numeral
+ *
+ * @param mixed $aValue Number to convert
+ * Or can be an array of numbers
+ * @param mixed $style Number indicating one of five possible forms
+ * Or can be an array of styles
+ *
+ * @return array|string Roman numeral, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function evaluate($aValue, $style = 0)
+ {
+ if (is_array($aValue) || is_array($style)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $aValue, $style);
+ }
+
+ try {
+ $aValue = Helpers::validateNumericNullBool($aValue);
+ if (is_bool($style)) {
+ $style = $style ? 0 : 4;
+ }
+ $style = Helpers::validateNumericNullSubstitution($style, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::calculateRoman((int) $aValue, (int) $style);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Round.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Round.php
new file mode 100644
index 0000000..776f7eb
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Round.php
@@ -0,0 +1,218 @@
+getMessage();
+ }
+
+ return round($number, (int) $precision);
+ }
+
+ /**
+ * ROUNDUP.
+ *
+ * Rounds a number up to a specified number of decimal places
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ * @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function up($number, $digits)
+ {
+ if (is_array($number) || is_array($digits)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ if ($number < 0.0) {
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+ }
+
+ return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+ }
+
+ /**
+ * ROUNDDOWN.
+ *
+ * Rounds a number down to a specified number of decimal places
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ * @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function down($number, $digits)
+ {
+ if (is_array($number) || is_array($digits)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ if ($number < 0.0) {
+ return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+ }
+
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+ }
+
+ /**
+ * MROUND.
+ *
+ * Rounds a number to the nearest multiple of a specified value
+ *
+ * @param mixed $number Expect float. Number to round, or can be an array of numbers
+ * @param mixed $multiple Expect int. Multiple to which you want to round, or can be an array of numbers.
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function multiple($number, $multiple)
+ {
+ if (is_array($number) || is_array($multiple)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $multiple);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullSubstitution($number, 0);
+ $multiple = Helpers::validateNumericNullSubstitution($multiple, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0 || $multiple == 0) {
+ return 0;
+ }
+ if ((Helpers::returnSign($number)) == (Helpers::returnSign($multiple))) {
+ $multiplier = 1 / $multiple;
+
+ return round($number * $multiplier) / $multiplier;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * EVEN.
+ *
+ * Returns number rounded up to the nearest even integer.
+ * You can use this function for processing items that come in twos. For example,
+ * a packing crate accepts rows of one or two items. The crate is full when
+ * the number of items, rounded up to the nearest two, matches the crate's
+ * capacity.
+ *
+ * Excel Function:
+ * EVEN(number)
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function even($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::getEven($number);
+ }
+
+ /**
+ * ODD.
+ *
+ * Returns number rounded up to the nearest odd integer.
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function odd($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $significance = Helpers::returnSign($number);
+ if ($significance == 0) {
+ return 1;
+ }
+
+ $result = ceil($number / $significance) * $significance;
+ if ($result == Helpers::getEven($result)) {
+ $result += $significance;
+ }
+
+ return $result;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
new file mode 100644
index 0000000..ecce359
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
@@ -0,0 +1,53 @@
+getMessage();
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sign.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sign.php
new file mode 100644
index 0000000..e40e1f6
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sign.php
@@ -0,0 +1,38 @@
+getMessage();
+ }
+
+ return Helpers::returnSign($number);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
new file mode 100644
index 0000000..bb9f15f
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return Helpers::numberOrNan(sqrt($number));
+ }
+
+ /**
+ * SQRTPI.
+ *
+ * Returns the square root of (number * pi).
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string Square Root of Number * Pi, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function pi($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullSubstitution($number, 0);
+ Helpers::validateNotNegative($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return sqrt($number * M_PI);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
new file mode 100644
index 0000000..6d8f472
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
@@ -0,0 +1,135 @@
+getWorksheet()->getRowDimension($row)->getVisible();
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ /**
+ * @param mixed $cellReference
+ * @param mixed $args
+ */
+ protected static function filterFormulaArgs($cellReference, $args): array
+ {
+ return array_filter(
+ $args,
+ function ($index) use ($cellReference) {
+ $explodeArray = explode('.', $index);
+ $row = $explodeArray[1] ?? '';
+ $column = $explodeArray[2] ?? '';
+ $retVal = true;
+ if ($cellReference->getWorksheet()->cellExists($column . $row)) {
+ //take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
+ $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
+ $cellFormula = !preg_match(
+ '/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i',
+ $cellReference->getWorksheet()->getCell($column . $row)->getValue() ?? ''
+ );
+
+ $retVal = !$isFormula || $cellFormula;
+ }
+
+ return $retVal;
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ private const CALL_FUNCTIONS = [
+ 1 => [Statistical\Averages::class, 'average'], // 1 and 101
+ [Statistical\Counts::class, 'COUNT'], // 2 and 102
+ [Statistical\Counts::class, 'COUNTA'], // 3 and 103
+ [Statistical\Maximum::class, 'max'], // 4 and 104
+ [Statistical\Minimum::class, 'min'], // 5 and 105
+ [Operations::class, 'product'], // 6 and 106
+ [Statistical\StandardDeviations::class, 'STDEV'], // 7 and 107
+ [Statistical\StandardDeviations::class, 'STDEVP'], // 8 and 108
+ [Sum::class, 'sumIgnoringStrings'], // 9 and 109
+ [Statistical\Variances::class, 'VAR'], // 10 and 110
+ [Statistical\Variances::class, 'VARP'], // 111 and 111
+ ];
+
+ /**
+ * SUBTOTAL.
+ *
+ * Returns a subtotal in a list or database.
+ *
+ * @param mixed $functionType
+ * A number 1 to 11 that specifies which function to
+ * use in calculating subtotals within a range
+ * list
+ * Numbers 101 to 111 shadow the functions of 1 to 11
+ * but ignore any values in the range that are
+ * in hidden rows
+ * @param mixed[] $args A mixed data series of values
+ *
+ * @return float|string
+ */
+ public static function evaluate($functionType, ...$args)
+ {
+ $cellReference = array_pop($args);
+ $bArgs = Functions::flattenArrayIndexed($args);
+ $aArgs = [];
+ // int keys must come before string keys for PHP 8.0+
+ // Otherwise, PHP thinks positional args follow keyword
+ // in the subsequent call to call_user_func_array.
+ // Fortunately, order of args is unimportant to Subtotal.
+ foreach ($bArgs as $key => $value) {
+ if (is_int($key)) {
+ $aArgs[$key] = $value;
+ }
+ }
+ foreach ($bArgs as $key => $value) {
+ if (!is_int($key)) {
+ $aArgs[$key] = $value;
+ }
+ }
+
+ try {
+ $subtotal = (int) Helpers::validateNumericNullBool($functionType);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Calculate
+ if ($subtotal > 100) {
+ $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
+ $subtotal -= 100;
+ }
+
+ $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
+ if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) {
+ /** @var callable */
+ $call = self::CALL_FUNCTIONS[$subtotal];
+
+ return call_user_func_array($call, $aArgs);
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sum.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sum.php
new file mode 100644
index 0000000..1a797c8
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Sum.php
@@ -0,0 +1,118 @@
+ $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg) || empty($arg)) {
+ if (is_string($arg)) {
+ $arg = (int) $arg;
+ }
+ $returnValue += $arg;
+ } elseif (is_bool($arg)) {
+ $returnValue += (int) $arg;
+ } elseif (ErrorValue::isError($arg)) {
+ return $arg;
+ // ignore non-numerics from cell, but fail as literals (except null)
+ } elseif ($arg !== null && !Functions::isCellValue($k)) {
+ return ExcelError::VALUE();
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * SUMPRODUCT.
+ *
+ * Excel Function:
+ * SUMPRODUCT(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function product(...$args)
+ {
+ $arrayList = $args;
+
+ $wrkArray = Functions::flattenArray(array_shift($arrayList));
+ $wrkCellCount = count($wrkArray);
+
+ for ($i = 0; $i < $wrkCellCount; ++$i) {
+ if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
+ $wrkArray[$i] = 0;
+ }
+ }
+
+ foreach ($arrayList as $matrixData) {
+ $array2 = Functions::flattenArray($matrixData);
+ $count = count($array2);
+ if ($wrkCellCount != $count) {
+ return ExcelError::VALUE();
+ }
+
+ foreach ($array2 as $i => $val) {
+ if ((!is_numeric($val)) || (is_string($val))) {
+ $val = 0;
+ }
+ $wrkArray[$i] *= $val;
+ }
+ }
+
+ return array_sum($wrkArray);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
new file mode 100644
index 0000000..34b397c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
@@ -0,0 +1,143 @@
+getMessage();
+ }
+
+ return $returnValue;
+ }
+
+ private static function getCount(array $array1, array $array2): int
+ {
+ $count = count($array1);
+ if ($count !== count($array2)) {
+ throw new Exception(ExcelError::NA());
+ }
+
+ return $count;
+ }
+
+ /**
+ * These functions accept only numeric arguments, not even strings which are numeric.
+ *
+ * @param mixed $item
+ */
+ private static function numericNotString($item): bool
+ {
+ return is_numeric($item) && !is_string($item);
+ }
+
+ /**
+ * SUMX2MY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXSquaredMinusYSquared($matrixData1, $matrixData2)
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+
+ /**
+ * SUMX2PY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXSquaredPlusYSquared($matrixData1, $matrixData2)
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+
+ /**
+ * SUMXMY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXMinusYSquared($matrixData1, $matrixData2)
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
new file mode 100644
index 0000000..845b6c1
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, sin($angle));
+ }
+
+ /**
+ * CSCH.
+ *
+ * Returns the hyperbolic cosecant of an angle.
+ *
+ * @param array|float $angle Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic cosecant of the angle
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function csch($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, sinh($angle));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
new file mode 100644
index 0000000..c06f04d
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ return cos($number);
+ }
+
+ /**
+ * COSH.
+ *
+ * Returns the result of builtin function cosh after validating args.
+ *
+ * @param mixed $number Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string hyperbolic cosine
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function cosh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return cosh($number);
+ }
+
+ /**
+ * ACOS.
+ *
+ * Returns the arccosine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arccosine of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acos($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(acos($number));
+ }
+
+ /**
+ * ACOSH.
+ *
+ * Returns the arc inverse hyperbolic cosine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The inverse hyperbolic cosine of the number, or an error string
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acosh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(acosh($number));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
new file mode 100644
index 0000000..eeedef9
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
@@ -0,0 +1,118 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(cos($angle), sin($angle));
+ }
+
+ /**
+ * COTH.
+ *
+ * Returns the hyperbolic cotangent of an angle.
+ *
+ * @param array|float $angle Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic cotangent of the angle
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function coth($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, tanh($angle));
+ }
+
+ /**
+ * ACOT.
+ *
+ * Returns the arccotangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arccotangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acot($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (M_PI / 2) - atan($number);
+ }
+
+ /**
+ * ACOTH.
+ *
+ * Returns the hyperbolic arccotangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic arccotangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acoth($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = ($number === 1) ? NAN : (log(($number + 1) / ($number - 1)) / 2);
+
+ return Helpers::numberOrNan($result);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
new file mode 100644
index 0000000..2d26e5d
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, cos($angle));
+ }
+
+ /**
+ * SECH.
+ *
+ * Returns the hyperbolic secant of an angle.
+ *
+ * @param array|float $angle Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic secant of the angle
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function sech($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, cosh($angle));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
new file mode 100644
index 0000000..6af568c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ return sin($angle);
+ }
+
+ /**
+ * SINH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @param mixed $angle Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string hyperbolic sine
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function sinh($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return sinh($angle);
+ }
+
+ /**
+ * ASIN.
+ *
+ * Returns the arcsine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arcsine of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function asin($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(asin($number));
+ }
+
+ /**
+ * ASINH.
+ *
+ * Returns the inverse hyperbolic sine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The inverse hyperbolic sine of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function asinh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(asinh($number));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
new file mode 100644
index 0000000..9e77021
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
@@ -0,0 +1,161 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(sin($angle), cos($angle));
+ }
+
+ /**
+ * TANH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @param mixed $angle Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string hyperbolic tangent
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function tanh($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return tanh($angle);
+ }
+
+ /**
+ * ATAN.
+ *
+ * Returns the arctangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arctangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function atan($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(atan($number));
+ }
+
+ /**
+ * ATANH.
+ *
+ * Returns the inverse hyperbolic tangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The inverse hyperbolic tangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function atanh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(atanh($number));
+ }
+
+ /**
+ * 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)
+ *
+ * @param mixed $xCoordinate should be float, the x-coordinate of the point, or can be an array of numbers
+ * @param mixed $yCoordinate should be float, the y-coordinate of the point, or can be an array of numbers
+ *
+ * @return array|float|string
+ * The inverse tangent of the specified x- and y-coordinates, or a string containing an error
+ * If an array of numbers is passed as one of the arguments, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function atan2($xCoordinate, $yCoordinate)
+ {
+ if (is_array($xCoordinate) || is_array($yCoordinate)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $xCoordinate, $yCoordinate);
+ }
+
+ try {
+ $xCoordinate = Helpers::validateNumericNullBool($xCoordinate);
+ $yCoordinate = Helpers::validateNumericNullBool($yCoordinate);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($xCoordinate == 0) && ($yCoordinate == 0)) {
+ return ExcelError::DIV0();
+ }
+
+ return atan2($yCoordinate, $xCoordinate);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trunc.php b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
new file mode 100644
index 0000000..943e209
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
@@ -0,0 +1,50 @@
+getMessage();
+ }
+
+ $digits = floor($digits);
+
+ // Truncate
+ $adjust = 10 ** $digits;
+
+ if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
+ return $value;
+ }
+
+ return ((int) ($value * $adjust)) / $adjust;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical.php
old mode 100755
new mode 100644
index 1f9c766..6758f47
--- a/PhpOffice/PhpSpreadsheet/Calculation/Statistical.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical.php
@@ -2,560 +2,29 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Conditional;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Permutations;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends;
+use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
+/**
+ * @deprecated 1.18.0
+ *
+ * @codeCoverageIgnore
+ */
class Statistical
{
const LOG_GAMMA_X_MAX_VALUE = 2.55e305;
- const XMININ = 2.23e-308;
const EPS = 2.22e-16;
const MAX_VALUE = 1.2e308;
- const MAX_ITERATIONS = 256;
const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
- private static function checkTrendArrays(&$array1, &$array2)
- {
- if (!is_array($array1)) {
- $array1 = [$array1];
- }
- if (!is_array($array2)) {
- $array2 = [$array2];
- }
-
- $array1 = Functions::flattenArray($array1);
- $array2 = Functions::flattenArray($array2);
- foreach ($array1 as $key => $value) {
- if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
- unset($array1[$key], $array2[$key]);
- }
- }
- foreach ($array2 as $key => $value) {
- if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
- unset($array1[$key], $array2[$key]);
- }
- }
- $array1 = array_merge($array1);
- $array2 = array_merge($array2);
-
- return true;
- }
-
- /**
- * 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 mixed $x require 0<=x<=1
- * @param mixed $p require p>0
- * @param mixed $q require q>0
- *
- * @return float 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) > self::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;
- }
-
- return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
- }
-
- // Function cache for logBeta function
- private static $logBetaCacheP = 0.0;
-
- private static $logBetaCacheQ = 0.0;
-
- private static $logBetaCacheResult = 0.0;
-
- /**
- * The natural logarithm of the beta function.
- *
- * @param mixed $p require p>0
- * @param mixed $q require q>0
- *
- * @return float 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::$logBetaCacheP || $q != self::$logBetaCacheQ) {
- self::$logBetaCacheP = $p;
- self::$logBetaCacheQ = $q;
- if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
- self::$logBetaCacheResult = 0.0;
- } else {
- self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
- }
- }
-
- return self::$logBetaCacheResult;
- }
-
- /**
- * 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
- *
- * @param mixed $x
- * @param mixed $p
- * @param mixed $q
- *
- * @return float
- */
- 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) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $frac = $h;
- $m = 1;
- $delta = 0.0;
- while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::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) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $c = 1.0 + $d / $c;
- if (abs($c) < self::XMININ) {
- $c = self::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) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $c = 1.0 + $d / $c;
- if (abs($c) < self::XMININ) {
- $c = self::XMININ;
- }
- $delta = $h * $c;
- $frac *= $delta;
- ++$m;
- }
-
- return $frac;
- }
-
- /**
- * 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 float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
- */
-
- // Function cache for logGamma
- private static $logGammaCacheResult = 0.0;
-
- private static $logGammaCacheX = 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 = [
- 4.945235359296727046734888,
- 201.8112620856775083915565,
- 2290.838373831346393026739,
- 11319.67205903380828685045,
- 28557.24635671635335736389,
- 38484.96228443793359990269,
- 26377.48787624195437963534,
- 7225.813979700288197698961,
- ];
- static $lg_p2 = [
- 4.974607845568932035012064,
- 542.4138599891070494101986,
- 15506.93864978364947665077,
- 184793.2904445632425417223,
- 1088204.76946882876749847,
- 3338152.967987029735917223,
- 5106661.678927352456275255,
- 3074109.054850539556250927,
- ];
- static $lg_p4 = [
- 14745.02166059939948905062,
- 2426813.369486704502836312,
- 121475557.4045093227939592,
- 2663432449.630976949898078,
- 29403789566.34553899906876,
- 170266573776.5398868392998,
- 492612579337.743088758812,
- 560625185622.3951465078242,
- ];
- static $lg_q1 = [
- 67.48212550303777196073036,
- 1113.332393857199323513008,
- 7738.757056935398733233834,
- 27639.87074403340708898585,
- 54993.10206226157329794414,
- 61611.22180066002127833352,
- 36351.27591501940507276287,
- 8785.536302431013170870835,
- ];
- static $lg_q2 = [
- 183.0328399370592604055942,
- 7765.049321445005871323047,
- 133190.3827966074194402448,
- 1136705.821321969608938755,
- 5267964.117437946917577538,
- 13467014.54311101692290052,
- 17827365.30353274213975932,
- 9533095.591844353613395747,
- ];
- static $lg_q4 = [
- 2690.530175870899333379843,
- 639388.5654300092398984238,
- 41355999.30241388052042842,
- 1120872109.61614794137657,
- 14886137286.78813811542398,
- 101680358627.2438228077304,
- 341747634550.7377132798597,
- 446315818741.9713286462081,
- ];
- static $lg_c = [
- -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::$logGammaCacheX) {
- return self::$logGammaCacheResult;
- }
- $y = $x;
- if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
- if ($y <= self::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(self::SQRT2PI) - 0.5 * $corr;
- $res += $y * ($corr - 1.0);
- }
- }
- } else {
- // --------------------------
- // Return for bad arguments
- // --------------------------
- $res = self::MAX_VALUE;
- }
- // ------------------------------
- // Final adjustments and return
- // ------------------------------
- self::$logGammaCacheX = $x;
- self::$logGammaCacheResult = $res;
-
- return $res;
- }
-
- //
- // 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;
- }
-
- //
- // Private implementation of the Gamma function
- //
- private static function gamma($data)
- {
- if ($data == 0.0) {
- return 0;
- }
-
- static $p0 = 1.000000000190015;
- static $p = [
- 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(self::SQRT2PI * $summer / $x));
- }
-
- /*
- * inverse_ncdf.php
- * -------------------
- * begin : Friday, January 16, 2004
- * copyright : (C) 2004 Michael Nickerson
- * email : nickersonm@yahoo.com
- *
- */
- private static function inverseNcdf($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 = [
- 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 = [
- 1 => -5.447609879822406e+01,
- 2 => 1.615858368580409e+02,
- 3 => -1.556989798598866e+02,
- 4 => 6.680131188771972e+01,
- 5 => -1.328068155288572e+01,
- ];
-
- static $c = [
- 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 = [
- 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 Functions::NULL();
- }
-
- /**
- * MS Excel does not count Booleans if passed as cell values, but they are counted if passed as literals.
- * OpenOffice Calc always counts Booleans.
- * Gnumeric never counts Booleans.
- *
- * @param mixed $arg
- * @param mixed $k
- *
- * @return int|mixed
- */
- private static function testAcceptedBoolean($arg, $k)
- {
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k) && (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_EXCEL)) ||
- (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
-
- return $arg;
- }
-
- /**
- * @param mixed $arg
- * @param mixed $k
- *
- * @return bool
- */
- private static function isAcceptedCountable($arg, $k)
- {
- if (((is_numeric($arg)) && (!is_string($arg))) ||
- ((is_numeric($arg)) && (!Functions::isCellValue($k)) &&
- (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC))) {
- return true;
- }
-
- return false;
- }
-
/**
* AVEDEV.
*
@@ -565,7 +34,9 @@ class Statistical
* Excel Function:
* AVEDEV(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the averageDeviations() method in the Statistical\Averages class instead
+ * @see Statistical\Averages::averageDeviations()
*
* @param mixed ...$args Data values
*
@@ -573,39 +44,7 @@ class Statistical
*/
public static function AVEDEV(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- // Return value
- $returnValue = 0;
-
- $aMean = self::AVERAGE(...$args);
- if ($aMean === Functions::DIV0()) {
- return Functions::NAN();
- } elseif ($aMean === Functions::VALUE()) {
- return Functions::VALUE();
- }
-
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- $arg = self::testAcceptedBoolean($arg, $k);
- // Is it a numeric value?
- // Strings containing numeric values are only counted if they are string literals (not cell values)
- // and then only in MS Excel and in Open Office, not in Gnumeric
- if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
- return Functions::VALUE();
- }
- if (self::isAcceptedCountable($arg, $k)) {
- $returnValue += abs($arg - $aMean);
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount === 0) {
- return Functions::DIV0();
- }
-
- return $returnValue / $aCount;
+ return Averages::averageDeviations(...$args);
}
/**
@@ -616,7 +55,9 @@ class Statistical
* Excel Function:
* AVERAGE(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the average() method in the Statistical\Averages class instead
+ * @see Statistical\Averages::average()
*
* @param mixed ...$args Data values
*
@@ -624,29 +65,7 @@ class Statistical
*/
public static function AVERAGE(...$args)
{
- $returnValue = $aCount = 0;
-
- // Loop through arguments
- foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
- $arg = self::testAcceptedBoolean($arg, $k);
- // Is it a numeric value?
- // Strings containing numeric values are only counted if they are string literals (not cell values)
- // and then only in MS Excel and in Open Office, not in Gnumeric
- if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
- return Functions::VALUE();
- }
- if (self::isAcceptedCountable($arg, $k)) {
- $returnValue += $arg;
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount > 0) {
- return $returnValue / $aCount;
- }
-
- return Functions::DIV0();
+ return Averages::average(...$args);
}
/**
@@ -657,7 +76,9 @@ class Statistical
* Excel Function:
* AVERAGEA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the averageA() method in the Statistical\Averages class instead
+ * @see Statistical\Averages::averageA()
*
* @param mixed ...$args Data values
*
@@ -665,31 +86,7 @@ class Statistical
*/
public static function AVERAGEA(...$args)
{
- $returnValue = null;
-
- $aCount = 0;
- // Loop through arguments
- foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
- if ((is_bool($arg)) &&
- (!Functions::isMatrixValue($k))) {
- } else {
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- $returnValue += $arg;
- ++$aCount;
- }
- }
- }
-
- if ($aCount > 0) {
- return $returnValue / $aCount;
- }
-
- return Functions::DIV0();
+ return Averages::averageA(...$args);
}
/**
@@ -700,49 +97,19 @@ class Statistical
* Excel Function:
* AVERAGEIF(value1[,value2[, ...]],condition)
*
- * @category Mathematical and Trigonometric Functions
+ * @deprecated 1.17.0
+ * Use the AVERAGEIF() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::AVERAGEIF()
*
- * @param mixed $aArgs Data values
+ * @param mixed $range Data values
* @param string $condition the criteria that defines which cells will be checked
- * @param mixed[] $averageArgs Data values
+ * @param mixed[] $averageRange Data values
*
- * @return float|string
+ * @return null|float|string
*/
- public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
+ public static function AVERAGEIF($range, $condition, $averageRange = [])
{
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $averageArgs = Functions::flattenArray($averageArgs);
- if (empty($averageArgs)) {
- $averageArgs = $aArgs;
- }
- $condition = Functions::ifCondition($condition);
- $conditionIsNumeric = strpos($condition, '"') === false;
-
- // Loop through arguments
- $aCount = 0;
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- if ($conditionIsNumeric) {
- continue;
- }
- $arg = Calculation::wrapResult(strtoupper($arg));
- } elseif (!$conditionIsNumeric) {
- continue;
- }
- $testCondition = '=' . $arg . $condition;
- if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- $returnValue += $averageArgs[$key];
- ++$aCount;
- }
- }
-
- if ($aCount > 0) {
- return $returnValue / $aCount;
- }
-
- return Functions::DIV0();
+ return Conditional::AVERAGEIF($range, $condition, $averageRange);
}
/**
@@ -750,44 +117,31 @@ class Statistical
*
* Returns the beta distribution.
*
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Beta class instead
+ * @see Statistical\Distributions\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 mixed $rMin
* @param mixed $rMax
*
- * @return float|string
+ * @return array|float|string
*/
public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
{
- $value = Functions::flattenSingleValue($value);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
- $rMin = Functions::flattenSingleValue($rMin);
- $rMax = Functions::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 Functions::NAN();
- }
- if ($rMin > $rMax) {
- $tmp = $rMin;
- $rMin = $rMax;
- $rMax = $tmp;
- }
- $value -= $rMin;
- $value /= ($rMax - $rMin);
-
- return self::incompleteBeta($value, $alpha, $beta);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Beta::distribution($value, $alpha, $beta, $rMin, $rMax);
}
/**
* BETAINV.
*
- * Returns the inverse of the beta distribution.
+ * Returns the inverse of the Beta distribution.
+ *
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\Beta class instead
+ * @see Statistical\Distributions\Beta::inverse()
*
* @param float $probability Probability at which you want to evaluate the distribution
* @param float $alpha Parameter to the distribution
@@ -795,48 +149,11 @@ class Statistical
* @param float $rMin Minimum value
* @param float $rMax Maximum value
*
- * @return float|string
+ * @return array|float|string
*/
public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
{
- $probability = Functions::flattenSingleValue($probability);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
- $rMin = Functions::flattenSingleValue($rMin);
- $rMax = Functions::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 Functions::NAN();
- }
- if ($rMin > $rMax) {
- $tmp = $rMin;
- $rMin = $rMax;
- $rMax = $tmp;
- }
- $a = 0;
- $b = 2;
-
- $i = 0;
- while ((($b - $a) > Functions::PRECISION) && ($i++ < self::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 == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return round($rMin + $guess * ($rMax - $rMin), 12);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Beta::inverse($probability, $alpha, $beta, $rMin, $rMax);
}
/**
@@ -848,43 +165,20 @@ class Statistical
* 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 bool $cumulative
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Binomial class instead
+ * @see Statistical\Distributions\Binomial::distribution()
*
- * @return float|string
+ * @param mixed $value Number of successes in trials
+ * @param mixed $trials Number of trials
+ * @param mixed $probability Probability of success on each trial
+ * @param mixed $cumulative
+ *
+ * @return array|float|string
*/
public static function BINOMDIST($value, $trials, $probability, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $trials = Functions::flattenSingleValue($trials);
- $probability = Functions::flattenSingleValue($probability);
-
- if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
- $value = floor($value);
- $trials = floor($trials);
- if (($value < 0) || ($value > $trials)) {
- return Functions::NAN();
- }
- if (($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- $summer = 0;
- for ($i = 0; $i <= $value; ++$i) {
- $summer += MathTrig::COMBIN($trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
- }
-
- return $summer;
- }
-
- return MathTrig::COMBIN($trials, $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Binomial::distribution($value, $trials, $probability, $cumulative);
}
/**
@@ -892,33 +186,18 @@ class Statistical
*
* Returns the one-tailed probability of the chi-squared distribution.
*
+ * @deprecated 1.18.0
+ * Use the distributionRightTail() method in the Statistical\Distributions\ChiSquared class instead
+ * @see Statistical\Distributions\ChiSquared::distributionRightTail()
+ *
* @param float $value Value for the function
* @param float $degrees degrees of freedom
*
- * @return float|string
+ * @return array|float|string
*/
public static function CHIDIST($value, $degrees)
{
- $value = Functions::flattenSingleValue($value);
- $degrees = Functions::flattenSingleValue($degrees);
-
- if ((is_numeric($value)) && (is_numeric($degrees))) {
- $degrees = floor($degrees);
- if ($degrees < 1) {
- return Functions::NAN();
- }
- if ($value < 0) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- return 1;
- }
-
- return Functions::NAN();
- }
-
- return 1 - (self::incompleteGamma($degrees / 2, $value / 2) / self::gamma($degrees / 2));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\ChiSquared::distributionRightTail($value, $degrees);
}
/**
@@ -926,59 +205,18 @@ class Statistical
*
* Returns the one-tailed probability of the chi-squared distribution.
*
+ * @deprecated 1.18.0
+ * Use the inverseRightTail() method in the Statistical\Distributions\ChiSquared class instead
+ * @see Statistical\Distributions\ChiSquared::inverseRightTail()
+ *
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
*
- * @return float|string
+ * @return array|float|string
*/
public static function CHIINV($probability, $degrees)
{
- $probability = Functions::flattenSingleValue($probability);
- $degrees = Functions::flattenSingleValue($degrees);
-
- if ((is_numeric($probability)) && (is_numeric($degrees))) {
- $degrees = floor($degrees);
-
- $xLo = 100;
- $xHi = 0;
-
- $x = $xNew = 1;
- $dx = 1;
- $i = 0;
-
- while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
- // Apply Newton-Raphson step
- $result = 1 - (self::incompleteGamma($degrees / 2, $x / 2) / self::gamma($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 == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return round($x, 12);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\ChiSquared::inverseRightTail($probability, $degrees);
}
/**
@@ -986,31 +224,19 @@ class Statistical
*
* Returns the confidence interval for a population mean
*
+ * @deprecated 1.18.0
+ * Use the CONFIDENCE() method in the Statistical\Confidence class instead
+ * @see Statistical\Confidence::CONFIDENCE()
+ *
* @param float $alpha
* @param float $stdDev Standard Deviation
* @param float $size
*
- * @return float|string
+ * @return array|float|string
*/
public static function CONFIDENCE($alpha, $stdDev, $size)
{
- $alpha = Functions::flattenSingleValue($alpha);
- $stdDev = Functions::flattenSingleValue($stdDev);
- $size = Functions::flattenSingleValue($size);
-
- if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
- $size = floor($size);
- if (($alpha <= 0) || ($alpha >= 1)) {
- return Functions::NAN();
- }
- if (($stdDev <= 0) || ($size < 1)) {
- return Functions::NAN();
- }
-
- return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
- }
-
- return Functions::VALUE();
+ return Confidence::CONFIDENCE($alpha, $stdDev, $size);
}
/**
@@ -1018,6 +244,10 @@ class Statistical
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
+ * @deprecated 1.18.0
+ * Use the CORREL() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::CORREL()
+ *
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
@@ -1025,24 +255,7 @@ class Statistical
*/
public static function CORREL($yValues, $xValues = null)
{
- if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
- return Functions::VALUE();
- }
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getCorrelation();
+ return Trends::CORREL($xValues, $yValues);
}
/**
@@ -1053,7 +266,9 @@ class Statistical
* Excel Function:
* COUNT(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the COUNT() method in the Statistical\Counts class instead
+ * @see Statistical\Counts::COUNT()
*
* @param mixed ...$args Data values
*
@@ -1061,21 +276,7 @@ class Statistical
*/
public static function COUNT(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- foreach ($aArgs as $k => $arg) {
- $arg = self::testAcceptedBoolean($arg, $k);
- // Is it a numeric value?
- // Strings containing numeric values are only counted if they are string literals (not cell values)
- // and then only in MS Excel and in Open Office, not in Gnumeric
- if (self::isAcceptedCountable($arg, $k)) {
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Counts::COUNT(...$args);
}
/**
@@ -1086,7 +287,9 @@ class Statistical
* Excel Function:
* COUNTA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the COUNTA() method in the Statistical\Counts class instead
+ * @see Statistical\Counts::COUNTA()
*
* @param mixed ...$args Data values
*
@@ -1094,18 +297,7 @@ class Statistical
*/
public static function COUNTA(...$args)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- foreach ($aArgs as $k => $arg) {
- // Nulls are counted if literals, but not if cell values
- if ($arg !== null || (!Functions::isCellValue($k))) {
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Counts::COUNTA(...$args);
}
/**
@@ -1116,26 +308,17 @@ class Statistical
* Excel Function:
* COUNTBLANK(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the COUNTBLANK() method in the Statistical\Counts class instead
+ * @see Statistical\Counts::COUNTBLANK()
*
- * @param mixed ...$args Data values
+ * @param mixed $range Data values
*
* @return int
*/
- public static function COUNTBLANK(...$args)
+ public static function COUNTBLANK($range)
{
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a blank cell?
- if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Counts::COUNTBLANK($range);
}
/**
@@ -1144,40 +327,20 @@ class Statistical
* Counts the number of cells that contain numbers within the list of arguments
*
* Excel Function:
- * COUNTIF(value1[,value2[, ...]],condition)
+ * COUNTIF(range,condition)
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the COUNTIF() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::COUNTIF()
*
- * @param mixed $aArgs Data values
+ * @param mixed $range Data values
* @param string $condition the criteria that defines which cells will be counted
*
- * @return int
+ * @return int|string
*/
- public static function COUNTIF($aArgs, $condition)
+ public static function COUNTIF($range, $condition)
{
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $condition = Functions::ifCondition($condition);
- $conditionIsNumeric = strpos($condition, '"') === false;
- // Loop through arguments
- foreach ($aArgs as $arg) {
- if (!is_numeric($arg)) {
- if ($conditionIsNumeric) {
- continue;
- }
- $arg = Calculation::wrapResult(strtoupper($arg));
- } elseif (!$conditionIsNumeric) {
- continue;
- }
- $testCondition = '=' . $arg . $condition;
- if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is it a value within our criteria
- ++$returnValue;
- }
- }
-
- return $returnValue;
+ return Conditional::COUNTIF($range, $condition);
}
/**
@@ -1188,68 +351,17 @@ class Statistical
* Excel Function:
* COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the COUNTIFS() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::COUNTIFS()
*
- * @param mixed $args Criterias
+ * @param mixed $args Pairs of Ranges and Criteria
*
- * @return int
+ * @return int|string
*/
public static function COUNTIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = 0;
-
- if (empty($arrayList)) {
- return $returnValue;
- }
-
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each arg and see if arguments and conditions are true
- foreach (array_keys($aArgsArray[0]) as $index) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $conditionIsNumeric = strpos($condition, '"') === false;
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- if ($conditionIsNumeric) {
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- $arg = Calculation::wrapResult(strtoupper($arg));
- } elseif (!$conditionIsNumeric) {
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- ++$returnValue;
- }
- }
-
- // Return
- return $returnValue;
+ return Conditional::COUNTIFS(...$args);
}
/**
@@ -1257,6 +369,10 @@ class Statistical
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
+ * @deprecated 1.18.0
+ * Use the COVAR() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::COVAR()
+ *
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
@@ -1264,21 +380,7 @@ class Statistical
*/
public static function COVAR($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getCovariance();
+ return Trends::COVAR($yValues, $xValues);
}
/**
@@ -1289,123 +391,19 @@ class Statistical
*
* See https://support.microsoft.com/en-us/help/828117/ for details of the algorithm used
*
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\Binomial class instead
+ * @see Statistical\Distributions\Binomial::inverse()
+ *
* @param float $trials number of Bernoulli trials
* @param float $probability probability of a success on each trial
* @param float $alpha criterion value
*
- * @return int|string
- *
- * @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).
+ * @return array|int|string
*/
public static function CRITBINOM($trials, $probability, $alpha)
{
- $trials = floor(Functions::flattenSingleValue($trials));
- $probability = Functions::flattenSingleValue($probability);
- $alpha = Functions::flattenSingleValue($alpha);
-
- if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
- $trials = (int) $trials;
- if ($trials < 0) {
- return Functions::NAN();
- } elseif (($probability < 0.0) || ($probability > 1.0)) {
- return Functions::NAN();
- } elseif (($alpha < 0.0) || ($alpha > 1.0)) {
- return Functions::NAN();
- }
-
- 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 - 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 Functions::VALUE();
+ return Statistical\Distributions\Binomial::inverse($trials, $probability, $alpha);
}
/**
@@ -1416,7 +414,9 @@ class Statistical
* Excel Function:
* DEVSQ(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the sumSquares() method in the Statistical\Deviations class instead
+ * @see Statistical\Deviations::sumSquares()
*
* @param mixed ...$args Data values
*
@@ -1424,40 +424,7 @@ class Statistical
*/
public static function DEVSQ(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- // Return value
- $returnValue = null;
-
- $aMean = self::AVERAGE($aArgs);
- if ($aMean != Functions::DIV0()) {
- $aCount = -1;
- foreach ($aArgs as $k => $arg) {
- // Is it a numeric value?
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k)) ||
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = pow(($arg - $aMean), 2);
- } else {
- $returnValue += pow(($arg - $aMean), 2);
- }
- ++$aCount;
- }
- }
-
- // Return
- if ($returnValue === null) {
- return Functions::NAN();
- }
-
- return $returnValue;
- }
-
- return self::NA();
+ return Statistical\Deviations::sumSquares(...$args);
}
/**
@@ -1467,32 +434,44 @@ class Statistical
* 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.
*
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Exponential class instead
+ * @see Statistical\Distributions\Exponential::distribution()
+ *
* @param float $value Value of the function
* @param float $lambda The parameter value
* @param bool $cumulative
*
- * @return float|string
+ * @return array|float|string
*/
public static function EXPONDIST($value, $lambda, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $lambda = Functions::flattenSingleValue($lambda);
- $cumulative = Functions::flattenSingleValue($cumulative);
+ return Statistical\Distributions\Exponential::distribution($value, $lambda, $cumulative);
+ }
- if ((is_numeric($value)) && (is_numeric($lambda))) {
- if (($value < 0) || ($lambda < 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return 1 - exp(0 - $value * $lambda);
- }
-
- return $lambda * exp(0 - $value * $lambda);
- }
- }
-
- return Functions::VALUE();
+ /**
+ * F.DIST.
+ *
+ * Returns the F probability distribution.
+ * You can use this function to determine whether two data sets have different degrees of diversity.
+ * For example, you can examine the test scores of men and women entering high school, and determine
+ * if the variability in the females is different from that found in the males.
+ *
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\F class instead
+ * @see Statistical\Distributions\F::distribution()
+ *
+ * @param float $value Value of the function
+ * @param int $u The numerator degrees of freedom
+ * @param int $v The denominator degrees of freedom
+ * @param bool $cumulative If cumulative is TRUE, F.DIST returns the cumulative distribution function;
+ * if FALSE, it returns the probability density function.
+ *
+ * @return array|float|string
+ */
+ public static function FDIST2($value, $u, $v, $cumulative)
+ {
+ return Statistical\Distributions\F::distribution($value, $u, $v, $cumulative);
}
/**
@@ -1502,23 +481,17 @@ class Statistical
* is normally distributed rather than skewed. Use this function to perform hypothesis
* testing on the correlation coefficient.
*
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Fisher class instead
+ * @see Statistical\Distributions\Fisher::distribution()
+ *
* @param float $value
*
- * @return float|string
+ * @return array|float|string
*/
public static function FISHER($value)
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_numeric($value)) {
- if (($value <= -1) || ($value >= 1)) {
- return Functions::NAN();
- }
-
- return 0.5 * log((1 + $value) / (1 - $value));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Fisher::distribution($value);
}
/**
@@ -1528,19 +501,17 @@ class Statistical
* analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
* FISHERINV(y) = x.
*
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\Fisher class instead
+ * @see Statistical\Distributions\Fisher::inverse()
+ *
* @param float $value
*
- * @return float|string
+ * @return array|float|string
*/
public static function FISHERINV($value)
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_numeric($value)) {
- return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Fisher::inverse($value);
}
/**
@@ -1548,32 +519,37 @@ class Statistical
*
* Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
*
+ * @deprecated 1.18.0
+ * Use the FORECAST() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::FORECAST()
+ *
* @param float $xValue Value of X for which we want to find Y
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
*
- * @return bool|float|string
+ * @return array|bool|float|string
*/
public static function FORECAST($xValue, $yValues, $xValues)
{
- $xValue = Functions::flattenSingleValue($xValue);
- if (!is_numeric($xValue)) {
- return Functions::VALUE();
- } elseif (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
+ return Trends::FORECAST($xValue, $yValues, $xValues);
+ }
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getValueOfYForX($xValue);
+ /**
+ * GAMMA.
+ *
+ * Returns the gamma function value.
+ *
+ * @deprecated 1.18.0
+ * Use the gamma() method in the Statistical\Distributions\Gamma class instead
+ * @see Statistical\Distributions\Gamma::gamma()
+ *
+ * @param float $value
+ *
+ * @return array|float|string The result, or a string containing an error
+ */
+ public static function GAMMAFunction($value)
+ {
+ return Statistical\Distributions\Gamma::gamma($value);
}
/**
@@ -1581,96 +557,40 @@ class Statistical
*
* Returns the gamma distribution.
*
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Gamma class instead
+ * @see Statistical\Distributions\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 bool $cumulative
*
- * @return float|string
+ * @return array|float|string
*/
public static function GAMMADIST($value, $a, $b, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $a = Functions::flattenSingleValue($a);
- $b = Functions::flattenSingleValue($b);
-
- if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
- if (($value < 0) || ($a <= 0) || ($b <= 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return self::incompleteGamma($a, $value / $b) / self::gamma($a);
- }
-
- return (1 / (pow($b, $a) * self::gamma($a))) * pow($value, $a - 1) * exp(0 - ($value / $b));
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Gamma::distribution($value, $a, $b, $cumulative);
}
/**
* GAMMAINV.
*
- * Returns the inverse of the beta distribution.
+ * Returns the inverse of the Gamma distribution.
+ *
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\Gamma class instead
+ * @see Statistical\Distributions\Gamma::inverse()
*
* @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|string
+ * @return array|float|string
*/
public static function GAMMAINV($probability, $alpha, $beta)
{
- $probability = Functions::flattenSingleValue($probability);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
-
- if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
- if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
-
- $xLo = 0;
- $xHi = $alpha * $beta * 5;
-
- $x = $xNew = 1;
- $error = $pdf = 0;
- $dx = 1024;
- $i = 0;
-
- while ((abs($dx) > Functions::PRECISION) && ($i++ < self::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 == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return $x;
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Gamma::inverse($probability, $alpha, $beta);
}
/**
@@ -1678,23 +598,36 @@ class Statistical
*
* Returns the natural logarithm of the gamma function.
*
+ * @deprecated 1.18.0
+ * Use the ln() method in the Statistical\Distributions\Gamma class instead
+ * @see Statistical\Distributions\Gamma::ln()
+ *
* @param float $value
*
- * @return float|string
+ * @return array|float|string
*/
public static function GAMMALN($value)
{
- $value = Functions::flattenSingleValue($value);
+ return Statistical\Distributions\Gamma::ln($value);
+ }
- if (is_numeric($value)) {
- if ($value <= 0) {
- return Functions::NAN();
- }
-
- return log(self::gamma($value));
- }
-
- return Functions::VALUE();
+ /**
+ * GAUSS.
+ *
+ * Calculates the probability that a member of a standard normal population will fall between
+ * the mean and z standard deviations from the mean.
+ *
+ * @deprecated 1.18.0
+ * Use the gauss() method in the Statistical\Distributions\StandardNormal class instead
+ * @see Statistical\Distributions\StandardNormal::gauss()
+ *
+ * @param float $value
+ *
+ * @return array|float|string The result, or a string containing an error
+ */
+ public static function GAUSS($value)
+ {
+ return Statistical\Distributions\StandardNormal::gauss($value);
}
/**
@@ -1707,7 +640,9 @@ class Statistical
* Excel Function:
* GEOMEAN(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the geometric() method in the Statistical\Averages\Mean class instead
+ * @see Statistical\Averages\Mean::geometric()
*
* @param mixed ...$args Data values
*
@@ -1715,17 +650,7 @@ class Statistical
*/
public static function GEOMEAN(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- $aMean = MathTrig::PRODUCT($aArgs);
- if (is_numeric($aMean) && ($aMean > 0)) {
- $aCount = self::COUNT($aArgs);
- if (self::MIN($aArgs) > 0) {
- return pow($aMean, (1 / $aCount));
- }
- }
-
- return Functions::NAN();
+ return Statistical\Averages\Mean::geometric(...$args);
}
/**
@@ -1733,31 +658,20 @@ class Statistical
*
* Returns values along a predicted exponential Trend
*
+ * @deprecated 1.18.0
+ * Use the GROWTH() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::GROWTH()
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param bool $const a logical value specifying whether to force the intersect to equal 0
*
- * @return array of float
+ * @return float[]
*/
public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
{
- $yValues = Functions::flattenArray($yValues);
- $xValues = Functions::flattenArray($xValues);
- $newValues = Functions::flattenArray($newValues);
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
-
- $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
- if (empty($newValues)) {
- $newValues = $bestFitExponential->getXValues();
- }
-
- $returnArray = [];
- foreach ($newValues as $xValue) {
- $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
- }
-
- return $returnArray;
+ return Trends::GROWTH($yValues, $xValues, $newValues, $const);
}
/**
@@ -1769,7 +683,9 @@ class Statistical
* Excel Function:
* HARMEAN(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the harmonic() method in the Statistical\Averages\Mean class instead
+ * @see Statistical\Averages\Mean::harmonic()
*
* @param mixed ...$args Data values
*
@@ -1777,32 +693,7 @@ class Statistical
*/
public static function HARMEAN(...$args)
{
- // Return value
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- if (self::MIN($aArgs) < 0) {
- return Functions::NAN();
- }
- $aCount = 0;
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($arg <= 0) {
- return Functions::NAN();
- }
- $returnValue += (1 / $arg);
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount > 0) {
- return 1 / ($returnValue / $aCount);
- }
-
- return Functions::NA();
+ return Statistical\Averages\Mean::harmonic(...$args);
}
/**
@@ -1811,37 +702,25 @@ class Statistical
* 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
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\HyperGeometric class instead
+ * @see Statistical\Distributions\HyperGeometric::distribution()
*
- * @return float|string
+ * @param mixed $sampleSuccesses Number of successes in the sample
+ * @param mixed $sampleNumber Size of the sample
+ * @param mixed $populationSuccesses Number of successes in the population
+ * @param mixed $populationNumber Population size
+ *
+ * @return array|float|string
*/
public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
{
- $sampleSuccesses = floor(Functions::flattenSingleValue($sampleSuccesses));
- $sampleNumber = floor(Functions::flattenSingleValue($sampleNumber));
- $populationSuccesses = floor(Functions::flattenSingleValue($populationSuccesses));
- $populationNumber = floor(Functions::flattenSingleValue($populationNumber));
-
- if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
- if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
- return Functions::NAN();
- }
- if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
- return Functions::NAN();
- }
- if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
- return Functions::NAN();
- }
-
- return MathTrig::COMBIN($populationSuccesses, $sampleSuccesses) *
- MathTrig::COMBIN($populationNumber - $populationSuccesses, $sampleNumber - $sampleSuccesses) /
- MathTrig::COMBIN($populationNumber, $sampleNumber);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\HyperGeometric::distribution(
+ $sampleSuccesses,
+ $sampleNumber,
+ $populationSuccesses,
+ $populationNumber
+ );
}
/**
@@ -1849,6 +728,10 @@ class Statistical
*
* Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
*
+ * @deprecated 1.18.0
+ * Use the INTERCEPT() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::INTERCEPT()
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@@ -1856,21 +739,7 @@ class Statistical
*/
public static function INTERCEPT($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getIntersect();
+ return Trends::INTERCEPT($yValues, $xValues);
}
/**
@@ -1881,38 +750,17 @@ class Statistical
* kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
* relatively flat distribution.
*
+ * @deprecated 1.18.0
+ * Use the kurtosis() method in the Statistical\Deviations class instead
+ * @see Statistical\Deviations::kurtosis()
+ *
* @param array ...$args Data Series
*
* @return float|string
*/
public static function KURT(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($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)) &&
- (!Functions::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 Functions::DIV0();
+ return Statistical\Deviations::kurtosis(...$args);
}
/**
@@ -1924,39 +772,17 @@ class Statistical
* Excel Function:
* LARGE(value1[,value2[, ...]],entry)
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the large() method in the Statistical\Size class instead
+ * @see Statistical\Size::large()
*
* @param mixed $args Data values
- * @param int $entry Position (ordered from the largest) in the array or range of data to return
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function LARGE(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = floor(array_pop($aArgs));
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- $mArgs = [];
- 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 Functions::NAN();
- }
- rsort($mArgs);
-
- return $mArgs[$entry];
- }
-
- return Functions::VALUE();
+ return Statistical\Size::large(...$args);
}
/**
@@ -1965,57 +791,20 @@ class Statistical
* 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.
*
+ * @deprecated 1.18.0
+ * Use the LINEST() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::LINEST()
+ *
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
* @param bool $stats a logical value specifying whether to return additional regression statistics
*
- * @return array
+ * @return array|int|string The result, or a string containing an error
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
- $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
- if ($xValues === null) {
- $xValues = range(1, count(Functions::flattenArray($yValues)));
- }
-
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return 0;
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
- if ($stats) {
- return [
- [
- $bestFitLinear->getSlope(),
- $bestFitLinear->getSlopeSE(),
- $bestFitLinear->getGoodnessOfFit(),
- $bestFitLinear->getF(),
- $bestFitLinear->getSSRegression(),
- ],
- [
- $bestFitLinear->getIntersect(),
- $bestFitLinear->getIntersectSE(),
- $bestFitLinear->getStdevOfResiduals(),
- $bestFitLinear->getDFResiduals(),
- $bestFitLinear->getSSResiduals(),
- ],
- ];
- }
-
- return [
- $bestFitLinear->getSlope(),
- $bestFitLinear->getIntersect(),
- ];
+ return Trends::LINEST($yValues, $xValues, $const, $stats);
}
/**
@@ -2024,63 +813,20 @@ class Statistical
* Calculates an exponential curve that best fits the X and Y data series,
* and then returns an array that describes the line.
*
+ * @deprecated 1.18.0
+ * Use the LOGEST() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::LOGEST()
+ *
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
* @param bool $stats a logical value specifying whether to return additional regression statistics
*
- * @return array
+ * @return array|int|string The result, or a string containing an error
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
- $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
- if ($xValues === null) {
- $xValues = range(1, count(Functions::flattenArray($yValues)));
- }
-
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- foreach ($yValues as $value) {
- if ($value <= 0.0) {
- return Functions::NAN();
- }
- }
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return 1;
- }
-
- $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
- if ($stats) {
- return [
- [
- $bestFitExponential->getSlope(),
- $bestFitExponential->getSlopeSE(),
- $bestFitExponential->getGoodnessOfFit(),
- $bestFitExponential->getF(),
- $bestFitExponential->getSSRegression(),
- ],
- [
- $bestFitExponential->getIntersect(),
- $bestFitExponential->getIntersectSE(),
- $bestFitExponential->getStdevOfResiduals(),
- $bestFitExponential->getDFResiduals(),
- $bestFitExponential->getSSResiduals(),
- ],
- ];
- }
-
- return [
- $bestFitExponential->getSlope(),
- $bestFitExponential->getIntersect(),
- ];
+ return Trends::LOGEST($yValues, $xValues, $const, $stats);
}
/**
@@ -2088,31 +834,23 @@ class Statistical
*
* Returns the inverse of the normal cumulative distribution
*
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\LogNormal class instead
+ * @see Statistical\Distributions\LogNormal::inverse()
+ *
* @param float $probability
* @param float $mean
* @param float $stdDev
*
- * @return float
+ * @return array|float|string The result, or a string containing an error
*
- * @todo Try implementing P J Acklam's refinement algorithm for greater
+ * @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 = Functions::flattenSingleValue($probability);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
- return Functions::NAN();
- }
-
- return exp($mean + $stdDev * self::NORMSINV($probability));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\LogNormal::inverse($probability, $mean, $stdDev);
}
/**
@@ -2121,27 +859,41 @@ class Statistical
* Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
* with parameters mean and standard_dev.
*
+ * @deprecated 1.18.0
+ * Use the cumulative() method in the Statistical\Distributions\LogNormal class instead
+ * @see Statistical\Distributions\LogNormal::cumulative()
+ *
* @param float $value
* @param float $mean
* @param float $stdDev
*
- * @return float
+ * @return array|float|string The result, or a string containing an error
*/
public static function LOGNORMDIST($value, $mean, $stdDev)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
+ return Statistical\Distributions\LogNormal::cumulative($value, $mean, $stdDev);
+ }
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($value <= 0) || ($stdDev <= 0)) {
- return Functions::NAN();
- }
-
- return self::NORMSDIST((log($value) - $mean) / $stdDev);
- }
-
- return Functions::VALUE();
+ /**
+ * LOGNORM.DIST.
+ *
+ * Returns the lognormal distribution of x, where ln(x) is normally distributed
+ * with parameters mean and standard_dev.
+ *
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\LogNormal class instead
+ * @see Statistical\Distributions\LogNormal::distribution()
+ *
+ * @param float $value
+ * @param float $mean
+ * @param float $stdDev
+ * @param bool $cumulative
+ *
+ * @return array|float|string The result, or a string containing an error
+ */
+ public static function LOGNORMDIST2($value, $mean, $stdDev, $cumulative = false)
+ {
+ return Statistical\Distributions\LogNormal::distribution($value, $mean, $stdDev, $cumulative);
}
/**
@@ -2151,9 +903,11 @@ class Statistical
* with negative numbers considered smaller than positive numbers.
*
* Excel Function:
- * MAX(value1[,value2[, ...]])
+ * max(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the MAX() method in the Statistical\Maximum class instead
+ * @see Statistical\Maximum::max()
*
* @param mixed ...$args Data values
*
@@ -2161,24 +915,7 @@ class Statistical
*/
public static function MAX(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if (($returnValue === null) || ($arg > $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Maximum::max(...$args);
}
/**
@@ -2187,9 +924,11 @@ class Statistical
* Returns the greatest value in a list of arguments, including numbers, text, and logical values
*
* Excel Function:
- * MAXA(value1[,value2[, ...]])
+ * maxA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the MAXA() method in the Statistical\Maximum class instead
+ * @see Statistical\Maximum::maxA()
*
* @param mixed ...$args Data values
*
@@ -2197,29 +936,7 @@ class Statistical
*/
public static function MAXA(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($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 = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if (($returnValue === null) || ($arg > $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Maximum::maxA(...$args);
}
/**
@@ -2230,7 +947,9 @@ class Statistical
* Excel Function:
* MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the MAXIFS() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::MAXIFS()
*
* @param mixed $args Data range and criterias
*
@@ -2238,47 +957,7 @@ class Statistical
*/
public static function MAXIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = null;
-
- $maxArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each arg and see if arguments and conditions are true
- foreach ($maxArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue = $returnValue === null ? $value : max($value, $returnValue);
- }
- }
-
- // Return
- return $returnValue;
+ return Conditional::MAXIFS(...$args);
}
/**
@@ -2289,39 +968,17 @@ class Statistical
* Excel Function:
* MEDIAN(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the median() method in the Statistical\Averages class instead
+ * @see Statistical\Averages::median()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MEDIAN(...$args)
{
- $returnValue = Functions::NAN();
-
- $mArgs = [];
- // Loop through arguments
- $aArgs = Functions::flattenArray($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 $returnValue;
+ return Statistical\Averages::median(...$args);
}
/**
@@ -2333,7 +990,9 @@ class Statistical
* Excel Function:
* MIN(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the min() method in the Statistical\Minimum class instead
+ * @see Statistical\Minimum::min()
*
* @param mixed ...$args Data values
*
@@ -2341,24 +1000,7 @@ class Statistical
*/
public static function MIN(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if (($returnValue === null) || ($arg < $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Minimum::min(...$args);
}
/**
@@ -2369,7 +1011,9 @@ class Statistical
* Excel Function:
* MINA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the minA() method in the Statistical\Minimum class instead
+ * @see Statistical\Minimum::minA()
*
* @param mixed ...$args Data values
*
@@ -2377,29 +1021,7 @@ class Statistical
*/
public static function MINA(...$args)
{
- $returnValue = null;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($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 = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if (($returnValue === null) || ($arg < $returnValue)) {
- $returnValue = $arg;
- }
- }
- }
-
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
+ return Minimum::minA(...$args);
}
/**
@@ -2410,7 +1032,9 @@ class Statistical
* Excel Function:
* MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the MINIFS() method in the Statistical\Conditional class instead
+ * @see Statistical\Conditional::MINIFS()
*
* @param mixed $args Data range and criterias
*
@@ -2418,85 +1042,7 @@ class Statistical
*/
public static function MINIFS(...$args)
{
- $arrayList = $args;
-
- // Return value
- $returnValue = null;
-
- $minArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each arg and see if arguments and conditions are true
- foreach ($minArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue = $returnValue === null ? $value : min($value, $returnValue);
- }
- }
-
- // Return
- return $returnValue;
- }
-
- //
- // 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 = [];
- 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[] = [
- '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 Functions::NA();
- }
-
- return $frequencyArray[0]['value'];
+ return Conditional::MINIFS(...$args);
}
/**
@@ -2507,32 +1053,17 @@ class Statistical
* Excel Function:
* MODE(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the mode() method in the Statistical\Averages class instead
+ * @see Statistical\Averages::mode()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MODE(...$args)
{
- $returnValue = Functions::NA();
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- $mArgs = [];
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $mArgs[] = $arg;
- }
- }
-
- if (!empty($mArgs)) {
- return self::modeCalc($mArgs);
- }
-
- return $returnValue;
+ return Statistical\Averages::mode(...$args);
}
/**
@@ -2544,34 +1075,19 @@ class Statistical
* 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
+ * @deprecated 1.18.0
+ * Use the negative() method in the Statistical\Distributions\Binomial class instead
+ * @see Statistical\Distributions\Binomial::negative()
*
- * @return float
+ * @param mixed $failures Number of Failures
+ * @param mixed $successes Threshold number of Successes
+ * @param mixed $probability Probability of success on each trial
+ *
+ * @return array|float|string The result, or a string containing an error
*/
public static function NEGBINOMDIST($failures, $successes, $probability)
{
- $failures = floor(Functions::flattenSingleValue($failures));
- $successes = floor(Functions::flattenSingleValue($successes));
- $probability = Functions::flattenSingleValue($probability);
-
- if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
- if (($failures < 0) || ($successes < 1)) {
- return Functions::NAN();
- } elseif (($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- if (($failures + $successes - 1) <= 0) {
- return Functions::NAN();
- }
- }
-
- return (MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures));
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Binomial::negative($failures, $successes, $probability);
}
/**
@@ -2581,33 +1097,20 @@ class Statistical
* 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 bool $cumulative
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Normal class instead
+ * @see Statistical\Distributions\Normal::distribution()
*
- * @return float
+ * @param mixed $value
+ * @param mixed $mean Mean Value
+ * @param mixed $stdDev Standard Deviation
+ * @param mixed $cumulative
+ *
+ * @return array|float|string The result, or a string containing an error
*/
public static function NORMDIST($value, $mean, $stdDev, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if ($stdDev < 0) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return 0.5 * (1 + Engineering::erfVal(($value - $mean) / ($stdDev * sqrt(2))));
- }
-
- return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean, 2) / (2 * ($stdDev * $stdDev))));
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Normal::distribution($value, $mean, $stdDev, $cumulative);
}
/**
@@ -2615,30 +1118,19 @@ class Statistical
*
* Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
*
- * @param float $probability
- * @param float $mean Mean Value
- * @param float $stdDev Standard Deviation
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\Normal class instead
+ * @see Statistical\Distributions\Normal::inverse()
*
- * @return float
+ * @param mixed $probability
+ * @param mixed $mean Mean Value
+ * @param mixed $stdDev Standard Deviation
+ *
+ * @return array|float|string The result, or a string containing an error
*/
public static function NORMINV($probability, $mean, $stdDev)
{
- $probability = Functions::flattenSingleValue($probability);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if (($probability < 0) || ($probability > 1)) {
- return Functions::NAN();
- }
- if ($stdDev < 0) {
- return Functions::NAN();
- }
-
- return (self::inverseNcdf($probability) * $stdDev) + $mean;
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Normal::inverse($probability, $mean, $stdDev);
}
/**
@@ -2648,15 +1140,38 @@ class Statistical
* 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
+ * @deprecated 1.18.0
+ * Use the cumulative() method in the Statistical\Distributions\StandardNormal class instead
+ * @see Statistical\Distributions\StandardNormal::cumulative()
*
- * @return float
+ * @param mixed $value
+ *
+ * @return array|float|string The result, or a string containing an error
*/
public static function NORMSDIST($value)
{
- $value = Functions::flattenSingleValue($value);
+ return Statistical\Distributions\StandardNormal::cumulative($value);
+ }
- return self::NORMDIST($value, 0, 1, true);
+ /**
+ * NORM.S.DIST.
+ *
+ * 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.
+ *
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\StandardNormal class instead
+ * @see Statistical\Distributions\StandardNormal::distribution()
+ *
+ * @param mixed $value
+ * @param mixed $cumulative
+ *
+ * @return array|float|string The result, or a string containing an error
+ */
+ public static function NORMSDIST2($value, $cumulative)
+ {
+ return Statistical\Distributions\StandardNormal::distribution($value, $cumulative);
}
/**
@@ -2664,13 +1179,17 @@ class Statistical
*
* Returns the inverse of the standard normal cumulative distribution
*
- * @param float $value
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\StandardNormal class instead
+ * @see Statistical\Distributions\StandardNormal::inverse()
*
- * @return float
+ * @param mixed $value
+ *
+ * @return array|float|string The result, or a string containing an error
*/
public static function NORMSINV($value)
{
- return self::NORMINV($value, 0, 1);
+ return Statistical\Distributions\StandardNormal::inverse($value);
}
/**
@@ -2681,95 +1200,40 @@ class Statistical
* Excel Function:
* PERCENTILE(value1[,value2[, ...]],entry)
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the PERCENTILE() method in the Statistical\Percentiles class instead
+ * @see Statistical\Percentiles::PERCENTILE()
*
* @param mixed $args Data values
- * @param float $entry Percentile value in the range 0..1, inclusive.
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function PERCENTILE(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = array_pop($aArgs);
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- if (($entry < 0) || ($entry > 1)) {
- return Functions::NAN();
- }
- $mArgs = [];
- 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];
- }
- $iNext = $iBase + 1;
- $iProportion = $index - $iBase;
-
- return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Percentiles::PERCENTILE(...$args);
}
/**
* PERCENTRANK.
*
* Returns the rank of a value in a data set as a percentage of the data set.
+ * Note that the returned rank is simply rounded to the appropriate significant digits,
+ * rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return
+ * 0.667 rather than 0.666
*
- * @param float[] $valueSet An array of, or a reference to, a list of numbers
- * @param int $value the number whose rank you want to find
- * @param int $significance the number of significant digits for the returned percentage value
+ * @deprecated 1.18.0
+ * Use the PERCENTRANK() method in the Statistical\Percentiles class instead
+ * @see Statistical\Percentiles::PERCENTRANK()
*
- * @return float
+ * @param mixed $valueSet An array of, or a reference to, a list of numbers
+ * @param mixed $value the number whose rank you want to find
+ * @param mixed $significance the number of significant digits for the returned percentage value
+ *
+ * @return float|string (string if result is an error)
*/
public static function PERCENTRANK($valueSet, $value, $significance = 3)
{
- $valueSet = Functions::flattenArray($valueSet);
- $value = Functions::flattenSingleValue($value);
- $significance = ($significance === null) ? 3 : (int) Functions::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 Functions::NAN();
- }
-
- $valueAdjustor = $valueCount - 1;
- if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
- return Functions::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);
+ return Statistical\Percentiles::PERCENTRANK($valueSet, $value, $significance);
}
/**
@@ -2781,26 +1245,18 @@ class Statistical
* combinations, for which the internal order is not significant. Use this function
* for lottery-style probability calculations.
*
+ * @deprecated 1.17.0
+ * Use the PERMUT() method in the Statistical\Permutations class instead
+ * @see Statistical\Permutations::PERMUT()
+ *
* @param int $numObjs Number of different objects
* @param int $numInSet Number of objects in each permutation
*
- * @return int|string Number of permutations
+ * @return array|float|int|string Number of permutations, or a string containing an error
*/
public static function PERMUT($numObjs, $numInSet)
{
- $numObjs = Functions::flattenSingleValue($numObjs);
- $numInSet = Functions::flattenSingleValue($numInSet);
-
- if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
- $numInSet = floor($numInSet);
- if ($numObjs < $numInSet) {
- return Functions::NAN();
- }
-
- return round(MathTrig::FACT($numObjs) / MathTrig::FACT($numObjs - $numInSet));
- }
-
- return Functions::VALUE();
+ return Permutations::PERMUT($numObjs, $numInSet);
}
/**
@@ -2810,37 +1266,19 @@ class Statistical
* 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 bool $cumulative
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Poisson class instead
+ * @see Statistical\Distributions\Poisson::distribution()
*
- * @return float
+ * @param mixed $value
+ * @param mixed $mean Mean Value
+ * @param mixed $cumulative
+ *
+ * @return array|float|string The result, or a string containing an error
*/
public static function POISSON($value, $mean, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
-
- if ((is_numeric($value)) && (is_numeric($mean))) {
- if (($value < 0) || ($mean <= 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- $summer = 0;
- $floor = floor($value);
- for ($i = 0; $i <= $floor; ++$i) {
- $summer += pow($mean, $i) / MathTrig::FACT($i);
- }
-
- return exp(0 - $mean) * $summer;
- }
-
- return (exp(0 - $mean) * pow($mean, $value)) / MathTrig::FACT($value);
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Poisson::distribution($value, $mean, $cumulative);
}
/**
@@ -2851,30 +1289,17 @@ class Statistical
* Excel Function:
* QUARTILE(value1[,value2[, ...]],entry)
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the QUARTILE() method in the Statistical\Percentiles class instead
+ * @see Statistical\Percentiles::QUARTILE()
*
* @param mixed $args Data values
- * @param int $entry Quartile value in the range 1..3, inclusive.
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function QUARTILE(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = floor(array_pop($aArgs));
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- $entry /= 4;
- if (($entry < 0) || ($entry > 1)) {
- return Functions::NAN();
- }
-
- return self::PERCENTILE($aArgs, $entry);
- }
-
- return Functions::VALUE();
+ return Statistical\Percentiles::QUARTILE(...$args);
}
/**
@@ -2882,35 +1307,19 @@ class Statistical
*
* Returns the rank of a number in a list of numbers.
*
- * @param int $value the number whose rank you want to find
- * @param float[] $valueSet An array of, or a reference to, a list of numbers
- * @param int $order Order to sort the values in the value set
+ * @deprecated 1.18.0
+ * Use the RANK() method in the Statistical\Percentiles class instead
+ * @see Statistical\Percentiles::RANK()
*
- * @return float
+ * @param mixed $value the number whose rank you want to find
+ * @param mixed $valueSet An array of, or a reference to, a list of numbers
+ * @param mixed $order Order to sort the values in the value set
+ *
+ * @return float|string The result, or a string containing an error
*/
public static function RANK($value, $valueSet, $order = 0)
{
- $value = Functions::flattenSingleValue($value);
- $valueSet = Functions::flattenArray($valueSet);
- $order = ($order === null) ? 0 : (int) Functions::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 Functions::NA();
- }
-
- return ++$pos;
+ return Statistical\Percentiles::RANK($value, $valueSet, $order);
}
/**
@@ -2918,28 +1327,18 @@ class Statistical
*
* Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
*
+ * @deprecated 1.18.0
+ * Use the RSQ() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::RSQ()
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
- * @return float|string
+ * @return float|string The result, or a string containing an error
*/
public static function RSQ($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getGoodnessOfFit();
+ return Trends::RSQ($yValues, $xValues);
}
/**
@@ -2950,35 +1349,17 @@ class Statistical
* asymmetric tail extending toward more positive values. Negative skewness indicates a
* distribution with an asymmetric tail extending toward more negative values.
*
+ * @deprecated 1.18.0
+ * Use the skew() method in the Statistical\Deviations class instead
+ * @see Statistical\Deviations::skew()
+ *
* @param array ...$args Data Series
*
- * @return float|string
+ * @return float|string The result, or a string containing an error
*/
public static function SKEW(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
- $mean = self::AVERAGE($aArgs);
- $stdDev = self::STDEV($aArgs);
-
- $count = $summer = 0;
- // Loop through arguments
- foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- (!Functions::isMatrixValue($k))) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summer += pow((($arg - $mean) / $stdDev), 3);
- ++$count;
- }
- }
- }
-
- if ($count > 2) {
- return $summer * ($count / (($count - 1) * ($count - 2)));
- }
-
- return Functions::DIV0();
+ return Statistical\Deviations::skew(...$args);
}
/**
@@ -2986,28 +1367,18 @@ class Statistical
*
* Returns the slope of the linear regression line through data points in known_y's and known_x's.
*
+ * @deprecated 1.18.0
+ * Use the SLOPE() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::SLOPE()
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
- * @return float|string
+ * @return float|string The result, or a string containing an error
*/
public static function SLOPE($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getSlope();
+ return Trends::SLOPE($yValues, $xValues);
}
/**
@@ -3019,39 +1390,17 @@ class Statistical
* Excel Function:
* SMALL(value1[,value2[, ...]],entry)
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the small() method in the Statistical\Size class instead
+ * @see Statistical\Size::small()
*
* @param mixed $args Data values
- * @param int $entry Position (ordered from the smallest) in the array or range of data to return
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SMALL(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $entry = array_pop($aArgs);
-
- if ((is_numeric($entry)) && (!is_string($entry))) {
- $mArgs = [];
- 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 Functions::NAN();
- }
- sort($mArgs);
-
- return $mArgs[$entry];
- }
-
- return Functions::VALUE();
+ return Statistical\Size::small(...$args);
}
/**
@@ -3059,27 +1408,19 @@ class Statistical
*
* Returns a normalized value from a distribution characterized by mean and standard_dev.
*
+ * @deprecated 1.18.0
+ * Use the execute() method in the Statistical\Standardize class instead
+ * @see Statistical\Standardize::execute()
+ *
* @param float $value Value to normalize
* @param float $mean Mean Value
* @param float $stdDev Standard Deviation
*
- * @return float Standardized value
+ * @return array|float|string Standardized value, or a string containing an error
*/
public static function STANDARDIZE($value, $mean, $stdDev)
{
- $value = Functions::flattenSingleValue($value);
- $mean = Functions::flattenSingleValue($mean);
- $stdDev = Functions::flattenSingleValue($stdDev);
-
- if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
- if ($stdDev <= 0) {
- return Functions::NAN();
- }
-
- return ($value - $mean) / $stdDev;
- }
-
- return Functions::VALUE();
+ return Statistical\Standardize::execute($value, $mean, $stdDev);
}
/**
@@ -3091,45 +1432,17 @@ class Statistical
* Excel Function:
* STDEV(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the STDEV() method in the Statistical\StandardDeviations class instead
+ * @see Statistical\StandardDeviations::STDEV()
*
* @param mixed ...$args Data values
*
- * @return float|string
+ * @return float|string The result, or a string containing an error
*/
public static function STDEV(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- // Return value
- $returnValue = null;
-
- $aMean = self::AVERAGE($aArgs);
- if ($aMean !== null) {
- $aCount = -1;
- foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = pow(($arg - $aMean), 2);
- } else {
- $returnValue += pow(($arg - $aMean), 2);
- }
- ++$aCount;
- }
- }
-
- // Return
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEV(...$args);
}
/**
@@ -3140,7 +1453,9 @@ class Statistical
* Excel Function:
* STDEVA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the STDEVA() method in the Statistical\StandardDeviations class instead
+ * @see Statistical\StandardDeviations::STDEVA()
*
* @param mixed ...$args Data values
*
@@ -3148,40 +1463,7 @@ class Statistical
*/
public static function STDEVA(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- $returnValue = null;
-
- $aMean = self::AVERAGEA($aArgs);
- if ($aMean !== null) {
- $aCount = -1;
- foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- (!Functions::isMatrixValue($k))) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if ($returnValue === null) {
- $returnValue = pow(($arg - $aMean), 2);
- } else {
- $returnValue += pow(($arg - $aMean), 2);
- }
- ++$aCount;
- }
- }
- }
-
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEVA(...$args);
}
/**
@@ -3192,7 +1474,9 @@ class Statistical
* Excel Function:
* STDEVP(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the STDEVP() method in the Statistical\StandardDeviations class instead
+ * @see Statistical\StandardDeviations::STDEVP()
*
* @param mixed ...$args Data values
*
@@ -3200,35 +1484,7 @@ class Statistical
*/
public static function STDEVP(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- $returnValue = null;
-
- $aMean = self::AVERAGE($aArgs);
- if ($aMean !== null) {
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = pow(($arg - $aMean), 2);
- } else {
- $returnValue += pow(($arg - $aMean), 2);
- }
- ++$aCount;
- }
- }
-
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEVP(...$args);
}
/**
@@ -3239,7 +1495,9 @@ class Statistical
* Excel Function:
* STDEVPA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the STDEVPA() method in the Statistical\StandardDeviations class instead
+ * @see Statistical\StandardDeviations::STDEVPA()
*
* @param mixed ...$args Data values
*
@@ -3247,45 +1505,16 @@ class Statistical
*/
public static function STDEVPA(...$args)
{
- $aArgs = Functions::flattenArrayIndexed($args);
-
- $returnValue = null;
-
- $aMean = self::AVERAGEA($aArgs);
- if ($aMean !== null) {
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- (!Functions::isMatrixValue($k))) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- if ($returnValue === null) {
- $returnValue = pow(($arg - $aMean), 2);
- } else {
- $returnValue += pow(($arg - $aMean), 2);
- }
- ++$aCount;
- }
- }
- }
-
- if (($aCount > 0) && ($returnValue >= 0)) {
- return sqrt($returnValue / $aCount);
- }
- }
-
- return Functions::DIV0();
+ return StandardDeviations::STDEVPA(...$args);
}
/**
* STEYX.
*
+ * @deprecated 1.18.0
+ * Use the STEYX() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::STEYX()
+ *
* Returns the standard error of the predicted y-value for each x in the regression.
*
* @param mixed[] $yValues Data Series Y
@@ -3295,21 +1524,7 @@ class Statistical
*/
public static function STEYX($yValues, $xValues)
{
- if (!self::checkTrendArrays($yValues, $xValues)) {
- return Functions::VALUE();
- }
- $yValueCount = count($yValues);
- $xValueCount = count($xValues);
-
- if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
- return Functions::NA();
- } elseif ($yValueCount == 1) {
- return Functions::DIV0();
- }
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
-
- return $bestFitLinear->getStdevOfResiduals();
+ return Trends::STEYX($yValues, $xValues);
}
/**
@@ -3317,122 +1532,38 @@ class Statistical
*
* Returns the probability of Student's T distribution.
*
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\StudentT class instead
+ * @see Statistical\Distributions\StudentT::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
+ * @return array|float|string The result, or a string containing an error
*/
public static function TDIST($value, $degrees, $tails)
{
- $value = Functions::flattenSingleValue($value);
- $degrees = floor(Functions::flattenSingleValue($degrees));
- $tails = floor(Functions::flattenSingleValue($tails));
-
- if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
- if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
- return Functions::NAN();
- }
- // 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 = Functions::M_2DIVPI * ($tsum + $ttheta);
- }
- $tValue = 0.5 * (1 + $tsum);
- if ($tails == 1) {
- return 1 - abs($tValue);
- }
-
- return 1 - abs((1 - $tValue) - $tValue);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\StudentT::distribution($value, $degrees, $tails);
}
/**
* TINV.
*
- * Returns the one-tailed probability of the chi-squared distribution.
+ * Returns the one-tailed probability of the Student-T distribution.
+ *
+ * @deprecated 1.18.0
+ * Use the inverse() method in the Statistical\Distributions\StudentT class instead
+ * @see Statistical\Distributions\StudentT::inverse()
*
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
*
- * @return float
+ * @return array|float|string The result, or a string containing an error
*/
public static function TINV($probability, $degrees)
{
- $probability = Functions::flattenSingleValue($probability);
- $degrees = floor(Functions::flattenSingleValue($degrees));
-
- if ((is_numeric($probability)) && (is_numeric($degrees))) {
- $xLo = 100;
- $xHi = 0;
-
- $x = $xNew = 1;
- $dx = 1;
- $i = 0;
-
- while ((abs($dx) > Functions::PRECISION) && ($i++ < self::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 == self::MAX_ITERATIONS) {
- return Functions::NA();
- }
-
- return round($x, 12);
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\StudentT::inverse($probability, $degrees);
}
/**
@@ -3440,31 +1571,20 @@ class Statistical
*
* Returns values along a linear Trend
*
+ * @deprecated 1.18.0
+ * Use the TREND() method in the Statistical\Trends class instead
+ * @see Statistical\Trends::TREND()
+ *
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param bool $const a logical value specifying whether to force the intersect to equal 0
*
- * @return array of float
+ * @return float[]
*/
public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
{
- $yValues = Functions::flattenArray($yValues);
- $xValues = Functions::flattenArray($xValues);
- $newValues = Functions::flattenArray($newValues);
- $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
-
- $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
- if (empty($newValues)) {
- $newValues = $bestFitLinear->getXValues();
- }
-
- $returnArray = [];
- foreach ($newValues as $xValue) {
- $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
- }
-
- return $returnArray;
+ return Trends::TREND($yValues, $xValues, $newValues, $const);
}
/**
@@ -3477,42 +1597,17 @@ class Statistical
* Excel Function:
* TRIMEAN(value1[,value2[, ...]], $discard)
*
- * @category Statistical Functions
+ * @deprecated 1.18.0
+ * Use the trim() method in the Statistical\Averages\Mean class instead
+ * @see Statistical\Averages\Mean::trim()
*
* @param mixed $args Data values
- * @param float $discard Percentage to discard
*
* @return float|string
*/
public static function TRIMMEAN(...$args)
{
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $percent = array_pop($aArgs);
-
- if ((is_numeric($percent)) && (!is_string($percent))) {
- if (($percent < 0) || ($percent > 1)) {
- return Functions::NAN();
- }
- $mArgs = [];
- 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 Functions::VALUE();
+ return Statistical\Averages\Mean::trim(...$args);
}
/**
@@ -3523,40 +1618,17 @@ class Statistical
* Excel Function:
* VAR(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the VAR() method in the Statistical\Variances class instead
+ * @see Statistical\Variances::VAR()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string (string if result is an error)
*/
public static function VARFunc(...$args)
{
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- $aCount = 0;
- foreach ($aArgs as $arg) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
-
- if ($aCount > 1) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
- }
-
- return $returnValue;
+ return Variances::VAR(...$args);
}
/**
@@ -3567,49 +1639,17 @@ class Statistical
* Excel Function:
* VARA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the VARA() method in the Statistical\Variances class instead
+ * @see Statistical\Variances::VARA()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string (string if result is an error)
*/
public static function VARA(...$args)
{
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if ((is_string($arg)) &&
- (Functions::isValue($k))) {
- return Functions::VALUE();
- } elseif ((is_string($arg)) &&
- (!Functions::isMatrixValue($k))) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
- }
-
- if ($aCount > 1) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
- }
-
- return $returnValue;
+ return Variances::VARA(...$args);
}
/**
@@ -3620,41 +1660,17 @@ class Statistical
* Excel Function:
* VARP(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the VARP() method in the Statistical\Variances class instead
+ * @see Statistical\Variances::VARP()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string (string if result is an error)
*/
public static function VARP(...$args)
{
- // Return value
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- $aCount = 0;
- foreach ($aArgs as $arg) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
-
- if ($aCount > 0) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
- }
-
- return $returnValue;
+ return Variances::VARP(...$args);
}
/**
@@ -3665,49 +1681,17 @@ class Statistical
* Excel Function:
* VARPA(value1[,value2[, ...]])
*
- * @category Statistical Functions
+ * @deprecated 1.17.0
+ * Use the VARPA() method in the Statistical\Variances class instead
+ * @see Statistical\Variances::VARPA()
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string (string if result is an error)
*/
public static function VARPA(...$args)
{
- $returnValue = Functions::DIV0();
-
- $summerA = $summerB = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArrayIndexed($args);
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if ((is_string($arg)) &&
- (Functions::isValue($k))) {
- return Functions::VALUE();
- } elseif ((is_string($arg)) &&
- (!Functions::isMatrixValue($k))) {
- } else {
- // Is it a numeric value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
- if (is_bool($arg)) {
- $arg = (int) $arg;
- } elseif (is_string($arg)) {
- $arg = 0;
- }
- $summerA += ($arg * $arg);
- $summerB += $arg;
- ++$aCount;
- }
- }
- }
-
- if ($aCount > 0) {
- $summerA *= $aCount;
- $summerB *= $summerB;
- $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
- }
-
- return $returnValue;
+ return Variances::VARPA(...$args);
}
/**
@@ -3716,58 +1700,42 @@ class Statistical
* Returns the Weibull distribution. Use this distribution in reliability
* analysis, such as calculating a device's mean time to failure.
*
+ * @deprecated 1.18.0
+ * Use the distribution() method in the Statistical\Distributions\Weibull class instead
+ * @see Statistical\Distributions\Weibull::distribution()
+ *
* @param float $value
* @param float $alpha Alpha Parameter
* @param float $beta Beta Parameter
* @param bool $cumulative
*
- * @return float
+ * @return array|float|string (string if result is an error)
*/
public static function WEIBULL($value, $alpha, $beta, $cumulative)
{
- $value = Functions::flattenSingleValue($value);
- $alpha = Functions::flattenSingleValue($alpha);
- $beta = Functions::flattenSingleValue($beta);
-
- if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
- if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
- return Functions::NAN();
- }
- if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
- if ($cumulative) {
- return 1 - exp(0 - pow($value / $beta, $alpha));
- }
-
- return ($alpha / pow($beta, $alpha)) * pow($value, $alpha - 1) * exp(0 - pow($value / $beta, $alpha));
- }
- }
-
- return Functions::VALUE();
+ return Statistical\Distributions\Weibull::distribution($value, $alpha, $beta, $cumulative);
}
/**
* ZTEST.
*
- * Returns the Weibull distribution. Use this distribution in reliability
- * analysis, such as calculating a device's mean time to failure.
+ * Returns the one-tailed P-value of a z-test.
+ *
+ * For a given hypothesized population mean, x, Z.TEST returns the probability that the sample mean would be
+ * greater than the average of observations in the data set (array) — that is, the observed sample mean.
+ *
+ * @deprecated 1.18.0
+ * Use the zTest() method in the Statistical\Distributions\StandardNormal class instead
+ * @see Statistical\Distributions\StandardNormal::zTest()
*
* @param float $dataSet
* @param float $m0 Alpha Parameter
* @param float $sigma Beta Parameter
*
- * @return float|string
+ * @return array|float|string (string if result is an error)
*/
public static function ZTEST($dataSet, $m0, $sigma = null)
{
- $dataSet = Functions::flattenArrayIndexed($dataSet);
- $m0 = Functions::flattenSingleValue($m0);
- $sigma = Functions::flattenSingleValue($sigma);
-
- if ($sigma === null) {
- $sigma = self::STDEV($dataSet);
- }
- $n = count($dataSet);
-
- return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n)));
+ return Statistical\Distributions\StandardNormal::zTest($dataSet, $m0, $sigma);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
new file mode 100644
index 0000000..10933f4
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
@@ -0,0 +1,70 @@
+ $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return ExcelError::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += abs($arg - $aMean);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount === 0) {
+ return ExcelError::DIV0();
+ }
+
+ return $returnValue / $aCount;
+ }
+
+ /**
+ * AVERAGE.
+ *
+ * Returns the average (arithmetic mean) of the arguments
+ *
+ * Excel Function:
+ * AVERAGE(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function average(...$args)
+ {
+ $returnValue = $aCount = 0;
+
+ // Loop through arguments
+ foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return ExcelError::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ }
+
+ return ExcelError::DIV0();
+ }
+
+ /**
+ * AVERAGEA.
+ *
+ * Returns the average of its arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * AVERAGEA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function averageA(...$args)
+ {
+ $returnValue = null;
+
+ $aCount = 0;
+ // Loop through arguments
+ foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
+ if (is_numeric($arg)) {
+ // do nothing
+ } elseif (is_bool($arg)) {
+ $arg = (int) $arg;
+ } elseif (!Functions::isMatrixValue($k)) {
+ $arg = 0;
+ } else {
+ return ExcelError::VALUE();
+ }
+ $returnValue += $arg;
+ ++$aCount;
+ }
+
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ }
+
+ return ExcelError::DIV0();
+ }
+
+ /**
+ * 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[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function median(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ $returnValue = ExcelError::NAN();
+
+ $aArgs = self::filterArguments($aArgs);
+ $valueCount = count($aArgs);
+ if ($valueCount > 0) {
+ sort($aArgs, SORT_NUMERIC);
+ $valueCount = $valueCount / 2;
+ if ($valueCount == floor($valueCount)) {
+ $returnValue = ($aArgs[$valueCount--] + $aArgs[$valueCount]) / 2;
+ } else {
+ $valueCount = floor($valueCount);
+ $returnValue = $aArgs[$valueCount];
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * MODE.
+ *
+ * Returns the most frequently occurring, or repetitive, value in an array or range of data
+ *
+ * Excel Function:
+ * MODE(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function mode(...$args)
+ {
+ $returnValue = ExcelError::NA();
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ $aArgs = self::filterArguments($aArgs);
+
+ if (!empty($aArgs)) {
+ return self::modeCalc($aArgs);
+ }
+
+ return $returnValue;
+ }
+
+ protected static function filterArguments($args)
+ {
+ return array_filter(
+ $args,
+ function ($value) {
+ // Is it a numeric value?
+ return is_numeric($value) && (!is_string($value));
+ }
+ );
+ }
+
+ //
+ // 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 = [];
+ $index = 0;
+ $maxfreq = 0;
+ $maxfreqkey = '';
+ $maxfreqdatum = '';
+ foreach ($data as $datum) {
+ $found = false;
+ ++$index;
+ foreach ($frequencyArray as $key => $value) {
+ if ((string) $value['value'] == (string) $datum) {
+ ++$frequencyArray[$key]['frequency'];
+ $freq = $frequencyArray[$key]['frequency'];
+ if ($freq > $maxfreq) {
+ $maxfreq = $freq;
+ $maxfreqkey = $key;
+ $maxfreqdatum = $datum;
+ } elseif ($freq == $maxfreq) {
+ if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) {
+ $maxfreqkey = $key;
+ $maxfreqdatum = $datum;
+ }
+ }
+ $found = true;
+
+ break;
+ }
+ }
+
+ if ($found === false) {
+ $frequencyArray[] = [
+ 'value' => $datum,
+ 'frequency' => 1,
+ 'index' => $index,
+ ];
+ }
+ }
+
+ if ($maxfreq <= 1) {
+ return ExcelError::NA();
+ }
+
+ return $maxfreqdatum;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
new file mode 100644
index 0000000..001c91e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
@@ -0,0 +1,132 @@
+ 0)) {
+ $aCount = Counts::COUNT($aArgs);
+ if (Minimum::min($aArgs) > 0) {
+ return $aMean ** (1 / $aCount);
+ }
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * 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[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string
+ */
+ public static function harmonic(...$args)
+ {
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ if (Minimum::min($aArgs) < 0) {
+ return ExcelError::NAN();
+ }
+
+ $returnValue = 0;
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ($arg <= 0) {
+ return ExcelError::NAN();
+ }
+ $returnValue += (1 / $arg);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return 1 / ($returnValue / $aCount);
+ }
+
+ return ExcelError::NA();
+ }
+
+ /**
+ * 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)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string
+ */
+ public static function trim(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ // Calculate
+ $percent = array_pop($aArgs);
+
+ if ((is_numeric($percent)) && (!is_string($percent))) {
+ if (($percent < 0) || ($percent > 1)) {
+ return ExcelError::NAN();
+ }
+
+ $mArgs = [];
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ $discard = floor(Counts::COUNT($mArgs) * $percent / 2);
+ sort($mArgs);
+
+ for ($i = 0; $i < $discard; ++$i) {
+ array_pop($mArgs);
+ array_shift($mArgs);
+ }
+
+ return Averages::average($mArgs);
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Conditional.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Conditional.php
new file mode 100644
index 0000000..5d40943
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Conditional.php
@@ -0,0 +1,310 @@
+getMessage();
+ }
+
+ if (($alpha <= 0) || ($alpha >= 1) || ($stdDev <= 0) || ($size < 1)) {
+ return ExcelError::NAN();
+ }
+ /** @var float */
+ $temp = Distributions\StandardNormal::inverse(1 - $alpha / 2);
+
+ return Functions::scalar($temp * $stdDev / sqrt($size));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Counts.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Counts.php
new file mode 100644
index 0000000..6792730
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Counts.php
@@ -0,0 +1,102 @@
+ $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if (self::isAcceptedCountable($arg, $k, true)) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * COUNTA.
+ *
+ * Counts the number of cells that are not empty within the list of arguments
+ *
+ * Excel Function:
+ * COUNTA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return int
+ */
+ public static function COUNTA(...$args)
+ {
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ foreach ($aArgs as $k => $arg) {
+ // Nulls are counted if literals, but not if cell values
+ if ($arg !== null || (!Functions::isCellValue($k))) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * COUNTBLANK.
+ *
+ * Counts the number of empty cells within the list of arguments
+ *
+ * Excel Function:
+ * COUNTBLANK(value1[,value2[, ...]])
+ *
+ * @param mixed $range Data values
+ *
+ * @return int
+ */
+ public static function COUNTBLANK($range)
+ {
+ if ($range === null) {
+ return 1;
+ }
+ if (!is_array($range) || array_key_exists(0, $range)) {
+ throw new CalcException('Must specify range of cells, not any kind of literal');
+ }
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($range);
+ foreach ($aArgs as $arg) {
+ // Is it a blank cell?
+ if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Deviations.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Deviations.php
new file mode 100644
index 0000000..6b1db3a
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Deviations.php
@@ -0,0 +1,142 @@
+ $arg) {
+ // Is it a numeric value?
+ if (
+ (is_bool($arg)) &&
+ ((!Functions::isCellValue($k)) ||
+ (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
+ ) {
+ $arg = (int) $arg;
+ }
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += ($arg - $aMean) ** 2;
+ ++$aCount;
+ }
+ }
+
+ return $aCount === 0 ? ExcelError::VALUE() : $returnValue;
+ }
+
+ /**
+ * 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 ...$args Data Series
+ *
+ * @return float|string
+ */
+ public static function kurtosis(...$args)
+ {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $mean = Averages::average($aArgs);
+ if (!is_numeric($mean)) {
+ return ExcelError::DIV0();
+ }
+ $stdDev = StandardDeviations::STDEV($aArgs);
+
+ if ($stdDev > 0) {
+ $count = $summer = 0;
+
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += (($arg - $mean) / $stdDev) ** 4;
+ ++$count;
+ }
+ }
+ }
+
+ if ($count > 3) {
+ return $summer * ($count * ($count + 1) /
+ (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 /
+ (($count - 2) * ($count - 3)));
+ }
+ }
+
+ return ExcelError::DIV0();
+ }
+
+ /**
+ * 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 ...$args Data Series
+ *
+ * @return float|int|string The result, or a string containing an error
+ */
+ public static function skew(...$args)
+ {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $mean = Averages::average($aArgs);
+ if (!is_numeric($mean)) {
+ return ExcelError::DIV0();
+ }
+ $stdDev = StandardDeviations::STDEV($aArgs);
+ if ($stdDev === 0.0 || is_string($stdDev)) {
+ return ExcelError::DIV0();
+ }
+
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } elseif (!is_numeric($arg)) {
+ return ExcelError::VALUE();
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += (($arg - $mean) / $stdDev) ** 3;
+ ++$count;
+ }
+ }
+ }
+
+ if ($count > 2) {
+ return $summer * ($count / (($count - 1) * ($count - 2)));
+ }
+
+ return ExcelError::DIV0();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
new file mode 100644
index 0000000..8b41ac8
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
@@ -0,0 +1,283 @@
+getMessage();
+ }
+
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
+ return ExcelError::NAN();
+ }
+
+ $value -= $rMin;
+ $value /= ($rMax - $rMin);
+
+ return self::incompleteBeta($value, $alpha, $beta);
+ }
+
+ /**
+ * BETAINV.
+ *
+ * Returns the inverse of the Beta distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $alpha Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $beta Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $rMin Minimum value as a float
+ * Or can be an array of values
+ * @param mixed $rMax Maximum value as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse($probability, $alpha, $beta, $rMin = 0.0, $rMax = 1.0)
+ {
+ if (is_array($probability) || is_array($alpha) || is_array($beta) || is_array($rMin) || is_array($rMax)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta, $rMin, $rMax);
+ }
+
+ $rMin = $rMin ?? 0.0;
+ $rMax = $rMax ?? 1.0;
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ $beta = DistributionValidations::validateFloat($beta);
+ $rMax = DistributionValidations::validateFloat($rMax);
+ $rMin = DistributionValidations::validateFloat($rMin);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateInverse($probability, $alpha, $beta, $rMin, $rMax);
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax)
+ {
+ $a = 0;
+ $b = 2;
+ $guess = ($a + $b) / 2;
+
+ $i = 0;
+ while ((($b - $a) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
+ $guess = ($a + $b) / 2;
+ $result = self::distribution($guess, $alpha, $beta);
+ if (($result === $probability) || ($result === 0.0)) {
+ $b = $a;
+ } elseif ($result > $probability) {
+ $b = $guess;
+ } else {
+ $a = $guess;
+ }
+ }
+
+ if ($i === self::MAX_ITERATIONS) {
+ return ExcelError::NA();
+ }
+
+ return round($rMin + $guess * ($rMax - $rMin), 12);
+ }
+
+ /**
+ * 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 float $x require 0<=x<=1
+ * @param float $p require p>0
+ * @param float $q require q>0
+ *
+ * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
+ */
+ public static function incompleteBeta(float $x, float $p, float $q): float
+ {
+ if ($x <= 0.0) {
+ return 0.0;
+ } elseif ($x >= 1.0) {
+ return 1.0;
+ } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::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;
+ }
+
+ return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
+ }
+
+ // Function cache for logBeta function
+ private static $logBetaCacheP = 0.0;
+
+ private static $logBetaCacheQ = 0.0;
+
+ private static $logBetaCacheResult = 0.0;
+
+ /**
+ * The natural logarithm of the beta function.
+ *
+ * @param float $p require p>0
+ * @param float $q require q>0
+ *
+ * @return float 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(float $p, float $q): float
+ {
+ if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
+ self::$logBetaCacheP = $p;
+ self::$logBetaCacheQ = $q;
+ if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
+ self::$logBetaCacheResult = 0.0;
+ } else {
+ self::$logBetaCacheResult = Gamma::logGamma($p) + Gamma::logGamma($q) - Gamma::logGamma($p + $q);
+ }
+ }
+
+ return self::$logBetaCacheResult;
+ }
+
+ /**
+ * 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(float $x, float $p, float $q): float
+ {
+ $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) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $frac = $h;
+ $m = 1;
+ $delta = 0.0;
+ while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::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) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < self::XMININ) {
+ $c = self::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) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < self::XMININ) {
+ $c = self::XMININ;
+ }
+ $delta = $h * $c;
+ $frac *= $delta;
+ ++$m;
+ }
+
+ return $frac;
+ }
+
+ /*
+ private static function betaValue(float $a, float $b): float
+ {
+ return (Gamma::gammaValue($a) * Gamma::gammaValue($b)) /
+ Gamma::gammaValue($a + $b);
+ }
+
+ private static function regularizedIncompleteBeta(float $value, float $a, float $b): float
+ {
+ return self::incompleteBeta($value, $a, $b) / self::betaValue($a, $b);
+ }
+ */
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
new file mode 100644
index 0000000..02b53e8
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
@@ -0,0 +1,237 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($value > $trials)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ return self::calculateCumulativeBinomial($value, $trials, $probability);
+ }
+ /** @var float */
+ $comb = Combinations::withoutRepetition($trials, $value);
+
+ return $comb * $probability ** $value
+ * (1 - $probability) ** ($trials - $value);
+ }
+
+ /**
+ * BINOM.DIST.RANGE.
+ *
+ * Returns returns the Binomial Distribution probability for the number of successes from a specified number
+ * of trials falling into a specified range.
+ *
+ * @param mixed $trials Integer number of trials
+ * Or can be an array of values
+ * @param mixed $probability Probability of success on each trial as a float
+ * Or can be an array of values
+ * @param mixed $successes The integer number of successes in trials
+ * Or can be an array of values
+ * @param mixed $limit Upper limit for successes in trials as null, or an integer
+ * If null, then this will indicate the same as the number of Successes
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function range($trials, $probability, $successes, $limit = null)
+ {
+ if (is_array($trials) || is_array($probability) || is_array($successes) || is_array($limit)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $successes, $limit);
+ }
+
+ $limit = $limit ?? $successes;
+
+ try {
+ $trials = DistributionValidations::validateInt($trials);
+ $probability = DistributionValidations::validateProbability($probability);
+ $successes = DistributionValidations::validateInt($successes);
+ $limit = DistributionValidations::validateInt($limit);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($successes < 0) || ($successes > $trials)) {
+ return ExcelError::NAN();
+ }
+ if (($limit < 0) || ($limit > $trials) || $limit < $successes) {
+ return ExcelError::NAN();
+ }
+
+ $summer = 0;
+ for ($i = $successes; $i <= $limit; ++$i) {
+ /** @var float */
+ $comb = Combinations::withoutRepetition($trials, $i);
+ $summer += $comb * $probability ** $i
+ * (1 - $probability) ** ($trials - $i);
+ }
+
+ return $summer;
+ }
+
+ /**
+ * 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 mixed $failures Number of Failures as an integer
+ * Or can be an array of values
+ * @param mixed $successes Threshold number of Successes as an integer
+ * Or can be an array of values
+ * @param mixed $probability Probability of success on each trial as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ *
+ * TODO Add support for the cumulative flag not present for NEGBINOMDIST, but introduced for NEGBINOM.DIST
+ * The cumulative default should be false to reflect the behaviour of NEGBINOMDIST
+ */
+ public static function negative($failures, $successes, $probability)
+ {
+ if (is_array($failures) || is_array($successes) || is_array($probability)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $failures, $successes, $probability);
+ }
+
+ try {
+ $failures = DistributionValidations::validateInt($failures);
+ $successes = DistributionValidations::validateInt($successes);
+ $probability = DistributionValidations::validateProbability($probability);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($failures < 0) || ($successes < 1)) {
+ return ExcelError::NAN();
+ }
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ if (($failures + $successes - 1) <= 0) {
+ return ExcelError::NAN();
+ }
+ }
+ /** @var float */
+ $comb = Combinations::withoutRepetition($failures + $successes - 1, $successes - 1);
+
+ return $comb
+ * ($probability ** $successes) * ((1 - $probability) ** $failures);
+ }
+
+ /**
+ * BINOM.INV.
+ *
+ * Returns the smallest value for which the cumulative binomial distribution is greater
+ * than or equal to a criterion value
+ *
+ * @param mixed $trials number of Bernoulli trials as an integer
+ * Or can be an array of values
+ * @param mixed $probability probability of a success on each trial as a float
+ * Or can be an array of values
+ * @param mixed $alpha criterion value as a float
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse($trials, $probability, $alpha)
+ {
+ if (is_array($trials) || is_array($probability) || is_array($alpha)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $alpha);
+ }
+
+ try {
+ $trials = DistributionValidations::validateInt($trials);
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($trials < 0) {
+ return ExcelError::NAN();
+ } elseif (($alpha < 0.0) || ($alpha > 1.0)) {
+ return ExcelError::NAN();
+ }
+
+ $successes = 0;
+ while ($successes <= $trials) {
+ $result = self::calculateCumulativeBinomial($successes, $trials, $probability);
+ if ($result >= $alpha) {
+ break;
+ }
+ ++$successes;
+ }
+
+ return $successes;
+ }
+
+ /**
+ * @return float|int
+ */
+ private static function calculateCumulativeBinomial(int $value, int $trials, float $probability)
+ {
+ $summer = 0;
+ for ($i = 0; $i <= $value; ++$i) {
+ /** @var float */
+ $comb = Combinations::withoutRepetition($trials, $i);
+ $summer += $comb * $probability ** $i
+ * (1 - $probability) ** ($trials - $i);
+ }
+
+ return $summer;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
new file mode 100644
index 0000000..c874336
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
@@ -0,0 +1,337 @@
+getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+ if ($value < 0) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2));
+ }
+
+ /**
+ * CHIDIST.
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $value Float value for which we want the probability
+ * Or can be an array of values
+ * @param mixed $degrees Integer degrees of freedom
+ * Or can be an array of values
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function distributionLeftTail($value, $degrees, $cumulative)
+ {
+ if (is_array($value) || is_array($degrees) || is_array($cumulative)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $cumulative);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $degrees = DistributionValidations::validateInt($degrees);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+ if ($value < 0) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative === true) {
+ return 1 - self::distributionRightTail($value, $degrees);
+ }
+
+ return ($value ** (($degrees / 2) - 1) * exp(-$value / 2)) /
+ ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
+ }
+
+ /**
+ * CHIINV.
+ *
+ * Returns the inverse of the right-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $degrees Integer degrees of freedom
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverseRightTail($probability, $degrees)
+ {
+ if (is_array($probability) || is_array($degrees)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+
+ $callback = function ($value) use ($degrees) {
+ return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2)
+ / Gamma::gammaValue($degrees / 2));
+ };
+
+ $newtonRaphson = new NewtonRaphson($callback);
+
+ return $newtonRaphson->execute($probability);
+ }
+
+ /**
+ * CHIINV.
+ *
+ * Returns the inverse of the left-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $degrees Integer degrees of freedom
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverseLeftTail($probability, $degrees)
+ {
+ if (is_array($probability) || is_array($degrees)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+
+ return self::inverseLeftTailCalculation($probability, $degrees);
+ }
+
+ /**
+ * CHITEST.
+ *
+ * Uses the chi-square test to calculate the probability that the differences between two supplied data sets
+ * (of observed and expected frequencies), are likely to be simply due to sampling error,
+ * or if they are likely to be real.
+ *
+ * @param mixed $actual an array of observed frequencies
+ * @param mixed $expected an array of expected frequencies
+ *
+ * @return float|string
+ */
+ public static function test($actual, $expected)
+ {
+ $rows = count($actual);
+ $actual = Functions::flattenArray($actual);
+ $expected = Functions::flattenArray($expected);
+ $columns = count($actual) / $rows;
+
+ $countActuals = count($actual);
+ $countExpected = count($expected);
+ if ($countActuals !== $countExpected || $countActuals === 1) {
+ return ExcelError::NAN();
+ }
+
+ $result = 0.0;
+ for ($i = 0; $i < $countActuals; ++$i) {
+ if ($expected[$i] == 0.0) {
+ return ExcelError::DIV0();
+ } elseif ($expected[$i] < 0.0) {
+ return ExcelError::NAN();
+ }
+ $result += (($actual[$i] - $expected[$i]) ** 2) / $expected[$i];
+ }
+
+ $degrees = self::degrees($rows, $columns);
+
+ $result = Functions::scalar(self::distributionRightTail($result, $degrees));
+
+ return $result;
+ }
+
+ protected static function degrees(int $rows, int $columns): int
+ {
+ if ($rows === 1) {
+ return $columns - 1;
+ } elseif ($columns === 1) {
+ return $rows - 1;
+ }
+
+ return ($columns - 1) * ($rows - 1);
+ }
+
+ private static function inverseLeftTailCalculation(float $probability, int $degrees): float
+ {
+ // bracket the root
+ $min = 0;
+ $sd = sqrt(2.0 * $degrees);
+ $max = 2 * $sd;
+ $s = -1;
+
+ while ($s * self::pchisq($max, $degrees) > $probability * $s) {
+ $min = $max;
+ $max += 2 * $sd;
+ }
+
+ // Find root using bisection
+ $chi2 = 0.5 * ($min + $max);
+
+ while (($max - $min) > self::EPS * $chi2) {
+ if ($s * self::pchisq($chi2, $degrees) > $probability * $s) {
+ $min = $chi2;
+ } else {
+ $max = $chi2;
+ }
+ $chi2 = 0.5 * ($min + $max);
+ }
+
+ return $chi2;
+ }
+
+ private static function pchisq($chi2, $degrees)
+ {
+ return self::gammp($degrees, 0.5 * $chi2);
+ }
+
+ private static function gammp($n, $x)
+ {
+ if ($x < 0.5 * $n + 1) {
+ return self::gser($n, $x);
+ }
+
+ return 1 - self::gcf($n, $x);
+ }
+
+ // Return the incomplete gamma function P(n/2,x) evaluated by
+ // series representation. Algorithm from numerical recipe.
+ // Assume that n is a positive integer and x>0, won't check arguments.
+ // Relative error controlled by the eps parameter
+ private static function gser($n, $x)
+ {
+ /** @var float */
+ $gln = Gamma::ln($n / 2);
+ $a = 0.5 * $n;
+ $ap = $a;
+ $sum = 1.0 / $a;
+ $del = $sum;
+ for ($i = 1; $i < 101; ++$i) {
+ ++$ap;
+ $del = $del * $x / $ap;
+ $sum += $del;
+ if ($del < $sum * self::EPS) {
+ break;
+ }
+ }
+
+ return $sum * exp(-$x + $a * log($x) - $gln);
+ }
+
+ // Return the incomplete gamma function Q(n/2,x) evaluated by
+ // its continued fraction representation. Algorithm from numerical recipe.
+ // Assume that n is a postive integer and x>0, won't check arguments.
+ // Relative error controlled by the eps parameter
+ private static function gcf($n, $x)
+ {
+ /** @var float */
+ $gln = Gamma::ln($n / 2);
+ $a = 0.5 * $n;
+ $b = $x + 1 - $a;
+ $fpmin = 1.e-300;
+ $c = 1 / $fpmin;
+ $d = 1 / $b;
+ $h = $d;
+ for ($i = 1; $i < 101; ++$i) {
+ $an = -$i * ($i - $a);
+ $b += 2;
+ $d = $an * $d + $b;
+ if (abs($d) < $fpmin) {
+ $d = $fpmin;
+ }
+ $c = $b + $an / $c;
+ if (abs($c) < $fpmin) {
+ $c = $fpmin;
+ }
+ $d = 1 / $d;
+ $del = $d * $c;
+ $h = $h * $del;
+ if (abs($del - 1) < self::EPS) {
+ break;
+ }
+ }
+
+ return $h * exp(-$x + $a * log($x) - $gln);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
new file mode 100644
index 0000000..bd45a22
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
@@ -0,0 +1,24 @@
+ 1.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $probability;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
new file mode 100644
index 0000000..a03671c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
@@ -0,0 +1,55 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($lambda < 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative === true) {
+ return 1 - exp(0 - $value * $lambda);
+ }
+
+ return $lambda * exp(0 - $value * $lambda);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
new file mode 100644
index 0000000..ff413b6
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ if ($value < 0 || $u < 1 || $v < 1) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ $adjustedValue = ($u * $value) / ($u * $value + $v);
+
+ return Beta::incompleteBeta($adjustedValue, $u / 2, $v / 2);
+ }
+
+ return (Gamma::gammaValue(($v + $u) / 2) /
+ (Gamma::gammaValue($u / 2) * Gamma::gammaValue($v / 2))) *
+ (($u / $v) ** ($u / 2)) *
+ (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
new file mode 100644
index 0000000..df9906e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
@@ -0,0 +1,74 @@
+getMessage();
+ }
+
+ if (($value <= -1) || ($value >= 1)) {
+ return ExcelError::NAN();
+ }
+
+ return 0.5 * log((1 + $value) / (1 - $value));
+ }
+
+ /**
+ * 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 mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse($probability)
+ {
+ if (is_array($probability)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $probability);
+ }
+
+ try {
+ DistributionValidations::validateFloat($probability);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (exp(2 * $probability) - 1) / (exp(2 * $probability) + 1);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
new file mode 100644
index 0000000..216f234
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
@@ -0,0 +1,151 @@
+getMessage();
+ }
+
+ if ((((int) $value) == ((float) $value)) && $value <= 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return self::gammaValue($value);
+ }
+
+ /**
+ * GAMMADIST.
+ *
+ * Returns the gamma distribution.
+ *
+ * @param mixed $value Float Value at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $a Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $b Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function distribution($value, $a, $b, $cumulative)
+ {
+ if (is_array($value) || is_array($a) || is_array($b) || is_array($cumulative)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $a, $b, $cumulative);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $a = DistributionValidations::validateFloat($a);
+ $b = DistributionValidations::validateFloat($b);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($value < 0) || ($a <= 0) || ($b <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateDistribution($value, $a, $b, $cumulative);
+ }
+
+ /**
+ * GAMMAINV.
+ *
+ * Returns the inverse of the Gamma distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $alpha Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $beta Parameter to the distribution as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse($probability, $alpha, $beta)
+ {
+ if (is_array($probability) || is_array($alpha) || is_array($beta)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ $beta = DistributionValidations::validateFloat($beta);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($alpha <= 0.0) || ($beta <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateInverse($probability, $alpha, $beta);
+ }
+
+ /**
+ * GAMMALN.
+ *
+ * Returns the natural logarithm of the gamma function.
+ *
+ * @param mixed $value Float Value at which you want to evaluate the distribution
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function ln($value)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($value <= 0) {
+ return ExcelError::NAN();
+ }
+
+ return log(self::gammaValue($value));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
new file mode 100644
index 0000000..8e6386e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
@@ -0,0 +1,380 @@
+ Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::calculateDistribution($x, $alpha, $beta, true);
+ $error = $result - $probability;
+
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+
+ $pdf = self::calculateDistribution($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 === self::MAX_ITERATIONS) {
+ return ExcelError::NA();
+ }
+
+ return $x;
+ }
+
+ //
+ // Implementation of the incomplete Gamma function
+ //
+ public static function incompleteGamma(float $a, float $x): float
+ {
+ static $max = 32;
+ $summer = 0;
+ for ($n = 0; $n <= $max; ++$n) {
+ $divisor = $a;
+ for ($i = 1; $i <= $n; ++$i) {
+ $divisor *= ($a + $i);
+ }
+ $summer += ($x ** $n / $divisor);
+ }
+
+ return $x ** $a * exp(0 - $x) * $summer;
+ }
+
+ //
+ // Implementation of the Gamma function
+ //
+ public static function gammaValue(float $value): float
+ {
+ if ($value == 0.0) {
+ return 0;
+ }
+
+ static $p0 = 1.000000000190015;
+ static $p = [
+ 1 => 76.18009172947146,
+ 2 => -86.50532032941677,
+ 3 => 24.01409824083091,
+ 4 => -1.231739572450155,
+ 5 => 1.208650973866179e-3,
+ 6 => -5.395239384953e-6,
+ ];
+
+ $y = $x = $value;
+ $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(self::SQRT2PI * $summer / $x));
+ }
+
+ private const LG_D1 = -0.5772156649015328605195174;
+
+ private const LG_D2 = 0.4227843350984671393993777;
+
+ private const LG_D4 = 1.791759469228055000094023;
+
+ private const LG_P1 = [
+ 4.945235359296727046734888,
+ 201.8112620856775083915565,
+ 2290.838373831346393026739,
+ 11319.67205903380828685045,
+ 28557.24635671635335736389,
+ 38484.96228443793359990269,
+ 26377.48787624195437963534,
+ 7225.813979700288197698961,
+ ];
+
+ private const LG_P2 = [
+ 4.974607845568932035012064,
+ 542.4138599891070494101986,
+ 15506.93864978364947665077,
+ 184793.2904445632425417223,
+ 1088204.76946882876749847,
+ 3338152.967987029735917223,
+ 5106661.678927352456275255,
+ 3074109.054850539556250927,
+ ];
+
+ private const LG_P4 = [
+ 14745.02166059939948905062,
+ 2426813.369486704502836312,
+ 121475557.4045093227939592,
+ 2663432449.630976949898078,
+ 29403789566.34553899906876,
+ 170266573776.5398868392998,
+ 492612579337.743088758812,
+ 560625185622.3951465078242,
+ ];
+
+ private const LG_Q1 = [
+ 67.48212550303777196073036,
+ 1113.332393857199323513008,
+ 7738.757056935398733233834,
+ 27639.87074403340708898585,
+ 54993.10206226157329794414,
+ 61611.22180066002127833352,
+ 36351.27591501940507276287,
+ 8785.536302431013170870835,
+ ];
+
+ private const LG_Q2 = [
+ 183.0328399370592604055942,
+ 7765.049321445005871323047,
+ 133190.3827966074194402448,
+ 1136705.821321969608938755,
+ 5267964.117437946917577538,
+ 13467014.54311101692290052,
+ 17827365.30353274213975932,
+ 9533095.591844353613395747,
+ ];
+
+ private const LG_Q4 = [
+ 2690.530175870899333379843,
+ 639388.5654300092398984238,
+ 41355999.30241388052042842,
+ 1120872109.61614794137657,
+ 14886137286.78813811542398,
+ 101680358627.2438228077304,
+ 341747634550.7377132798597,
+ 446315818741.9713286462081,
+ ];
+
+ private const LG_C = [
+ -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
+ private const LG_FRTBIG = 2.25e76;
+
+ private const PNT68 = 0.6796875;
+
+ // Function cache for logGamma
+ private static $logGammaCacheResult = 0.0;
+
+ private static $logGammaCacheX = 0.0;
+
+ /**
+ * logGamma function.
+ *
+ * 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.
+ *
+ *
+ * @version 1.1
+ *
+ * @author Jaco van Kooten
+ *
+ * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
+ */
+ public static function logGamma(float $x): float
+ {
+ if ($x == self::$logGammaCacheX) {
+ return self::$logGammaCacheResult;
+ }
+
+ $y = $x;
+ if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
+ if ($y <= self::EPS) {
+ $res = -log($y);
+ } elseif ($y <= 1.5) {
+ $res = self::logGamma1($y);
+ } elseif ($y <= 4.0) {
+ $res = self::logGamma2($y);
+ } elseif ($y <= 12.0) {
+ $res = self::logGamma3($y);
+ } else {
+ $res = self::logGamma4($y);
+ }
+ } else {
+ // --------------------------
+ // Return for bad arguments
+ // --------------------------
+ $res = self::MAX_VALUE;
+ }
+
+ // ------------------------------
+ // Final adjustments and return
+ // ------------------------------
+ self::$logGammaCacheX = $x;
+ self::$logGammaCacheResult = $res;
+
+ return $res;
+ }
+
+ private static function logGamma1(float $y)
+ {
+ // ---------------------
+ // EPS .LT. X .LE. 1.5
+ // ---------------------
+ if ($y < self::PNT68) {
+ $corr = -log($y);
+ $xm1 = $y;
+ } else {
+ $corr = 0.0;
+ $xm1 = $y - 1.0;
+ }
+
+ $xden = 1.0;
+ $xnum = 0.0;
+ if ($y <= 0.5 || $y >= self::PNT68) {
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm1 + self::LG_P1[$i];
+ $xden = $xden * $xm1 + self::LG_Q1[$i];
+ }
+
+ return $corr + $xm1 * (self::LG_D1 + $xm1 * ($xnum / $xden));
+ }
+
+ $xm2 = $y - 1.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + self::LG_P2[$i];
+ $xden = $xden * $xm2 + self::LG_Q2[$i];
+ }
+
+ return $corr + $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
+ }
+
+ private static function logGamma2(float $y)
+ {
+ // ---------------------
+ // 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 + self::LG_P2[$i];
+ $xden = $xden * $xm2 + self::LG_Q2[$i];
+ }
+
+ return $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
+ }
+
+ protected static function logGamma3(float $y)
+ {
+ // ----------------------
+ // 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 + self::LG_P4[$i];
+ $xden = $xden * $xm4 + self::LG_Q4[$i];
+ }
+
+ return self::LG_D4 + $xm4 * ($xnum / $xden);
+ }
+
+ protected static function logGamma4(float $y)
+ {
+ // ---------------------------------
+ // Evaluate for argument .GE. 12.0
+ // ---------------------------------
+ $res = 0.0;
+ if ($y <= self::LG_FRTBIG) {
+ $res = self::LG_C[6];
+ $ysq = $y * $y;
+ for ($i = 0; $i < 6; ++$i) {
+ $res = $res / $ysq + self::LG_C[$i];
+ }
+ $res /= $y;
+ $corr = log($y);
+ $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
+ $res += $y * ($corr - 1.0);
+ }
+
+ return $res;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
new file mode 100644
index 0000000..b3ad69d
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
@@ -0,0 +1,76 @@
+getMessage();
+ }
+
+ if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
+ return ExcelError::NAN();
+ }
+ if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
+ return ExcelError::NAN();
+ }
+ if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
+ return ExcelError::NAN();
+ }
+
+ $successesPopulationAndSample = (float) Combinations::withoutRepetition($populationSuccesses, $sampleSuccesses);
+ $numbersPopulationAndSample = (float) Combinations::withoutRepetition($populationNumber, $sampleNumber);
+ $adjustedPopulationAndSample = (float) Combinations::withoutRepetition(
+ $populationNumber - $populationSuccesses,
+ $sampleNumber - $sampleSuccesses
+ );
+
+ return $successesPopulationAndSample * $adjustedPopulationAndSample / $numbersPopulationAndSample;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
new file mode 100644
index 0000000..d572d23
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
@@ -0,0 +1,139 @@
+getMessage();
+ }
+
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ return StandardNormal::cumulative((log($value) - $mean) / $stdDev);
+ }
+
+ /**
+ * LOGNORM.DIST.
+ *
+ * Returns the lognormal distribution of x, where ln(x) is normally distributed
+ * with parameters mean and standard_dev.
+ *
+ * @param mixed $value Float value for which we want the probability
+ * Or can be an array of values
+ * @param mixed $mean Mean value as a float
+ * Or can be an array of values
+ * @param mixed $stdDev Standard Deviation as a float
+ * Or can be an array of values
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function distribution($value, $mean, $stdDev, $cumulative = false)
+ {
+ if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative === true) {
+ return StandardNormal::distribution((log($value) - $mean) / $stdDev, true);
+ }
+
+ return (1 / (sqrt(2 * M_PI) * $stdDev * $value)) *
+ exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
+ }
+
+ /**
+ * LOGINV.
+ *
+ * Returns the inverse of the lognormal cumulative distribution
+ *
+ * @param mixed $probability Float probability for which we want the value
+ * Or can be an array of values
+ * @param mixed $mean Mean Value as a float
+ * Or can be an array of values
+ * @param mixed $stdDev Standard Deviation as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ *
+ * @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 inverse($probability, $mean, $stdDev)
+ {
+ if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($stdDev <= 0) {
+ return ExcelError::NAN();
+ }
+ /** @var float */
+ $inverse = StandardNormal::inverse($probability);
+
+ return exp($mean + $stdDev * $inverse);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
new file mode 100644
index 0000000..b994864
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
@@ -0,0 +1,63 @@
+callback = $callback;
+ }
+
+ public function execute(float $probability)
+ {
+ $xLo = 100;
+ $xHi = 0;
+
+ $dx = 1;
+ $x = $xNew = 1;
+ $i = 0;
+
+ while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = call_user_func($this->callback, $x);
+ $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 == self::MAX_ITERATIONS) {
+ return ExcelError::NA();
+ }
+
+ return $x;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
new file mode 100644
index 0000000..67533c4
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
@@ -0,0 +1,180 @@
+getMessage();
+ }
+
+ if ($stdDev < 0) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ return 0.5 * (1 + Engineering\Erf::erfValue(($value - $mean) / ($stdDev * sqrt(2))));
+ }
+
+ return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev))));
+ }
+
+ /**
+ * NORMINV.
+ *
+ * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
+ *
+ * @param mixed $probability Float probability for which we want the value
+ * Or can be an array of values
+ * @param mixed $mean Mean Value as a float
+ * Or can be an array of values
+ * @param mixed $stdDev Standard Deviation as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse($probability, $mean, $stdDev)
+ {
+ if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($stdDev < 0) {
+ return ExcelError::NAN();
+ }
+
+ return (self::inverseNcdf($probability) * $stdDev) + $mean;
+ }
+
+ /*
+ * inverse_ncdf.php
+ * -------------------
+ * begin : Friday, January 16, 2004
+ * copyright : (C) 2004 Michael Nickerson
+ * email : nickersonm@yahoo.com
+ *
+ */
+ private static function inverseNcdf($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 = [
+ 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 = [
+ 1 => -5.447609879822406e+01,
+ 2 => 1.615858368580409e+02,
+ 3 => -1.556989798598866e+02,
+ 4 => 6.680131188771972e+01,
+ 5 => -1.328068155288572e+01,
+ ];
+
+ static $c = [
+ 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 = [
+ 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_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);
+ }
+
+ // 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);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
new file mode 100644
index 0000000..041c34a
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
@@ -0,0 +1,66 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($mean < 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ $summer = 0;
+ $floor = floor($value);
+ for ($i = 0; $i <= $floor; ++$i) {
+ /** @var float */
+ $fact = MathTrig\Factorial::fact($i);
+ $summer += $mean ** $i / $fact;
+ }
+
+ return exp(0 - $mean) * $summer;
+ }
+ /** @var float */
+ $fact = MathTrig\Factorial::fact($value);
+
+ return (exp(0 - $mean) * $mean ** $value) / $fact;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
new file mode 100644
index 0000000..a655fa7
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
@@ -0,0 +1,151 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateDistribution($value, $degrees, $tails);
+ }
+
+ /**
+ * TINV.
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability for the function
+ * Or can be an array of values
+ * @param mixed $degrees Integer value for degrees of freedom
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse($probability, $degrees)
+ {
+ if (is_array($probability) || is_array($degrees)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $callback = function ($value) use ($degrees) {
+ return self::distribution($value, $degrees, 2);
+ };
+
+ $newtonRaphson = new NewtonRaphson($callback);
+
+ return $newtonRaphson->execute($probability);
+ }
+
+ /**
+ * @return float
+ */
+ private static function calculateDistribution(float $value, int $degrees, int $tails)
+ {
+ // 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);
+
+ 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 = Functions::M_2DIVPI * ($tsum + $ttheta);
+ }
+
+ $tValue = 0.5 * (1 + $tsum);
+ if ($tails == 1) {
+ return 1 - abs($tValue);
+ }
+
+ return 1 - abs((1 - $tValue) - $tValue);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
new file mode 100644
index 0000000..51392c4
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
@@ -0,0 +1,57 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ return 1 - exp(0 - ($value / $beta) ** $alpha);
+ }
+
+ return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
new file mode 100644
index 0000000..bd17b06
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
@@ -0,0 +1,17 @@
+ $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * MAXA.
+ *
+ * Returns the greatest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MAXA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float
+ */
+ public static function maxA(...$args)
+ {
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ foreach ($aArgs as $arg) {
+ if (ErrorValue::isError($arg)) {
+ $returnValue = $arg;
+
+ break;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ if (($returnValue === null) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Minimum.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Minimum.php
new file mode 100644
index 0000000..0dbbf3a
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Minimum.php
@@ -0,0 +1,89 @@
+getMessage();
+ }
+
+ if (($entry < 0) || ($entry > 1)) {
+ return ExcelError::NAN();
+ }
+
+ $mArgs = self::percentileFilterValues($aArgs);
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs);
+ $count = Counts::COUNT($mArgs);
+ $index = $entry * ($count - 1);
+ $iBase = floor($index);
+ if ($index == $iBase) {
+ return $mArgs[$index];
+ }
+ $iNext = $iBase + 1;
+ $iProportion = $index - $iBase;
+
+ return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * PERCENTRANK.
+ *
+ * Returns the rank of a value in a data set as a percentage of the data set.
+ * Note that the returned rank is simply rounded to the appropriate significant digits,
+ * rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return
+ * 0.667 rather than 0.666
+ *
+ * @param mixed $valueSet An array of (float) values, or a reference to, a list of numbers
+ * @param mixed $value The number whose rank you want to find
+ * @param mixed $significance The (integer) number of significant digits for the returned percentage value
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function PERCENTRANK($valueSet, $value, $significance = 3)
+ {
+ $valueSet = Functions::flattenArray($valueSet);
+ $value = Functions::flattenSingleValue($value);
+ $significance = ($significance === null) ? 3 : Functions::flattenSingleValue($significance);
+
+ try {
+ $value = StatisticalValidations::validateFloat($value);
+ $significance = StatisticalValidations::validateInt($significance);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $valueSet = self::rankFilterValues($valueSet);
+ $valueCount = count($valueSet);
+ if ($valueCount == 0) {
+ return ExcelError::NA();
+ }
+ sort($valueSet, SORT_NUMERIC);
+
+ $valueAdjustor = $valueCount - 1;
+ if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
+ return ExcelError::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);
+ }
+
+ /**
+ * QUARTILE.
+ *
+ * Returns the quartile of a data set.
+ *
+ * Excel Function:
+ * QUARTILE(value1[,value2[, ...]],entry)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function QUARTILE(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+ $entry = array_pop($aArgs);
+
+ try {
+ $entry = StatisticalValidations::validateFloat($entry);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $entry = floor($entry);
+ $entry /= 4;
+ if (($entry < 0) || ($entry > 1)) {
+ return ExcelError::NAN();
+ }
+
+ return self::PERCENTILE($aArgs, $entry);
+ }
+
+ /**
+ * RANK.
+ *
+ * Returns the rank of a number in a list of numbers.
+ *
+ * @param mixed $value The number whose rank you want to find
+ * @param mixed $valueSet An array of float values, or a reference to, a list of numbers
+ * @param mixed $order Order to sort the values in the value set
+ *
+ * @return float|string The result, or a string containing an error (0 = Descending, 1 = Ascending)
+ */
+ public static function RANK($value, $valueSet, $order = self::RANK_SORT_DESCENDING)
+ {
+ $value = Functions::flattenSingleValue($value);
+ $valueSet = Functions::flattenArray($valueSet);
+ $order = ($order === null) ? self::RANK_SORT_DESCENDING : Functions::flattenSingleValue($order);
+
+ try {
+ $value = StatisticalValidations::validateFloat($value);
+ $order = StatisticalValidations::validateInt($order);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $valueSet = self::rankFilterValues($valueSet);
+ if ($order === self::RANK_SORT_DESCENDING) {
+ rsort($valueSet, SORT_NUMERIC);
+ } else {
+ sort($valueSet, SORT_NUMERIC);
+ }
+
+ $pos = array_search($value, $valueSet);
+ if ($pos === false) {
+ return ExcelError::NA();
+ }
+
+ return ++$pos;
+ }
+
+ protected static function percentileFilterValues(array $dataSet)
+ {
+ return array_filter(
+ $dataSet,
+ function ($value): bool {
+ return is_numeric($value) && !is_string($value);
+ }
+ );
+ }
+
+ protected static function rankFilterValues(array $dataSet)
+ {
+ return array_filter(
+ $dataSet,
+ function ($value): bool {
+ return is_numeric($value);
+ }
+ );
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Permutations.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Permutations.php
new file mode 100644
index 0000000..5d9d304
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Permutations.php
@@ -0,0 +1,90 @@
+getMessage();
+ }
+
+ if ($numObjs < $numInSet) {
+ return ExcelError::NAN();
+ }
+ $result = round(MathTrig\Factorial::fact($numObjs) / MathTrig\Factorial::fact($numObjs - $numInSet));
+
+ return IntOrFloat::evaluate($result);
+ }
+
+ /**
+ * PERMUTATIONA.
+ *
+ * Returns the number of permutations for a given number of objects (with repetitions)
+ * that can be selected from the total objects.
+ *
+ * @param mixed $numObjs Integer number of different objects
+ * Or can be an array of values
+ * @param mixed $numInSet Integer number of objects in each permutation
+ * Or can be an array of values
+ *
+ * @return array|float|int|string Number of permutations, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function PERMUTATIONA($numObjs, $numInSet)
+ {
+ if (is_array($numObjs) || is_array($numInSet)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
+ }
+
+ try {
+ $numObjs = StatisticalValidations::validateInt($numObjs);
+ $numInSet = StatisticalValidations::validateInt($numInSet);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($numObjs < 0 || $numInSet < 0) {
+ return ExcelError::NAN();
+ }
+
+ $result = $numObjs ** $numInSet;
+
+ return IntOrFloat::evaluate($result);
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Size.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Size.php
new file mode 100644
index 0000000..2eef5fc
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/Size.php
@@ -0,0 +1,97 @@
+= $count) {
+ return ExcelError::NAN();
+ }
+ rsort($mArgs);
+
+ return $mArgs[$entry];
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * 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)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function small(...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $entry = (int) floor($entry);
+
+ $mArgs = self::filter($aArgs);
+ $count = Counts::COUNT($mArgs);
+ --$entry;
+ if ($count === 0 || $entry < 0 || $entry >= $count) {
+ return ExcelError::NAN();
+ }
+ sort($mArgs);
+
+ return $mArgs[$entry];
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * @param mixed[] $args Data values
+ */
+ protected static function filter(array $args): array
+ {
+ $mArgs = [];
+
+ foreach ($args as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ return $mArgs;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
new file mode 100644
index 0000000..af27120
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
@@ -0,0 +1,95 @@
+getMessage();
+ }
+
+ if ($stdDev <= 0) {
+ return ExcelError::NAN();
+ }
+
+ return ($value - $mean) / $stdDev;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
new file mode 100644
index 0000000..e23a52c
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
@@ -0,0 +1,45 @@
+ $value) {
+ if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
+ unset($array1[$key], $array2[$key]);
+ }
+ }
+ }
+
+ private static function checkTrendArrays(&$array1, &$array2): void
+ {
+ if (!is_array($array1)) {
+ $array1 = [$array1];
+ }
+ if (!is_array($array2)) {
+ $array2 = [$array2];
+ }
+
+ $array1 = Functions::flattenArray($array1);
+ $array2 = Functions::flattenArray($array2);
+
+ self::filterTrendValues($array1, $array2);
+ self::filterTrendValues($array2, $array1);
+
+ // Reset the array indexes
+ $array1 = array_merge($array1);
+ $array2 = array_merge($array2);
+ }
+
+ protected static function validateTrendArrays(array $yValues, array $xValues): void
+ {
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
+ throw new Exception(ExcelError::NA());
+ } elseif ($yValueCount === 1) {
+ throw new Exception(ExcelError::DIV0());
+ }
+ }
+
+ /**
+ * CORREL.
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param null|mixed $xValues array of mixed Data Series X
+ *
+ * @return float|string
+ */
+ public static function CORREL($yValues, $xValues = null)
+ {
+ if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
+ return ExcelError::VALUE();
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getCorrelation();
+ }
+
+ /**
+ * COVAR.
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param mixed $xValues array of mixed Data Series X
+ *
+ * @return float|string
+ */
+ public static function COVAR($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getCovariance();
+ }
+
+ /**
+ * FORECAST.
+ *
+ * Calculates, or predicts, a future value by using existing values.
+ * The predicted value is a y-value for a given x-value.
+ *
+ * @param mixed $xValue Float value of X for which we want to find Y
+ * Or can be an array of values
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param mixed $xValues of mixed Data Series X
+ *
+ * @return array|bool|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function FORECAST($xValue, $yValues, $xValues)
+ {
+ if (is_array($xValue)) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $xValue, $yValues, $xValues);
+ }
+
+ try {
+ $xValue = StatisticalValidations::validateFloat($xValue);
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getValueOfYForX($xValue);
+ }
+
+ /**
+ * GROWTH.
+ *
+ * Returns values along a predicted exponential Trend
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ * @param mixed[] $newValues Values of X for which we want to find Y
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ *
+ * @return float[]
+ */
+ public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
+ {
+ $yValues = Functions::flattenArray($yValues);
+ $xValues = Functions::flattenArray($xValues);
+ $newValues = Functions::flattenArray($newValues);
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+
+ $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
+ if (empty($newValues)) {
+ $newValues = $bestFitExponential->getXValues();
+ }
+
+ $returnArray = [];
+ foreach ($newValues as $xValue) {
+ $returnArray[0][] = [$bestFitExponential->getValueOfYForX($xValue)];
+ }
+
+ return $returnArray;
+ }
+
+ /**
+ * INTERCEPT.
+ *
+ * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string
+ */
+ public static function INTERCEPT($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getIntersect();
+ }
+
+ /**
+ * 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 mixed[] $yValues Data Series Y
+ * @param null|mixed[] $xValues Data Series X
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
+ *
+ * @return array|int|string The result, or a string containing an error
+ */
+ public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
+ {
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+ $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
+ if ($xValues === null) {
+ $xValues = $yValues;
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
+
+ if ($stats === true) {
+ return [
+ [
+ $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect(),
+ ],
+ [
+ $bestFitLinear->getSlopeSE(),
+ ($const === false) ? ExcelError::NA() : $bestFitLinear->getIntersectSE(),
+ ],
+ [
+ $bestFitLinear->getGoodnessOfFit(),
+ $bestFitLinear->getStdevOfResiduals(),
+ ],
+ [
+ $bestFitLinear->getF(),
+ $bestFitLinear->getDFResiduals(),
+ ],
+ [
+ $bestFitLinear->getSSRegression(),
+ $bestFitLinear->getSSResiduals(),
+ ],
+ ];
+ }
+
+ return [
+ $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect(),
+ ];
+ }
+
+ /**
+ * LOGEST.
+ *
+ * Calculates an exponential curve that best fits the X and Y data series,
+ * and then returns an array that describes the line.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param null|mixed[] $xValues Data Series X
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
+ *
+ * @return array|int|string The result, or a string containing an error
+ */
+ public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
+ {
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+ $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
+ if ($xValues === null) {
+ $xValues = $yValues;
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ foreach ($yValues as $value) {
+ if ($value < 0.0) {
+ return ExcelError::NAN();
+ }
+ }
+
+ $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
+
+ if ($stats === true) {
+ return [
+ [
+ $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect(),
+ ],
+ [
+ $bestFitExponential->getSlopeSE(),
+ ($const === false) ? ExcelError::NA() : $bestFitExponential->getIntersectSE(),
+ ],
+ [
+ $bestFitExponential->getGoodnessOfFit(),
+ $bestFitExponential->getStdevOfResiduals(),
+ ],
+ [
+ $bestFitExponential->getF(),
+ $bestFitExponential->getDFResiduals(),
+ ],
+ [
+ $bestFitExponential->getSSRegression(),
+ $bestFitExponential->getSSResiduals(),
+ ],
+ ];
+ }
+
+ return [
+ $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect(),
+ ];
+ }
+
+ /**
+ * RSQ.
+ *
+ * Returns the square of the Pearson product moment correlation coefficient through data points
+ * in known_y's and known_x's.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function RSQ($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getGoodnessOfFit();
+ }
+
+ /**
+ * SLOPE.
+ *
+ * Returns the slope of the linear regression line through data points in known_y's and known_x's.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function SLOPE($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getSlope();
+ }
+
+ /**
+ * STEYX.
+ *
+ * Returns the standard error of the predicted y-value for each x in the regression.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string
+ */
+ public static function STEYX($yValues, $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getStdevOfResiduals();
+ }
+
+ /**
+ * TREND.
+ *
+ * Returns values along a linear Trend
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ * @param mixed[] $newValues Values of X for which we want to find Y
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ *
+ * @return float[]
+ */
+ public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
+ {
+ $yValues = Functions::flattenArray($yValues);
+ $xValues = Functions::flattenArray($xValues);
+ $newValues = Functions::flattenArray($newValues);
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
+ if (empty($newValues)) {
+ $newValues = $bestFitLinear->getXValues();
+ }
+
+ $returnArray = [];
+ foreach ($newValues as $xValue) {
+ $returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
+ }
+
+ return $returnArray;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
new file mode 100644
index 0000000..e533467
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
@@ -0,0 +1,28 @@
+ 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARA.
+ *
+ * Estimates variance based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARA(...$args)
+ {
+ $returnValue = ExcelError::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) && (Functions::isValue($k))) {
+ return ExcelError::VALUE();
+ } elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARP.
+ *
+ * Calculates variance based on the entire population
+ *
+ * Excel Function:
+ * VARP(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARP(...$args)
+ {
+ // Return value
+ $returnValue = ExcelError::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ $arg = self::datatypeAdjustmentBooleans($arg);
+
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * $aCount);
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARPA.
+ *
+ * Calculates variance based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARPA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARPA(...$args)
+ {
+ $returnValue = ExcelError::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) && (Functions::isValue($k))) {
+ return ExcelError::VALUE();
+ } elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * $aCount);
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData.php
old mode 100755
new mode 100644
index e30b2ff..97f4629
--- a/PhpOffice/PhpSpreadsheet/Calculation/TextData.php
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData.php
@@ -2,140 +2,89 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
-use PhpOffice\PhpSpreadsheet\Shared\Date;
-use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
-use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use DateTimeInterface;
+/**
+ * @deprecated 1.18.0
+ */
class TextData
{
- private static $invalidChars;
-
- private static function unicodeToOrd($character)
- {
- return unpack('V', iconv('UTF-8', 'UCS-4LE', $character))[1];
- }
-
/**
* CHARACTER.
*
+ * @deprecated 1.18.0
+ * Use the character() method in the TextData\CharacterConvert class instead
+ * @see TextData\CharacterConvert::character()
+ *
* @param string $character Value
*
- * @return string
+ * @return array|string
*/
public static function CHARACTER($character)
{
- $character = Functions::flattenSingleValue($character);
-
- if ((!is_numeric($character)) || ($character < 0)) {
- return Functions::VALUE();
- }
-
- if (function_exists('iconv')) {
- return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
- }
-
- return mb_convert_encoding('' . (int) $character . ';', 'UTF-8', 'HTML-ENTITIES');
+ return TextData\CharacterConvert::character($character);
}
/**
* TRIMNONPRINTABLE.
*
+ * @deprecated 1.18.0
+ * Use the nonPrintable() method in the TextData\Trim class instead
+ * @see TextData\Trim::nonPrintable()
+ *
* @param mixed $stringValue Value to check
*
- * @return string
+ * @return null|array|string
*/
public static function TRIMNONPRINTABLE($stringValue = '')
{
- $stringValue = Functions::flattenSingleValue($stringValue);
-
- if (is_bool($stringValue)) {
- return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- 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;
+ return TextData\Trim::nonPrintable($stringValue);
}
/**
* TRIMSPACES.
*
+ * @deprecated 1.18.0
+ * Use the spaces() method in the TextData\Trim class instead
+ * @see TextData\Trim::spaces()
+ *
* @param mixed $stringValue Value to check
*
- * @return string
+ * @return array|string
*/
public static function TRIMSPACES($stringValue = '')
{
- $stringValue = Functions::flattenSingleValue($stringValue);
- if (is_bool($stringValue)) {
- return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (is_string($stringValue) || is_numeric($stringValue)) {
- return trim(preg_replace('/ +/', ' ', trim($stringValue, ' ')), ' ');
- }
-
- return null;
+ return TextData\Trim::spaces($stringValue);
}
/**
* ASCIICODE.
*
- * @param string $characters Value
+ * @deprecated 1.18.0
+ * Use the code() method in the TextData\CharacterConvert class instead
+ * @see TextData\CharacterConvert::code()
*
- * @return int
+ * @param array|string $characters Value
+ *
+ * @return array|int|string A string if arguments are invalid
*/
public static function ASCIICODE($characters)
{
- if (($characters === null) || ($characters === '')) {
- return Functions::VALUE();
- }
- $characters = Functions::flattenSingleValue($characters);
- if (is_bool($characters)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $characters = (int) $characters;
- } else {
- $characters = ($characters) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
- }
-
- $character = $characters;
- if (mb_strlen($characters, 'UTF-8') > 1) {
- $character = mb_substr($characters, 0, 1, 'UTF-8');
- }
-
- return self::unicodeToOrd($character);
+ return TextData\CharacterConvert::code($characters);
}
/**
* CONCATENATE.
*
+ * @deprecated 1.18.0
+ * Use the CONCATENATE() method in the TextData\Concatenate class instead
+ * @see TextData\Concatenate::CONCATENATE()
+ *
* @return string
*/
public static function CONCATENATE(...$args)
{
- $returnValue = '';
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- if (is_bool($arg)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $arg = (int) $arg;
- } else {
- $arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
- }
- $returnValue .= $arg;
- }
-
- return $returnValue;
+ return TextData\Concatenate::CONCATENATE(...$args);
}
/**
@@ -144,254 +93,160 @@ class TextData
* 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)..
*
+ * @deprecated 1.18.0
+ * Use the DOLLAR() method in the TextData\Format class instead
+ * @see TextData\Format::DOLLAR()
+ *
* @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
+ * @return array|string
*/
public static function DOLLAR($value = 0, $decimals = 2)
{
- $value = Functions::flattenSingleValue($value);
- $decimals = $decimals === null ? 0 : Functions::flattenSingleValue($decimals);
-
- // Validate parameters
- if (!is_numeric($value) || !is_numeric($decimals)) {
- return Functions::NAN();
- }
- $decimals = floor($decimals);
-
- $mask = '$#,##0';
- if ($decimals > 0) {
- $mask .= '.' . str_repeat('0', $decimals);
- } else {
- $round = pow(10, abs($decimals));
- if ($value < 0) {
- $round = 0 - $round;
- }
- $value = MathTrig::MROUND($value, $round);
- }
-
- return NumberFormat::toFormattedString($value, $mask);
+ return TextData\Format::DOLLAR($value, $decimals);
}
/**
- * SEARCHSENSITIVE.
+ * FIND.
*
- * @param string $needle The string to look for
- * @param string $haystack The string in which to look
- * @param int $offset Offset within $haystack
+ * @deprecated 1.18.0
+ * Use the sensitive() method in the TextData\Search class instead
+ * @see TextData\Search::sensitive()
*
- * @return string
+ * @param array|string $needle The string to look for
+ * @param array|string $haystack The string in which to look
+ * @param array|int $offset Offset within $haystack
+ *
+ * @return array|int|string
*/
public static function SEARCHSENSITIVE($needle, $haystack, $offset = 1)
{
- $needle = Functions::flattenSingleValue($needle);
- $haystack = Functions::flattenSingleValue($haystack);
- $offset = Functions::flattenSingleValue($offset);
-
- if (!is_bool($needle)) {
- if (is_bool($haystack)) {
- $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
- if (StringHelper::countCharacters($needle) == 0) {
- return $offset;
- }
-
- $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
- if ($pos !== false) {
- return ++$pos;
- }
- }
- }
-
- return Functions::VALUE();
+ return TextData\Search::sensitive($needle, $haystack, $offset);
}
/**
- * SEARCHINSENSITIVE.
+ * SEARCH.
*
- * @param string $needle The string to look for
- * @param string $haystack The string in which to look
- * @param int $offset Offset within $haystack
+ * @deprecated 1.18.0
+ * Use the insensitive() method in the TextData\Search class instead
+ * @see TextData\Search::insensitive()
*
- * @return string
+ * @param array|string $needle The string to look for
+ * @param array|string $haystack The string in which to look
+ * @param array|int $offset Offset within $haystack
+ *
+ * @return array|int|string
*/
public static function SEARCHINSENSITIVE($needle, $haystack, $offset = 1)
{
- $needle = Functions::flattenSingleValue($needle);
- $haystack = Functions::flattenSingleValue($haystack);
- $offset = Functions::flattenSingleValue($offset);
-
- if (!is_bool($needle)) {
- if (is_bool($haystack)) {
- $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
- if (StringHelper::countCharacters($needle) == 0) {
- return $offset;
- }
-
- $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
- if ($pos !== false) {
- return ++$pos;
- }
- }
- }
-
- return Functions::VALUE();
+ return TextData\Search::insensitive($needle, $haystack, $offset);
}
/**
* FIXEDFORMAT.
*
+ * @deprecated 1.18.0
+ * Use the FIXEDFORMAT() method in the TextData\Format class instead
+ * @see TextData\Format::FIXEDFORMAT()
+ *
* @param mixed $value Value to check
* @param int $decimals
* @param bool $no_commas
*
- * @return string
+ * @return array|string
*/
public static function FIXEDFORMAT($value, $decimals = 2, $no_commas = false)
{
- $value = Functions::flattenSingleValue($value);
- $decimals = Functions::flattenSingleValue($decimals);
- $no_commas = Functions::flattenSingleValue($no_commas);
-
- // Validate parameters
- if (!is_numeric($value) || !is_numeric($decimals)) {
- return Functions::NAN();
- }
- $decimals = floor($decimals);
-
- $valueResult = round($value, $decimals);
- if ($decimals < 0) {
- $decimals = 0;
- }
- if (!$no_commas) {
- $valueResult = number_format($valueResult, $decimals);
- }
-
- return (string) $valueResult;
+ return TextData\Format::FIXEDFORMAT($value, $decimals, $no_commas);
}
/**
* LEFT.
*
- * @param string $value Value
- * @param int $chars Number of characters
+ * @deprecated 1.18.0
+ * Use the left() method in the TextData\Extract class instead
+ * @see TextData\Extract::left()
*
- * @return string
+ * @param array|string $value Value
+ * @param array|int $chars Number of characters
+ *
+ * @return array|string
*/
public static function LEFT($value = '', $chars = 1)
{
- $value = Functions::flattenSingleValue($value);
- $chars = Functions::flattenSingleValue($chars);
-
- if ($chars < 0) {
- return Functions::VALUE();
- }
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return mb_substr($value, 0, $chars, 'UTF-8');
+ return TextData\Extract::left($value, $chars);
}
/**
* MID.
*
- * @param string $value Value
- * @param int $start Start character
- * @param int $chars Number of characters
+ * @deprecated 1.18.0
+ * Use the mid() method in the TextData\Extract class instead
+ * @see TextData\Extract::mid()
*
- * @return string
+ * @param array|string $value Value
+ * @param array|int $start Start character
+ * @param array|int $chars Number of characters
+ *
+ * @return array|string
*/
public static function MID($value = '', $start = 1, $chars = null)
{
- $value = Functions::flattenSingleValue($value);
- $start = Functions::flattenSingleValue($start);
- $chars = Functions::flattenSingleValue($chars);
-
- if (($start < 1) || ($chars < 0)) {
- return Functions::VALUE();
- }
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- if (empty($chars)) {
- return '';
- }
-
- return mb_substr($value, --$start, $chars, 'UTF-8');
+ return TextData\Extract::mid($value, $start, $chars);
}
/**
* RIGHT.
*
- * @param string $value Value
- * @param int $chars Number of characters
+ * @deprecated 1.18.0
+ * Use the right() method in the TextData\Extract class instead
+ * @see TextData\Extract::right()
*
- * @return string
+ * @param array|string $value Value
+ * @param array|int $chars Number of characters
+ *
+ * @return array|string
*/
public static function RIGHT($value = '', $chars = 1)
{
- $value = Functions::flattenSingleValue($value);
- $chars = Functions::flattenSingleValue($chars);
-
- if ($chars < 0) {
- return Functions::VALUE();
- }
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
+ return TextData\Extract::right($value, $chars);
}
/**
* STRINGLENGTH.
*
+ * @deprecated 1.18.0
+ * Use the length() method in the TextData\Text class instead
+ * @see TextData\Text::length()
+ *
* @param string $value Value
*
- * @return int
+ * @return array|int
*/
public static function STRINGLENGTH($value = '')
{
- $value = Functions::flattenSingleValue($value);
-
- if (is_bool($value)) {
- $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return mb_strlen($value, 'UTF-8');
+ return TextData\Text::length($value);
}
/**
* LOWERCASE.
*
- * Converts a string value to upper case.
+ * Converts a string value to lower case.
*
- * @param string $mixedCaseString
+ * @deprecated 1.18.0
+ * Use the lower() method in the TextData\CaseConvert class instead
+ * @see TextData\CaseConvert::lower()
*
- * @return string
+ * @param array|string $mixedCaseString
+ *
+ * @return array|string
*/
public static function LOWERCASE($mixedCaseString)
{
- $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
-
- if (is_bool($mixedCaseString)) {
- $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return StringHelper::strToLower($mixedCaseString);
+ return TextData\CaseConvert::lower($mixedCaseString);
}
/**
@@ -399,229 +254,140 @@ class TextData
*
* Converts a string value to upper case.
*
+ * @deprecated 1.18.0
+ * Use the upper() method in the TextData\CaseConvert class instead
+ * @see TextData\CaseConvert::upper()
+ *
* @param string $mixedCaseString
*
- * @return string
+ * @return array|string
*/
public static function UPPERCASE($mixedCaseString)
{
- $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
-
- if (is_bool($mixedCaseString)) {
- $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return StringHelper::strToUpper($mixedCaseString);
+ return TextData\CaseConvert::upper($mixedCaseString);
}
/**
* PROPERCASE.
*
- * Converts a string value to upper case.
+ * Converts a string value to proper/title case.
*
- * @param string $mixedCaseString
+ * @deprecated 1.18.0
+ * Use the proper() method in the TextData\CaseConvert class instead
+ * @see TextData\CaseConvert::proper()
*
- * @return string
+ * @param array|string $mixedCaseString
+ *
+ * @return array|string
*/
public static function PROPERCASE($mixedCaseString)
{
- $mixedCaseString = Functions::flattenSingleValue($mixedCaseString);
-
- if (is_bool($mixedCaseString)) {
- $mixedCaseString = ($mixedCaseString) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
-
- return StringHelper::strToTitle($mixedCaseString);
+ return TextData\CaseConvert::proper($mixedCaseString);
}
/**
* REPLACE.
*
+ * @deprecated 1.18.0
+ * Use the replace() method in the TextData\Replace class instead
+ * @see TextData\Replace::replace()
+ *
* @param string $oldText String to modify
* @param int $start Start character
* @param int $chars Number of characters
* @param string $newText String to replace in defined position
*
- * @return string
+ * @return array|string
*/
public static function REPLACE($oldText, $start, $chars, $newText)
{
- $oldText = Functions::flattenSingleValue($oldText);
- $start = Functions::flattenSingleValue($start);
- $chars = Functions::flattenSingleValue($chars);
- $newText = Functions::flattenSingleValue($newText);
-
- $left = self::LEFT($oldText, $start - 1);
- $right = self::RIGHT($oldText, self::STRINGLENGTH($oldText) - ($start + $chars) + 1);
-
- return $left . $newText . $right;
+ return TextData\Replace::replace($oldText, $start, $chars, $newText);
}
/**
* SUBSTITUTE.
*
+ * @deprecated 1.18.0
+ * Use the substitute() method in the TextData\Replace class instead
+ * @see TextData\Replace::substitute()
+ *
* @param string $text Value
* @param string $fromText From Value
* @param string $toText To Value
* @param int $instance Instance Number
*
- * @return string
+ * @return array|string
*/
public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0)
{
- $text = Functions::flattenSingleValue($text);
- $fromText = Functions::flattenSingleValue($fromText);
- $toText = Functions::flattenSingleValue($toText);
- $instance = floor(Functions::flattenSingleValue($instance));
-
- if ($instance == 0) {
- return str_replace($fromText, $toText, $text);
- }
-
- $pos = -1;
- while ($instance > 0) {
- $pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
- if ($pos === false) {
- break;
- }
- --$instance;
- }
-
- if ($pos !== false) {
- return self::REPLACE($text, ++$pos, mb_strlen($fromText, 'UTF-8'), $toText);
- }
-
- return $text;
+ return TextData\Replace::substitute($text, $fromText, $toText, $instance);
}
/**
* RETURNSTRING.
*
+ * @deprecated 1.18.0
+ * Use the test() method in the TextData\Text class instead
+ * @see TextData\Text::test()
+ *
* @param mixed $testValue Value to check
*
- * @return null|string
+ * @return null|array|string
*/
public static function RETURNSTRING($testValue = '')
{
- $testValue = Functions::flattenSingleValue($testValue);
-
- if (is_string($testValue)) {
- return $testValue;
- }
-
- return null;
+ return TextData\Text::test($testValue);
}
/**
* TEXTFORMAT.
*
+ * @deprecated 1.18.0
+ * Use the TEXTFORMAT() method in the TextData\Format class instead
+ * @see TextData\Format::TEXTFORMAT()
+ *
* @param mixed $value Value to check
* @param string $format Format mask to use
*
- * @return string
+ * @return array|string
*/
public static function TEXTFORMAT($value, $format)
{
- $value = Functions::flattenSingleValue($value);
- $format = Functions::flattenSingleValue($format);
-
- if ((is_string($value)) && (!is_numeric($value)) && Date::isDateTimeFormatCode($format)) {
- $value = DateTime::DATEVALUE($value);
- }
-
- return (string) NumberFormat::toFormattedString($value, $format);
+ return TextData\Format::TEXTFORMAT($value, $format);
}
/**
* VALUE.
*
+ * @deprecated 1.18.0
+ * Use the VALUE() method in the TextData\Format class instead
+ * @see TextData\Format::VALUE()
+ *
* @param mixed $value Value to check
*
- * @return bool
+ * @return array|DateTimeInterface|float|int|string A string if arguments are invalid
*/
public static function VALUE($value = '')
{
- $value = Functions::flattenSingleValue($value);
-
- if (!is_numeric($value)) {
- $numberValue = str_replace(
- StringHelper::getThousandsSeparator(),
- '',
- trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
- );
- if (is_numeric($numberValue)) {
- return (float) $numberValue;
- }
-
- $dateSetting = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
-
- if (strpos($value, ':') !== false) {
- $timeValue = DateTime::TIMEVALUE($value);
- if ($timeValue !== Functions::VALUE()) {
- Functions::setReturnDateType($dateSetting);
-
- return $timeValue;
- }
- }
- $dateValue = DateTime::DATEVALUE($value);
- if ($dateValue !== Functions::VALUE()) {
- Functions::setReturnDateType($dateSetting);
-
- return $dateValue;
- }
- Functions::setReturnDateType($dateSetting);
-
- return Functions::VALUE();
- }
-
- return (float) $value;
+ return TextData\Format::VALUE($value);
}
/**
* NUMBERVALUE.
*
+ * @deprecated 1.18.0
+ * Use the NUMBERVALUE() method in the TextData\Format class instead
+ * @see TextData\Format::NUMBERVALUE()
+ *
* @param mixed $value Value to check
* @param string $decimalSeparator decimal separator, defaults to locale defined value
* @param string $groupSeparator group/thosands separator, defaults to locale defined value
*
- * @return float|string
+ * @return array|float|string
*/
public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
{
- $value = Functions::flattenSingleValue($value);
- $decimalSeparator = Functions::flattenSingleValue($decimalSeparator);
- $groupSeparator = Functions::flattenSingleValue($groupSeparator);
-
- if (!is_numeric($value)) {
- $decimalSeparator = empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : $decimalSeparator;
- $groupSeparator = empty($groupSeparator) ? StringHelper::getThousandsSeparator() : $groupSeparator;
-
- $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
- if ($decimalPositions > 1) {
- return Functions::VALUE();
- }
- $decimalOffset = array_pop($matches[0])[1];
- if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
- return Functions::VALUE();
- }
-
- $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
-
- // Handle the special case of trailing % signs
- $percentageString = rtrim($value, '%');
- if (!is_numeric($percentageString)) {
- return Functions::VALUE();
- }
-
- $percentageAdjustment = strlen($value) - strlen($percentageString);
- if ($percentageAdjustment) {
- $value = (float) $percentageString;
- $value /= pow(10, $percentageAdjustment * 2);
- }
- }
-
- return (float) $value;
+ return TextData\Format::NUMBERVALUE($value, $decimalSeparator, $groupSeparator);
}
/**
@@ -629,44 +395,54 @@ class TextData
* EXACT is case-sensitive but ignores formatting differences.
* Use EXACT to test text being entered into a document.
*
- * @param $value1
- * @param $value2
+ * @deprecated 1.18.0
+ * Use the exact() method in the TextData\Text class instead
+ * @see TextData\Text::exact()
*
- * @return bool
+ * @param mixed $value1
+ * @param mixed $value2
+ *
+ * @return array|bool
*/
public static function EXACT($value1, $value2)
{
- $value1 = Functions::flattenSingleValue($value1);
- $value2 = Functions::flattenSingleValue($value2);
-
- return (string) $value2 === (string) $value1;
+ return TextData\Text::exact($value1, $value2);
}
/**
* TEXTJOIN.
*
+ * @deprecated 1.18.0
+ * Use the TEXTJOIN() method in the TextData\Concatenate class instead
+ * @see TextData\Concatenate::TEXTJOIN()
+ *
* @param mixed $delimiter
* @param mixed $ignoreEmpty
* @param mixed $args
*
- * @return string
+ * @return array|string
*/
public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args)
{
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $key => &$arg) {
- if ($ignoreEmpty && trim($arg) == '') {
- unset($aArgs[$key]);
- } elseif (is_bool($arg)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $arg = (int) $arg;
- } else {
- $arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
- }
- }
+ return TextData\Concatenate::TEXTJOIN($delimiter, $ignoreEmpty, ...$args);
+ }
- return implode($delimiter, $aArgs);
+ /**
+ * REPT.
+ *
+ * Returns the result of builtin function repeat after validating args.
+ *
+ * @deprecated 1.18.0
+ * Use the builtinREPT() method in the TextData\Concatenate class instead
+ * @see TextData\Concatenate::builtinREPT()
+ *
+ * @param array|string $str Should be numeric
+ * @param mixed $number Should be int
+ *
+ * @return array|string
+ */
+ public static function builtinREPT($str, $number)
+ {
+ return TextData\Concatenate::builtinREPT($str, $number);
}
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/CaseConvert.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
new file mode 100644
index 0000000..f1aea16
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
@@ -0,0 +1,80 @@
+ 255) {
+ return ExcelError::VALUE();
+ }
+ $result = iconv('UCS-4LE', 'UTF-8', pack('V', $character));
+
+ return ($result === false) ? '' : $result;
+ }
+
+ /**
+ * CODE.
+ *
+ * @param mixed $characters String character to convert to its ASCII value
+ * Or can be an array of values
+ *
+ * @return array|int|string A string if arguments are invalid
+ * If an array of values is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function code($characters)
+ {
+ if (is_array($characters)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $characters);
+ }
+
+ $characters = Helpers::extractString($characters);
+ if ($characters === '') {
+ return ExcelError::VALUE();
+ }
+
+ $character = $characters;
+ if (mb_strlen($characters, 'UTF-8') > 1) {
+ $character = mb_substr($characters, 0, 1, 'UTF-8');
+ }
+
+ return self::unicodeToOrd($character);
+ }
+
+ private static function unicodeToOrd(string $character): int
+ {
+ $retVal = 0;
+ $iconv = iconv('UTF-8', 'UCS-4LE', $character);
+ if ($iconv !== false) {
+ $result = unpack('V', $iconv);
+ if (is_array($result) && isset($result[1])) {
+ $retVal = $result[1];
+ }
+ }
+
+ return $retVal;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Concatenate.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Concatenate.php
new file mode 100644
index 0000000..37c135d
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Concatenate.php
@@ -0,0 +1,137 @@
+ DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::CALC();
+
+ break;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * TEXTJOIN.
+ *
+ * @param mixed $delimiter The delimter to use between the joined arguments
+ * Or can be an array of values
+ * @param mixed $ignoreEmpty true/false Flag indicating whether empty arguments should be skipped
+ * Or can be an array of values
+ * @param mixed $args The values to join
+ *
+ * @return array|string The joined string
+ * If an array of values is passed for the $delimiter or $ignoreEmpty arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function TEXTJOIN($delimiter = '', $ignoreEmpty = true, ...$args)
+ {
+ if (is_array($delimiter) || is_array($ignoreEmpty)) {
+ return self::evaluateArrayArgumentsSubset(
+ [self::class, __FUNCTION__],
+ 2,
+ $delimiter,
+ $ignoreEmpty,
+ ...$args
+ );
+ }
+
+ $delimiter ??= '';
+ $ignoreEmpty ??= true;
+ $aArgs = Functions::flattenArray($args);
+ $returnValue = self::evaluateTextJoinArray($ignoreEmpty, $aArgs);
+
+ $returnValue ??= implode($delimiter, $aArgs);
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::CALC();
+ }
+
+ return $returnValue;
+ }
+
+ private static function evaluateTextJoinArray(bool $ignoreEmpty, array &$aArgs): ?string
+ {
+ foreach ($aArgs as $key => &$arg) {
+ $value = Helpers::extractString($arg);
+ if (ErrorValue::isError($value)) {
+ return $value;
+ }
+
+ if ($ignoreEmpty === true && ((is_string($arg) && trim($arg) === '') || $arg === null)) {
+ unset($aArgs[$key]);
+ } elseif (is_bool($arg)) {
+ $arg = Helpers::convertBooleanValue($arg);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * REPT.
+ *
+ * Returns the result of builtin function round after validating args.
+ *
+ * @param mixed $stringValue The value to repeat
+ * Or can be an array of values
+ * @param mixed $repeatCount The number of times the string value should be repeated
+ * Or can be an array of values
+ *
+ * @return array|string The repeated string
+ * If an array of values is passed for the $stringValue or $repeatCount arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function builtinREPT($stringValue, $repeatCount)
+ {
+ if (is_array($stringValue) || is_array($repeatCount)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $stringValue, $repeatCount);
+ }
+
+ $stringValue = Helpers::extractString($stringValue);
+
+ if (!is_numeric($repeatCount) || $repeatCount < 0) {
+ $returnValue = ExcelError::VALUE();
+ } elseif (ErrorValue::isError($stringValue)) {
+ $returnValue = $stringValue;
+ } else {
+ $returnValue = str_repeat($stringValue, (int) $repeatCount);
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::VALUE(); // note VALUE not CALC
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Extract.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Extract.php
new file mode 100644
index 0000000..d3668f8
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Extract.php
@@ -0,0 +1,280 @@
+getMessage();
+ }
+
+ return mb_substr($value ?? '', 0, $chars, 'UTF-8');
+ }
+
+ /**
+ * MID.
+ *
+ * @param mixed $value String value from which to extract characters
+ * Or can be an array of values
+ * @param mixed $start Integer offset of the first character that we want to extract
+ * Or can be an array of values
+ * @param mixed $chars The number of characters to extract (as an integer)
+ * Or can be an array of values
+ *
+ * @return array|string The joined string
+ * If an array of values is passed for the $value, $start or $chars arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function mid($value, $start, $chars)
+ {
+ if (is_array($value) || is_array($start) || is_array($chars)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $start, $chars);
+ }
+
+ try {
+ $value = Helpers::extractString($value);
+ $start = Helpers::extractInt($start, 1);
+ $chars = Helpers::extractInt($chars, 0);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ return mb_substr($value ?? '', --$start, $chars, 'UTF-8');
+ }
+
+ /**
+ * RIGHT.
+ *
+ * @param mixed $value String value from which to extract characters
+ * Or can be an array of values
+ * @param mixed $chars The number of characters to extract (as an integer)
+ * Or can be an array of values
+ *
+ * @return array|string The joined string
+ * If an array of values is passed for the $value or $chars arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function right($value, $chars = 1)
+ {
+ if (is_array($value) || is_array($chars)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $chars);
+ }
+
+ try {
+ $value = Helpers::extractString($value);
+ $chars = Helpers::extractInt($chars, 0, 1);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ return mb_substr($value ?? '', mb_strlen($value ?? '', 'UTF-8') - $chars, $chars, 'UTF-8');
+ }
+
+ /**
+ * TEXTBEFORE.
+ *
+ * @param mixed $text the text that you're searching
+ * Or can be an array of values
+ * @param null|array|string $delimiter the text that marks the point before which you want to extract
+ * Multiple delimiters can be passed as an array of string values
+ * @param mixed $instance The instance of the delimiter after which you want to extract the text.
+ * By default, this is the first instance (1).
+ * A negative value means start searching from the end of the text string.
+ * Or can be an array of values
+ * @param mixed $matchMode Determines whether the match is case-sensitive or not.
+ * 0 - Case-sensitive
+ * 1 - Case-insensitive
+ * Or can be an array of values
+ * @param mixed $matchEnd Treats the end of text as a delimiter.
+ * 0 - Don't match the delimiter against the end of the text.
+ * 1 - Match the delimiter against the end of the text.
+ * Or can be an array of values
+ * @param mixed $ifNotFound value to return if no match is found
+ * The default is a #N/A Error
+ * Or can be an array of values
+ *
+ * @return mixed|mixed[] the string extracted from text before the delimiter; or the $ifNotFound value
+ * If an array of values is passed for any of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function before($text, $delimiter, $instance = 1, $matchMode = 0, $matchEnd = 0, $ifNotFound = '#N/A')
+ {
+ if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
+ return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ }
+
+ $text = Helpers::extractString($text ?? '');
+ $instance = (int) $instance;
+ $matchMode = (int) $matchMode;
+ $matchEnd = (int) $matchEnd;
+
+ $split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ if (is_string($split)) {
+ return $split;
+ }
+ if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
+ return ($instance > 0) ? '' : $text;
+ }
+
+ // Adjustment for a match as the first element of the split
+ $flags = self::matchFlags($matchMode);
+ $delimiter = self::buildDelimiter($delimiter);
+ $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
+ $oddReverseAdjustment = count($split) % 2;
+
+ $split = ($instance < 0)
+ ? array_slice($split, 0, max(count($split) - (abs($instance) * 2 - 1) - $adjust - $oddReverseAdjustment, 0))
+ : array_slice($split, 0, $instance * 2 - 1 - $adjust);
+
+ return implode('', $split);
+ }
+
+ /**
+ * TEXTAFTER.
+ *
+ * @param mixed $text the text that you're searching
+ * @param null|array|string $delimiter the text that marks the point before which you want to extract
+ * Multiple delimiters can be passed as an array of string values
+ * @param mixed $instance The instance of the delimiter after which you want to extract the text.
+ * By default, this is the first instance (1).
+ * A negative value means start searching from the end of the text string.
+ * Or can be an array of values
+ * @param mixed $matchMode Determines whether the match is case-sensitive or not.
+ * 0 - Case-sensitive
+ * 1 - Case-insensitive
+ * Or can be an array of values
+ * @param mixed $matchEnd Treats the end of text as a delimiter.
+ * 0 - Don't match the delimiter against the end of the text.
+ * 1 - Match the delimiter against the end of the text.
+ * Or can be an array of values
+ * @param mixed $ifNotFound value to return if no match is found
+ * The default is a #N/A Error
+ * Or can be an array of values
+ *
+ * @return mixed|mixed[] the string extracted from text before the delimiter; or the $ifNotFound value
+ * If an array of values is passed for any of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function after($text, $delimiter, $instance = 1, $matchMode = 0, $matchEnd = 0, $ifNotFound = '#N/A')
+ {
+ if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
+ return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ }
+
+ $text = Helpers::extractString($text ?? '');
+ $instance = (int) $instance;
+ $matchMode = (int) $matchMode;
+ $matchEnd = (int) $matchEnd;
+
+ $split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ if (is_string($split)) {
+ return $split;
+ }
+ if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
+ return ($instance < 0) ? '' : $text;
+ }
+
+ // Adjustment for a match as the first element of the split
+ $flags = self::matchFlags($matchMode);
+ $delimiter = self::buildDelimiter($delimiter);
+ $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
+ $oddReverseAdjustment = count($split) % 2;
+
+ $split = ($instance < 0)
+ ? array_slice($split, count($split) - ((int) abs($instance + 1) * 2) - $adjust - $oddReverseAdjustment)
+ : array_slice($split, $instance * 2 - $adjust);
+
+ return implode('', $split);
+ }
+
+ /**
+ * @param null|array|string $delimiter
+ * @param int $matchMode
+ * @param int $matchEnd
+ * @param mixed $ifNotFound
+ *
+ * @return array|string
+ */
+ private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound)
+ {
+ $flags = self::matchFlags($matchMode);
+ $delimiter = self::buildDelimiter($delimiter);
+
+ if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) {
+ return $ifNotFound;
+ }
+
+ $split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+ if ($split === false) {
+ return ExcelError::NA();
+ }
+
+ if ($instance === 0 || abs($instance) > StringHelper::countCharacters($text)) {
+ return ExcelError::VALUE();
+ }
+
+ if ($matchEnd === 0 && (abs($instance) > floor(count($split) / 2))) {
+ return ExcelError::NA();
+ } elseif ($matchEnd !== 0 && (abs($instance) - 1 > ceil(count($split) / 2))) {
+ return ExcelError::NA();
+ }
+
+ return $split;
+ }
+
+ /**
+ * @param null|array|string $delimiter the text that marks the point before which you want to extract
+ * Multiple delimiters can be passed as an array of string values
+ */
+ private static function buildDelimiter($delimiter): string
+ {
+ if (is_array($delimiter)) {
+ $delimiter = Functions::flattenArray($delimiter);
+ $quotedDelimiters = array_map(
+ function ($delimiter) {
+ return preg_quote($delimiter ?? '');
+ },
+ $delimiter
+ );
+ $delimiters = implode('|', $quotedDelimiters);
+
+ return '(' . $delimiters . ')';
+ }
+
+ return '(' . preg_quote($delimiter ?? '') . ')';
+ }
+
+ private static function matchFlags(int $matchMode): string
+ {
+ return ($matchMode === 0) ? 'mu' : 'miu';
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Format.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Format.php
new file mode 100644
index 0000000..93e7282
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Format.php
@@ -0,0 +1,314 @@
+getMessage();
+ }
+
+ $mask = '$#,##0';
+ if ($decimals > 0) {
+ $mask .= '.' . str_repeat('0', $decimals);
+ } else {
+ $round = 10 ** abs($decimals);
+ if ($value < 0) {
+ $round = 0 - $round;
+ }
+ $value = MathTrig\Round::multiple($value, $round);
+ }
+ $mask = "{$mask};-{$mask}";
+
+ return NumberFormat::toFormattedString($value, $mask);
+ }
+
+ /**
+ * FIXED.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $decimals Integer value for the number of decimal places that should be formatted
+ * Or can be an array of values
+ * @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false)
+ {
+ if (is_array($value) || is_array($decimals) || is_array($noCommas)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals, $noCommas);
+ }
+
+ try {
+ $value = Helpers::extractFloat($value);
+ $decimals = Helpers::extractInt($decimals, -100, 0, true);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ $valueResult = round($value, $decimals);
+ if ($decimals < 0) {
+ $decimals = 0;
+ }
+ if ($noCommas === false) {
+ $valueResult = number_format(
+ $valueResult,
+ $decimals,
+ StringHelper::getDecimalSeparator(),
+ StringHelper::getThousandsSeparator()
+ );
+ }
+
+ return (string) $valueResult;
+ }
+
+ /**
+ * TEXT.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $format A string with the Format mask that should be used
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function TEXTFORMAT($value, $format)
+ {
+ if (is_array($value) || is_array($format)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
+ }
+
+ $value = Helpers::extractString($value);
+ $format = Helpers::extractString($format);
+
+ if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) {
+ $value = DateTimeExcel\DateValue::fromString($value);
+ }
+
+ return (string) NumberFormat::toFormattedString($value, $format);
+ }
+
+ /**
+ * @param mixed $value Value to check
+ *
+ * @return mixed
+ */
+ private static function convertValue($value)
+ {
+ $value = $value ?? 0;
+ if (is_bool($value)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $value = (int) $value;
+ } else {
+ throw new CalcExp(ExcelError::VALUE());
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * VALUE.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|DateTimeInterface|float|int|string A string if arguments are invalid
+ * If an array of values is passed for the argument, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function VALUE($value = '')
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = self::convertValue($value);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+ if (!is_numeric($value)) {
+ $numberValue = str_replace(
+ StringHelper::getThousandsSeparator(),
+ '',
+ trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
+ );
+ if (is_numeric($numberValue)) {
+ return (float) $numberValue;
+ }
+
+ $dateSetting = Functions::getReturnDateType();
+ Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
+
+ if (strpos($value, ':') !== false) {
+ $timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
+ if ($timeValue !== ExcelError::VALUE()) {
+ Functions::setReturnDateType($dateSetting);
+
+ return $timeValue;
+ }
+ }
+ $dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value));
+ if ($dateValue !== ExcelError::VALUE()) {
+ Functions::setReturnDateType($dateSetting);
+
+ return $dateValue;
+ }
+ Functions::setReturnDateType($dateSetting);
+
+ return ExcelError::VALUE();
+ }
+
+ return (float) $value;
+ }
+
+ /**
+ * TEXT.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $format
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function valueToText($value, $format = false)
+ {
+ if (is_array($value) || is_array($format)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
+ }
+
+ $format = (bool) $format;
+
+ if (is_object($value) && $value instanceof RichText) {
+ $value = $value->getPlainText();
+ }
+ if (is_string($value)) {
+ $value = ($format === true) ? Calculation::wrapResult($value) : $value;
+ $value = str_replace("\n", '', $value);
+ } elseif (is_bool($value)) {
+ $value = Calculation::getLocaleBoolean($value ? 'TRUE' : 'FALSE');
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * @param mixed $decimalSeparator
+ */
+ private static function getDecimalSeparator($decimalSeparator): string
+ {
+ return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
+ }
+
+ /**
+ * @param mixed $groupSeparator
+ */
+ private static function getGroupSeparator($groupSeparator): string
+ {
+ return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
+ }
+
+ /**
+ * NUMBERVALUE.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
+ * Or can be an array of values
+ * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ */
+ public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
+ {
+ if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
+ }
+
+ try {
+ $value = self::convertValue($value);
+ $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
+ $groupSeparator = self::getGroupSeparator($groupSeparator);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ if (!is_numeric($value)) {
+ $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
+ if ($decimalPositions > 1) {
+ return ExcelError::VALUE();
+ }
+ $decimalOffset = array_pop($matches[0])[1]; // @phpstan-ignore-line
+ if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
+ return ExcelError::VALUE();
+ }
+
+ $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
+
+ // Handle the special case of trailing % signs
+ $percentageString = rtrim($value, '%');
+ if (!is_numeric($percentageString)) {
+ return ExcelError::VALUE();
+ }
+
+ $percentageAdjustment = strlen($value) - strlen($percentageString);
+ if ($percentageAdjustment) {
+ $value = (float) $percentageString;
+ $value /= 10 ** ($percentageAdjustment * 2);
+ }
+ }
+
+ return is_array($value) ? ExcelError::VALUE() : (float) $value;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Helpers.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Helpers.php
new file mode 100644
index 0000000..e7b67a3
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Helpers.php
@@ -0,0 +1,91 @@
+getMessage();
+ }
+ $returnValue = $left . $newText . $right;
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::VALUE();
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * SUBSTITUTE.
+ *
+ * @param mixed $text The text string value to modify
+ * Or can be an array of values
+ * @param mixed $fromText The string value that we want to replace in $text
+ * Or can be an array of values
+ * @param mixed $toText The string value that we want to replace with in $text
+ * Or can be an array of values
+ * @param mixed $instance Integer instance Number for the occurrence of frmText to change
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function substitute($text = '', $fromText = '', $toText = '', $instance = null)
+ {
+ if (is_array($text) || is_array($fromText) || is_array($toText) || is_array($instance)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $text, $fromText, $toText, $instance);
+ }
+
+ try {
+ $text = Helpers::extractString($text, true);
+ $fromText = Helpers::extractString($fromText, true);
+ $toText = Helpers::extractString($toText, true);
+ if ($instance === null) {
+ $returnValue = str_replace($fromText, $toText, $text);
+ } else {
+ if (is_bool($instance)) {
+ if ($instance === false || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
+ return ExcelError::Value();
+ }
+ $instance = 1;
+ }
+ $instance = Helpers::extractInt($instance, 1, 0, true);
+ $returnValue = self::executeSubstitution($text, $fromText, $toText, $instance);
+ }
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::VALUE();
+ }
+
+ return $returnValue;
+ }
+
+ private static function executeSubstitution(string $text, string $fromText, string $toText, int $instance): string
+ {
+ $pos = -1;
+ while ($instance > 0) {
+ $pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
+ if ($pos === false) {
+ return $text;
+ }
+ --$instance;
+ }
+
+ return Functions::scalar(self::REPLACE($text, ++$pos, StringHelper::countCharacters($fromText), $toText));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Search.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Search.php
new file mode 100644
index 0000000..10b6a1a
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Search.php
@@ -0,0 +1,97 @@
+getMessage();
+ }
+
+ if (StringHelper::countCharacters($haystack) >= $offset) {
+ if (StringHelper::countCharacters($needle) === 0) {
+ return $offset;
+ }
+
+ $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * SEARCH (case insensitive search).
+ *
+ * @param mixed $needle The string to look for
+ * Or can be an array of values
+ * @param mixed $haystack The string in which to look
+ * Or can be an array of values
+ * @param mixed $offset Integer offset within $haystack to start searching from
+ * Or can be an array of values
+ *
+ * @return array|int|string The offset where the first occurrence of needle was found in the haystack
+ * If an array of values is passed for the $value or $chars arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function insensitive($needle, $haystack, $offset = 1)
+ {
+ if (is_array($needle) || is_array($haystack) || is_array($offset)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
+ }
+
+ try {
+ $needle = Helpers::extractString($needle);
+ $haystack = Helpers::extractString($haystack);
+ $offset = Helpers::extractInt($offset, 1, 0, true);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ if (StringHelper::countCharacters($haystack) >= $offset) {
+ if (StringHelper::countCharacters($needle) === 0) {
+ return $offset;
+ }
+
+ $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Text.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Text.php
new file mode 100644
index 0000000..7b97f91
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Text.php
@@ -0,0 +1,255 @@
+ 1) {
+ $quotedDelimiters = array_map(
+ function ($delimiter) {
+ return preg_quote($delimiter ?? '');
+ },
+ $valueSet
+ );
+ $delimiters = implode('|', $quotedDelimiters);
+
+ return '(' . $delimiters . ')';
+ }
+
+ return '(' . preg_quote(/** @scrutinizer ignore-type */ Functions::flattenSingleValue($delimiter)) . ')';
+ }
+
+ private static function matchFlags(bool $matchMode): string
+ {
+ return ($matchMode === true) ? 'miu' : 'mu';
+ }
+
+ public static function fromArray(array $array, int $format = 0): string
+ {
+ $result = [];
+ foreach ($array as $row) {
+ $cells = [];
+ foreach ($row as $cellValue) {
+ $value = ($format === 1) ? self::formatValueMode1($cellValue) : self::formatValueMode0($cellValue);
+ $cells[] = $value;
+ }
+ $result[] = implode(($format === 1) ? ',' : ', ', $cells);
+ }
+
+ $result = implode(($format === 1) ? ';' : ', ', $result);
+
+ return ($format === 1) ? '{' . $result . '}' : $result;
+ }
+
+ /**
+ * @param mixed $cellValue
+ */
+ private static function formatValueMode0($cellValue): string
+ {
+ if (is_bool($cellValue)) {
+ return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
+ }
+
+ return (string) $cellValue;
+ }
+
+ /**
+ * @param mixed $cellValue
+ */
+ private static function formatValueMode1($cellValue): string
+ {
+ if (is_string($cellValue) && ErrorValue::isError($cellValue) === false) {
+ return Calculation::FORMULA_STRING_QUOTE . $cellValue . Calculation::FORMULA_STRING_QUOTE;
+ } elseif (is_bool($cellValue)) {
+ return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
+ }
+
+ return (string) $cellValue;
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/TextData/Trim.php b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Trim.php
new file mode 100644
index 0000000..27eceb9
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/TextData/Trim.php
@@ -0,0 +1,52 @@
+branchPruner = $branchPruner;
+ }
+
/**
* Return the number of entries on the stack.
- *
- * @return int
*/
- public function count()
+ public function count(): int
{
return $this->count;
}
@@ -33,28 +42,14 @@ class Stack
/**
* Push a new entry onto the stack.
*
- * @param mixed $type
* @param mixed $value
- * @param mixed $reference
- * @param null|string $storeKey will store the result under this alias
- * @param null|string $onlyIf will only run computation if the matching
- * store key is true
- * @param null|string $onlyIfNot will only run computation if the matching
- * store key is false
*/
- public function push(
- $type,
- $value,
- $reference = null,
- $storeKey = null,
- $onlyIf = null,
- $onlyIfNot = null
- ) {
- $stackItem = $this->getStackItem($type, $value, $reference, $storeKey, $onlyIf, $onlyIfNot);
-
+ public function push(string $type, $value, ?string $reference = null): void
+ {
+ $stackItem = $this->getStackItem($type, $value, $reference);
$this->stack[$this->count++] = $stackItem;
- if ($type == 'Function') {
+ if ($type === 'Function') {
$localeFunction = Calculation::localeFunc($value);
if ($localeFunction != $value) {
$this->stack[($this->count - 1)]['localeValue'] = $localeFunction;
@@ -62,29 +57,37 @@ class Stack
}
}
- public function getStackItem(
- $type,
- $value,
- $reference = null,
- $storeKey = null,
- $onlyIf = null,
- $onlyIfNot = null
- ) {
+ public function pushStackItem(array $stackItem): void
+ {
+ $this->stack[$this->count++] = $stackItem;
+ }
+
+ /**
+ * @param mixed $value
+ */
+ public function getStackItem(string $type, $value, ?string $reference = null): array
+ {
$stackItem = [
'type' => $type,
'value' => $value,
'reference' => $reference,
];
- if (isset($storeKey)) {
+ // will store the result under this alias
+ $storeKey = $this->branchPruner->currentCondition();
+ if (isset($storeKey) || $reference === 'NULL') {
$stackItem['storeKey'] = $storeKey;
}
- if (isset($onlyIf)) {
+ // will only run computation if the matching store key is true
+ $onlyIf = $this->branchPruner->currentOnlyIf();
+ if (isset($onlyIf) || $reference === 'NULL') {
$stackItem['onlyIf'] = $onlyIf;
}
- if (isset($onlyIfNot)) {
+ // will only run computation if the matching store key is false
+ $onlyIfNot = $this->branchPruner->currentOnlyIfNot();
+ if (isset($onlyIfNot) || $reference === 'NULL') {
$stackItem['onlyIfNot'] = $onlyIfNot;
}
@@ -93,10 +96,8 @@ class Stack
/**
* Pop the last entry from the stack.
- *
- * @return mixed
*/
- public function pop()
+ public function pop(): ?array
{
if ($this->count > 0) {
return $this->stack[--$this->count];
@@ -107,12 +108,8 @@ class Stack
/**
* Return an entry from the stack without removing it.
- *
- * @param int $n number indicating how far back in the stack we want to look
- *
- * @return mixed
*/
- public function last($n = 1)
+ public function last(int $n = 1): ?array
{
if ($this->count - $n < 0) {
return null;
@@ -124,26 +121,9 @@ class Stack
/**
* Clear the stack.
*/
- public function clear()
+ public function clear(): void
{
$this->stack = [];
$this->count = 0;
}
-
- public function __toString()
- {
- $str = 'Stack: ';
- foreach ($this->stack as $index => $item) {
- if ($index > $this->count - 1) {
- break;
- }
- $value = $item['value'] ?? 'no value';
- while (is_array($value)) {
- $value = array_pop($value);
- }
- $str .= $value . ' |> ';
- }
-
- return $str;
- }
}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/Web.php b/PhpOffice/PhpSpreadsheet/Calculation/Web.php
new file mode 100644
index 0000000..f4dd40e
--- /dev/null
+++ b/PhpOffice/PhpSpreadsheet/Calculation/Web.php
@@ -0,0 +1,30 @@
+ 2048) {
+ return ExcelError::VALUE(); // Invalid URL length
+ }
+
+ if (!preg_match('/^http[s]?:\/\//', $url)) {
+ return ExcelError::VALUE(); // Invalid protocol
+ }
+
+ // Get results from the the webservice
+ $client = Settings::getHttpClient();
+ $requestFactory = Settings::getRequestFactory();
+ $request = $requestFactory->createRequest('GET', $url);
+
+ try {
+ $response = $client->sendRequest($request);
+ } catch (ClientExceptionInterface $e) {
+ return ExcelError::VALUE(); // cURL error
+ }
+
+ if ($response->getStatusCode() != 200) {
+ return ExcelError::VALUE(); // cURL error
+ }
+
+ $output = $response->getBody()->getContents();
+ if (strlen($output) > 32767) {
+ return ExcelError::VALUE(); // Output not a string or too long
+ }
+
+ return $output;
+ }
+
+ /**
+ * URLENCODE.
+ *
+ * Returns data from a web service on the Internet or Intranet.
+ *
+ * Excel Function:
+ * urlEncode(text)
+ *
+ * @param mixed $text
+ *
+ * @return string the url encoded output
+ */
+ public static function urlEncode($text)
+ {
+ if (!is_string($text)) {
+ return ExcelError::VALUE();
+ }
+
+ return str_replace('+', '%20', urlencode($text));
+ }
+}
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/functionlist.txt b/PhpOffice/PhpSpreadsheet/Calculation/functionlist.txt
deleted file mode 100755
index 97a0cee..0000000
--- a/PhpOffice/PhpSpreadsheet/Calculation/functionlist.txt
+++ /dev/null
@@ -1,388 +0,0 @@
-ABS
-ACCRINT
-ACCRINTM
-ACOS
-ACOSH
-ACOT
-ACOTH
-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
-BITAND
-BITLSHIFT
-BITOR
-BITRSHIFT
-BITXOR
-CEILING
-CELL
-CHAR
-CHIDIST
-CHIINV
-CHITEST
-CHOOSE
-CLEAN
-CODE
-COLUMN
-COLUMNS
-COMBIN
-COMPLEX
-CONCAT
-CONCATENATE
-CONFIDENCE
-CONVERT
-CORREL
-COS
-COSH
-COT
-COTH
-COUNT
-COUNTA
-COUNTBLANK
-COUNTIF
-COUNTIFS
-COUPDAYBS
-COUPDAYBS
-COUPDAYSNC
-COUPNCD
-COUPNUM
-COUPPCD
-COVAR
-CRITBINOM
-CSC
-CSCH
-CUBEKPIMEMBER
-CUBEMEMBER
-CUBEMEMBERPROPERTY
-CUBERANKEDMEMBER
-CUBESET
-CUBESETCOUNT
-CUBEVALUE
-CUMIPMT
-CUMPRINC
-DATE
-DATEDIF
-DATEVALUE
-DAVERAGE
-DAY
-DAYS
-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
-ERF.PRECISE
-ERFC
-ERFC.PRECISE
-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
-IMCOSH
-IMCOT
-IMCSC
-IMCSCH
-IMEXP
-IMLN
-IMLOG10
-IMLOG2
-IMPOWER
-IMPRODUCT
-IMREAL
-IMSEC
-IMSECH
-IMSIN
-IMSINH
-IMSQRT
-IMSUB
-IMSUM
-IMTAN
-INDEX
-INDIRECT
-INFO
-INT
-INTERCEPT
-INTRATE
-IPMT
-IRR
-ISBLANK
-ISERR
-ISERROR
-ISEVEN
-ISLOGICAL
-ISNA
-ISNONTEXT
-ISNUMBER
-ISODD
-ISOWEEKNUM
-ISPMT
-ISREF
-ISTEXT
-JIS
-KURT
-LARGE
-LCM
-LEFT
-LEFTB
-LEN
-LENB
-LINEST
-LN
-LOG
-LOG10
-LOGEST
-LOGINV
-LOGNORMDIST
-LOOKUP
-LOWER
-MATCH
-MAX
-MAXA
-MAXIFS
-MDETERM
-MDURATION
-MEDIAN
-MID
-MIDB
-MIN
-MINA
-MINIFS
-MINUTE
-MINVERSE
-MIRR
-MMULT
-MOD
-MODE
-MONTH
-MROUND
-MULTINOMIAL
-N
-NA
-NEGBINOMDIST
-NETWORKDAYS
-NOMINAL
-NORMDIST
-NORMINV
-NORMSDIST
-NORMSINV
-NOT
-NOW
-NPER
-NPV
-NUMBERVALUE
-OCT2BIN
-OCT2DEC
-OCT2HEX
-ODD
-ODDFPRICE
-ODDFYIELD
-ODDLPRICE
-ODDLYIELD
-OFFSET
-OR
-PDURATION
-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
-RRI
-RSQ
-RTD
-SEARCH
-SEARCHB
-SEC
-SECH
-SECOND
-SERIESSUM
-SIGN
-SIN
-SINH
-SKEW
-SLN
-SLOPE
-SMALL
-SQRT
-SQRTPI
-STANDARDIZE
-STDEV
-STDEV.A
-STDEV.P
-STDEVA
-STDEVP
-STDEVPA
-STEYX
-SUBSTITUTE
-SUBTOTAL
-SUM
-SUMIF
-SUMIFS
-SUMPRODUCT
-SUMSQ
-SUMX2MY2
-SUMX2PY2
-SUMXMY2
-SWITCH
-SYD
-T
-TAN
-TANH
-TBILLEQ
-TBILLPRICE
-TBILLYIELD
-TDIST
-TEXT
-TEXTJOIN
-TIME
-TIMEVALUE
-TINV
-TODAY
-TRANSPOSE
-TREND
-TRIM
-TRIMMEAN
-TRUE
-TRUNC
-TTEST
-TYPE
-UNICHAR
-UNIORD
-UPPER
-USDOLLAR
-VALUE
-VAR
-VARA
-VARP
-VARPA
-VDB
-VLOOKUP
-WEEKDAY
-WEEKNUM
-WEIBULL
-WORKDAY
-XIRR
-XNPV
-XOR
-YEAR
-YEARFRAC
-YIELD
-YIELDDISC
-YIELDMAT
-ZTEST
diff --git a/PhpOffice/PhpSpreadsheet/Calculation/locale/Translations.xlsx b/PhpOffice/PhpSpreadsheet/Calculation/locale/Translations.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..c0cc1ce88a51e09576f373803194bd1603b66fa8
GIT binary patch
literal 108747
zcmeEt^+Q!{w=E&v-6hi99Rh+%DP5aVy1PR{LInZo4r$nw^d>hT4bt7Y>2B`A_dEAH
z=iYPwf^+#{5o@ja%o<~k=KhVQ9;kQM9a8GwI*eCMy*5@*egj9&TBi0+pw6o1+Lf?^>
zg@5ZS>wgT&^KalMcLob3{N%&L(}+TXo8iS6<>qOD$7A{|WB5^?*@^TlNwYiV7Z1Bq
zn`+t(QAa}Sd>m0vtK6?O%5pf?Gq<+UX;s5ROXt^ynt3#RC+mHFEFO~OB#Yqcx#zN5
zEr!7ZtZTCo4eel*+)@V|dWxcxl2DWz&pTDga2$f)H{OpLPch{D|B#+S~ezv+wCgALW+3Acu_vz^-
zzr69=>6@u>#5$?NcY>GHw~x=PpEvHJv1i%YGQU|7WuAhi@f29u%p54K<#C7ml#jU3
zfQ*-9oDcoJNz&F`qIQMH@=C8n&<8|E4PfY2K`2C3Nkyz&a?dk~242Bh3=dnhS
zjNz#JXf+ss2nYB0hzR%YzmNsd-O+b;fGk{ryBGjj7&)8Sx^QtmUH?Bx{10Y^{}g)Z
z*q}-$HzvxF82%ydMoT^wjf~|2C#scDNH?Nq#GIzxD)X_ik55&*alHIX)1KN#QK`AU
zkF}NQo?W;U&Oh0!4A5Oul-73DTG*)-Jk;cnEwbpte$JczsNUJ=S|<}*K2tXkkc`ym
z>QXCDx`(K}2am`M{LWQ0VnrtwmiVmaHNr7o*zM(C(&2<9vOhuSqTa_Xq^-tYE17*@_Si}i{%ez
z*(3c^cd-)dOS4r9iY55Jjp7|tS-|8$8Gv%3(#BL?K_{|1*kseObc04!HvFyjDE
zc-nG#I5^vwIyl%oP2oH(JBL|O!UxHnhp^vEW6j^6g_Gxh#w}B8%1)T`e_8zX1Njo8
zlet)^^=!bBlZ+*cHUSKGx^nn
zzS9wv^BNpQ%5R6hJ@YylE;z*^QDqy-#DpvS7~=eMcckh?uT{hb8Q2ObR)B;-tAa2(
zp%kerd4NUJ%%q`~N{7LmXct$;2_4x8OqPwtr2K|$;uET#G16v#+C-HCTQ7?v1F@>I
z9lqftT%{?JIu{2s?^}FBNiBnAO?Z0;s-NVkRN?QZicz|D~1=nWa1ifAw!+f
zp8S~2v=M`@#6J0nDg=DB_n&sWNc_bW?@V^*q}s{wLPFN$H^jUxw^6;?Lg_~Z7BBF7
zuqhHCDaW0Qeo7Lp6y7tc`xW*f9$)R(*<#NAOfOcmuBDO7#dv^
zhWMdOXtH*JwA9`r*P6|;mZ65boS!erR*bC~!F6h~;pjV^j9+H`0OkJ8ac@mgo7{Gf
zLlo8`rHg*#nNTOkvq%+HyYil>1W$9*VTj#ql^P}W9t;aBv
z6O8{3l=x_q>omZ`g95-LheL%2K>5$m`_It)j{yh|sAS;T|JfhSas9vm094}ZyWkB8
zC;Tj0jRK00agHB26=}V)-)K&f6dp3(BVfh6Z7k9m`sKT|Jv88bY9)Gs4X2TbHu0=>ZeBdTy7_7
zc8CTiDNzyTucNnznpn?O$Kp|2B|LdfKhCM0RB(Vr=FT%jGcVjVC#*ePZw3RuDd>g2
zHjw_zI*!Xg)AxP=A7NuvSk__Vj3wr=Fxm0e3ff>-oZh~VoXY_xjx>xodFPGU58b|t;Ku;n%rHBsZ@)lXip=J;M6jizf7rq^z{=upfSedWJCQDZwIo%F?qw3)f@>%y_E
zQMEiaZaXV0-GjY$ZF)Au}mWw7(!Rm&rZN~
zO+PH!#5kG~S>PuI?}*WB*H`?5%dhcwQT270T_@6l#z+i3w$&yjk~ms(P<7zW&jOfi
z{!E0cZf^;<@YypT&iER6LN5@}@fq|vJ-a6ZyJ)*c1Ju`OljX)MI`QSR=r^u3Tudvx
zY09yho>E>4`FK%x=b{lEM%(5wiCGfy@$@<|ISd(GiNbP=gE>7+8&b7(-A@JMC3R;`qhJP;fdkq&Gx99IRolz1FKht5~hbzTYo1`0*uy*>*v;^<^jd@s2N`LCOB-9Zw+f*qq
z;r{}*kRFgg)atCCmGa!V?69t)wI`fmz_UUvMaFM>Hn)yz^Q;v4)4~jUWEt~O$*2V)
zY?`1+8vk3Mn;g`MGUU@MMec#gENpImrcnm6yu4Q*Z=x08UrZtds!RJ&Fa$=Jp;W{K
z@ha`A`@H&)i){8akaJg~8XKn
zw!Y%*(Bd989Co|hGI4c1Vx9J|EQZ;nfXTX5x7a%8K~uS
zQ4^X;>2%(nbXPVs#Vf1vY=AKGzB|o^H>q!~p{EieD~A}f=>@Hc#f0GQA$>}v^A$yH
zIwhh`%n#bCY~1}b0$MI3;+I-P7yXGAMuGR!6i)
za7|772dPHncF&9(tc8M__N}gP3)SZguR~fetd?cS8^6nQ=0#wzc+d@kUSp8_`4d{>
zOjiba9g1OfC?i_`{ZGwjvlotMpPtz_FVyyX?C*ZLEEC#0+wZ#D$I_?Co6-=RZwiQB
ziM`uUF`_R1Ji0oI@a9@LLo`LBGJ9l+uZ_KP?g)-s)wFFpId`tRr*4JCIPHyyahkkh
zo3N=?pp$*6Lucw-1_>i&wv;`*>kKASOfLVg=KmaHMInJH0`*IDFiWl62H
znq)J~QjH-|MCUi0>jM&z9Bkh-oKUA2u&unI*~*#1P%_*w
zQQ6h+7IKugG*f#HGL%+QNEx*LB2G{T`FZkpDaTveUhU`MOR>U6%GMXH(N`o;
zyhl4_|GoOf6cO@cG!a+HB$(__@mU@oa=GS5SJ&3G*P=^4oVF#>*>?J-EHq~GEV@(Z
zm?E8rcy+9purhQ+dreYDX7QEN?4uvTgae;krumto(Dm>Y?$B2!?eT#haPM97$w5cuCcZC5v
z^cGsC3d#?->)=`UR%x-hqj8ZS%)sB$OciFel&3sSpArvvPmsyrv|U|X`4mta;9}VDwfwus^`k=Z-)oiw0^%)
zW~!D!3Le~bR+nt|cEf!k2t+Vrh8UbsbR-uHw|BZRf>xtaC6
zd?rTiMnZ-Ji_M>Ggt}(=)2u{Z`J}Ay8)yBeU#hSS2fO=svy@@Qt@o6^I?3=PZ89b#y;n!VG?|
znAA^)`JPbxgU9nZt$+yL_+^E@GlB$gLf_{j6Afr=0cllrz2(O70u(zxeo4`%7BzrIJ?E$5)Z|M|UidzUv^4(z;fc
zT6K%c+@0Ozc=j<&_x9gB3Kk5|FUePA{wJSiMp8s>w~%Mkp8q}?3C&XdvWdZV78`FC
znea@6f9x(^q9c;9EMzjrdlmMr7DcnuA(XIH$L@UE?weYtDqdD&5l8NaAjb2t%_``l
z{-nLB+*xWJhJ&M8;=vy_(>D2Z2q9SbDwd1O$
zml4U$+4)c;h6~z((@rXNvU#j`+Cuy}yzdbZCDS8Q#jvq)7wWz(H!Mp3sDBe1@#~!1Ahpj}ZJMEdfyZ+3F{Z^0Ghl_r2
zz}Qk`KvBR|e{^dz$uaoQ?Cv`3CQNGkuyqIgc%8W0BlWOl9PHw)U+dtGduW9z-{N{n$o)&dvQye;1uOAqEzNVyTuo=Q9Ok{jUmhTO_#TEnZ-kx4uN)?hXSrHdm_SrKQ79HzH@x3(r(%%&Q@;TAV
zu;i@94jlilFV9E5kCVPo!*k8R>hSYS#_C|GA3|PXY8^!WRAD8Enz(GoSjaKbKW`EA
z8Q-J(!=~vZlFH@Rmmof01B4HnRE2I`Gm@7Q!l5SxOOM}WbcveL>UdtSuhuO9Aq)7&
zI58G}yleoLBqzu{^|g)cE{)6XjdqGGwgfLs*K&``}#8J&(L(^W7-
zYfw;^oR!eL0IZ4N*Y&if+=d9jxEt4RyUFjj)OTh|$B;`~!TJZOuB6&Sr5?tAQq*h0?8_X=v8ocdrW?Vnu5X#7V~SH)lk%g_?#8jz4MN36emU
z>{BpSN7n2P@G%R={L1stJyt$&hWnkna28$(r@M9TqbCKZ9eI3pTBs|U6cp$k=+Esn
z3$v+K-7uHEHg*POcZ%r!3PUE$=7Y>nS+hA6TbkzZ~rS
zu$mB=C=T+wWdLp+B$V_LH8tw9USc(t^lF?5Yn3I4FDX;M{c%?Tdr#0*&yQzLa#V6JXng&^%6ZCf9$S8>;xi>u08^N@lZ|N?g->E)
zkA}Tsl*45ayQ^b74r4z{5Km&C42r}g%T%cgYa~^Qqq}Vn#b%1bIY+!t)Hfz6Qb^Q9
z$p-i-?W1Ipdy(TNNw&_3({bG@x}~4CByNJKqhgffNj^0NYofL>;4By>K$V-jlY=JJ
z%QDwkU6g~SV05=#-VJdDS>6wxWytQKb-7@-rS&8fEr&mNKNNTBB5Q#ceo5nz??LT##S2QhRaGO}->TpHzF8cWchYc6`nIYu=ETV=
zs#$GTSs*bie~eu}0GcpCeKBlso87bEnaW|)88d53*yG}G`aVqvc;0vAgCu(H*p{t^
zDTC<6A@bCg*!x#Zn{0ZO9O}9bh%3oyQwmZo>vtWCuID*vxQ>wgFjsyMg!v#YZYQNl
z=6<+5>~_zzu{*EkEeI^kSf9Lj`pre=)auk{U+?0?V=9bSDLcP43Yp*JV*HG=K~&|3
zIG9MTfr`xgW!=5!)L~zuKZ{JcXXHExy%Um{pVw8Jx_$oAZM-u{%XEB9=9nZPg
z66JT{mVGZY=F<|XV(Fih;oSF8j<<-#n=gXsQ@7}e`
zd^Rc6sr6-VWwBk$#_-j&Z#%oWBQ4c1q)ZBuvGeh@fp0LP+ux}>5=NR#4u;WxmYUr;
zynt@fNNYPTQIGq*1)CIm?>r3HpEQe(*|?BMKll(;up-g;)J_z{tsY*ToKNPX0rP>Y
z1}YC{7I71@PLe&Y$51pXLK^3>0i%u7yB|_FgKSgy7fmqPBn`vDyHmF{Dkd^(Hb-8u
zI56MbpXNWGg|_Wq(kMIV+%0Zk-9`fq3MtOrId3D|QGYnwYWC&P<#$Rz30rgW;%nFn
z_v@p%H?J}f&hq#03+M7ZAg(?cPPDt!=+>;YiJvW3K4MUsybA9{!?>~Z);vOMy&Q8$
zdg1H{*%aFxbsdFT(MSaho(yB0Hzjdq2w$&xcOB0GUC+f&P%f#kZrGHdeRy!wW?LU-
zl%As6|2GFErBkeKSLBQ^TV?ysHOnpU&QxQ55Ly9g>)!nt#f>n^luguCwBbYBH7st2
zT#9F+?>vb9`P=%A5_^T@TjryN@{8u&&1hPZRsczF03n;&(8RR&QuPtU=VK3X~Us6`6*56RZ0L?ym07OAp{S*bx;1_+p=Y2hIbmxU+XR{*CcUBgm!L#=@r1VFQbaE
z&+lf{0E)pu`1&Rx`{ftCom?t|VVh&;oI7n)g_>GkW?I+MCG#KM-dm1ghXvP-e*Bgr
zgR>zoK|(~;e3b%zrZCn;kna4y^IzWUA-ZwBxukS(Ethtp(CW3JhaG!Rjx+Gu=c=pd
z`nPY3ee-EeA2WC$g4*(j?B;+X!0*H=+maXlSLU**^XDmCND5=21a{6ETxTkqvSGWR
zJ;%`yo*{vyo77&3%+Kl%+d{4G94N0nrchK0CGTd(eGMdy9tBkhI0heky$5z7xSW-6
zm{Nn!tiB-;AX?<`^9@4(sJ?pQFysS^+@#qj4tv|aW}x9oA~~2qG-*C0@08@9+*FL{
za0;{4P{ptnjLcoO5{y(0s=H=3?;V!!sv8d17K8R28)(FuEKh?^Ky3ZxAMtC7CZt{6
zF>bbhdel%~=J)^J8vhOxFmjE?*plK%d)(_G*(>72=rIC;Ez9PW4nK1}p3WmnLwhbaZ@~918lszycf(^XH7vArZ
zk~B9gVtR5fd5Ir{GtE7ku4qE~mVKii9*U>peQ%f4wwkEjHVNNt*^@sHELE=QS8q_VkncNIow_rDO={n#O(xqIf*iaEsK~3S
z-(K1NBo0PBx?f>3w+{K2P`i_{u}b$}8?ftz^G^-q(V^WEMzEo=uvQ(v88{{w*W{)N
zPbgI582aWHk{>@v1h*zwiA0rBq291M0DxA8@LHzb!puaF)O45r)bYf2#$=lOie!rwInb|DAF0WF4m8om;JCPj)Nv3e7Y$GRgrqpu|
zpQqR#kDhrO%Z&A&6>pB6?RmFn?yc^_gae#FmX83@2(7Ib6zF2bbHzMm0(=N`#FLE&?g&@-ClpYO^ibLs2PD+w+Y2HdIc0*UpLY
zs};!d!_%lRPrExC*a6Zpz5PUCf5LY3v_dj3cDxfY+0*l*G3SRzKE6paVb3VGBbfA?
z^zw6stgmk`0;JfhJ7ts71&9POr{jVh--L7!$5k*$X$sO-q&vmdf(CRX+T3~p4!zS-
zRUmz?Sd=;A4xkFA*H(7CFRg8U-Eihn)x+fYMSu1quWn_q&w}d{*!c*)aDZt%7a@{$B(X;`B+Nyex5rfYheXO
zv(foJ!)Ggx{+}82*Qn)J65J{9L*0KBY*cNL0{q3&C~kSk(^%w
zvQx5{%&T^5eOJM545WVQkZ5s-eaE9yZZMb%3<-SKnww~JntoK1l}WPe!6UKPSN*qN
zOj4^tTfHXqq9PA4m;wN|eZm2&f2=5!2g5k-(NY$3rQDy%sm#=mXb||-xI&=?4}EM9eQ_p1pd>FCDCDbsoLcm^-pz8
zTMtX5zKHaEBFD3p@PN++<9J)09&^)-HCUMfO-Mjh^MdPzAT!)Mm&@LR9%s2b-jh5Z
zpl;%ChVzRL*Xw(Mg0m0;Wkvj_tnq7kNq})x7ho<)o^&eW_`DJa9mYm2Slt|TO3AA4
zh7lau#TD{|h5$pbKPGtZnd2
za^1>vt)AfrVEgxz;|A+tLK3CP0ga0cRCQxDeOM0cbHuHilNzIPkIsM7{i?NhxW(k=
zs{K6Wa9aWB8w>5bJAwPsQ<;0t{4Y#@E&DuKwy(A(p#d#D<)jRd0mj6WAF`cPTRlO}
zPH6?2{+=Ip%IhC%^Am`A7=eWFgT$RWmWEr%w!?DWeHEp=uLY<|#)A9}3zxd)5)bw+
z%o?D7<=kxrPNY!u%rk(fRI8g{h^NEUGk-F{sR#UJ&Yh{_ss|lI(PG=}C|N^DZ^jY1
z{2z`syVsH@WF^GG?<7emk3En4b5&CDMAJjW+&FvlqhObL4^H=Y3PxsnLbVe%)4!Zh
z($h~~Q%Ut@h<=mXy?P%to{S)qUQ(QWinaX~`0+PyPy4I$HTA8bwNX{^!nGTTq5uP_
z$5Cyser0PDcPE^;+mu34c8St*x1bTKJcg2?dFk9Jd72C``K`7O@Apza#if@|?TJU{+Z^?3
zV2+v9IC6x-ka|2w#ssdQ$AGSANm>HN(PT=qSG%PXMG_aA2l%edjS;6+SOAo_1?v`5
zEr|`J5HhV~XewF_oyh}6ocEhl_vmVzv;$X2zLPsYgnAk&9iZKI>&kB*Frp4Q-J3np
zgR!R9m3V5`d{za$PJ<>)<87|1oG-hv2I>Q@y3kSuB!CW#X*wP+?^E@UR!PVSVp8uS
zP}e0n8$ByVZIZs35i+Rv1mwS)`%`rvk$~}F_2plEqLS5Qc)W3E?mf(Ovz{1j{zY3w
zjMDU$b*j_jGqt}{hnDj2)Hja|t)KFB=I4KGodinFmKqk1Hr(WP_%DFj+_;uk023Ij
z&fQs!*dfg?3FtUI0(hKr*|gDf^^f@!o20`(!>7OVWwo|Sk>+~uMK_j_0iasY#Qu8T
z9(Y>AD$poUooN201gnSZFDfxpwUaqB93N>6z}Fd1Zu8L5KW)HvC7jN>TBj@&cltAS9yvSu(eQKnh?2w2x|oU4*U4bm3S0RTEbW*7
z*_9G7Nj$B3<_cMjyD;}won{16Dy#zNVbeM&4;#J{2L`8d3NYMyx!bJter?IvL<}3O
z99R<&!vie1a|228wrsRq1+u-OSfaqyx=DCv^-_v!{18ZVPQ$u)7t=m4G3%8XT}Y2N
z<82yDHx9h7ZiYKu$&l*n|EZy!@_dQ{x%_Iz6(uqJ%;;|PaSf^R)aJ%YXTUUR@|VCp
zPd`M=x}(v4oTkmpsxq-u*4a@ZBc*@5M%ViJPfCEn6ESMA;&|B?hJzphc)g#T0S+K9
zh)5;7Q@uE{T*5e(5T6#f=O-AuF!|8Z5Zm$jPt#wWNGW*;=tS9oq;KdpOItbQv)TnS
zRUHZ?7P*%9hMVQPZ6u3;PK1g)MB3@h@D3()Y0m>faenb~rm$jR$yQ@J$4-!S=3`fp
z(MD};tr>iref2J9NtCWzrX~)+)=-@lOK+l9+b_Gfnt9E~A
zE>-RNWp_m*-Cq~GDpRpZ!72R`=l0_1|JDrhC@LLa8t(l*o-A1R6m4n4irK08&ep(VqtghV80XgKF6p;S^
z_Bgk?k78fV;m6?zef4w6-~2OAGi!asGgIQWT?0O(?lE`gq$+X@DC6Wu;{p$R^Qb3cP
z2WIwnk5ke#oN;elth1^nFxG`nP!2?;Or$vQl@G+uv(|Oa>dP^5mQko|D8iX@-uG*O
z0n$V*x%dPPcSAn_8Vv_Se~O>YaGt-q-kwfjO-MSfdeXmXqYD%HIF7+lYsB`SG~Ioj{#7O1E3%o^#&k^*9<^z?#b>&rY~UE>&b5O9Oth^&n<`Q
z-64&b&;BN^5X`8;9p~xf@O^KfMWSsfDdtEzC8Qi}H{J5L8&RwhSU$?mUWF-Cx6hZp8<
zjno&4QYnTwCgqGz{9_tn)dmQs&H8^#Q%h=!du@ztK!EMo4T;B8=T;`Qzg35!2XLtI
zB74$GFP?t>XGCH1CD1Vz#pa?W+h6lxQ>z{RKx+9!K{%-|sjGj9nN6I^EO6$-T#5%A
zoFLD8gvL7aSK_oezcxvTCAoJ?%+-Vy?ZzkBJT&eMjTUn}ZyYbfoq3&eu=gzr^VB8SeHn(z7mPzfV~rBb;iNpJx8#CvU?$e
zOetXPARZ?Z&*$nVn$P^x2&u8nwW_xM5`MXTi0poaZKbu4%$jiY6`%-k*SX?T)J~1m
zg8O3THXeP&TcM30C6YG-wt!hb=hJE+$7WHD5N8g8yTh4$2_?-S5z%o#*~LcfQfBMJFE>$0NrwK@34FP{?Z*hpOq6-_BOj38^DnM{Ycze<7%0IgQi<>trG
z7RL^FtYz<OZhldY;SLfLzqv972rG0xb@7bBPy?qt%xx7o+Q8XztOf>5v0->MLw*U#
zs%->}XxN_*QXp=|b_tBHWII2=v4yQC$I7l<{o>f!oKyZ>RtF2iUwfM10d#&UezH@k9;i-60dm>
zj?snJRVdo6wIpci#h1vmpPHPPi~{O2F1E^cojsNAG*#ERzJ)L(AV}@DY_)jp{9sBHxiv5LW;`v9M7SH
zO~6^>6r15FWG5)X3ch^m?qzg2N$>r+*~?>=rRM71-PC&hPc#{fVfQUOc`yOh06xDg
z@oe|Bpwro30|bEM#8MJUB=6uZc5WDZdn*(`t$hc8o(x3b)&~x^wp0L!x%ovb_<;Q$
z&QRh~Q>t-*j4+C)pLAs^6RW0;o_CHts|1VBbd`ABq*6(}%XlZI0`=P0GY?(>;m}-o
zC6zU?Dzj;gv(%gM)I;D1~01}txe0G2ZW%fR^ubpBj9Hhl#+_JR1^UeNGK0tbUkLs{$q
zg5Tyds?Lnj>^UCG&CQ($6{!~Z!iIpmnD06LJyU)h$1|1q4UR4CU41$UndQM7vYF!L
z+5^;6GoYT1Lo7PS0L$4=mRoMeuQ)}tTL=4r-~McFKzhj~fXB~M7`sQ$gM>O+0&c2%
zb0Vsnt~`5
zRxJdPe!ayeRl;fV+3&p60osG&&-ek-8&33A`$CB@&hT#7r)VEnr^Uc_19U`(bf65bBypCcp(l1NhxTZIY!(-
z`-SnrN%h{GdS=0fMzZ&W8`zJ$iG-y-^FXnV=UAMZ?Dk!>Jw4&%iuUqys1jvj>~1J0
zBXn1(EplrQJO5XHPu>2jyVCP6t;zE4fV&)%H2D~ejjLP~7VKChO2?PmI7<=foq}A4
zCnrgXmyG={Hpam)EYM^w42kJh<$4TgT*WjKz|D}%B_GBqn~aA9srzTih)!Tozj##>
z%p~AE3_w%uRsXDrQi;KFyMfa~#$0oAfBy%=#Z>kr;ZvBmtm6H)cl*nwzupB!BzQg?
zmGlFwz~@J6P4IS$cAl-k%ENXU<~^-{ZvwC|{X>=0&>vP=UvAa99J&MNl$YlloAVnu
zG^ZzvAKRftMpG2CI{>m&F49j5q<}@X!?^e#cD;cPRiZ(i3z>DoJoq)H!D4l?pCxjA
zUa|J8PWb*Pk)+2;&nygQb6abEaq#1>@TV;GG&c(YrP`f3|7?B(RNMi#;BIG_@e5<)
z3efn-XXheY_QI+G)oUmng4ynrpMbM)^E-eYZ;Pb>)$iJAWJp