| 1 |
<?php |
| 2 |
|
| 3 |
namespace Box\Spout\Reader\XLSX\Helper; |
| 4 |
|
| 5 |
use Box\Spout\Common\Exception\InvalidArgumentException; |
| 6 |
|
| 7 |
/** |
| 8 |
* Class CellHelper |
| 9 |
* This class provides helper functions when working with cells |
| 10 |
* |
| 11 |
* @package Box\Spout\Reader\XLSX\Helper |
| 12 |
*/ |
| 13 |
class CellHelper |
| 14 |
{ |
| 15 |
// Using ord() is super slow... Using a pre-computed hash table instead. |
| 16 |
private static $columnLetterToIndexMapping = [ |
| 17 |
'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, |
| 18 |
'H' => 7, 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, |
| 19 |
'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, |
| 20 |
'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, |
| 21 |
]; |
| 22 |
|
| 23 |
/** |
| 24 |
* Fills the missing indexes of an array with a given value. |
| 25 |
* For instance, $dataArray = []; $a[1] = 1; $a[3] = 3; |
| 26 |
* Calling fillMissingArrayIndexes($dataArray, 'FILL') will return this array: ['FILL', 1, 'FILL', 3] |
| 27 |
* |
| 28 |
* @param array $dataArray The array to fill |
| 29 |
* @param string|void $fillValue optional |
| 30 |
* @return array |
| 31 |
*/ |
| 32 |
public static function fillMissingArrayIndexes($dataArray, $fillValue = '') |
| 33 |
{ |
| 34 |
if (empty($dataArray)) { |
| 35 |
return []; |
| 36 |
} |
| 37 |
$existingIndexes = array_keys($dataArray); |
| 38 |
|
| 39 |
$newIndexes = array_fill_keys(range(0, max($existingIndexes)), $fillValue); |
| 40 |
$dataArray += $newIndexes; |
| 41 |
|
| 42 |
ksort($dataArray); |
| 43 |
|
| 44 |
return $dataArray; |
| 45 |
} |
| 46 |
|
| 47 |
/** |
| 48 |
* Returns the base 10 column index associated to the cell index (base 26). |
| 49 |
* Excel uses A to Z letters for column indexing, where A is the 1st column, |
| 50 |
* Z is the 26th and AA is the 27th. |
| 51 |
* The mapping is zero based, so that A1 maps to 0, B2 maps to 1, Z13 to 25 and AA4 to 26. |
| 52 |
* |
| 53 |
* @param string $cellIndex The Excel cell index ('A1', 'BC13', ...) |
| 54 |
* @return int |
| 55 |
* @throws \Box\Spout\Common\Exception\InvalidArgumentException When the given cell index is invalid |
| 56 |
*/ |
| 57 |
public static function getColumnIndexFromCellIndex($cellIndex) |
| 58 |
{ |
| 59 |
if (!self::isValidCellIndex($cellIndex)) { |
| 60 |
throw new InvalidArgumentException('Cannot get column index from an invalid cell index.'); |
| 61 |
} |
| 62 |
|
| 63 |
$columnIndex = 0; |
| 64 |
|
| 65 |
// Remove row information |
| 66 |
$columnLetters = preg_replace('/\d/', '', $cellIndex); |
| 67 |
|
| 68 |
// strlen() is super slow too... Using isset() is way faster and not too unreadable, |
| 69 |
// since we checked before that there are between 1 and 3 letters. |
| 70 |
$columnLength = isset($columnLetters[1]) ? (isset($columnLetters[2]) ? 3 : 2) : 1; |
| 71 |
|
| 72 |
// Looping over the different letters of the column is slower than this method. |
| 73 |
// Also, not using the pow() function because it's slooooow... |
| 74 |
switch ($columnLength) { |
| 75 |
case 1: |
| 76 |
$columnIndex = (self::$columnLetterToIndexMapping[$columnLetters]); |
| 77 |
break; |
| 78 |
case 2: |
| 79 |
$firstLetterIndex = (self::$columnLetterToIndexMapping[$columnLetters[0]] + 1) * 26; |
| 80 |
$secondLetterIndex = self::$columnLetterToIndexMapping[$columnLetters[1]]; |
| 81 |
$columnIndex = $firstLetterIndex + $secondLetterIndex; |
| 82 |
break; |
| 83 |
case 3: |
| 84 |
$firstLetterIndex = (self::$columnLetterToIndexMapping[$columnLetters[0]] + 1) * 676; |
| 85 |
$secondLetterIndex = (self::$columnLetterToIndexMapping[$columnLetters[1]] + 1) * 26; |
| 86 |
$thirdLetterIndex = self::$columnLetterToIndexMapping[$columnLetters[2]]; |
| 87 |
$columnIndex = $firstLetterIndex + $secondLetterIndex + $thirdLetterIndex; |
| 88 |
break; |
| 89 |
} |
| 90 |
|
| 91 |
return $columnIndex; |
| 92 |
} |
| 93 |
|
| 94 |
/** |
| 95 |
* Returns whether a cell index is valid, in an Excel world. |
| 96 |
* To be valid, the cell index should start with capital letters and be followed by numbers. |
| 97 |
* There can only be 3 letters, as there can only be 16,384 rows, which is equivalent to 'XFE'. |
| 98 |
* |
| 99 |
* @param string $cellIndex The Excel cell index ('A1', 'BC13', ...) |
| 100 |
* @return bool |
| 101 |
*/ |
| 102 |
protected static function isValidCellIndex($cellIndex) |
| 103 |
{ |
| 104 |
return (preg_match('/^[A-Z]{1,3}\d+$/', $cellIndex) === 1); |
| 105 |
} |
| 106 |
} |
| 107 |
|