PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/common/libraries/plugin/phpexcel/PHPExcel/ReferenceHelper.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 719 lines | 569 code | 46 blank | 104 comment | 71 complexity | 6ca686cc138dcdff6f1535666d646798 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0
  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2011 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel
  23. * @copyright Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.6, 2011-02-27
  26. */
  27. /**
  28. * PHPExcel_ReferenceHelper (Singleton)
  29. *
  30. * @category PHPExcel
  31. * @package PHPExcel
  32. * @copyright Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  33. */
  34. class PHPExcel_ReferenceHelper
  35. {
  36. /** Constants */
  37. /** Regular Expressions */
  38. const REFHELPER_REGEXP_CELLREF = '((\w*|\'[^!]*\')!)?(?<![:a-z\$])(\$?[a-z]{1,3}\$?\d+)(?=[^:!\d\'])';
  39. const REFHELPER_REGEXP_CELLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}\$?\d+):(\$?[a-z]{1,3}\$?\d+)';
  40. const REFHELPER_REGEXP_ROWRANGE = '((\w*|\'[^!]*\')!)?(\$?\d+):(\$?\d+)';
  41. const REFHELPER_REGEXP_COLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
  42. /**
  43. * Instance of this class
  44. *
  45. * @var PHPExcel_ReferenceHelper
  46. */
  47. private static $_instance;
  48. /**
  49. * Get an instance of this class
  50. *
  51. * @return PHPExcel_ReferenceHelper
  52. */
  53. public static function getInstance()
  54. {
  55. if (! isset(self :: $_instance) || is_null(self :: $_instance))
  56. {
  57. self :: $_instance = new PHPExcel_ReferenceHelper();
  58. }
  59. return self :: $_instance;
  60. }
  61. /**
  62. * Create a new PHPExcel_ReferenceHelper
  63. */
  64. protected function __construct()
  65. {
  66. }
  67. /**
  68. * Insert a new column, updating all possible related data
  69. *
  70. * @param int $pBefore Insert before this one
  71. * @param int $pNumCols Number of columns to insert
  72. * @param int $pNumRows Number of rows to insert
  73. * @throws Exception
  74. */
  75. public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = null)
  76. {
  77. $aCellCollection = $pSheet->getCellCollection();
  78. // Get coordinates of $pBefore
  79. $beforeColumn = 'A';
  80. $beforeRow = 1;
  81. list($beforeColumn, $beforeRow) = PHPExcel_Cell :: coordinateFromString($pBefore);
  82. // Clear cells if we are removing columns or rows
  83. $highestColumn = $pSheet->getHighestColumn();
  84. $highestRow = $pSheet->getHighestRow();
  85. // 1. Clear column strips if we are removing columns
  86. if ($pNumCols < 0 && PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 2 + $pNumCols > 0)
  87. {
  88. for($i = 1; $i <= $highestRow - 1; ++ $i)
  89. {
  90. for($j = PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 1 + $pNumCols; $j <= PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 2; ++ $j)
  91. {
  92. $coordinate = PHPExcel_Cell :: stringFromColumnIndex($j) . $i;
  93. $pSheet->removeConditionalStyles($coordinate);
  94. if ($pSheet->cellExists($coordinate))
  95. {
  96. $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType :: TYPE_NULL);
  97. $pSheet->getCell($coordinate)->setXfIndex(0);
  98. }
  99. }
  100. }
  101. }
  102. // 2. Clear row strips if we are removing rows
  103. if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0)
  104. {
  105. for($i = PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 1; $i <= PHPExcel_Cell :: columnIndexFromString($highestColumn) - 1; ++ $i)
  106. {
  107. for($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++ $j)
  108. {
  109. $coordinate = PHPExcel_Cell :: stringFromColumnIndex($i) . $j;
  110. $pSheet->removeConditionalStyles($coordinate);
  111. if ($pSheet->cellExists($coordinate))
  112. {
  113. $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType :: TYPE_NULL);
  114. $pSheet->getCell($coordinate)->setXfIndex(0);
  115. }
  116. }
  117. }
  118. }
  119. // Loop through cells, bottom-up, and change cell coordinates
  120. while (($cellID = ($pNumCols < 0 || $pNumRows < 0) ? array_shift($aCellCollection) : array_pop($aCellCollection)))
  121. {
  122. $cell = $pSheet->getCell($cellID);
  123. // New coordinates
  124. $newCoordinates = PHPExcel_Cell :: stringFromColumnIndex(PHPExcel_Cell :: columnIndexFromString($cell->getColumn()) - 1 + $pNumCols) . ($cell->getRow() + $pNumRows);
  125. // Should the cell be updated? Move value and cellXf index from one cell to another.
  126. if ((PHPExcel_Cell :: columnIndexFromString($cell->getColumn()) >= PHPExcel_Cell :: columnIndexFromString($beforeColumn)) && ($cell->getRow() >= $beforeRow))
  127. {
  128. // Update cell styles
  129. $pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex());
  130. $cell->setXfIndex(0);
  131. // Insert this cell at its new location
  132. if ($cell->getDataType() == PHPExcel_Cell_DataType :: TYPE_FORMULA)
  133. {
  134. // Formula should be adjusted
  135. $pSheet->getCell($newCoordinates)->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
  136. }
  137. else
  138. {
  139. // Formula should not be adjusted
  140. $pSheet->getCell($newCoordinates)->setValue($cell->getValue());
  141. }
  142. // Clear the original cell
  143. $pSheet->getCell($cell->getCoordinate())->setValue('');
  144. }
  145. else
  146. {
  147. /* We don't need to update styles for rows/columns before our insertion position,
  148. but we do still need to adjust any formulae in those cells */
  149. if ($cell->getDataType() == PHPExcel_Cell_DataType :: TYPE_FORMULA)
  150. {
  151. // Formula should be adjusted
  152. $cell->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
  153. }
  154. }
  155. }
  156. // Duplicate styles for the newly inserted cells
  157. $highestColumn = $pSheet->getHighestColumn();
  158. $highestRow = $pSheet->getHighestRow();
  159. if ($pNumCols > 0 && PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 2 > 0)
  160. {
  161. for($i = $beforeRow; $i <= $highestRow - 1; ++ $i)
  162. {
  163. // Style
  164. $coordinate = PHPExcel_Cell :: stringFromColumnIndex(PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 2) . $i;
  165. if ($pSheet->cellExists($coordinate))
  166. {
  167. $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
  168. $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ? $pSheet->getConditionalStyles($coordinate) : false;
  169. for($j = PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 1; $j <= PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 2 + $pNumCols; ++ $j)
  170. {
  171. $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
  172. if ($conditionalStyles)
  173. {
  174. $cloned = array();
  175. foreach ($conditionalStyles as $conditionalStyle)
  176. {
  177. $cloned[] = clone $conditionalStyle;
  178. }
  179. $pSheet->setConditionalStyles(PHPExcel_Cell :: stringFromColumnIndex($j) . $i, $cloned);
  180. }
  181. }
  182. }
  183. }
  184. }
  185. if ($pNumRows > 0 && $beforeRow - 1 > 0)
  186. {
  187. for($i = PHPExcel_Cell :: columnIndexFromString($beforeColumn) - 1; $i <= PHPExcel_Cell :: columnIndexFromString($highestColumn) - 1; ++ $i)
  188. {
  189. // Style
  190. $coordinate = PHPExcel_Cell :: stringFromColumnIndex($i) . ($beforeRow - 1);
  191. if ($pSheet->cellExists($coordinate))
  192. {
  193. $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
  194. $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ? $pSheet->getConditionalStyles($coordinate) : false;
  195. for($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++ $j)
  196. {
  197. $pSheet->getCell(PHPExcel_Cell :: stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
  198. if ($conditionalStyles)
  199. {
  200. $cloned = array();
  201. foreach ($conditionalStyles as $conditionalStyle)
  202. {
  203. $cloned[] = clone $conditionalStyle;
  204. }
  205. $pSheet->setConditionalStyles(PHPExcel_Cell :: stringFromColumnIndex($i) . $j, $cloned);
  206. }
  207. }
  208. }
  209. }
  210. }
  211. // Update worksheet: column dimensions
  212. $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
  213. if (count($aColumnDimensions) > 0)
  214. {
  215. foreach ($aColumnDimensions as $objColumnDimension)
  216. {
  217. $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
  218. list($newReference) = PHPExcel_Cell :: coordinateFromString($newReference);
  219. if ($objColumnDimension->getColumnIndex() != $newReference)
  220. {
  221. $objColumnDimension->setColumnIndex($newReference);
  222. }
  223. }
  224. $pSheet->refreshColumnDimensions();
  225. }
  226. // Update worksheet: row dimensions
  227. $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
  228. if (count($aRowDimensions) > 0)
  229. {
  230. foreach ($aRowDimensions as $objRowDimension)
  231. {
  232. $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
  233. list(, $newReference) = PHPExcel_Cell :: coordinateFromString($newReference);
  234. if ($objRowDimension->getRowIndex() != $newReference)
  235. {
  236. $objRowDimension->setRowIndex($newReference);
  237. }
  238. }
  239. $pSheet->refreshRowDimensions();
  240. $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
  241. for($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++ $i)
  242. {
  243. $newDimension = $pSheet->getRowDimension($i);
  244. $newDimension->setRowHeight($copyDimension->getRowHeight());
  245. $newDimension->setVisible($copyDimension->getVisible());
  246. $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
  247. $newDimension->setCollapsed($copyDimension->getCollapsed());
  248. }
  249. }
  250. // Update worksheet: breaks
  251. $aBreaks = array_reverse($pSheet->getBreaks(), true);
  252. foreach ($aBreaks as $key => $value)
  253. {
  254. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  255. if ($key != $newReference)
  256. {
  257. $pSheet->setBreak($newReference, $value);
  258. $pSheet->setBreak($key, PHPExcel_Worksheet :: BREAK_NONE);
  259. }
  260. }
  261. // Update worksheet: comments
  262. $aComments = $pSheet->getComments();
  263. $aNewComments = array(); // the new array of all comments
  264. foreach ($aComments as $key => &$value)
  265. {
  266. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  267. $aNewComments[$newReference] = $value;
  268. }
  269. $pSheet->setComments($aNewComments); // replace the comments array
  270. // Update worksheet: hyperlinks
  271. $aHyperlinkCollection = array_reverse($pSheet->getHyperlinkCollection(), true);
  272. foreach ($aHyperlinkCollection as $key => $value)
  273. {
  274. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  275. if ($key != $newReference)
  276. {
  277. $pSheet->setHyperlink($newReference, $value);
  278. $pSheet->setHyperlink($key, null);
  279. }
  280. }
  281. // Update worksheet: data validations
  282. $aDataValidationCollection = array_reverse($pSheet->getDataValidationCollection(), true);
  283. foreach ($aDataValidationCollection as $key => $value)
  284. {
  285. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  286. if ($key != $newReference)
  287. {
  288. $pSheet->setDataValidation($newReference, $value);
  289. $pSheet->setDataValidation($key, null);
  290. }
  291. }
  292. // Update worksheet: merge cells
  293. $aMergeCells = $pSheet->getMergeCells();
  294. $aNewMergeCells = array(); // the new array of all merge cells
  295. foreach ($aMergeCells as $key => &$value)
  296. {
  297. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  298. $aNewMergeCells[$newReference] = $newReference;
  299. }
  300. $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
  301. // Update worksheet: protected cells
  302. $aProtectedCells = array_reverse($pSheet->getProtectedCells(), true);
  303. foreach ($aProtectedCells as $key => $value)
  304. {
  305. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  306. if ($key != $newReference)
  307. {
  308. $pSheet->protectCells($newReference, $value, true);
  309. $pSheet->unprotectCells($key);
  310. }
  311. }
  312. // Update worksheet: autofilter
  313. if ($pSheet->getAutoFilter() != '')
  314. {
  315. $pSheet->setAutoFilter($this->updateCellReference($pSheet->getAutoFilter(), $pBefore, $pNumCols, $pNumRows));
  316. }
  317. // Update worksheet: freeze pane
  318. if ($pSheet->getFreezePane() != '')
  319. {
  320. $pSheet->freezePane($this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows));
  321. }
  322. // Page setup
  323. if ($pSheet->getPageSetup()->isPrintAreaSet())
  324. {
  325. $pSheet->getPageSetup()->setPrintArea($this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows));
  326. }
  327. // Update worksheet: drawings
  328. $aDrawings = $pSheet->getDrawingCollection();
  329. foreach ($aDrawings as $objDrawing)
  330. {
  331. $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
  332. if ($objDrawing->getCoordinates() != $newReference)
  333. {
  334. $objDrawing->setCoordinates($newReference);
  335. }
  336. }
  337. // Update workbook: named ranges
  338. if (count($pSheet->getParent()->getNamedRanges()) > 0)
  339. {
  340. foreach ($pSheet->getParent()->getNamedRanges() as $namedRange)
  341. {
  342. if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode())
  343. {
  344. $namedRange->setRange($this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows));
  345. }
  346. }
  347. }
  348. // Garbage collect
  349. $pSheet->garbageCollect();
  350. }
  351. /**
  352. * Update references within formulas
  353. *
  354. * @param string $pFormula Formula to update
  355. * @param int $pBefore Insert before this one
  356. * @param int $pNumCols Number of columns to insert
  357. * @param int $pNumRows Number of rows to insert
  358. * @return string Updated formula
  359. * @throws Exception
  360. */
  361. public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '')
  362. {
  363. // Update cell references in the formula
  364. $formulaBlocks = explode('"', $pFormula);
  365. $i = false;
  366. foreach ($formulaBlocks as &$formulaBlock)
  367. {
  368. // Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
  369. if ($i = ! $i)
  370. {
  371. $adjustCount = 0;
  372. $newCellTokens = $cellTokens = array();
  373. // Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
  374. $matchCount = preg_match_all('/' . self :: REFHELPER_REGEXP_ROWRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
  375. if ($matchCount > 0)
  376. {
  377. foreach ($matches as $match)
  378. {
  379. $fromString = ($match[2] > '') ? $match[2] . '!' : '';
  380. $fromString .= $match[3] . ':' . $match[4];
  381. $modified3 = substr($this->updateCellReference('$A' . $match[3], $pBefore, $pNumCols, $pNumRows), 2);
  382. $modified4 = substr($this->updateCellReference('$A' . $match[4], $pBefore, $pNumCols, $pNumRows), 2);
  383. if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4)
  384. {
  385. if (($match[2] == '') || (trim($match[2], "'") == $sheetName))
  386. {
  387. $toString = ($match[2] > '') ? $match[2] . '!' : '';
  388. $toString .= $modified3 . ':' . $modified4;
  389. // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
  390. $column = 100000;
  391. $row = 10000000 + trim($match[3], '$');
  392. $cellIndex = $column . $row;
  393. $newCellTokens[$cellIndex] = preg_quote($toString);
  394. $cellTokens[$cellIndex] = '/(?<!\d)' . preg_quote($fromString) . '(?!\d)/i';
  395. ++ $adjustCount;
  396. }
  397. }
  398. }
  399. }
  400. // Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
  401. $matchCount = preg_match_all('/' . self :: REFHELPER_REGEXP_COLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
  402. if ($matchCount > 0)
  403. {
  404. foreach ($matches as $match)
  405. {
  406. $fromString = ($match[2] > '') ? $match[2] . '!' : '';
  407. $fromString .= $match[3] . ':' . $match[4];
  408. $modified3 = substr($this->updateCellReference($match[3] . '$1', $pBefore, $pNumCols, $pNumRows), 0, - 2);
  409. $modified4 = substr($this->updateCellReference($match[4] . '$1', $pBefore, $pNumCols, $pNumRows), 0, - 2);
  410. if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4)
  411. {
  412. if (($match[2] == '') || (trim($match[2], "'") == $sheetName))
  413. {
  414. $toString = ($match[2] > '') ? $match[2] . '!' : '';
  415. $toString .= $modified3 . ':' . $modified4;
  416. // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
  417. $column = PHPExcel_Cell :: columnIndexFromString(trim($match[3], '$')) + 100000;
  418. $row = 10000000;
  419. $cellIndex = $column . $row;
  420. $newCellTokens[$cellIndex] = preg_quote($toString);
  421. $cellTokens[$cellIndex] = '/(?<![A-Z])' . preg_quote($fromString) . '(?![A-Z])/i';
  422. ++ $adjustCount;
  423. }
  424. }
  425. }
  426. }
  427. // Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
  428. $matchCount = preg_match_all('/' . self :: REFHELPER_REGEXP_CELLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
  429. if ($matchCount > 0)
  430. {
  431. foreach ($matches as $match)
  432. {
  433. $fromString = ($match[2] > '') ? $match[2] . '!' : '';
  434. $fromString .= $match[3] . ':' . $match[4];
  435. $modified3 = $this->updateCellReference($match[3], $pBefore, $pNumCols, $pNumRows);
  436. $modified4 = $this->updateCellReference($match[4], $pBefore, $pNumCols, $pNumRows);
  437. if ($match[3] . $match[4] !== $modified3 . $modified4)
  438. {
  439. if (($match[2] == '') || (trim($match[2], "'") == $sheetName))
  440. {
  441. $toString = ($match[2] > '') ? $match[2] . '!' : '';
  442. $toString .= $modified3 . ':' . $modified4;
  443. list($column, $row) = PHPExcel_Cell :: coordinateFromString($match[3]);
  444. // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
  445. $column = PHPExcel_Cell :: columnIndexFromString(trim($column, '$')) + 100000;
  446. $row = trim($row, '$') + 10000000;
  447. $cellIndex = $column . $row;
  448. $newCellTokens[$cellIndex] = preg_quote($toString);
  449. $cellTokens[$cellIndex] = '/(?<![A-Z])' . preg_quote($fromString) . '(?!\d)/i';
  450. ++ $adjustCount;
  451. }
  452. }
  453. }
  454. }
  455. // Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
  456. $matchCount = preg_match_all('/' . self :: REFHELPER_REGEXP_CELLREF . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
  457. if ($matchCount > 0)
  458. {
  459. foreach ($matches as $match)
  460. {
  461. $fromString = ($match[2] > '') ? $match[2] . '!' : '';
  462. $fromString .= $match[3];
  463. $modified3 = $this->updateCellReference($match[3], $pBefore, $pNumCols, $pNumRows);
  464. if ($match[3] !== $modified3)
  465. {
  466. if (($match[2] == '') || (trim($match[2], "'") == $sheetName))
  467. {
  468. $toString = ($match[2] > '') ? $match[2] . '!' : '';
  469. $toString .= $modified3;
  470. list($column, $row) = PHPExcel_Cell :: coordinateFromString($match[3]);
  471. // Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
  472. $column = PHPExcel_Cell :: columnIndexFromString(trim($column, '$')) + 100000;
  473. $row = trim($row, '$') + 10000000;
  474. $cellIndex = $column . $row;
  475. $newCellTokens[$cellIndex] = preg_quote($toString);
  476. $cellTokens[$cellIndex] = '/(?<![A-Z])' . preg_quote($fromString) . '(?!\d)/i';
  477. ++ $adjustCount;
  478. }
  479. }
  480. }
  481. }
  482. if ($adjustCount > 0)
  483. {
  484. krsort($cellTokens);
  485. krsort($newCellTokens);
  486. // Update cell references in the formula
  487. $formulaBlock = str_replace('\\', '', preg_replace($cellTokens, $newCellTokens, $formulaBlock));
  488. }
  489. }
  490. }
  491. unset($formulaBlock);
  492. // Then rebuild the formula string
  493. return implode('"', $formulaBlocks);
  494. }
  495. /**
  496. * Update cell reference
  497. *
  498. * @param string $pCellRange Cell range
  499. * @param int $pBefore Insert before this one
  500. * @param int $pNumCols Number of columns to increment
  501. * @param int $pNumRows Number of rows to increment
  502. * @return string Updated cell range
  503. * @throws Exception
  504. */
  505. public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
  506. {
  507. // Is it in another worksheet? Will not have to update anything.
  508. if (strpos($pCellRange, "!") !== false)
  509. {
  510. return $pCellRange;
  511. // Is it a range or a single cell?
  512. }
  513. elseif (strpos($pCellRange, ':') === false && strpos($pCellRange, ',') === false)
  514. {
  515. // Single cell
  516. return $this->_updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
  517. }
  518. elseif (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false)
  519. {
  520. // Range
  521. return $this->_updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
  522. }
  523. else
  524. {
  525. // Return original
  526. return $pCellRange;
  527. }
  528. }
  529. /**
  530. * Update named formulas (i.e. containing worksheet references / named ranges)
  531. *
  532. * @param PHPExcel $pPhpExcel Object to update
  533. * @param string $oldName Old name (name to replace)
  534. * @param string $newName New name
  535. */
  536. public function updateNamedFormulas(PHPExcel $pPhpExcel, $oldName = '', $newName = '')
  537. {
  538. if ($oldName == '')
  539. {
  540. return;
  541. }
  542. foreach ($pPhpExcel->getWorksheetIterator() as $sheet)
  543. {
  544. foreach ($sheet->getCellCollection(false) as $cellID)
  545. {
  546. $cell = $sheet->getCell($cellID);
  547. if (! is_null($cell) && $cell->getDataType() == PHPExcel_Cell_DataType :: TYPE_FORMULA)
  548. {
  549. $formula = $cell->getValue();
  550. if (strpos($formula, $oldName) !== false)
  551. {
  552. $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
  553. $formula = str_replace($oldName . "!", $newName . "!", $formula);
  554. $cell->setValueExplicit($formula, PHPExcel_Cell_DataType :: TYPE_FORMULA);
  555. }
  556. }
  557. }
  558. }
  559. }
  560. /**
  561. * Update cell range
  562. *
  563. * @param string $pCellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
  564. * @param int $pBefore Insert before this one
  565. * @param int $pNumCols Number of columns to increment
  566. * @param int $pNumRows Number of rows to increment
  567. * @return string Updated cell range
  568. * @throws Exception
  569. */
  570. private function _updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
  571. {
  572. if (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false)
  573. {
  574. // Update range
  575. $range = PHPExcel_Cell :: splitRange($pCellRange);
  576. $ic = count($range);
  577. for($i = 0; $i < $ic; ++ $i)
  578. {
  579. $jc = count($range[$i]);
  580. for($j = 0; $j < $jc; ++ $j)
  581. {
  582. if (ctype_alpha($range[$i][$j]))
  583. {
  584. $r = PHPExcel_Cell :: coordinateFromString($this->_updateSingleCellReference($range[$i][$j] . '1', $pBefore, $pNumCols, $pNumRows));
  585. $range[$i][$j] = $r[0];
  586. }
  587. elseif (ctype_digit($range[$i][$j]))
  588. {
  589. $r = PHPExcel_Cell :: coordinateFromString($this->_updateSingleCellReference('A' . $range[$i][$j], $pBefore, $pNumCols, $pNumRows));
  590. $range[$i][$j] = $r[1];
  591. }
  592. else
  593. {
  594. $range[$i][$j] = $this->_updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
  595. }
  596. }
  597. }
  598. // Recreate range string
  599. return PHPExcel_Cell :: buildRange($range);
  600. }
  601. else
  602. {
  603. throw new Exception("Only cell ranges may be passed to this method.");
  604. }
  605. }
  606. /**
  607. * Update single cell reference
  608. *
  609. * @param string $pCellReference Single cell reference
  610. * @param int $pBefore Insert before this one
  611. * @param int $pNumCols Number of columns to increment
  612. * @param int $pNumRows Number of rows to increment
  613. * @return string Updated cell reference
  614. * @throws Exception
  615. */
  616. private function _updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
  617. {
  618. if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false)
  619. {
  620. // Get coordinates of $pBefore
  621. list($beforeColumn, $beforeRow) = PHPExcel_Cell :: coordinateFromString($pBefore);
  622. // Get coordinates of $pCellReference
  623. list($newColumn, $newRow) = PHPExcel_Cell :: coordinateFromString($pCellReference);
  624. // Verify which parts should be updated
  625. $updateColumn = (($newColumn{0} != '$') && ($beforeColumn{0} != '$') && PHPExcel_Cell :: columnIndexFromString($newColumn) >= PHPExcel_Cell :: columnIndexFromString($beforeColumn));
  626. $updateRow = (($newRow{0} != '$') && ($beforeRow{0} != '$') && $newRow >= $beforeRow);
  627. // Create new column reference
  628. if ($updateColumn)
  629. {
  630. $newColumn = PHPExcel_Cell :: stringFromColumnIndex(PHPExcel_Cell :: columnIndexFromString($newColumn) - 1 + $pNumCols);
  631. }
  632. // Create new row reference
  633. if ($updateRow)
  634. {
  635. $newRow = $newRow + $pNumRows;
  636. }
  637. // Return new reference
  638. return $newColumn . $newRow;
  639. }
  640. else
  641. {
  642. throw new Exception("Only single cell references may be passed to this method.");
  643. }
  644. }
  645. /**
  646. * __clone implementation. Cloning should not be allowed in a Singleton!
  647. *
  648. * @throws Exception
  649. */
  650. public final function __clone()
  651. {
  652. throw new Exception("Cloning a Singleton is not allowed!");
  653. }
  654. }