PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/chamilo/chamilo/
PHP | 632 lines | 469 code | 59 blank | 104 comment | 72 complexity | c9cd68e960531597a146486d57c6bb9c MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT
  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. if (!isset(self::$_instance) || is_null(self::$_instance)) {
  55. self::$_instance = new PHPExcel_ReferenceHelper();
  56. }
  57. return self::$_instance;
  58. }
  59. /**
  60. * Create a new PHPExcel_ReferenceHelper
  61. */
  62. protected function __construct() {
  63. }
  64. /**
  65. * Insert a new column, updating all possible related data
  66. *
  67. * @param int $pBefore Insert before this one
  68. * @param int $pNumCols Number of columns to insert
  69. * @param int $pNumRows Number of rows to insert
  70. * @throws Exception
  71. */
  72. public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = null) {
  73. $aCellCollection = $pSheet->getCellCollection();
  74. // Get coordinates of $pBefore
  75. $beforeColumn = 'A';
  76. $beforeRow = 1;
  77. list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
  78. // Clear cells if we are removing columns or rows
  79. $highestColumn = $pSheet->getHighestColumn();
  80. $highestRow = $pSheet->getHighestRow();
  81. // 1. Clear column strips if we are removing columns
  82. if ($pNumCols < 0 && PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 + $pNumCols > 0) {
  83. for ($i = 1; $i <= $highestRow - 1; ++$i) {
  84. for ($j = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1 + $pNumCols; $j <= PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2; ++$j) {
  85. $coordinate = PHPExcel_Cell::stringFromColumnIndex($j) . $i;
  86. $pSheet->removeConditionalStyles($coordinate);
  87. if ($pSheet->cellExists($coordinate)) {
  88. $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
  89. $pSheet->getCell($coordinate)->setXfIndex(0);
  90. }
  91. }
  92. }
  93. }
  94. // 2. Clear row strips if we are removing rows
  95. if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
  96. for ($i = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
  97. for ($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++$j) {
  98. $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . $j;
  99. $pSheet->removeConditionalStyles($coordinate);
  100. if ($pSheet->cellExists($coordinate)) {
  101. $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
  102. $pSheet->getCell($coordinate)->setXfIndex(0);
  103. }
  104. }
  105. }
  106. }
  107. // Loop through cells, bottom-up, and change cell coordinates
  108. while (($cellID = ($pNumCols < 0 || $pNumRows < 0) ? array_shift($aCellCollection) : array_pop($aCellCollection))) {
  109. $cell = $pSheet->getCell($cellID);
  110. // New coordinates
  111. $newCoordinates = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1 + $pNumCols ) . ($cell->getRow() + $pNumRows);
  112. // Should the cell be updated? Move value and cellXf index from one cell to another.
  113. if ((PHPExcel_Cell::columnIndexFromString( $cell->getColumn() ) >= PHPExcel_Cell::columnIndexFromString($beforeColumn)) &&
  114. ($cell->getRow() >= $beforeRow)) {
  115. // Update cell styles
  116. $pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex());
  117. $cell->setXfIndex(0);
  118. // Insert this cell at its new location
  119. if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
  120. // Formula should be adjusted
  121. $pSheet->getCell($newCoordinates)
  122. ->setValue($this->updateFormulaReferences($cell->getValue(),
  123. $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
  124. } else {
  125. // Formula should not be adjusted
  126. $pSheet->getCell($newCoordinates)->setValue($cell->getValue());
  127. }
  128. // Clear the original cell
  129. $pSheet->getCell($cell->getCoordinate())->setValue('');
  130. } else {
  131. /* We don't need to update styles for rows/columns before our insertion position,
  132. but we do still need to adjust any formulae in those cells */
  133. if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
  134. // Formula should be adjusted
  135. $cell->setValue($this->updateFormulaReferences($cell->getValue(),
  136. $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
  137. }
  138. }
  139. }
  140. // Duplicate styles for the newly inserted cells
  141. $highestColumn = $pSheet->getHighestColumn();
  142. $highestRow = $pSheet->getHighestRow();
  143. if ($pNumCols > 0 && PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 > 0) {
  144. for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
  145. // Style
  146. $coordinate = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 ) . $i;
  147. if ($pSheet->cellExists($coordinate)) {
  148. $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
  149. $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
  150. $pSheet->getConditionalStyles($coordinate) : false;
  151. for ($j = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1; $j <= PHPExcel_Cell::columnIndexFromString($beforeColumn) - 2 + $pNumCols; ++$j) {
  152. $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
  153. if ($conditionalStyles) {
  154. $cloned = array();
  155. foreach ($conditionalStyles as $conditionalStyle) {
  156. $cloned[] = clone $conditionalStyle;
  157. }
  158. $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($j) . $i, $cloned);
  159. }
  160. }
  161. }
  162. }
  163. }
  164. if ($pNumRows > 0 && $beforeRow - 1 > 0) {
  165. for ($i = PHPExcel_Cell::columnIndexFromString($beforeColumn) - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
  166. // Style
  167. $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . ($beforeRow - 1);
  168. if ($pSheet->cellExists($coordinate)) {
  169. $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
  170. $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
  171. $pSheet->getConditionalStyles($coordinate) : false;
  172. for ($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++$j) {
  173. $pSheet->getCell(PHPExcel_Cell::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
  174. if ($conditionalStyles) {
  175. $cloned = array();
  176. foreach ($conditionalStyles as $conditionalStyle) {
  177. $cloned[] = clone $conditionalStyle;
  178. }
  179. $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($i) . $j, $cloned);
  180. }
  181. }
  182. }
  183. }
  184. }
  185. // Update worksheet: column dimensions
  186. $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
  187. if (count($aColumnDimensions) > 0) {
  188. foreach ($aColumnDimensions as $objColumnDimension) {
  189. $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
  190. list($newReference) = PHPExcel_Cell::coordinateFromString($newReference);
  191. if ($objColumnDimension->getColumnIndex() != $newReference) {
  192. $objColumnDimension->setColumnIndex($newReference);
  193. }
  194. }
  195. $pSheet->refreshColumnDimensions();
  196. }
  197. // Update worksheet: row dimensions
  198. $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
  199. if (count($aRowDimensions) > 0) {
  200. foreach ($aRowDimensions as $objRowDimension) {
  201. $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
  202. list(, $newReference) = PHPExcel_Cell::coordinateFromString($newReference);
  203. if ($objRowDimension->getRowIndex() != $newReference) {
  204. $objRowDimension->setRowIndex($newReference);
  205. }
  206. }
  207. $pSheet->refreshRowDimensions();
  208. $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
  209. for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) {
  210. $newDimension = $pSheet->getRowDimension($i);
  211. $newDimension->setRowHeight($copyDimension->getRowHeight());
  212. $newDimension->setVisible($copyDimension->getVisible());
  213. $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
  214. $newDimension->setCollapsed($copyDimension->getCollapsed());
  215. }
  216. }
  217. // Update worksheet: breaks
  218. $aBreaks = array_reverse($pSheet->getBreaks(), true);
  219. foreach ($aBreaks as $key => $value) {
  220. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  221. if ($key != $newReference) {
  222. $pSheet->setBreak( $newReference, $value );
  223. $pSheet->setBreak( $key, PHPExcel_Worksheet::BREAK_NONE );
  224. }
  225. }
  226. // Update worksheet: comments
  227. $aComments = $pSheet->getComments();
  228. $aNewComments = array(); // the new array of all comments
  229. foreach ($aComments as $key => &$value) {
  230. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  231. $aNewComments[$newReference] = $value;
  232. }
  233. $pSheet->setComments($aNewComments); // replace the comments array
  234. // Update worksheet: hyperlinks
  235. $aHyperlinkCollection = array_reverse($pSheet->getHyperlinkCollection(), true);
  236. foreach ($aHyperlinkCollection as $key => $value) {
  237. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  238. if ($key != $newReference) {
  239. $pSheet->setHyperlink( $newReference, $value );
  240. $pSheet->setHyperlink( $key, null );
  241. }
  242. }
  243. // Update worksheet: data validations
  244. $aDataValidationCollection = array_reverse($pSheet->getDataValidationCollection(), true);
  245. foreach ($aDataValidationCollection as $key => $value) {
  246. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  247. if ($key != $newReference) {
  248. $pSheet->setDataValidation( $newReference, $value );
  249. $pSheet->setDataValidation( $key, null );
  250. }
  251. }
  252. // Update worksheet: merge cells
  253. $aMergeCells = $pSheet->getMergeCells();
  254. $aNewMergeCells = array(); // the new array of all merge cells
  255. foreach ($aMergeCells as $key => &$value) {
  256. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  257. $aNewMergeCells[$newReference] = $newReference;
  258. }
  259. $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
  260. // Update worksheet: protected cells
  261. $aProtectedCells = array_reverse($pSheet->getProtectedCells(), true);
  262. foreach ($aProtectedCells as $key => $value) {
  263. $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
  264. if ($key != $newReference) {
  265. $pSheet->protectCells( $newReference, $value, true );
  266. $pSheet->unprotectCells( $key );
  267. }
  268. }
  269. // Update worksheet: autofilter
  270. if ($pSheet->getAutoFilter() != '') {
  271. $pSheet->setAutoFilter( $this->updateCellReference($pSheet->getAutoFilter(), $pBefore, $pNumCols, $pNumRows) );
  272. }
  273. // Update worksheet: freeze pane
  274. if ($pSheet->getFreezePane() != '') {
  275. $pSheet->freezePane( $this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows) );
  276. }
  277. // Page setup
  278. if ($pSheet->getPageSetup()->isPrintAreaSet()) {
  279. $pSheet->getPageSetup()->setPrintArea( $this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows) );
  280. }
  281. // Update worksheet: drawings
  282. $aDrawings = $pSheet->getDrawingCollection();
  283. foreach ($aDrawings as $objDrawing) {
  284. $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
  285. if ($objDrawing->getCoordinates() != $newReference) {
  286. $objDrawing->setCoordinates($newReference);
  287. }
  288. }
  289. // Update workbook: named ranges
  290. if (count($pSheet->getParent()->getNamedRanges()) > 0) {
  291. foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
  292. if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
  293. $namedRange->setRange(
  294. $this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows)
  295. );
  296. }
  297. }
  298. }
  299. // Garbage collect
  300. $pSheet->garbageCollect();
  301. }
  302. /**
  303. * Update references within formulas
  304. *
  305. * @param string $pFormula Formula to update
  306. * @param int $pBefore Insert before this one
  307. * @param int $pNumCols Number of columns to insert
  308. * @param int $pNumRows Number of rows to insert
  309. * @return string Updated formula
  310. * @throws Exception
  311. */
  312. public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '') {
  313. // Update cell references in the formula
  314. $formulaBlocks = explode('"',$pFormula);
  315. $i = false;
  316. foreach($formulaBlocks as &$formulaBlock) {
  317. // Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
  318. if ($i = !$i) {
  319. $adjustCount = 0;
  320. $newCellTokens = $cellTokens = array();
  321. // Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
  322. $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_ROWRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
  323. if ($matchCount > 0) {
  324. foreach($matches as $match) {
  325. $fromString = ($match[2] > '') ? $match[2].'!' : '';
  326. $fromString .= $match[3].':'.$match[4];
  327. $modified3 = substr($this->updateCellReference('$A'.$match[3],$pBefore,$pNumCols,$pNumRows),2);
  328. $modified4 = substr($this->updateCellReference('$A'.$match[4],$pBefore,$pNumCols,$pNumRows),2);
  329. if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
  330. if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
  331. $toString = ($match[2] > '') ? $match[2].'!' : '';
  332. $toString .= $modified3.':'.$modified4;
  333. // 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
  334. $column = 100000;
  335. $row = 10000000+trim($match[3],'$');
  336. $cellIndex = $column.$row;
  337. $newCellTokens[$cellIndex] = preg_quote($toString);
  338. $cellTokens[$cellIndex] = '/(?<!\d)'.preg_quote($fromString).'(?!\d)/i';
  339. ++$adjustCount;
  340. }
  341. }
  342. }
  343. }
  344. // Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
  345. $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_COLRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
  346. if ($matchCount > 0) {
  347. foreach($matches as $match) {
  348. $fromString = ($match[2] > '') ? $match[2].'!' : '';
  349. $fromString .= $match[3].':'.$match[4];
  350. $modified3 = substr($this->updateCellReference($match[3].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
  351. $modified4 = substr($this->updateCellReference($match[4].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
  352. if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
  353. if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
  354. $toString = ($match[2] > '') ? $match[2].'!' : '';
  355. $toString .= $modified3.':'.$modified4;
  356. // 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
  357. $column = PHPExcel_Cell::columnIndexFromString(trim($match[3],'$')) + 100000;
  358. $row = 10000000;
  359. $cellIndex = $column.$row;
  360. $newCellTokens[$cellIndex] = preg_quote($toString);
  361. $cellTokens[$cellIndex] = '/(?<![A-Z])'.preg_quote($fromString).'(?![A-Z])/i';
  362. ++$adjustCount;
  363. }
  364. }
  365. }
  366. }
  367. // Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
  368. $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_CELLRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
  369. if ($matchCount > 0) {
  370. foreach($matches as $match) {
  371. $fromString = ($match[2] > '') ? $match[2].'!' : '';
  372. $fromString .= $match[3].':'.$match[4];
  373. $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
  374. $modified4 = $this->updateCellReference($match[4],$pBefore,$pNumCols,$pNumRows);
  375. if ($match[3].$match[4] !== $modified3.$modified4) {
  376. if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
  377. $toString = ($match[2] > '') ? $match[2].'!' : '';
  378. $toString .= $modified3.':'.$modified4;
  379. list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
  380. // 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
  381. $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
  382. $row = trim($row,'$') + 10000000;
  383. $cellIndex = $column.$row;
  384. $newCellTokens[$cellIndex] = preg_quote($toString);
  385. $cellTokens[$cellIndex] = '/(?<![A-Z])'.preg_quote($fromString).'(?!\d)/i';
  386. ++$adjustCount;
  387. }
  388. }
  389. }
  390. }
  391. // Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
  392. $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_CELLREF.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
  393. if ($matchCount > 0) {
  394. foreach($matches as $match) {
  395. $fromString = ($match[2] > '') ? $match[2].'!' : '';
  396. $fromString .= $match[3];
  397. $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
  398. if ($match[3] !== $modified3) {
  399. if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
  400. $toString = ($match[2] > '') ? $match[2].'!' : '';
  401. $toString .= $modified3;
  402. list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
  403. // 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
  404. $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
  405. $row = trim($row,'$') + 10000000;
  406. $cellIndex = $column.$row;
  407. $newCellTokens[$cellIndex] = preg_quote($toString);
  408. $cellTokens[$cellIndex] = '/(?<![A-Z])'.preg_quote($fromString).'(?!\d)/i';
  409. ++$adjustCount;
  410. }
  411. }
  412. }
  413. }
  414. if ($adjustCount > 0) {
  415. krsort($cellTokens);
  416. krsort($newCellTokens);
  417. // Update cell references in the formula
  418. $formulaBlock = str_replace('\\','',preg_replace($cellTokens,$newCellTokens,$formulaBlock));
  419. }
  420. }
  421. }
  422. unset($formulaBlock);
  423. // Then rebuild the formula string
  424. return implode('"',$formulaBlocks);
  425. }
  426. /**
  427. * Update cell reference
  428. *
  429. * @param string $pCellRange Cell range
  430. * @param int $pBefore Insert before this one
  431. * @param int $pNumCols Number of columns to increment
  432. * @param int $pNumRows Number of rows to increment
  433. * @return string Updated cell range
  434. * @throws Exception
  435. */
  436. public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
  437. // Is it in another worksheet? Will not have to update anything.
  438. if (strpos($pCellRange, "!") !== false) {
  439. return $pCellRange;
  440. // Is it a range or a single cell?
  441. } elseif (strpos($pCellRange, ':') === false && strpos($pCellRange, ',') === false) {
  442. // Single cell
  443. return $this->_updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
  444. } elseif (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false) {
  445. // Range
  446. return $this->_updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
  447. } else {
  448. // Return original
  449. return $pCellRange;
  450. }
  451. }
  452. /**
  453. * Update named formulas (i.e. containing worksheet references / named ranges)
  454. *
  455. * @param PHPExcel $pPhpExcel Object to update
  456. * @param string $oldName Old name (name to replace)
  457. * @param string $newName New name
  458. */
  459. public function updateNamedFormulas(PHPExcel $pPhpExcel, $oldName = '', $newName = '') {
  460. if ($oldName == '') {
  461. return;
  462. }
  463. foreach ($pPhpExcel->getWorksheetIterator() as $sheet) {
  464. foreach ($sheet->getCellCollection(false) as $cellID) {
  465. $cell = $sheet->getCell($cellID);
  466. if (!is_null($cell) && $cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
  467. $formula = $cell->getValue();
  468. if (strpos($formula, $oldName) !== false) {
  469. $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
  470. $formula = str_replace($oldName . "!", $newName . "!", $formula);
  471. $cell->setValueExplicit($formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
  472. }
  473. }
  474. }
  475. }
  476. }
  477. /**
  478. * Update cell range
  479. *
  480. * @param string $pCellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
  481. * @param int $pBefore Insert before this one
  482. * @param int $pNumCols Number of columns to increment
  483. * @param int $pNumRows Number of rows to increment
  484. * @return string Updated cell range
  485. * @throws Exception
  486. */
  487. private function _updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
  488. if (strpos($pCellRange,':') !== false || strpos($pCellRange, ',') !== false) {
  489. // Update range
  490. $range = PHPExcel_Cell::splitRange($pCellRange);
  491. $ic = count($range);
  492. for ($i = 0; $i < $ic; ++$i) {
  493. $jc = count($range[$i]);
  494. for ($j = 0; $j < $jc; ++$j) {
  495. if (ctype_alpha($range[$i][$j])) {
  496. $r = PHPExcel_Cell::coordinateFromString($this->_updateSingleCellReference($range[$i][$j].'1', $pBefore, $pNumCols, $pNumRows));
  497. $range[$i][$j] = $r[0];
  498. } elseif(ctype_digit($range[$i][$j])) {
  499. $r = PHPExcel_Cell::coordinateFromString($this->_updateSingleCellReference('A'.$range[$i][$j], $pBefore, $pNumCols, $pNumRows));
  500. $range[$i][$j] = $r[1];
  501. } else {
  502. $range[$i][$j] = $this->_updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
  503. }
  504. }
  505. }
  506. // Recreate range string
  507. return PHPExcel_Cell::buildRange($range);
  508. } else {
  509. throw new Exception("Only cell ranges may be passed to this method.");
  510. }
  511. }
  512. /**
  513. * Update single cell reference
  514. *
  515. * @param string $pCellReference Single cell reference
  516. * @param int $pBefore Insert before this one
  517. * @param int $pNumCols Number of columns to increment
  518. * @param int $pNumRows Number of rows to increment
  519. * @return string Updated cell reference
  520. * @throws Exception
  521. */
  522. private function _updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
  523. if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) {
  524. // Get coordinates of $pBefore
  525. list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
  526. // Get coordinates of $pCellReference
  527. list($newColumn, $newRow) = PHPExcel_Cell::coordinateFromString( $pCellReference );
  528. // Verify which parts should be updated
  529. $updateColumn = (($newColumn{0} != '$') && ($beforeColumn{0} != '$') &&
  530. PHPExcel_Cell::columnIndexFromString($newColumn) >= PHPExcel_Cell::columnIndexFromString($beforeColumn));
  531. $updateRow = (($newRow{0} != '$') && ($beforeRow{0} != '$') &&
  532. $newRow >= $beforeRow);
  533. // Create new column reference
  534. if ($updateColumn) {
  535. $newColumn = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($newColumn) - 1 + $pNumCols );
  536. }
  537. // Create new row reference
  538. if ($updateRow) {
  539. $newRow = $newRow + $pNumRows;
  540. }
  541. // Return new reference
  542. return $newColumn . $newRow;
  543. } else {
  544. throw new Exception("Only single cell references may be passed to this method.");
  545. }
  546. }
  547. /**
  548. * __clone implementation. Cloning should not be allowed in a Singleton!
  549. *
  550. * @throws Exception
  551. */
  552. public final function __clone() {
  553. throw new Exception("Cloning a Singleton is not allowed!");
  554. }
  555. }