Fix cell ranges causing coordinate merge error

Fixes #319
Closes #328
This commit is contained in:
MaxTingle 2018-01-09 21:31:06 +00:00 committed by Adrien Crivelli
parent 4e0344c3af
commit 49775bd972
No known key found for this signature in database
GPG Key ID: B182FD79DC6DE92E
7 changed files with 170 additions and 98 deletions

View File

@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Freeze Panes takes wrong coordinates for XLSX - [#322](https://github.com/PHPOffice/PhpSpreadsheet/issues/322)
- `COLUMNS` and `ROWS` functions crashed in some cases - [#336](https://github.com/PHPOffice/PhpSpreadsheet/issues/336)
- Support XML file without styles - [#331](https://github.com/PHPOffice/PhpSpreadsheet/pull/331)
- Cell coordinates which are already a range cause an exception [#319](https://github.com/PHPOffice/PhpSpreadsheet/issues/319)
## [1.0.0] - 2017-12-25

View File

@ -32,7 +32,7 @@ abstract class Coordinate
{
if (preg_match("/^([$]?[A-Z]{1,3})([$]?\d{1,7})$/", $pCoordinateString, $matches)) {
return [$matches[1], $matches[2]];
} elseif ((strpos($pCoordinateString, ':') !== false) || (strpos($pCoordinateString, ',') !== false)) {
} elseif (self::coordinateIsRange($pCoordinateString)) {
throw new Exception('Cell coordinate string can not be a range of cells');
} elseif ($pCoordinateString == '') {
throw new Exception('Cell coordinate can not be zero-length string');
@ -41,6 +41,18 @@ abstract class Coordinate
throw new Exception('Invalid cell coordinate ' . $pCoordinateString);
}
/**
* Checks if a coordinate represents a range of cells.
*
* @param string $coord eg: 'A1' or 'A1:A2' or 'A1:A2,C1:C2'
*
* @return bool Whether the coordinate represents a range of cells
*/
public static function coordinateIsRange($coord)
{
return (strpos($coord, ':') !== false) || (strpos($coord, ',') !== false);
}
/**
* Make string row, column or cell coordinate absolute.
*
@ -53,7 +65,10 @@ abstract class Coordinate
*/
public static function absoluteReference($pCoordinateString)
{
if (strpos($pCoordinateString, ':') === false && strpos($pCoordinateString, ',') === false) {
if (self::coordinateIsRange($pCoordinateString)) {
throw new Exception('Cell coordinate string can not be a range of cells');
}
// Split out any worksheet name from the reference
$worksheet = '';
$cellAddress = explode('!', $pCoordinateString);
@ -74,9 +89,6 @@ abstract class Coordinate
return $worksheet . self::absoluteCoordinate($pCoordinateString);
}
throw new Exception('Cell coordinate string can not be a range of cells');
}
/**
* Make string coordinate absolute.
*
@ -88,7 +100,10 @@ abstract class Coordinate
*/
public static function absoluteCoordinate($pCoordinateString)
{
if (strpos($pCoordinateString, ':') === false && strpos($pCoordinateString, ',') === false) {
if (self::coordinateIsRange($pCoordinateString)) {
throw new Exception('Cell coordinate string can not be a range of cells');
}
// Split out any worksheet name from the coordinate
$worksheet = '';
$cellAddress = explode('!', $pCoordinateString);
@ -107,9 +122,6 @@ abstract class Coordinate
return $worksheet . '$' . $column . '$' . $row;
}
throw new Exception('Cell coordinate string can not be a range of cells');
}
/**
* Split range into coordinate strings.
*
@ -330,7 +342,7 @@ abstract class Coordinate
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($pRange)));
foreach ($cellBlocks as $cellBlock) {
// Single cell?
if (strpos($cellBlock, ':') === false && strpos($cellBlock, ',') === false) {
if (!self::coordinateIsRange($cellBlock)) {
$returnValue[] = $cellBlock;
continue;
@ -400,8 +412,15 @@ abstract class Coordinate
public static function mergeRangesInCollection(array $pCoordCollection)
{
$hashedValues = [];
$mergedCoordCollection = [];
foreach ($pCoordCollection as $coord => $value) {
if (self::coordinateIsRange($coord)) {
$mergedCoordCollection[$coord] = $value;
continue;
}
list($column, $row) = self::coordinateFromString($coord);
$row = (int) (ltrim($row, '$'));
$hashCode = $column . '-' . (is_object($value) ? $value->getHashCode() : $value);
@ -417,7 +436,6 @@ abstract class Coordinate
}
}
$mergedCoordCollection = [];
ksort($hashedValues);
foreach ($hashedValues as $hashedValue) {

View File

@ -760,7 +760,7 @@ class ReferenceHelper
* Update cell reference.
*
* @param string $pCellRange Cell range
* @param int $pBefore Insert before this one
* @param string $pBefore Insert before this one
* @param int $pNumCols Number of columns to increment
* @param int $pNumRows Number of rows to increment
*
@ -774,13 +774,14 @@ class ReferenceHelper
if (strpos($pCellRange, '!') !== false) {
return $pCellRange;
// Is it a range or a single cell?
} elseif (strpos($pCellRange, ':') === false && strpos($pCellRange, ',') === false) {
} elseif (!Coordinate::coordinateIsRange($pCellRange)) {
// Single cell
return $this->updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
} elseif (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false) {
} elseif (Coordinate::coordinateIsRange($pCellRange)) {
// Range
return $this->updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
}
// Return original
return $pCellRange;
}
@ -817,7 +818,7 @@ class ReferenceHelper
* Update cell range.
*
* @param string $pCellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
* @param int $pBefore Insert before this one
* @param string $pBefore Insert before this one
* @param int $pNumCols Number of columns to increment
* @param int $pNumRows Number of rows to increment
*
@ -827,7 +828,10 @@ class ReferenceHelper
*/
private function updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
{
if (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false) {
if (!Coordinate::coordinateIsRange($pCellRange)) {
throw new Exception('Only cell ranges may be passed to this method.');
}
// Update range
$range = Coordinate::splitRange($pCellRange);
$ic = count($range);
@ -850,14 +854,11 @@ class ReferenceHelper
return Coordinate::buildRange($range);
}
throw new Exception('Only cell ranges may be passed to this method.');
}
/**
* Update single cell reference.
*
* @param string $pCellReference Single cell reference
* @param int $pBefore Insert before this one
* @param string $pBefore Insert before this one
* @param int $pNumCols Number of columns to increment
* @param int $pNumRows Number of rows to increment
*
@ -867,7 +868,10 @@ class ReferenceHelper
*/
private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
{
if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) {
if (Coordinate::coordinateIsRange($pCellReference)) {
throw new Exception('Only single cell references may be passed to this method.');
}
// Get coordinate of $pBefore
list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);
@ -892,9 +896,6 @@ class ReferenceHelper
return $newColumn . $newRow;
}
throw new Exception('Only single cell references may be passed to this method.');
}
/**
* __clone implementation. Cloning should not be allowed in a Singleton!
*

View File

@ -1216,7 +1216,7 @@ class Worksheet implements IComparable
// Uppercase coordinate
$pCoordinate = strtoupper($pCoordinate);
if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
if (Coordinate::coordinateIsRange($pCoordinate)) {
throw new Exception('Cell coordinate can not be a range of cells.');
} elseif (strpos($pCoordinate, '$') !== false) {
throw new Exception('Cell coordinate must not be absolute.');
@ -1324,7 +1324,7 @@ class Worksheet implements IComparable
// Uppercase coordinate
$pCoordinate = strtoupper($pCoordinate);
if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
if (Coordinate::coordinateIsRange($pCoordinate)) {
throw new Exception('Cell coordinate can not be a range of cells.');
} elseif (strpos($pCoordinate, '$') !== false) {
throw new Exception('Cell coordinate must not be absolute.');
@ -1993,7 +1993,7 @@ class Worksheet implements IComparable
*/
public function freezePane($cell, $topLeftCell = null)
{
if (is_string($cell) && (strpos($cell, ':') !== false || strpos($cell, ',') !== false)) {
if (is_string($cell) && Coordinate::coordinateIsRange($cell)) {
throw new Exception('Freeze pane can not be set on a range of cells.');
}
@ -2337,7 +2337,7 @@ class Worksheet implements IComparable
// Uppercase coordinate
$pCellCoordinate = strtoupper($pCellCoordinate);
if (strpos($pCellCoordinate, ':') !== false || strpos($pCellCoordinate, ',') !== false) {
if (Coordinate::coordinateIsRange($pCellCoordinate)) {
throw new Exception('Cell coordinate string can not be a range of cells.');
} elseif (strpos($pCellCoordinate, '$') !== false) {
throw new Exception('Cell coordinate string must not be absolute.');
@ -2428,7 +2428,7 @@ class Worksheet implements IComparable
// Convert '1:3' to 'A1:XFD3'
$pCoordinate = preg_replace('/^(\d+):(\d+)$/', 'A${1}:XFD${2}', $pCoordinate);
if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
if (Coordinate::coordinateIsRange($pCoordinate)) {
list($first) = Coordinate::splitRange($pCoordinate);
$this->activeCell = $first[0];
} else {

View File

@ -331,4 +331,20 @@ class CoordinateTest extends TestCase
{
return require 'data/CellMergeRangesInCollection.php';
}
/**
* @dataProvider providerCoordinateIsRange
*
* @param mixed $expectedResult
*/
public function testCoordinateIsRange($expectedResult, ...$args)
{
$result = Coordinate::coordinateIsRange(...$args);
self::assertEquals($expectedResult, $result);
}
public function providerCoordinateIsRange()
{
return require 'data/CoordinateIsRange.php';
}
}

View File

@ -55,4 +55,16 @@ return [
'A3' => 'z',
],
],
[
[
'C1' => 'x',
'A1:A3' => 'x',
'A1:A3,C1:C3' => 'y',
],
[
'C1' => 'x',
'A1:A3' => 'x',
'A1:A3,C1:C3' => 'y',
],
],
];

View File

@ -0,0 +1,24 @@
<?php
return [
[
false,
'A1',
],
[
false,
'$A$1',
],
[
true,
'A1,C3',
],
[
true,
'A1:A10',
],
[
true,
'A1:A10,C4',
],
];