diff --git a/src/PhpSpreadsheet/Cell/Coordinate.php b/src/PhpSpreadsheet/Cell/Coordinate.php index 506783979..b60691302 100644 --- a/src/PhpSpreadsheet/Cell/Coordinate.php +++ b/src/PhpSpreadsheet/Cell/Coordinate.php @@ -260,13 +260,13 @@ abstract class Coordinate } /** - * Column index from string. + * Return a column index number from a string column address. * - * @param string $columnAddress eg 'A' + * @param string $columnAddress eg 'A', 'AZ', 'BA' or 'IV' * - * @return int Column index (A = 1) + * @return int The column index, offset from 1. (A = 1, Az = 52, BA = 53, IV = 256). */ - public static function columnIndexFromString($columnAddress) + public static function columnIndexFromString(string $columnAddress): int { // Using a lookup cache adds a slight memory overhead, but boosts speed // caching using a static within the method is faster than a class static, @@ -291,6 +291,10 @@ abstract class Coordinate // We also use the language construct isset() rather than the more costly strlen() function to match the // length of $columnAddress for improved performance if (isset($columnAddress[0])) { + if (ctype_alpha($columnAddress) === false) { + throw new Exception('Column string address must be alpha characters only'); + } + if (!isset($columnAddress[1])) { $indexCache[$columnAddress] = $columnLookup[$columnAddress]; @@ -310,22 +314,28 @@ abstract class Coordinate } throw new Exception( - 'Column string index can not be ' . ((isset($columnAddress[0])) ? 'longer than 3 characters' : 'empty') + 'Column string address can not be ' . ((isset($columnAddress[0])) ? 'longer than 3 characters' : 'empty') ); } /** - * String from column index. + * Return a string column address from a column index number. * - * @param int $columnIndex Column index (A = 1) + * @param int $columnIndex the column index, This should always be a positive integer * - * @return string + * @return string The string column address for the specified index. eg (A = 1, Az = 52, BA = 53, IV = 256). */ - public static function stringFromColumnIndex($columnIndex) + public static function stringFromColumnIndex(int $columnIndex): string { static $indexCache = []; static $lookupCache = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + if ($columnIndex <= 0) { + var_dump($columnIndex); + + throw new Exception('Column index must be a positive integer'); + } + if (!isset($indexCache[$columnIndex])) { $indexValue = $columnIndex; $base26 = ''; diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 52df94e4c..6beb7a021 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -280,7 +280,9 @@ class Xlsx extends BaseReader $xml->close(); $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1; - $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); + $tmpInfo['lastColumnLetter'] = ($tmpInfo['lastColumnIndex'] < 0) + ? null + : Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); $worksheetInfo[] = $tmpInfo; } diff --git a/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php b/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php index 8ed234279..283383fa5 100644 --- a/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php +++ b/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php @@ -37,7 +37,7 @@ class CoordinateTest extends TestCase Coordinate::columnIndexFromString($cellAddress); } catch (\Exception $e) { self::assertInstanceOf(Exception::class, $e); - self::assertEquals($e->getMessage(), 'Column string index can not be longer than 3 characters'); + self::assertEquals($e->getMessage(), 'Column string address can not be longer than 3 characters'); return; } @@ -52,7 +52,22 @@ class CoordinateTest extends TestCase Coordinate::columnIndexFromString($cellAddress); } catch (\Exception $e) { self::assertInstanceOf(Exception::class, $e); - self::assertEquals($e->getMessage(), 'Column string index can not be empty'); + self::assertEquals($e->getMessage(), 'Column string address can not be empty'); + + return; + } + self::fail('An expected exception has not been raised.'); + } + + public function testColumnIndexFromInvalidString(): void + { + $cellAddress = '1'; + + try { + Coordinate::columnIndexFromString($cellAddress); + } catch (\Exception $e) { + self::assertInstanceOf(Exception::class, $e); + self::assertEquals($e->getMessage(), 'Column string address must be alpha characters only'); return; } @@ -79,6 +94,21 @@ class CoordinateTest extends TestCase return require 'tests/data/ColumnIndex.php'; } + public function testStringFromInvalidColumnIndex(): void + { + $columnIndex = -1; + + try { + $string = Coordinate::stringFromColumnIndex($columnIndex); + } catch (\Exception $e) { + self::assertInstanceOf(Exception::class, $e); + self::assertEquals($e->getMessage(), 'Column index must be a positive integer'); + + return; + } + self::fail('An expected exception has not been raised.'); + } + /** * @dataProvider providerCoordinates * diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php index cc2269b2a..4e4a83546 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php @@ -99,6 +99,7 @@ class WorksheetInfoNamesTest extends TestCase self::assertSame('Chart1', $chartSheetInfo['worksheetName']); self::assertSame(-1, $chartSheetInfo['lastColumnIndex']); + self::assertNull($chartSheetInfo['lastColumnLetter']); self::assertSame(0, $chartSheetInfo['totalRows']); self::assertSame(0, $chartSheetInfo['totalColumns']); } diff --git a/tests/data/ColumnIndex.php b/tests/data/ColumnIndex.php index 750ef823f..3f585b0c6 100644 --- a/tests/data/ColumnIndex.php +++ b/tests/data/ColumnIndex.php @@ -25,6 +25,10 @@ return [ 'BA', 53, ], + [ + 'BB', + 54, + ], [ 'BZ', 78, @@ -49,4 +53,8 @@ return [ 'BAA', 1379, ], + [ + 'XFD', + 16384, + ], ]; diff --git a/tests/data/ColumnString.php b/tests/data/ColumnString.php index 78d21aaa7..2746162bc 100644 --- a/tests/data/ColumnString.php +++ b/tests/data/ColumnString.php @@ -25,6 +25,10 @@ return [ 53, 'BA', ], + [ + 54, + 'BB', + ], [ 78, 'BZ', @@ -49,4 +53,8 @@ return [ 1379, 'BAA', ], + [ + 16384, + 'XFD', + ], ];