Namespacing Part 3 - Charts (#2852)

* Namespacing Part 3 - Charts

Charts still need some work, but they are now in good enough shape that the code that reads them can handle namespacing properly. Rather than a set of static routines, Reader/Xlsx/Chart needs to be changed to a regular object, with the relevant namespaces passed in the constructor. I am still looking for a test spreadsheet with non-standard namespacing for a full-blown test, but, in the meantime, the existing tests and samples will suffice. When I find a test case, I will certainly add it.

The resulting code is actually simpler than it had been because of the elimination of the need for a NamespacesMeta variable in all the function signatures. Because of that, enough errors disappeared from Phpstan that I decided to just finish that job - its baseline file now dips below 5,000 lines.

* Update Change Log

Need to document many chart changes.
This commit is contained in:
oleibman 2022-05-25 00:56:26 -07:00 committed by GitHub
parent 13e1b9a939
commit 9e4ff929b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 98 additions and 201 deletions

View File

@ -9,13 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added ### Added
- Add point size option for scatter charts - Add point size option for scatter charts [Issue #2298](https://github.com/PHPOffice/PhpSpreadsheet/issues/2298) [PR #2801](https://github.com/PHPOffice/PhpSpreadsheet/pull/2801)
- Basic support for Xlsx reading/writing Chart Sheets [PR #2830](https://github.com/PHPOffice/PhpSpreadsheet/pull/2830) - Basic support for Xlsx reading/writing Chart Sheets [PR #2830](https://github.com/PHPOffice/PhpSpreadsheet/pull/2830)
Note that a ChartSheet is still only written as a normal Worksheet containing a single chart, not as an actual ChartSheet. Note that a ChartSheet is still only written as a normal Worksheet containing a single chart, not as an actual ChartSheet.
- Added Worksheet visibility in Ods Reader [PR #2851](https://github.com/PHPOffice/PhpSpreadsheet/pull/2851) and Gnumeric Reader [PR #2853](https://github.com/PHPOffice/PhpSpreadsheet/pull/2853) - Added Worksheet visibility in Ods Reader [PR #2851](https://github.com/PHPOffice/PhpSpreadsheet/pull/2851) and Gnumeric Reader [PR #2853](https://github.com/PHPOffice/PhpSpreadsheet/pull/2853)
- Added Worksheet visibility in Ods Writer [PR #2850](https://github.com/PHPOffice/PhpSpreadsheet/pull/2850) - Added Worksheet visibility in Ods Writer [PR #2850](https://github.com/PHPOffice/PhpSpreadsheet/pull/2850)
- Allow Csv Reader to treat string as contents of file [Issue #1285](https://github.com/PHPOffice/PhpSpreadsheet/issues/1285) [PR #2792](https://github.com/PHPOffice/PhpSpreadsheet/pull/2792)
- Allow Csv Reader to store null string rather than leave cell empty [Issue #2840](https://github.com/PHPOffice/PhpSpreadsheet/issues/2840) [PR #2842](https://github.com/PHPOffice/PhpSpreadsheet/pull/2842)
### Changed ### Changed
@ -36,6 +38,10 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Xls Reader resolving absolute named ranges to relative ranges [Issue #2826](https://github.com/PHPOffice/PhpSpreadsheet/issues/2826) [PR #2827](https://github.com/PHPOffice/PhpSpreadsheet/pull/2827) - Xls Reader resolving absolute named ranges to relative ranges [Issue #2826](https://github.com/PHPOffice/PhpSpreadsheet/issues/2826) [PR #2827](https://github.com/PHPOffice/PhpSpreadsheet/pull/2827)
- Null value handling in the Excel Math/Trig PRODUCT() function [Issue #2833](https://github.com/PHPOffice/PhpSpreadsheet/issues/2833) [PR #2834](https://github.com/PHPOffice/PhpSpreadsheet/pull/2834) - Null value handling in the Excel Math/Trig PRODUCT() function [Issue #2833](https://github.com/PHPOffice/PhpSpreadsheet/issues/2833) [PR #2834](https://github.com/PHPOffice/PhpSpreadsheet/pull/2834)
- Invalid Print Area defined in Xlsx corrupts internal storage of print area [Issue #2848](https://github.com/PHPOffice/PhpSpreadsheet/issues/2848) [PR #2849](https://github.com/PHPOffice/PhpSpreadsheet/pull/2849) - Invalid Print Area defined in Xlsx corrupts internal storage of print area [Issue #2848](https://github.com/PHPOffice/PhpSpreadsheet/issues/2848) [PR #2849](https://github.com/PHPOffice/PhpSpreadsheet/pull/2849)
- Time interval formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
- Copy from Xls(x) to Html/Pdf loses drawings [PR #2788](https://github.com/PHPOffice/PhpSpreadsheet/pull/2788)
- Html Reader converting cell containing 0 to null string [Issue #2810](https://github.com/PHPOffice/PhpSpreadsheet/issues/2810) [PR #2813](https://github.com/PHPOffice/PhpSpreadsheet/pull/2813)
- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852)
## 1.23.0 - 2022-04-24 ## 1.23.0 - 2022-04-24

View File

@ -2515,136 +2515,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php path: src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has parameter \\$chartDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has parameter \\$plotType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$seriesDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValues\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValues\\(\\) has parameter \\$dataType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValues\\(\\) has parameter \\$seriesValueSet with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValuesMultiLevel\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValuesMultiLevel\\(\\) has parameter \\$dataType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValuesMultiLevel\\(\\) has parameter \\$seriesValueSet with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartLayoutDetails\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartLayoutDetails\\(\\) has parameter \\$chartDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartLayoutDetails\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartTitle\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:parseRichText\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readChartAttributes\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readChartAttributes\\(\\) has parameter \\$chartDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#1 \\$position of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend constructor expects string, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#3 \\$overlay of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend constructor expects bool, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#3 \\$plotOrder of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeries constructor expects array\\<int\\>, array\\<int\\|string, bool\\|float\\|int\\|string\\|null\\> given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#4 \\$pointCount of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues constructor expects int, null given\\.$#"
count: 4
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#6 \\$displayBlanksAs of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart constructor expects string, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#7 \\$plotDirection of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeries constructor expects string\\|null, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\ColumnAndRowAttributes\\:\\:isFilteredColumn\\(\\) has no return type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\ColumnAndRowAttributes\\:\\:isFilteredColumn\\(\\) has no return type specified\\.$#"
count: 1 count: 1

View File

@ -1645,7 +1645,8 @@ class Xlsx extends BaseReader
if ($this->includeCharts) { if ($this->includeCharts) {
$chartEntryRef = ltrim((string) $contentType['PartName'], '/'); $chartEntryRef = ltrim((string) $contentType['PartName'], '/');
$chartElements = $this->loadZip($chartEntryRef); $chartElements = $this->loadZip($chartEntryRef);
$objChart = Chart::readChart($chartElements, basename($chartEntryRef, '.xml')); $chartReader = new Chart();
$objChart = $chartReader->readChart($chartElements, basename($chartEntryRef, '.xml'));
if (isset($charts[$chartEntryRef])) { if (isset($charts[$chartEntryRef])) {
$chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id']; $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
if (isset($chartDetails[$chartPositionRef])) { if (isset($chartDetails[$chartPositionRef])) {

View File

@ -16,6 +16,18 @@ use SimpleXMLElement;
class Chart class Chart
{ {
/** @var string */
private $cNamespace;
/** @var string */
private $aNamespace;
public function __construct(string $cNamespace = Namespaces::CHART, string $aNamespace = Namespaces::DRAWINGML)
{
$this->cNamespace = $cNamespace;
$this->aNamespace = $aNamespace;
}
/** /**
* @param string $name * @param string $name
* @param string $format * @param string $format
@ -47,10 +59,9 @@ class Chart
* *
* @return \PhpOffice\PhpSpreadsheet\Chart\Chart * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
*/ */
public static function readChart(SimpleXMLElement $chartElements, $chartName) public function readChart(SimpleXMLElement $chartElements, $chartName)
{ {
$namespacesChartMeta = $chartElements->getNamespaces(true); $chartElementsC = $chartElements->children($this->cNamespace);
$chartElementsC = $chartElements->children($namespacesChartMeta['c']);
$XaxisLabel = $YaxisLabel = $legend = $title = null; $XaxisLabel = $YaxisLabel = $legend = $title = null;
$dispBlanksAs = $plotVisOnly = null; $dispBlanksAs = $plotVisOnly = null;
@ -60,7 +71,7 @@ class Chart
switch ($chartElementKey) { switch ($chartElementKey) {
case 'chart': case 'chart':
foreach ($chartElement as $chartDetailsKey => $chartDetails) { foreach ($chartElement as $chartDetailsKey => $chartDetails) {
$chartDetailsC = $chartDetails->children($namespacesChartMeta['c']); $chartDetailsC = $chartDetails->children($this->cNamespace);
switch ($chartDetailsKey) { switch ($chartDetailsKey) {
case 'view3D': case 'view3D':
$rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer'); $rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer');
@ -75,24 +86,24 @@ class Chart
foreach ($chartDetails as $chartDetailKey => $chartDetail) { foreach ($chartDetails as $chartDetailKey => $chartDetail) {
switch ($chartDetailKey) { switch ($chartDetailKey) {
case 'layout': case 'layout':
$plotAreaLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta); $plotAreaLayout = $this->chartLayoutDetails($chartDetail);
break; break;
case 'catAx': case 'catAx':
if (isset($chartDetail->title)) { if (isset($chartDetail->title)) {
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
} }
break; break;
case 'dateAx': case 'dateAx':
if (isset($chartDetail->title)) { if (isset($chartDetail->title)) {
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
} }
break; break;
case 'valAx': case 'valAx':
if (isset($chartDetail->title, $chartDetail->axPos)) { if (isset($chartDetail->title, $chartDetail->axPos)) {
$axisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); $axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
$axPos = self::getAttribute($chartDetail->axPos, 'val', 'string'); $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
switch ($axPos) { switch ($axPos) {
@ -113,70 +124,72 @@ class Chart
case 'barChart': case 'barChart':
case 'bar3DChart': case 'bar3DChart':
$barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string'); $barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotDirection($barDirection); $plotSer->setPlotDirection("$barDirection");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'lineChart': case 'lineChart':
case 'line3DChart': case 'line3DChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'areaChart': case 'areaChart':
case 'area3DChart': case 'area3DChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'doughnutChart': case 'doughnutChart':
case 'pieChart': case 'pieChart':
case 'pie3DChart': case 'pie3DChart':
$explosion = isset($chartDetail->ser->explosion); $explosion = isset($chartDetail->ser->explosion);
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($explosion); $plotSer->setPlotStyle("$explosion");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'scatterChart': case 'scatterChart':
/** @var string */
$scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string'); $scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($scatterStyle); $plotSer->setPlotStyle($scatterStyle);
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'bubbleChart': case 'bubbleChart':
$bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer'); $bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($bubbleScale); $plotSer->setPlotStyle("$bubbleScale");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'radarChart': case 'radarChart':
/** @var string */
$radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string'); $radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($radarStyle); $plotSer->setPlotStyle($radarStyle);
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'surfaceChart': case 'surfaceChart':
case 'surface3DChart': case 'surface3DChart':
$wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean'); $wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($wireFrame); $plotSer->setPlotStyle("$wireFrame");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'stockChart': case 'stockChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = self::readChartAttributes($plotAreaLayout); $plotAttributes = $this->readChartAttributes($plotAreaLayout);
break; break;
} }
@ -185,7 +198,7 @@ class Chart
$plotAreaLayout = new Layout(); $plotAreaLayout = new Layout();
} }
$plotArea = new PlotArea($plotAreaLayout, $plotSeries); $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
self::setChartAttributes($plotAreaLayout, $plotAttributes); $this->setChartAttributes($plotAreaLayout, $plotAttributes);
break; break;
case 'plotVisOnly': case 'plotVisOnly':
@ -197,7 +210,7 @@ class Chart
break; break;
case 'title': case 'title':
$title = self::chartTitle($chartDetails, $namespacesChartMeta); $title = $this->chartTitle($chartDetails);
break; break;
case 'legend': case 'legend':
@ -215,19 +228,19 @@ class Chart
break; break;
case 'layout': case 'layout':
$legendLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta); $legendLayout = $this->chartLayoutDetails($chartDetail);
break; break;
} }
} }
$legend = new Legend($legendPos, $legendLayout, $legendOverlay); $legend = new Legend("$legendPos", $legendLayout, (bool) $legendOverlay);
break; break;
} }
} }
} }
} }
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel); $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel);
if (is_int($rotX)) { if (is_int($rotX)) {
$chart->setRotX($rotX); $chart->setRotX($rotX);
} }
@ -244,25 +257,25 @@ class Chart
return $chart; return $chart;
} }
private static function chartTitle(SimpleXMLElement $titleDetails, array $namespacesChartMeta) private function chartTitle(SimpleXMLElement $titleDetails): Title
{ {
$caption = []; $caption = [];
$titleLayout = null; $titleLayout = null;
foreach ($titleDetails as $titleDetailKey => $chartDetail) { foreach ($titleDetails as $titleDetailKey => $chartDetail) {
switch ($titleDetailKey) { switch ($titleDetailKey) {
case 'tx': case 'tx':
$titleDetails = $chartDetail->rich->children($namespacesChartMeta['a']); $titleDetails = $chartDetail->rich->children($this->aNamespace);
foreach ($titleDetails as $titleKey => $titleDetail) { foreach ($titleDetails as $titleKey => $titleDetail) {
switch ($titleKey) { switch ($titleKey) {
case 'p': case 'p':
$titleDetailPart = $titleDetail->children($namespacesChartMeta['a']); $titleDetailPart = $titleDetail->children($this->aNamespace);
$caption[] = self::parseRichText($titleDetailPart); $caption[] = $this->parseRichText($titleDetailPart);
} }
} }
break; break;
case 'layout': case 'layout':
$titleLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta); $titleLayout = $this->chartLayoutDetails($chartDetail);
break; break;
} }
@ -271,12 +284,12 @@ class Chart
return new Title($caption, $titleLayout); return new Title($caption, $titleLayout);
} }
private static function chartLayoutDetails($chartDetail, $namespacesChartMeta) private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
{ {
if (!isset($chartDetail->manualLayout)) { if (!isset($chartDetail->manualLayout)) {
return null; return null;
} }
$details = $chartDetail->manualLayout->children($namespacesChartMeta['c']); $details = $chartDetail->manualLayout->children($this->cNamespace);
if ($details === null) { if ($details === null) {
return null; return null;
} }
@ -288,13 +301,13 @@ class Chart
return new Layout($layout); return new Layout($layout);
} }
private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType) private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType): DataSeries
{ {
$multiSeriesType = null; $multiSeriesType = null;
$smoothLine = false; $smoothLine = false;
$seriesLabel = $seriesCategory = $seriesValues = $plotOrder = []; $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = [];
$seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']); $seriesDetailSet = $chartDetail->children($this->cNamespace);
foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) { foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
switch ($seriesDetailKey) { switch ($seriesDetailKey) {
case 'grouping': case 'grouping':
@ -322,11 +335,11 @@ class Chart
break; break;
case 'tx': case 'tx':
$seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta); $seriesLabel[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
break; break;
case 'spPr': case 'spPr':
$children = $seriesDetail->children($namespacesChartMeta['a']); $children = $seriesDetail->children($this->aNamespace);
$ln = $children->ln; $ln = $children->ln;
$lineWidth = self::getAttribute($ln, 'w', 'string'); $lineWidth = self::getAttribute($ln, 'w', 'string');
if (is_countable($ln->noFill) && count($ln->noFill) === 1) { if (is_countable($ln->noFill) && count($ln->noFill) === 1) {
@ -343,9 +356,9 @@ class Chart
$pointSize = self::getAttribute($seriesDetail->size, 'val', 'string'); $pointSize = self::getAttribute($seriesDetail->size, 'val', 'string');
$pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null; $pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
if (count($seriesDetail->spPr) === 1) { if (count($seriesDetail->spPr) === 1) {
$ln = $seriesDetail->spPr->children($namespacesChartMeta['a']); $ln = $seriesDetail->spPr->children($this->aNamespace);
if (count($ln->solidFill) === 1) { if (count($ln->solidFill) === 1) {
$srgbClr = self::getattribute($ln->solidFill->srgbClr, 'val', 'string'); $srgbClr = self::getAttribute($ln->solidFill->srgbClr, 'val', 'string');
} }
} }
@ -355,19 +368,19 @@ class Chart
break; break;
case 'cat': case 'cat':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta); $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
break; break;
case 'val': case 'val':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize"); $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break; break;
case 'xVal': case 'xVal':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize"); $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break; break;
case 'yVal': case 'yVal':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, "$marker", "$srgbClr", "$pointSize"); $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break; break;
case 'bubble3D': case 'bubble3D':
@ -423,17 +436,21 @@ class Chart
} }
} }
/** @phpstan-ignore-next-line */
return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine); return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
} }
private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, ?string $marker = null, ?string $srgbClr = null, ?string $pointSize = null) /**
* @return mixed
*/
private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?string $srgbClr = null, ?string $pointSize = null)
{ {
if (isset($seriesDetail->strRef)) { if (isset($seriesDetail->strRef)) {
$seriesSource = (string) $seriesDetail->strRef->f; $seriesSource = (string) $seriesDetail->strRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize"); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->strRef->strCache)) { if (isset($seriesDetail->strRef->strCache)) {
$seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's'); $seriesData = $this->chartDataSeriesValues($seriesDetail->strRef->strCache->children($this->cNamespace), 's');
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -442,9 +459,9 @@ class Chart
return $seriesValues; return $seriesValues;
} elseif (isset($seriesDetail->numRef)) { } elseif (isset($seriesDetail->numRef)) {
$seriesSource = (string) $seriesDetail->numRef->f; $seriesSource = (string) $seriesDetail->numRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize"); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->numRef->numCache)) { if (isset($seriesDetail->numRef->numCache)) {
$seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c'])); $seriesData = $this->chartDataSeriesValues($seriesDetail->numRef->numCache->children($this->cNamespace));
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -453,10 +470,10 @@ class Chart
return $seriesValues; return $seriesValues;
} elseif (isset($seriesDetail->multiLvlStrRef)) { } elseif (isset($seriesDetail->multiLvlStrRef)) {
$seriesSource = (string) $seriesDetail->multiLvlStrRef->f; $seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize"); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) { if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's'); $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($this->cNamespace), 's');
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -465,10 +482,10 @@ class Chart
return $seriesValues; return $seriesValues;
} elseif (isset($seriesDetail->multiLvlNumRef)) { } elseif (isset($seriesDetail->multiLvlNumRef)) {
$seriesSource = (string) $seriesDetail->multiLvlNumRef->f; $seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker, $srgbClr, "$pointSize"); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) { if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's'); $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($this->cNamespace), 's');
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -480,7 +497,7 @@ class Chart
return null; return null;
} }
private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n') private function chartDataSeriesValues(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
{ {
$seriesVal = []; $seriesVal = [];
$formatCode = ''; $formatCode = '';
@ -500,7 +517,7 @@ class Chart
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer'); $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
if ($dataType == 's') { if ($dataType == 's') {
$seriesVal[$pointVal] = (string) $seriesValue->v; $seriesVal[$pointVal] = (string) $seriesValue->v;
} elseif ($seriesValue->v === ExcelError::NA()) { } elseif ((string) $seriesValue->v === ExcelError::NA()) {
$seriesVal[$pointVal] = null; $seriesVal[$pointVal] = null;
} else { } else {
$seriesVal[$pointVal] = (float) $seriesValue->v; $seriesVal[$pointVal] = (float) $seriesValue->v;
@ -517,7 +534,7 @@ class Chart
]; ];
} }
private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n') private function chartDataSeriesValuesMultiLevel(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
{ {
$seriesVal = []; $seriesVal = [];
$formatCode = ''; $formatCode = '';
@ -538,7 +555,7 @@ class Chart
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer'); $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
if ($dataType == 's') { if ($dataType == 's') {
$seriesVal[$pointVal][] = (string) $seriesValue->v; $seriesVal[$pointVal][] = (string) $seriesValue->v;
} elseif ($seriesValue->v === ExcelError::NA()) { } elseif ((string) $seriesValue->v === ExcelError::NA()) {
$seriesVal[$pointVal] = null; $seriesVal[$pointVal] = null;
} else { } else {
$seriesVal[$pointVal][] = (float) $seriesValue->v; $seriesVal[$pointVal][] = (float) $seriesValue->v;
@ -556,7 +573,7 @@ class Chart
]; ];
} }
private static function parseRichText(SimpleXMLElement $titleDetailPart) private function parseRichText(SimpleXMLElement $titleDetailPart): RichText
{ {
$value = new RichText(); $value = new RichText();
$objText = null; $objText = null;
@ -773,7 +790,10 @@ class Chart
return $value; return $value;
} }
private static function readChartAttributes($chartDetail) /**
* @param null|Layout|SimpleXMLElement $chartDetail
*/
private function readChartAttributes($chartDetail): array
{ {
$plotAttributes = []; $plotAttributes = [];
if (isset($chartDetail->dLbls)) { if (isset($chartDetail->dLbls)) {
@ -806,7 +826,7 @@ class Chart
/** /**
* @param mixed $plotAttributes * @param mixed $plotAttributes
*/ */
private static function setChartAttributes(Layout $plotArea, $plotAttributes): void private function setChartAttributes(Layout $plotArea, $plotAttributes): void
{ {
foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) { foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
switch ($plotAttributeKey) { switch ($plotAttributeKey) {

View File

@ -963,7 +963,7 @@ class Chart extends WriterPart
if ($id1 !== '0') { if ($id1 !== '0') {
$objWriter->startElement('c:crossAx'); $objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2); $objWriter->writeAttribute('val', $id1);
$objWriter->endElement(); $objWriter->endElement();
if ($xAxis->getAxisOptionsProperty('horizontal_crosses_value') !== null) { if ($xAxis->getAxisOptionsProperty('horizontal_crosses_value') !== null) {