Source for file ReferenceHelper.php
Documentation is available at ReferenceHelper.php
* Copyright (c) 2006 - 2008 PHPExcel
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* @copyright Copyright (c) 2006 - 2008 PHPExcel (http://www.codeplex.com/PHPExcel)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
* @version 1.6.4, 2008-10-27
/** PHPExcel_Worksheet */
require_once 'PHPExcel/Worksheet.php';
require_once 'PHPExcel/Cell.php';
/** PHPExcel_Cell_DataType */
require_once 'PHPExcel/Cell/DataType.php';
require_once 'PHPExcel/Style.php';
/** PHPExcel_Worksheet_Drawing */
require_once 'PHPExcel/Worksheet/Drawing.php';
/** PHPExcel_Calculation_FormulaParser */
require_once 'PHPExcel/Calculation/FormulaParser.php';
/** PHPExcel_Calculation_FormulaToken */
require_once 'PHPExcel/Calculation/FormulaToken.php';
* PHPExcel_ReferenceHelper (Singleton)
* @copyright Copyright (c) 2006 - 2008 PHPExcel (http://www.codeplex.com/PHPExcel)
* @var PHPExcel_ReferenceHelper
private static $_instance;
* Get an instance of this class
* @return PHPExcel_ReferenceHelper
if (!isset (self::$_instance) || is_null(self::$_instance)) {
self::$_instance = new PHPExcel_ReferenceHelper();
* Create a new PHPExcel_Calculation
* Insert a new column, updating all possible related data
* @param int $pBefore Insert before this one
* @param int $pNumCols Number of columns to insert
* @param int $pNumRows Number of rows to insert
public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = null) {
// Get a copy of the cell collection
/*$aTemp = $pSheet->getCellCollection();
$aCellCollection = array();
foreach ($aTemp as $key => $value) {
$aCellCollection[$key] = clone $value;
$aCellCollection = $pSheet->getCellCollection();
// Get coordinates of $pBefore
list ($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
$highestColumn = $pSheet->getHighestColumn();
$highestRow = $pSheet->getHighestRow();
for ($i = 1; $i <= $highestRow - 1; ++ $i) {
if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
$pSheet->duplicateStyle( new PHPExcel_Style(), ($i. ($beforeRow + $pNumRows)). ':'. ($i. ($beforeRow - 1)));
// Loop trough cells, bottom-up, and change cell coordinates
while ( ($cell = ($pNumCols < 0 || $pNumRows < 0) ? array_shift($aCellCollection) : array_pop($aCellCollection)) ) {
// Should the cell be updated?
($cell->getRow() >= $beforeRow)
$pSheet->duplicateStyle( $pSheet->getStyle($cell->getCoordinate()), $newCoordinates . ':' . $newCoordinates );
$pSheet->duplicateStyle( $pSheet->getDefaultStyle(), $cell->getCoordinate() . ':' . $cell->getCoordinate() );
// Insert this cell at its new location
// Formula should be adjusted
// Formula should not be adjusted
$pSheet->setCellValue($newCoordinates, $cell->getValue());
// Clear the original cell
$pSheet->setCellValue($cell->getCoordinate(), '');
// Duplicate styles for the newly inserted cells
$highestColumn = $pSheet->getHighestColumn();
$highestRow = $pSheet->getHighestRow();
for ($i = $beforeRow; $i <= $highestRow - 1; ++ $i) {
if ($pNumRows > 0 && $beforeRow - 1 > 0) {
// Update worksheet: column dimensions
$aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
foreach ($aColumnDimensions as $objColumnDimension) {
$newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
if ($objColumnDimension->getColumnIndex() != $newReference) {
$objColumnDimension->setColumnIndex($newReference);
$pSheet->refreshColumnDimensions();
// Update worksheet: row dimensions
$aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
foreach ($aRowDimensions as $objRowDimension) {
$newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
if ($objRowDimension->getRowIndex() != $newReference) {
$objRowDimension->setRowIndex($newReference);
$pSheet->refreshRowDimensions();
$copyDimension = $pSheet->getRowDimension($beforeRow - 1);
for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++ $i) {
$newDimension = $pSheet->getRowDimension($i);
$newDimension->setRowHeight($copyDimension->getRowHeight());
$newDimension->setVisible($copyDimension->getVisible());
$newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
$newDimension->setCollapsed($copyDimension->getCollapsed());
// Update worksheet: breaks
foreach ($aBreaks as $key => $value) {
if ($key != $newReference) {
$pSheet->setBreak( $newReference, $value );
// Update worksheet: merge cells
foreach ($aMergeCells as $key => $value) {
if ($key != $newReference) {
$pSheet->mergeCells( $newReference );
$pSheet->unmergeCells( $key );
// Update worksheet: protected cells
$aProtectedCells = array_reverse($pSheet->getProtectedCells(), true);
foreach ($aProtectedCells as $key => $value) {
if ($key != $newReference) {
$pSheet->protectCells( $newReference, $value, true );
$pSheet->unprotectCells( $key );
// Update worksheet: autofilter
if ($pSheet->getAutoFilter() != '') {
$pSheet->setAutoFilter( $this->updateCellReference($pSheet->getAutoFilter(), $pBefore, $pNumCols, $pNumRows) );
// Update worksheet: freeze pane
if ($pSheet->getFreezePane() != '') {
$pSheet->setFreezePane( $this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows) );
if ($pSheet->getPageSetup()->isPrintAreaSet()) {
$pSheet->getPageSetup()->setPrintArea( $this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows) );
// Update worksheet: drawings
$aDrawings = $pSheet->getDrawingCollection();
foreach ($aDrawings as $objDrawing) {
$newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
if ($objDrawing->getCoordinates() != $newReference) {
$objDrawing->setCoordinates($newReference);
// Update workbook: named ranges
if (count($pSheet->getParent()->getNamedRanges()) > 0) {
foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
$pSheet->garbageCollect();
* Update references within formulas
* @param string $pFormula Formula to update
* @param int $pBefore Insert before this one
* @param int $pNumCols Number of columns to insert
* @param int $pNumRows Number of rows to insert
* @return string Updated formula
$executableFormulaArray = array();
// Parse formula into a tree of tokens
// Loop trough parsed tokens and create an executable formula
$tokenCount = $objParser->getTokenCount();
for ($i = 0; $i < $tokenCount; ++ $i) {
$token = $objParser->getToken($i);
// Is it a cell reference? Not a cell range?
$newCellReference = $this->updateCellReference($token->getValue(), $pBefore, $pNumCols, $pNumRows);
// Add adjusted cell coordinate to executable formula array
array_push($executableFormulaArray, $newCellReference);
// Is it a subexpression?
switch($token->getTokenSubType()) {
// Add to executable formula array
// Check the function type
if ($token->getValue() == 'ARRAY' || $token->getValue() == 'ARRAYROW') {
// An array or an array row...
// A regular function call...
switch($token->getTokenSubType()) {
// Add to executable formula array
// Add to executable formula array
array_push($executableFormulaArray, '"' . $token->getValue() . '"');
array_push($executableFormulaArray, $token->getValue());
return '=' . implode(' ', $executableFormulaArray);
* @param string $pCellRange Cell range
* @param int $pBefore Insert before this one
* @param int $pNumCols Number of columns to increment
* @param int $pNumRows Number of rows to increment
* @return string Updated cell range
public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
// Is it a range or a single cell?
if (strpos($pCellRange, ':') === false) {
* @param string $pCellRange Cell range
* @param int $pBefore Insert before this one
* @param int $pNumCols Number of columns to increment
* @param int $pNumRows Number of rows to increment
* @return string Updated cell range
private function _updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
if (strpos($pCellRange,':') !== false) {
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 int $pNumCols Number of columns to increment
* @param int $pNumRows Number of rows to increment
* @return string Updated cell reference
if (strpos($pCellReference, ':') === false) {
// Get coordinates of $pBefore
// Verify which parts should be updated
&& (strpos($newColumn, '$') === false)
&& (strpos($beforeColumn, '$') === false);
$updateRow = ($newRow >= $beforeRow)
&& (strpos($newRow, '$') === false)
&& (strpos($beforeRow, '$') === false);
// Create new column reference
// Create new row reference
$newRow = $newRow + $pNumRows;
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!
throw new Exception("Cloning a Singleton is not allowed!");
|