Memory Leak in Sample35 (#3062)

* Memory Leak in Sample35

All but 6 chart samples can be rendered by Sample35. Of those 6, 3 of the problems are because the script runs out of memory processing them. Adopting a suggestion from @MAKS-dev in issue #2092, adding a call to gc_collect_cycles after the charts from each spreadsheet is rendered appears to make it possible to include those 3 spreadsheets in Sample35 after all.

Also take advantage of this opportunity to correct a number (hopefully all) of Scrutinizer problems with JpgraphRendererBases.

* Minor Fix

Problem running 8.1 unit tests.

* Resolve Problems with Pie 3D Charts

Minor fix, leaving only one spreadsheet unusable in Sample35. The reasons for its unusability are now documented in the code.

* Mitoteam Made Changes

Discussing this problem with them, they decided they should make a change for Pie3D rather than forcing us to use the workaround pushed earlier. Change to require mitoteam 10.2.3, revert workaround.
This commit is contained in:
oleibman 2022-09-14 10:12:53 -07:00 committed by GitHub
parent 6c1651e995
commit 8513c6418c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 54 deletions

View File

@ -81,7 +81,7 @@
"dealerdirect/phpcodesniffer-composer-installer": "dev-master", "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
"dompdf/dompdf": "^1.0 || ^2.0", "dompdf/dompdf": "^1.0 || ^2.0",
"friendsofphp/php-cs-fixer": "^3.2", "friendsofphp/php-cs-fixer": "^3.2",
"mitoteam/jpgraph": "10.2.2", "mitoteam/jpgraph": "10.2.3",
"mpdf/mpdf": "8.1.1", "mpdf/mpdf": "8.1.1",
"phpcompatibility/php-compatibility": "^9.3", "phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^1.1", "phpstan/phpstan": "^1.1",

16
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "8512207f173cb137bc2085b7fdf698bc", "content-hash": "b5bdb9f96d18ce59557436521053fdd9",
"packages": [ "packages": [
{ {
"name": "ezyang/htmlpurifier", "name": "ezyang/htmlpurifier",
@ -1342,16 +1342,16 @@
}, },
{ {
"name": "mitoteam/jpgraph", "name": "mitoteam/jpgraph",
"version": "10.2.2", "version": "10.2.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mitoteam/jpgraph.git", "url": "https://github.com/mitoteam/jpgraph.git",
"reference": "6d87fc342afaf8a9a898a3122b5f0f34fc82c4cf" "reference": "21121535537e05c32e7964327b80746462a6057d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mitoteam/jpgraph/zipball/6d87fc342afaf8a9a898a3122b5f0f34fc82c4cf", "url": "https://api.github.com/repos/mitoteam/jpgraph/zipball/21121535537e05c32e7964327b80746462a6057d",
"reference": "6d87fc342afaf8a9a898a3122b5f0f34fc82c4cf", "reference": "21121535537e05c32e7964327b80746462a6057d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1375,16 +1375,16 @@
"name": "JpGraph team" "name": "JpGraph team"
} }
], ],
"description": "Composer compatible version of JpGraph library with PHP 8.2 support", "description": "JpGraph library composer package with PHP 8.2 support",
"homepage": "https://github.com/mitoteam/jpgraph", "homepage": "https://github.com/mitoteam/jpgraph",
"keywords": [ "keywords": [
"jpgraph" "jpgraph"
], ],
"support": { "support": {
"issues": "https://github.com/mitoteam/jpgraph/issues", "issues": "https://github.com/mitoteam/jpgraph/issues",
"source": "https://github.com/mitoteam/jpgraph/tree/10.2.2" "source": "https://github.com/mitoteam/jpgraph/tree/10.2.3"
}, },
"time": "2022-09-09T08:16:10+00:00" "time": "2022-09-14T04:02:09+00:00"
}, },
{ {
"name": "mpdf/mpdf", "name": "mpdf/mpdf",

View File

@ -25,12 +25,10 @@ if (count($inputFileNames) === 1) {
$unresolvedErrors = []; $unresolvedErrors = [];
} else { } else {
$unresolvedErrors = [ $unresolvedErrors = [
// The following spreadsheet was created by 3rd party software,
// and doesn't include the data that usually accompanies a chart.
// That is good enough for Excel, but not for JpGraph.
'32readwriteBubbleChart2.xlsx', '32readwriteBubbleChart2.xlsx',
'32readwritePieChart3.xlsx',
'32readwritePieChart4.xlsx',
'32readwritePieChart3D1.xlsx',
'32readwritePieChartExploded1.xlsx',
'32readwritePieChartExploded3D1.xlsx',
]; ];
} }
foreach ($inputFileNames as $inputFileName) { foreach ($inputFileNames as $inputFileName) {
@ -42,7 +40,9 @@ foreach ($inputFileNames as $inputFileName) {
continue; continue;
} }
if (in_array($inputFileNameShort, $unresolvedErrors, true)) { if (in_array($inputFileNameShort, $unresolvedErrors, true)) {
$helper->log('File ' . $inputFileNameShort . ' does not yet work with this script'); $helper->log('*****');
$helper->log('***** File ' . $inputFileNameShort . ' does not yet work with this script');
$helper->log('*****');
continue; continue;
} }
@ -92,6 +92,7 @@ foreach ($inputFileNames as $inputFileName) {
$spreadsheet->disconnectWorksheets(); $spreadsheet->disconnectWorksheets();
unset($spreadsheet); unset($spreadsheet);
gc_collect_cycles();
} }
$helper->log('Done rendering charts as images'); $helper->log('Done rendering charts as images');

View File

@ -102,13 +102,11 @@ abstract class JpGraphRendererBase implements IRenderer
return $seriesPlot; return $seriesPlot;
} }
private function formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation = '') private function formatDataSetLabels($groupID, $datasetLabels, $rotation = '')
{ {
$datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode(); $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode() ?? '';
if ($datasetLabelFormatCode !== null) { // Retrieve any label formatting code
// Retrieve any label formatting code $datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
$datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
}
$testCurrentIndex = 0; $testCurrentIndex = 0;
foreach ($datasetLabels as $i => $datasetLabel) { foreach ($datasetLabels as $i => $datasetLabel) {
@ -273,7 +271,7 @@ abstract class JpGraphRendererBase implements IRenderer
$this->renderTitle(); $this->renderTitle();
} }
private function renderPlotLine($groupID, $filled = false, $combination = false, $dimensions = '2d'): void private function renderPlotLine($groupID, $filled = false, $combination = false): void
{ {
$grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping(); $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
@ -281,7 +279,7 @@ abstract class JpGraphRendererBase implements IRenderer
$labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointCount(); $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointCount();
if ($labelCount > 0) { if ($labelCount > 0) {
$datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
$datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount); $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels);
$this->graph->xaxis->SetTickLabels($datasetLabels); $this->graph->xaxis->SetTickLabels($datasetLabels);
} }
@ -353,7 +351,7 @@ abstract class JpGraphRendererBase implements IRenderer
$labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointCount(); $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointCount();
if ($labelCount > 0) { if ($labelCount > 0) {
$datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
$datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation); $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $rotation);
// Rotate for bar rather than column chart // Rotate for bar rather than column chart
if ($rotation == 'bar') { if ($rotation == 'bar') {
$datasetLabels = array_reverse($datasetLabels); $datasetLabels = array_reverse($datasetLabels);
@ -430,11 +428,9 @@ abstract class JpGraphRendererBase implements IRenderer
private function renderPlotScatter($groupID, $bubble): void private function renderPlotScatter($groupID, $bubble): void
{ {
$grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
$scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
$seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
$seriesPlots = [];
// Loop through each data series in turn // Loop through each data series in turn
for ($i = 0; $i < $seriesCount; ++$i) { for ($i = 0; $i < $seriesCount; ++$i) {
@ -478,7 +474,6 @@ abstract class JpGraphRendererBase implements IRenderer
$radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
$seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
$seriesPlots = [];
// Loop through each data series in turn // Loop through each data series in turn
for ($i = 0; $i < $seriesCount; ++$i) { for ($i = 0; $i < $seriesCount; ++$i) {
@ -513,15 +508,11 @@ abstract class JpGraphRendererBase implements IRenderer
private function renderPlotContour($groupID): void private function renderPlotContour($groupID): void
{ {
$contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
$seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
$seriesPlots = [];
$dataValues = []; $dataValues = [];
// Loop through each data series in turn // Loop through each data series in turn
for ($i = 0; $i < $seriesCount; ++$i) { for ($i = 0; $i < $seriesCount; ++$i) {
$dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
$dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues(); $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
$dataValues[$i] = $dataValuesX; $dataValues[$i] = $dataValuesX;
@ -565,7 +556,7 @@ abstract class JpGraphRendererBase implements IRenderer
$labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount(); $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
if ($labelCount > 0) { if ($labelCount > 0) {
$datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
$datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount); $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels);
$this->graph->xaxis->SetTickLabels($datasetLabels); $this->graph->xaxis->SetTickLabels($datasetLabels);
} }
@ -575,21 +566,21 @@ abstract class JpGraphRendererBase implements IRenderer
$this->graph->Add($seriesPlot); $this->graph->Add($seriesPlot);
} }
private function renderAreaChart($groupCount, $dimensions = '2d'): void private function renderAreaChart($groupCount): void
{ {
$this->renderCartesianPlotArea(); $this->renderCartesianPlotArea();
for ($i = 0; $i < $groupCount; ++$i) { for ($i = 0; $i < $groupCount; ++$i) {
$this->renderPlotLine($i, true, false, $dimensions); $this->renderPlotLine($i, true, false);
} }
} }
private function renderLineChart($groupCount, $dimensions = '2d'): void private function renderLineChart($groupCount): void
{ {
$this->renderCartesianPlotArea(); $this->renderCartesianPlotArea();
for ($i = 0; $i < $groupCount; ++$i) { for ($i = 0; $i < $groupCount; ++$i) {
$this->renderPlotLine($i, false, false, $dimensions); $this->renderPlotLine($i, false, false);
} }
} }
@ -626,19 +617,17 @@ abstract class JpGraphRendererBase implements IRenderer
$iLimit = ($multiplePlots) ? $groupCount : 1; $iLimit = ($multiplePlots) ? $groupCount : 1;
for ($groupID = 0; $groupID < $iLimit; ++$groupID) { for ($groupID = 0; $groupID < $iLimit; ++$groupID) {
$grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
$exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
$datasetLabels = []; $datasetLabels = [];
if ($groupID == 0) { if ($groupID == 0) {
$labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount(); $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
if ($labelCount > 0) { if ($labelCount > 0) {
$datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues(); $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
$datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $labelCount); $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels);
} }
} }
$seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
$seriesPlots = [];
// For pie charts, we only display the first series: doughnut charts generally display all series // For pie charts, we only display the first series: doughnut charts generally display all series
$jLimit = ($multiplePlots) ? $seriesCount : 1; $jLimit = ($multiplePlots) ? $seriesCount : 1;
// Loop through each data series in turn // Loop through each data series in turn
@ -669,7 +658,7 @@ abstract class JpGraphRendererBase implements IRenderer
$seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4)); $seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4));
} }
if ($doughnut) { if ($doughnut && method_exists($seriesPlot, 'SetMidColor')) {
$seriesPlot->SetMidColor('white'); $seriesPlot->SetMidColor('white');
} }
@ -710,7 +699,7 @@ abstract class JpGraphRendererBase implements IRenderer
} }
} }
private function renderContourChart($groupCount, $dimensions): void private function renderContourChart($groupCount): void
{ {
$this->renderCartesianPlotArea('intint'); $this->renderCartesianPlotArea('intint');
@ -719,7 +708,7 @@ abstract class JpGraphRendererBase implements IRenderer
} }
} }
private function renderCombinationChart($groupCount, $dimensions, $outputDestination) private function renderCombinationChart($groupCount, $outputDestination)
{ {
$this->renderCartesianPlotArea(); $this->renderCartesianPlotArea();
@ -728,10 +717,8 @@ abstract class JpGraphRendererBase implements IRenderer
$chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType(); $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
switch ($chartType) { switch ($chartType) {
case 'area3DChart': case 'area3DChart':
$dimensions = '3d';
// no break
case 'areaChart': case 'areaChart':
$this->renderPlotLine($i, true, true, $dimensions); $this->renderPlotLine($i, true, true);
break; break;
case 'bar3DChart': case 'bar3DChart':
@ -742,10 +729,8 @@ abstract class JpGraphRendererBase implements IRenderer
break; break;
case 'line3DChart': case 'line3DChart':
$dimensions = '3d';
// no break
case 'lineChart': case 'lineChart':
$this->renderPlotLine($i, false, true, $dimensions); $this->renderPlotLine($i, false, true);
break; break;
case 'scatterChart': case 'scatterChart':
@ -792,7 +777,7 @@ abstract class JpGraphRendererBase implements IRenderer
return false; return false;
} else { } else {
return $this->renderCombinationChart($groupCount, $dimensions, $outputDestination); return $this->renderCombinationChart($groupCount, $outputDestination);
} }
} }
@ -801,7 +786,7 @@ abstract class JpGraphRendererBase implements IRenderer
$dimensions = '3d'; $dimensions = '3d';
// no break // no break
case 'areaChart': case 'areaChart':
$this->renderAreaChart($groupCount, $dimensions); $this->renderAreaChart($groupCount);
break; break;
case 'bar3DChart': case 'bar3DChart':
@ -815,7 +800,7 @@ abstract class JpGraphRendererBase implements IRenderer
$dimensions = '3d'; $dimensions = '3d';
// no break // no break
case 'lineChart': case 'lineChart':
$this->renderLineChart($groupCount, $dimensions); $this->renderLineChart($groupCount);
break; break;
case 'pie3DChart': case 'pie3DChart':
@ -845,10 +830,8 @@ abstract class JpGraphRendererBase implements IRenderer
break; break;
case 'surface3DChart': case 'surface3DChart':
$dimensions = '3d';
// no break
case 'surfaceChart': case 'surfaceChart':
$this->renderContourChart($groupCount, $dimensions); $this->renderContourChart($groupCount);
break; break;
case 'stockChart': case 'stockChart':