1 | <?php |
---|
2 | /** |
---|
3 | * PHPExcel |
---|
4 | * |
---|
5 | * Copyright (c) 2006 - 2014 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_Cell |
---|
23 | * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel) |
---|
24 | * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL |
---|
25 | * @version 1.8.0, 2014-03-02 |
---|
26 | */ |
---|
27 | |
---|
28 | |
---|
29 | /** |
---|
30 | * PHPExcel_Cell |
---|
31 | * |
---|
32 | * @category PHPExcel |
---|
33 | * @package PHPExcel_Cell |
---|
34 | * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel) |
---|
35 | */ |
---|
36 | class PHPExcel_Cell |
---|
37 | { |
---|
38 | |
---|
39 | /** |
---|
40 | * Default range variable constant |
---|
41 | * |
---|
42 | * @var string |
---|
43 | */ |
---|
44 | const DEFAULT_RANGE = 'A1:A1'; |
---|
45 | |
---|
46 | /** |
---|
47 | * Value binder to use |
---|
48 | * |
---|
49 | * @var PHPExcel_Cell_IValueBinder |
---|
50 | */ |
---|
51 | private static $_valueBinder = NULL; |
---|
52 | |
---|
53 | /** |
---|
54 | * Value of the cell |
---|
55 | * |
---|
56 | * @var mixed |
---|
57 | */ |
---|
58 | private $_value; |
---|
59 | |
---|
60 | /** |
---|
61 | * Calculated value of the cell (used for caching) |
---|
62 | * This returns the value last calculated by MS Excel or whichever spreadsheet program was used to |
---|
63 | * create the original spreadsheet file. |
---|
64 | * Note that this value is not guaranteed to reflect the actual calculated value because it is |
---|
65 | * possible that auto-calculation was disabled in the original spreadsheet, and underlying data |
---|
66 | * values used by the formula have changed since it was last calculated. |
---|
67 | * |
---|
68 | * @var mixed |
---|
69 | */ |
---|
70 | private $_calculatedValue = NULL; |
---|
71 | |
---|
72 | /** |
---|
73 | * Type of the cell data |
---|
74 | * |
---|
75 | * @var string |
---|
76 | */ |
---|
77 | private $_dataType; |
---|
78 | |
---|
79 | /** |
---|
80 | * Parent worksheet |
---|
81 | * |
---|
82 | * @var PHPExcel_CachedObjectStorage_CacheBase |
---|
83 | */ |
---|
84 | private $_parent; |
---|
85 | |
---|
86 | /** |
---|
87 | * Index to cellXf |
---|
88 | * |
---|
89 | * @var int |
---|
90 | */ |
---|
91 | private $_xfIndex; |
---|
92 | |
---|
93 | /** |
---|
94 | * Attributes of the formula |
---|
95 | * |
---|
96 | */ |
---|
97 | private $_formulaAttributes; |
---|
98 | |
---|
99 | |
---|
100 | /** |
---|
101 | * Send notification to the cache controller |
---|
102 | * |
---|
103 | * @return void |
---|
104 | **/ |
---|
105 | public function notifyCacheController() { |
---|
106 | $this->_parent->updateCacheData($this); |
---|
107 | |
---|
108 | return $this; |
---|
109 | } |
---|
110 | |
---|
111 | public function detach() { |
---|
112 | $this->_parent = NULL; |
---|
113 | } |
---|
114 | |
---|
115 | public function attach(PHPExcel_CachedObjectStorage_CacheBase $parent) { |
---|
116 | |
---|
117 | |
---|
118 | $this->_parent = $parent; |
---|
119 | } |
---|
120 | |
---|
121 | |
---|
122 | /** |
---|
123 | * Create a new Cell |
---|
124 | * |
---|
125 | * @param mixed $pValue |
---|
126 | * @param string $pDataType |
---|
127 | * @param PHPExcel_Worksheet $pSheet |
---|
128 | * @throws PHPExcel_Exception |
---|
129 | */ |
---|
130 | public function __construct($pValue = NULL, $pDataType = NULL, PHPExcel_Worksheet $pSheet = NULL) |
---|
131 | { |
---|
132 | // Initialise cell value |
---|
133 | $this->_value = $pValue; |
---|
134 | |
---|
135 | // Set worksheet cache |
---|
136 | $this->_parent = $pSheet->getCellCacheController(); |
---|
137 | |
---|
138 | // Set datatype? |
---|
139 | if ($pDataType !== NULL) { |
---|
140 | if ($pDataType == PHPExcel_Cell_DataType::TYPE_STRING2) |
---|
141 | $pDataType = PHPExcel_Cell_DataType::TYPE_STRING; |
---|
142 | $this->_dataType = $pDataType; |
---|
143 | } else { |
---|
144 | if (!self::getValueBinder()->bindValue($this, $pValue)) { |
---|
145 | throw new PHPExcel_Exception("Value could not be bound to cell."); |
---|
146 | } |
---|
147 | } |
---|
148 | |
---|
149 | // set default index to cellXf |
---|
150 | $this->_xfIndex = 0; |
---|
151 | } |
---|
152 | |
---|
153 | /** |
---|
154 | * Get cell coordinate column |
---|
155 | * |
---|
156 | * @return string |
---|
157 | */ |
---|
158 | public function getColumn() |
---|
159 | { |
---|
160 | return $this->_parent->getCurrentColumn(); |
---|
161 | } |
---|
162 | |
---|
163 | /** |
---|
164 | * Get cell coordinate row |
---|
165 | * |
---|
166 | * @return int |
---|
167 | */ |
---|
168 | public function getRow() |
---|
169 | { |
---|
170 | return $this->_parent->getCurrentRow(); |
---|
171 | } |
---|
172 | |
---|
173 | /** |
---|
174 | * Get cell coordinate |
---|
175 | * |
---|
176 | * @return string |
---|
177 | */ |
---|
178 | public function getCoordinate() |
---|
179 | { |
---|
180 | return $this->_parent->getCurrentAddress(); |
---|
181 | } |
---|
182 | |
---|
183 | /** |
---|
184 | * Get cell value |
---|
185 | * |
---|
186 | * @return mixed |
---|
187 | */ |
---|
188 | public function getValue() |
---|
189 | { |
---|
190 | return $this->_value; |
---|
191 | } |
---|
192 | |
---|
193 | /** |
---|
194 | * Get cell value with formatting |
---|
195 | * |
---|
196 | * @return string |
---|
197 | */ |
---|
198 | public function getFormattedValue() |
---|
199 | { |
---|
200 | return (string) PHPExcel_Style_NumberFormat::toFormattedString( |
---|
201 | $this->getCalculatedValue(), |
---|
202 | $this->getWorksheet()->getParent()->getCellXfByIndex($this->getXfIndex()) |
---|
203 | ->getNumberFormat()->getFormatCode() |
---|
204 | ); |
---|
205 | } |
---|
206 | |
---|
207 | /** |
---|
208 | * Set cell value |
---|
209 | * |
---|
210 | * Sets the value for a cell, automatically determining the datatype using the value binder |
---|
211 | * |
---|
212 | * @param mixed $pValue Value |
---|
213 | * @return PHPExcel_Cell |
---|
214 | * @throws PHPExcel_Exception |
---|
215 | */ |
---|
216 | public function setValue($pValue = NULL) |
---|
217 | { |
---|
218 | if (!self::getValueBinder()->bindValue($this, $pValue)) { |
---|
219 | throw new PHPExcel_Exception("Value could not be bound to cell."); |
---|
220 | } |
---|
221 | return $this; |
---|
222 | } |
---|
223 | |
---|
224 | /** |
---|
225 | * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder) |
---|
226 | * |
---|
227 | * @param mixed $pValue Value |
---|
228 | * @param string $pDataType Explicit data type |
---|
229 | * @return PHPExcel_Cell |
---|
230 | * @throws PHPExcel_Exception |
---|
231 | */ |
---|
232 | public function setValueExplicit($pValue = NULL, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING) |
---|
233 | { |
---|
234 | // set the value according to data type |
---|
235 | switch ($pDataType) { |
---|
236 | case PHPExcel_Cell_DataType::TYPE_NULL: |
---|
237 | $this->_value = $pValue; |
---|
238 | break; |
---|
239 | case PHPExcel_Cell_DataType::TYPE_STRING2: |
---|
240 | $pDataType = PHPExcel_Cell_DataType::TYPE_STRING; |
---|
241 | case PHPExcel_Cell_DataType::TYPE_STRING: |
---|
242 | case PHPExcel_Cell_DataType::TYPE_INLINE: |
---|
243 | $this->_value = PHPExcel_Cell_DataType::checkString($pValue); |
---|
244 | break; |
---|
245 | case PHPExcel_Cell_DataType::TYPE_NUMERIC: |
---|
246 | $this->_value = (float)$pValue; |
---|
247 | break; |
---|
248 | case PHPExcel_Cell_DataType::TYPE_FORMULA: |
---|
249 | $this->_value = (string)$pValue; |
---|
250 | break; |
---|
251 | case PHPExcel_Cell_DataType::TYPE_BOOL: |
---|
252 | $this->_value = (bool)$pValue; |
---|
253 | break; |
---|
254 | case PHPExcel_Cell_DataType::TYPE_ERROR: |
---|
255 | $this->_value = PHPExcel_Cell_DataType::checkErrorCode($pValue); |
---|
256 | break; |
---|
257 | default: |
---|
258 | throw new PHPExcel_Exception('Invalid datatype: ' . $pDataType); |
---|
259 | break; |
---|
260 | } |
---|
261 | |
---|
262 | // set the datatype |
---|
263 | $this->_dataType = $pDataType; |
---|
264 | |
---|
265 | return $this->notifyCacheController(); |
---|
266 | } |
---|
267 | |
---|
268 | /** |
---|
269 | * Get calculated cell value |
---|
270 | * |
---|
271 | * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling |
---|
272 | * |
---|
273 | * @param boolean $resetLog Whether the calculation engine logger should be reset or not |
---|
274 | * @return mixed |
---|
275 | * @throws PHPExcel_Exception |
---|
276 | */ |
---|
277 | public function getCalculatedValue($resetLog = TRUE) |
---|
278 | { |
---|
279 | //echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().PHP_EOL; |
---|
280 | if ($this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA) { |
---|
281 | try { |
---|
282 | //echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value'.PHP_EOL; |
---|
283 | $result = PHPExcel_Calculation::getInstance( |
---|
284 | $this->getWorksheet()->getParent() |
---|
285 | )->calculateCellValue($this,$resetLog); |
---|
286 | //echo $this->getCoordinate().' calculation result is '.$result.PHP_EOL; |
---|
287 | // We don't yet handle array returns |
---|
288 | if (is_array($result)) { |
---|
289 | while (is_array($result)) { |
---|
290 | $result = array_pop($result); |
---|
291 | } |
---|
292 | } |
---|
293 | } catch ( PHPExcel_Exception $ex ) { |
---|
294 | if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->_calculatedValue !== NULL)) { |
---|
295 | //echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL; |
---|
296 | return $this->_calculatedValue; // Fallback for calculations referencing external files. |
---|
297 | } |
---|
298 | //echo 'Calculation Exception: '.$ex->getMessage().PHP_EOL; |
---|
299 | $result = '#N/A'; |
---|
300 | throw new PHPExcel_Calculation_Exception( |
---|
301 | $this->getWorksheet()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage() |
---|
302 | ); |
---|
303 | } |
---|
304 | |
---|
305 | if ($result === '#Not Yet Implemented') { |
---|
306 | //echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL; |
---|
307 | return $this->_calculatedValue; // Fallback if calculation engine does not support the formula. |
---|
308 | } |
---|
309 | //echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().PHP_EOL; |
---|
310 | return $result; |
---|
311 | } elseif($this->_value instanceof PHPExcel_RichText) { |
---|
312 | // echo 'Cell value for '.$this->getCoordinate().' is rich text: Returning data value of '.$this->_value.'<br />'; |
---|
313 | return $this->_value->getPlainText(); |
---|
314 | } |
---|
315 | // echo 'Cell value for '.$this->getCoordinate().' is not a formula: Returning data value of '.$this->_value.'<br />'; |
---|
316 | return $this->_value; |
---|
317 | } |
---|
318 | |
---|
319 | /** |
---|
320 | * Set old calculated value (cached) |
---|
321 | * |
---|
322 | * @param mixed $pValue Value |
---|
323 | * @return PHPExcel_Cell |
---|
324 | */ |
---|
325 | public function setCalculatedValue($pValue = NULL) |
---|
326 | { |
---|
327 | if ($pValue !== NULL) { |
---|
328 | $this->_calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue; |
---|
329 | } |
---|
330 | |
---|
331 | return $this->notifyCacheController(); |
---|
332 | } |
---|
333 | |
---|
334 | /** |
---|
335 | * Get old calculated value (cached) |
---|
336 | * This returns the value last calculated by MS Excel or whichever spreadsheet program was used to |
---|
337 | * create the original spreadsheet file. |
---|
338 | * Note that this value is not guaranteed to refelect the actual calculated value because it is |
---|
339 | * possible that auto-calculation was disabled in the original spreadsheet, and underlying data |
---|
340 | * values used by the formula have changed since it was last calculated. |
---|
341 | * |
---|
342 | * @return mixed |
---|
343 | */ |
---|
344 | public function getOldCalculatedValue() |
---|
345 | { |
---|
346 | return $this->_calculatedValue; |
---|
347 | } |
---|
348 | |
---|
349 | /** |
---|
350 | * Get cell data type |
---|
351 | * |
---|
352 | * @return string |
---|
353 | */ |
---|
354 | public function getDataType() |
---|
355 | { |
---|
356 | return $this->_dataType; |
---|
357 | } |
---|
358 | |
---|
359 | /** |
---|
360 | * Set cell data type |
---|
361 | * |
---|
362 | * @param string $pDataType |
---|
363 | * @return PHPExcel_Cell |
---|
364 | */ |
---|
365 | public function setDataType($pDataType = PHPExcel_Cell_DataType::TYPE_STRING) |
---|
366 | { |
---|
367 | if ($pDataType == PHPExcel_Cell_DataType::TYPE_STRING2) |
---|
368 | $pDataType = PHPExcel_Cell_DataType::TYPE_STRING; |
---|
369 | |
---|
370 | $this->_dataType = $pDataType; |
---|
371 | |
---|
372 | return $this->notifyCacheController(); |
---|
373 | } |
---|
374 | |
---|
375 | /** |
---|
376 | * Identify if the cell contains a formula |
---|
377 | * |
---|
378 | * @return boolean |
---|
379 | */ |
---|
380 | public function isFormula() |
---|
381 | { |
---|
382 | return $this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA; |
---|
383 | } |
---|
384 | |
---|
385 | /** |
---|
386 | * Does this cell contain Data validation rules? |
---|
387 | * |
---|
388 | * @return boolean |
---|
389 | * @throws PHPExcel_Exception |
---|
390 | */ |
---|
391 | public function hasDataValidation() |
---|
392 | { |
---|
393 | if (!isset($this->_parent)) { |
---|
394 | throw new PHPExcel_Exception('Cannot check for data validation when cell is not bound to a worksheet'); |
---|
395 | } |
---|
396 | |
---|
397 | return $this->getWorksheet()->dataValidationExists($this->getCoordinate()); |
---|
398 | } |
---|
399 | |
---|
400 | /** |
---|
401 | * Get Data validation rules |
---|
402 | * |
---|
403 | * @return PHPExcel_Cell_DataValidation |
---|
404 | * @throws PHPExcel_Exception |
---|
405 | */ |
---|
406 | public function getDataValidation() |
---|
407 | { |
---|
408 | if (!isset($this->_parent)) { |
---|
409 | throw new PHPExcel_Exception('Cannot get data validation for cell that is not bound to a worksheet'); |
---|
410 | } |
---|
411 | |
---|
412 | return $this->getWorksheet()->getDataValidation($this->getCoordinate()); |
---|
413 | } |
---|
414 | |
---|
415 | /** |
---|
416 | * Set Data validation rules |
---|
417 | * |
---|
418 | * @param PHPExcel_Cell_DataValidation $pDataValidation |
---|
419 | * @return PHPExcel_Cell |
---|
420 | * @throws PHPExcel_Exception |
---|
421 | */ |
---|
422 | public function setDataValidation(PHPExcel_Cell_DataValidation $pDataValidation = NULL) |
---|
423 | { |
---|
424 | if (!isset($this->_parent)) { |
---|
425 | throw new PHPExcel_Exception('Cannot set data validation for cell that is not bound to a worksheet'); |
---|
426 | } |
---|
427 | |
---|
428 | $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation); |
---|
429 | |
---|
430 | return $this->notifyCacheController(); |
---|
431 | } |
---|
432 | |
---|
433 | /** |
---|
434 | * Does this cell contain a Hyperlink? |
---|
435 | * |
---|
436 | * @return boolean |
---|
437 | * @throws PHPExcel_Exception |
---|
438 | */ |
---|
439 | public function hasHyperlink() |
---|
440 | { |
---|
441 | if (!isset($this->_parent)) { |
---|
442 | throw new PHPExcel_Exception('Cannot check for hyperlink when cell is not bound to a worksheet'); |
---|
443 | } |
---|
444 | |
---|
445 | return $this->getWorksheet()->hyperlinkExists($this->getCoordinate()); |
---|
446 | } |
---|
447 | |
---|
448 | /** |
---|
449 | * Get Hyperlink |
---|
450 | * |
---|
451 | * @return PHPExcel_Cell_Hyperlink |
---|
452 | * @throws PHPExcel_Exception |
---|
453 | */ |
---|
454 | public function getHyperlink() |
---|
455 | { |
---|
456 | if (!isset($this->_parent)) { |
---|
457 | throw new PHPExcel_Exception('Cannot get hyperlink for cell that is not bound to a worksheet'); |
---|
458 | } |
---|
459 | |
---|
460 | return $this->getWorksheet()->getHyperlink($this->getCoordinate()); |
---|
461 | } |
---|
462 | |
---|
463 | /** |
---|
464 | * Set Hyperlink |
---|
465 | * |
---|
466 | * @param PHPExcel_Cell_Hyperlink $pHyperlink |
---|
467 | * @return PHPExcel_Cell |
---|
468 | * @throws PHPExcel_Exception |
---|
469 | */ |
---|
470 | public function setHyperlink(PHPExcel_Cell_Hyperlink $pHyperlink = NULL) |
---|
471 | { |
---|
472 | if (!isset($this->_parent)) { |
---|
473 | throw new PHPExcel_Exception('Cannot set hyperlink for cell that is not bound to a worksheet'); |
---|
474 | } |
---|
475 | |
---|
476 | $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink); |
---|
477 | |
---|
478 | return $this->notifyCacheController(); |
---|
479 | } |
---|
480 | |
---|
481 | /** |
---|
482 | * Get parent worksheet |
---|
483 | * |
---|
484 | * @return PHPExcel_CachedObjectStorage_CacheBase |
---|
485 | */ |
---|
486 | public function getParent() { |
---|
487 | return $this->_parent; |
---|
488 | } |
---|
489 | |
---|
490 | /** |
---|
491 | * Get parent worksheet |
---|
492 | * |
---|
493 | * @return PHPExcel_Worksheet |
---|
494 | */ |
---|
495 | public function getWorksheet() { |
---|
496 | return $this->_parent->getParent(); |
---|
497 | } |
---|
498 | |
---|
499 | /** |
---|
500 | * Get cell style |
---|
501 | * |
---|
502 | * @return PHPExcel_Style |
---|
503 | */ |
---|
504 | public function getStyle() |
---|
505 | { |
---|
506 | return $this->getWorksheet()->getParent()->getCellXfByIndex($this->getXfIndex()); |
---|
507 | } |
---|
508 | |
---|
509 | /** |
---|
510 | * Re-bind parent |
---|
511 | * |
---|
512 | * @param PHPExcel_Worksheet $parent |
---|
513 | * @return PHPExcel_Cell |
---|
514 | */ |
---|
515 | public function rebindParent(PHPExcel_Worksheet $parent) { |
---|
516 | $this->_parent = $parent->getCellCacheController(); |
---|
517 | |
---|
518 | return $this->notifyCacheController(); |
---|
519 | } |
---|
520 | |
---|
521 | /** |
---|
522 | * Is cell in a specific range? |
---|
523 | * |
---|
524 | * @param string $pRange Cell range (e.g. A1:A1) |
---|
525 | * @return boolean |
---|
526 | */ |
---|
527 | public function isInRange($pRange = 'A1:A1') |
---|
528 | { |
---|
529 | list($rangeStart,$rangeEnd) = self::rangeBoundaries($pRange); |
---|
530 | |
---|
531 | // Translate properties |
---|
532 | $myColumn = self::columnIndexFromString($this->getColumn()); |
---|
533 | $myRow = $this->getRow(); |
---|
534 | |
---|
535 | // Verify if cell is in range |
---|
536 | return (($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) && |
---|
537 | ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow) |
---|
538 | ); |
---|
539 | } |
---|
540 | |
---|
541 | /** |
---|
542 | * Coordinate from string |
---|
543 | * |
---|
544 | * @param string $pCoordinateString |
---|
545 | * @return array Array containing column and row (indexes 0 and 1) |
---|
546 | * @throws PHPExcel_Exception |
---|
547 | */ |
---|
548 | public static function coordinateFromString($pCoordinateString = 'A1') |
---|
549 | { |
---|
550 | if (preg_match("/^([$]?[A-Z]{1,3})([$]?\d{1,7})$/", $pCoordinateString, $matches)) { |
---|
551 | return array($matches[1],$matches[2]); |
---|
552 | } elseif ((strpos($pCoordinateString,':') !== FALSE) || (strpos($pCoordinateString,',') !== FALSE)) { |
---|
553 | throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells'); |
---|
554 | } elseif ($pCoordinateString == '') { |
---|
555 | throw new PHPExcel_Exception('Cell coordinate can not be zero-length string'); |
---|
556 | } |
---|
557 | |
---|
558 | throw new PHPExcel_Exception('Invalid cell coordinate '.$pCoordinateString); |
---|
559 | } |
---|
560 | |
---|
561 | /** |
---|
562 | * Make string row, column or cell coordinate absolute |
---|
563 | * |
---|
564 | * @param string $pCoordinateString e.g. 'A' or '1' or 'A1' |
---|
565 | * Note that this value can be a row or column reference as well as a cell reference |
---|
566 | * @return string Absolute coordinate e.g. '$A' or '$1' or '$A$1' |
---|
567 | * @throws PHPExcel_Exception |
---|
568 | */ |
---|
569 | public static function absoluteReference($pCoordinateString = 'A1') |
---|
570 | { |
---|
571 | if (strpos($pCoordinateString,':') === FALSE && strpos($pCoordinateString,',') === FALSE) { |
---|
572 | // Split out any worksheet name from the reference |
---|
573 | $worksheet = ''; |
---|
574 | $cellAddress = explode('!',$pCoordinateString); |
---|
575 | if (count($cellAddress) > 1) { |
---|
576 | list($worksheet,$pCoordinateString) = $cellAddress; |
---|
577 | } |
---|
578 | if ($worksheet > '') $worksheet .= '!'; |
---|
579 | |
---|
580 | // Create absolute coordinate |
---|
581 | if (ctype_digit($pCoordinateString)) { |
---|
582 | return $worksheet . '$' . $pCoordinateString; |
---|
583 | } elseif (ctype_alpha($pCoordinateString)) { |
---|
584 | return $worksheet . '$' . strtoupper($pCoordinateString); |
---|
585 | } |
---|
586 | return $worksheet . self::absoluteCoordinate($pCoordinateString); |
---|
587 | } |
---|
588 | |
---|
589 | throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells'); |
---|
590 | } |
---|
591 | |
---|
592 | /** |
---|
593 | * Make string coordinate absolute |
---|
594 | * |
---|
595 | * @param string $pCoordinateString e.g. 'A1' |
---|
596 | * @return string Absolute coordinate e.g. '$A$1' |
---|
597 | * @throws PHPExcel_Exception |
---|
598 | */ |
---|
599 | public static function absoluteCoordinate($pCoordinateString = 'A1') |
---|
600 | { |
---|
601 | if (strpos($pCoordinateString,':') === FALSE && strpos($pCoordinateString,',') === FALSE) { |
---|
602 | // Split out any worksheet name from the coordinate |
---|
603 | $worksheet = ''; |
---|
604 | $cellAddress = explode('!',$pCoordinateString); |
---|
605 | if (count($cellAddress) > 1) { |
---|
606 | list($worksheet,$pCoordinateString) = $cellAddress; |
---|
607 | } |
---|
608 | if ($worksheet > '') $worksheet .= '!'; |
---|
609 | |
---|
610 | // Create absolute coordinate |
---|
611 | list($column, $row) = self::coordinateFromString($pCoordinateString); |
---|
612 | $column = ltrim($column,'$'); |
---|
613 | $row = ltrim($row,'$'); |
---|
614 | return $worksheet . '$' . $column . '$' . $row; |
---|
615 | } |
---|
616 | |
---|
617 | throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells'); |
---|
618 | } |
---|
619 | |
---|
620 | /** |
---|
621 | * Split range into coordinate strings |
---|
622 | * |
---|
623 | * @param string $pRange e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4' |
---|
624 | * @return array Array containg one or more arrays containing one or two coordinate strings |
---|
625 | * e.g. array('B4','D9') or array(array('B4','D9'),array('H2','O11')) |
---|
626 | * or array('B4') |
---|
627 | */ |
---|
628 | public static function splitRange($pRange = 'A1:A1') |
---|
629 | { |
---|
630 | // Ensure $pRange is a valid range |
---|
631 | if(empty($pRange)) { |
---|
632 | $pRange = self::DEFAULT_RANGE; |
---|
633 | } |
---|
634 | |
---|
635 | $exploded = explode(',', $pRange); |
---|
636 | $counter = count($exploded); |
---|
637 | for ($i = 0; $i < $counter; ++$i) { |
---|
638 | $exploded[$i] = explode(':', $exploded[$i]); |
---|
639 | } |
---|
640 | return $exploded; |
---|
641 | } |
---|
642 | |
---|
643 | /** |
---|
644 | * Build range from coordinate strings |
---|
645 | * |
---|
646 | * @param array $pRange Array containg one or more arrays containing one or two coordinate strings |
---|
647 | * @return string String representation of $pRange |
---|
648 | * @throws PHPExcel_Exception |
---|
649 | */ |
---|
650 | public static function buildRange($pRange) |
---|
651 | { |
---|
652 | // Verify range |
---|
653 | if (!is_array($pRange) || empty($pRange) || !is_array($pRange[0])) { |
---|
654 | throw new PHPExcel_Exception('Range does not contain any information'); |
---|
655 | } |
---|
656 | |
---|
657 | // Build range |
---|
658 | $imploded = array(); |
---|
659 | $counter = count($pRange); |
---|
660 | for ($i = 0; $i < $counter; ++$i) { |
---|
661 | $pRange[$i] = implode(':', $pRange[$i]); |
---|
662 | } |
---|
663 | $imploded = implode(',', $pRange); |
---|
664 | |
---|
665 | return $imploded; |
---|
666 | } |
---|
667 | |
---|
668 | /** |
---|
669 | * Calculate range boundaries |
---|
670 | * |
---|
671 | * @param string $pRange Cell range (e.g. A1:A1) |
---|
672 | * @return array Range coordinates array(Start Cell, End Cell) |
---|
673 | * where Start Cell and End Cell are arrays (Column Number, Row Number) |
---|
674 | */ |
---|
675 | public static function rangeBoundaries($pRange = 'A1:A1') |
---|
676 | { |
---|
677 | // Ensure $pRange is a valid range |
---|
678 | if(empty($pRange)) { |
---|
679 | $pRange = self::DEFAULT_RANGE; |
---|
680 | } |
---|
681 | |
---|
682 | // Uppercase coordinate |
---|
683 | $pRange = strtoupper($pRange); |
---|
684 | |
---|
685 | // Extract range |
---|
686 | if (strpos($pRange, ':') === FALSE) { |
---|
687 | $rangeA = $rangeB = $pRange; |
---|
688 | } else { |
---|
689 | list($rangeA, $rangeB) = explode(':', $pRange); |
---|
690 | } |
---|
691 | |
---|
692 | // Calculate range outer borders |
---|
693 | $rangeStart = self::coordinateFromString($rangeA); |
---|
694 | $rangeEnd = self::coordinateFromString($rangeB); |
---|
695 | |
---|
696 | // Translate column into index |
---|
697 | $rangeStart[0] = self::columnIndexFromString($rangeStart[0]); |
---|
698 | $rangeEnd[0] = self::columnIndexFromString($rangeEnd[0]); |
---|
699 | |
---|
700 | return array($rangeStart, $rangeEnd); |
---|
701 | } |
---|
702 | |
---|
703 | /** |
---|
704 | * Calculate range dimension |
---|
705 | * |
---|
706 | * @param string $pRange Cell range (e.g. A1:A1) |
---|
707 | * @return array Range dimension (width, height) |
---|
708 | */ |
---|
709 | public static function rangeDimension($pRange = 'A1:A1') |
---|
710 | { |
---|
711 | // Calculate range outer borders |
---|
712 | list($rangeStart,$rangeEnd) = self::rangeBoundaries($pRange); |
---|
713 | |
---|
714 | return array( ($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1) ); |
---|
715 | } |
---|
716 | |
---|
717 | /** |
---|
718 | * Calculate range boundaries |
---|
719 | * |
---|
720 | * @param string $pRange Cell range (e.g. A1:A1) |
---|
721 | * @return array Range coordinates array(Start Cell, End Cell) |
---|
722 | * where Start Cell and End Cell are arrays (Column ID, Row Number) |
---|
723 | */ |
---|
724 | public static function getRangeBoundaries($pRange = 'A1:A1') |
---|
725 | { |
---|
726 | // Ensure $pRange is a valid range |
---|
727 | if(empty($pRange)) { |
---|
728 | $pRange = self::DEFAULT_RANGE; |
---|
729 | } |
---|
730 | |
---|
731 | // Uppercase coordinate |
---|
732 | $pRange = strtoupper($pRange); |
---|
733 | |
---|
734 | // Extract range |
---|
735 | if (strpos($pRange, ':') === FALSE) { |
---|
736 | $rangeA = $rangeB = $pRange; |
---|
737 | } else { |
---|
738 | list($rangeA, $rangeB) = explode(':', $pRange); |
---|
739 | } |
---|
740 | |
---|
741 | return array( self::coordinateFromString($rangeA), self::coordinateFromString($rangeB)); |
---|
742 | } |
---|
743 | |
---|
744 | /** |
---|
745 | * Column index from string |
---|
746 | * |
---|
747 | * @param string $pString |
---|
748 | * @return int Column index (base 1 !!!) |
---|
749 | */ |
---|
750 | public static function columnIndexFromString($pString = 'A') |
---|
751 | { |
---|
752 | // Using a lookup cache adds a slight memory overhead, but boosts speed |
---|
753 | // caching using a static within the method is faster than a class static, |
---|
754 | // though it's additional memory overhead |
---|
755 | static $_indexCache = array(); |
---|
756 | |
---|
757 | if (isset($_indexCache[$pString])) |
---|
758 | return $_indexCache[$pString]; |
---|
759 | |
---|
760 | // It's surprising how costly the strtoupper() and ord() calls actually are, so we use a lookup array rather than use ord() |
---|
761 | // and make it case insensitive to get rid of the strtoupper() as well. Because it's a static, there's no significant |
---|
762 | // memory overhead either |
---|
763 | static $_columnLookup = array( |
---|
764 | 'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8, 'I' => 9, 'J' => 10, 'K' => 11, 'L' => 12, 'M' => 13, |
---|
765 | 'N' => 14, 'O' => 15, 'P' => 16, 'Q' => 17, 'R' => 18, 'S' => 19, 'T' => 20, 'U' => 21, 'V' => 22, 'W' => 23, 'X' => 24, 'Y' => 25, 'Z' => 26, |
---|
766 | 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8, 'i' => 9, 'j' => 10, 'k' => 11, 'l' => 12, 'm' => 13, |
---|
767 | 'n' => 14, 'o' => 15, 'p' => 16, 'q' => 17, 'r' => 18, 's' => 19, 't' => 20, 'u' => 21, 'v' => 22, 'w' => 23, 'x' => 24, 'y' => 25, 'z' => 26 |
---|
768 | ); |
---|
769 | |
---|
770 | // We also use the language construct isset() rather than the more costly strlen() function to match the length of $pString |
---|
771 | // for improved performance |
---|
772 | if (isset($pString{0})) { |
---|
773 | if (!isset($pString{1})) { |
---|
774 | $_indexCache[$pString] = $_columnLookup[$pString]; |
---|
775 | return $_indexCache[$pString]; |
---|
776 | } elseif(!isset($pString{2})) { |
---|
777 | $_indexCache[$pString] = $_columnLookup[$pString{0}] * 26 + $_columnLookup[$pString{1}]; |
---|
778 | return $_indexCache[$pString]; |
---|
779 | } elseif(!isset($pString{3})) { |
---|
780 | $_indexCache[$pString] = $_columnLookup[$pString{0}] * 676 + $_columnLookup[$pString{1}] * 26 + $_columnLookup[$pString{2}]; |
---|
781 | return $_indexCache[$pString]; |
---|
782 | } |
---|
783 | } |
---|
784 | throw new PHPExcel_Exception("Column string index can not be " . ((isset($pString{0})) ? "longer than 3 characters" : "empty")); |
---|
785 | } |
---|
786 | |
---|
787 | /** |
---|
788 | * String from columnindex |
---|
789 | * |
---|
790 | * @param int $pColumnIndex Column index (base 0 !!!) |
---|
791 | * @return string |
---|
792 | */ |
---|
793 | public static function stringFromColumnIndex($pColumnIndex = 0) |
---|
794 | { |
---|
795 | // Using a lookup cache adds a slight memory overhead, but boosts speed |
---|
796 | // caching using a static within the method is faster than a class static, |
---|
797 | // though it's additional memory overhead |
---|
798 | static $_indexCache = array(); |
---|
799 | |
---|
800 | if (!isset($_indexCache[$pColumnIndex])) { |
---|
801 | // Determine column string |
---|
802 | if ($pColumnIndex < 26) { |
---|
803 | $_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex); |
---|
804 | } elseif ($pColumnIndex < 702) { |
---|
805 | $_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) . |
---|
806 | chr(65 + $pColumnIndex % 26); |
---|
807 | } else { |
---|
808 | $_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) . |
---|
809 | chr(65 + ((($pColumnIndex - 26) % 676) / 26)) . |
---|
810 | chr(65 + $pColumnIndex % 26); |
---|
811 | } |
---|
812 | } |
---|
813 | return $_indexCache[$pColumnIndex]; |
---|
814 | } |
---|
815 | |
---|
816 | /** |
---|
817 | * Extract all cell references in range |
---|
818 | * |
---|
819 | * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25) |
---|
820 | * @return array Array containing single cell references |
---|
821 | */ |
---|
822 | public static function extractAllCellReferencesInRange($pRange = 'A1') { |
---|
823 | // Returnvalue |
---|
824 | $returnValue = array(); |
---|
825 | |
---|
826 | // Explode spaces |
---|
827 | $cellBlocks = explode(' ', str_replace('$', '', strtoupper($pRange))); |
---|
828 | foreach ($cellBlocks as $cellBlock) { |
---|
829 | // Single cell? |
---|
830 | if (strpos($cellBlock,':') === FALSE && strpos($cellBlock,',') === FALSE) { |
---|
831 | $returnValue[] = $cellBlock; |
---|
832 | continue; |
---|
833 | } |
---|
834 | |
---|
835 | // Range... |
---|
836 | $ranges = self::splitRange($cellBlock); |
---|
837 | foreach($ranges as $range) { |
---|
838 | // Single cell? |
---|
839 | if (!isset($range[1])) { |
---|
840 | $returnValue[] = $range[0]; |
---|
841 | continue; |
---|
842 | } |
---|
843 | |
---|
844 | // Range... |
---|
845 | list($rangeStart, $rangeEnd) = $range; |
---|
846 | sscanf($rangeStart,'%[A-Z]%d', $startCol, $startRow); |
---|
847 | sscanf($rangeEnd,'%[A-Z]%d', $endCol, $endRow); |
---|
848 | $endCol++; |
---|
849 | |
---|
850 | // Current data |
---|
851 | $currentCol = $startCol; |
---|
852 | $currentRow = $startRow; |
---|
853 | |
---|
854 | // Loop cells |
---|
855 | while ($currentCol != $endCol) { |
---|
856 | while ($currentRow <= $endRow) { |
---|
857 | $returnValue[] = $currentCol.$currentRow; |
---|
858 | ++$currentRow; |
---|
859 | } |
---|
860 | ++$currentCol; |
---|
861 | $currentRow = $startRow; |
---|
862 | } |
---|
863 | } |
---|
864 | } |
---|
865 | |
---|
866 | // Sort the result by column and row |
---|
867 | $sortKeys = array(); |
---|
868 | foreach (array_unique($returnValue) as $coord) { |
---|
869 | sscanf($coord,'%[A-Z]%d', $column, $row); |
---|
870 | $sortKeys[sprintf('%3s%09d',$column,$row)] = $coord; |
---|
871 | } |
---|
872 | ksort($sortKeys); |
---|
873 | |
---|
874 | // Return value |
---|
875 | return array_values($sortKeys); |
---|
876 | } |
---|
877 | |
---|
878 | /** |
---|
879 | * Compare 2 cells |
---|
880 | * |
---|
881 | * @param PHPExcel_Cell $a Cell a |
---|
882 | * @param PHPExcel_Cell $b Cell b |
---|
883 | * @return int Result of comparison (always -1 or 1, never zero!) |
---|
884 | */ |
---|
885 | public static function compareCells(PHPExcel_Cell $a, PHPExcel_Cell $b) |
---|
886 | { |
---|
887 | if ($a->getRow() < $b->getRow()) { |
---|
888 | return -1; |
---|
889 | } elseif ($a->getRow() > $b->getRow()) { |
---|
890 | return 1; |
---|
891 | } elseif (self::columnIndexFromString($a->getColumn()) < self::columnIndexFromString($b->getColumn())) { |
---|
892 | return -1; |
---|
893 | } else { |
---|
894 | return 1; |
---|
895 | } |
---|
896 | } |
---|
897 | |
---|
898 | /** |
---|
899 | * Get value binder to use |
---|
900 | * |
---|
901 | * @return PHPExcel_Cell_IValueBinder |
---|
902 | */ |
---|
903 | public static function getValueBinder() { |
---|
904 | if (self::$_valueBinder === NULL) { |
---|
905 | self::$_valueBinder = new PHPExcel_Cell_DefaultValueBinder(); |
---|
906 | } |
---|
907 | |
---|
908 | return self::$_valueBinder; |
---|
909 | } |
---|
910 | |
---|
911 | /** |
---|
912 | * Set value binder to use |
---|
913 | * |
---|
914 | * @param PHPExcel_Cell_IValueBinder $binder |
---|
915 | * @throws PHPExcel_Exception |
---|
916 | */ |
---|
917 | public static function setValueBinder(PHPExcel_Cell_IValueBinder $binder = NULL) { |
---|
918 | if ($binder === NULL) { |
---|
919 | throw new PHPExcel_Exception("A PHPExcel_Cell_IValueBinder is required for PHPExcel to function correctly."); |
---|
920 | } |
---|
921 | |
---|
922 | self::$_valueBinder = $binder; |
---|
923 | } |
---|
924 | |
---|
925 | /** |
---|
926 | * Implement PHP __clone to create a deep clone, not just a shallow copy. |
---|
927 | */ |
---|
928 | public function __clone() { |
---|
929 | $vars = get_object_vars($this); |
---|
930 | foreach ($vars as $key => $value) { |
---|
931 | if ((is_object($value)) && ($key != '_parent')) { |
---|
932 | $this->$key = clone $value; |
---|
933 | } else { |
---|
934 | $this->$key = $value; |
---|
935 | } |
---|
936 | } |
---|
937 | } |
---|
938 | |
---|
939 | /** |
---|
940 | * Get index to cellXf |
---|
941 | * |
---|
942 | * @return int |
---|
943 | */ |
---|
944 | public function getXfIndex() |
---|
945 | { |
---|
946 | return $this->_xfIndex; |
---|
947 | } |
---|
948 | |
---|
949 | /** |
---|
950 | * Set index to cellXf |
---|
951 | * |
---|
952 | * @param int $pValue |
---|
953 | * @return PHPExcel_Cell |
---|
954 | */ |
---|
955 | public function setXfIndex($pValue = 0) |
---|
956 | { |
---|
957 | $this->_xfIndex = $pValue; |
---|
958 | |
---|
959 | return $this->notifyCacheController(); |
---|
960 | } |
---|
961 | |
---|
962 | /** |
---|
963 | * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling |
---|
964 | */ |
---|
965 | public function setFormulaAttributes($pAttributes) |
---|
966 | { |
---|
967 | $this->_formulaAttributes = $pAttributes; |
---|
968 | return $this; |
---|
969 | } |
---|
970 | |
---|
971 | /** |
---|
972 | * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling |
---|
973 | */ |
---|
974 | public function getFormulaAttributes() |
---|
975 | { |
---|
976 | return $this->_formulaAttributes; |
---|
977 | } |
---|
978 | |
---|
979 | /** |
---|
980 | * Convert to string |
---|
981 | * |
---|
982 | * @return string |
---|
983 | */ |
---|
984 | public function __toString() |
---|
985 | { |
---|
986 | return (string) $this->getValue(); |
---|
987 | } |
---|
988 | |
---|
989 | } |
---|
990 | |
---|