MDL-44017 Events: Added unit test for report_viewed events
[moodle.git] / lib / phpexcel / PHPExcel.php
blobcc70286d4364d7f527643317891bde2a53fe6ab8
1 <?php
2 /**
3 * PHPExcel
5 * Copyright (c) 2006 - 2012 PHPExcel
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.
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.
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
21 * @category PHPExcel
22 * @package PHPExcel
23 * @copyright Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
24 * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
25 * @version ##VERSION##, ##DATE##
29 /** PHPExcel root directory */
30 if (!defined('PHPEXCEL_ROOT')) {
31 define('PHPEXCEL_ROOT', dirname(__FILE__) . '/');
32 require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
36 /**
37 * PHPExcel
39 * @category PHPExcel
40 * @package PHPExcel
41 * @copyright Copyright (c) 2006 - 2012 PHPExcel (http://www.codeplex.com/PHPExcel)
43 class PHPExcel
45 /**
46 * Document properties
48 * @var PHPExcel_DocumentProperties
50 private $_properties;
52 /**
53 * Document security
55 * @var PHPExcel_DocumentSecurity
57 private $_security;
59 /**
60 * Collection of Worksheet objects
62 * @var PHPExcel_Worksheet[]
64 private $_workSheetCollection = array();
66 /**
67 * Active sheet index
69 * @var int
71 private $_activeSheetIndex = 0;
73 /**
74 * Named ranges
76 * @var PHPExcel_NamedRange[]
78 private $_namedRanges = array();
80 /**
81 * CellXf supervisor
83 * @var PHPExcel_Style
85 private $_cellXfSupervisor;
87 /**
88 * CellXf collection
90 * @var PHPExcel_Style[]
92 private $_cellXfCollection = array();
94 /**
95 * CellStyleXf collection
97 * @var PHPExcel_Style[]
99 private $_cellStyleXfCollection = array();
102 * Create a new PHPExcel with one Worksheet
104 public function __construct()
106 // Initialise worksheet collection and add one worksheet
107 $this->_workSheetCollection = array();
108 $this->_workSheetCollection[] = new PHPExcel_Worksheet($this);
109 $this->_activeSheetIndex = 0;
111 // Create document properties
112 $this->_properties = new PHPExcel_DocumentProperties();
114 // Create document security
115 $this->_security = new PHPExcel_DocumentSecurity();
117 // Set named ranges
118 $this->_namedRanges = array();
120 // Create the cellXf supervisor
121 $this->_cellXfSupervisor = new PHPExcel_Style(true);
122 $this->_cellXfSupervisor->bindParent($this);
124 // Create the default style
125 $this->addCellXf(new PHPExcel_Style);
126 $this->addCellStyleXf(new PHPExcel_Style);
131 * Disconnect all worksheets from this PHPExcel workbook object,
132 * typically so that the PHPExcel object can be unset
135 public function disconnectWorksheets() {
136 foreach($this->_workSheetCollection as $k => &$worksheet) {
137 $worksheet->disconnectCells();
138 $this->_workSheetCollection[$k] = null;
140 unset($worksheet);
141 $this->_workSheetCollection = array();
145 * Get properties
147 * @return PHPExcel_DocumentProperties
149 public function getProperties()
151 return $this->_properties;
155 * Set properties
157 * @param PHPExcel_DocumentProperties $pValue
159 public function setProperties(PHPExcel_DocumentProperties $pValue)
161 $this->_properties = $pValue;
165 * Get security
167 * @return PHPExcel_DocumentSecurity
169 public function getSecurity()
171 return $this->_security;
175 * Set security
177 * @param PHPExcel_DocumentSecurity $pValue
179 public function setSecurity(PHPExcel_DocumentSecurity $pValue)
181 $this->_security = $pValue;
185 * Get active sheet
187 * @return PHPExcel_Worksheet
189 public function getActiveSheet()
191 return $this->_workSheetCollection[$this->_activeSheetIndex];
195 * Create sheet and add it to this workbook
197 * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
198 * @return PHPExcel_Worksheet
199 * @throws Exception
201 public function createSheet($iSheetIndex = NULL)
203 $newSheet = new PHPExcel_Worksheet($this);
204 $this->addSheet($newSheet, $iSheetIndex);
205 return $newSheet;
209 * Chech if a sheet with a specified name already exists
211 * @param string $pSheetName Name of the worksheet to check
212 * @return boolean
214 public function sheetNameExists($pSheetName)
216 return ($this->getSheetByName($pSheetName) !== NULL);
220 * Add sheet
222 * @param PHPExcel_Worksheet $pSheet
223 * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
224 * @return PHPExcel_Worksheet
225 * @throws Exception
227 public function addSheet(PHPExcel_Worksheet $pSheet, $iSheetIndex = NULL)
229 if ($this->sheetNameExists($pSheet->getTitle())) {
230 throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename this worksheet first.");
233 if($iSheetIndex === NULL) {
234 if ($this->_activeSheetIndex < 0) {
235 $this->_activeSheetIndex = 0;
237 $this->_workSheetCollection[] = $pSheet;
238 } else {
239 // Insert the sheet at the requested index
240 array_splice(
241 $this->_workSheetCollection,
242 $iSheetIndex,
244 array($pSheet)
247 // Adjust active sheet index if necessary
248 if ($this->_activeSheetIndex >= $iSheetIndex) {
249 ++$this->_activeSheetIndex;
252 return $pSheet;
256 * Remove sheet by index
258 * @param int $pIndex Active sheet index
259 * @throws Exception
261 public function removeSheetByIndex($pIndex = 0)
263 if ($pIndex > count($this->_workSheetCollection) - 1) {
264 throw new Exception("Sheet index is out of bounds.");
265 } else {
266 array_splice($this->_workSheetCollection, $pIndex, 1);
268 // Adjust active sheet index if necessary
269 if (($this->_activeSheetIndex >= $pIndex) &&
270 ($pIndex > count($this->_workSheetCollection) - 1)) {
271 --$this->_activeSheetIndex;
277 * Get sheet by index
279 * @param int $pIndex Sheet index
280 * @return PHPExcel_Worksheet
281 * @throws Exception
283 public function getSheet($pIndex = 0)
285 if ($pIndex > count($this->_workSheetCollection) - 1) {
286 throw new Exception("Sheet index is out of bounds.");
287 } else {
288 return $this->_workSheetCollection[$pIndex];
293 * Get all sheets
295 * @return PHPExcel_Worksheet[]
297 public function getAllSheets()
299 return $this->_workSheetCollection;
303 * Get sheet by name
305 * @param string $pName Sheet name
306 * @return PHPExcel_Worksheet
307 * @throws Exception
309 public function getSheetByName($pName = '')
311 $worksheetCount = count($this->_workSheetCollection);
312 for ($i = 0; $i < $worksheetCount; ++$i) {
313 if ($this->_workSheetCollection[$i]->getTitle() == $pName) {
314 return $this->_workSheetCollection[$i];
318 return null;
322 * Get index for sheet
324 * @param PHPExcel_Worksheet $pSheet
325 * @return Sheet index
326 * @throws Exception
328 public function getIndex(PHPExcel_Worksheet $pSheet)
330 foreach ($this->_workSheetCollection as $key => $value) {
331 if ($value->getHashCode() == $pSheet->getHashCode()) {
332 return $key;
338 * Set index for sheet by sheet name.
340 * @param string $sheetName Sheet name to modify index for
341 * @param int $newIndex New index for the sheet
342 * @return New sheet index
343 * @throws Exception
345 public function setIndexByName($sheetName, $newIndex)
347 $oldIndex = $this->getIndex($this->getSheetByName($sheetName));
348 $pSheet = array_splice(
349 $this->_workSheetCollection,
350 $oldIndex,
353 array_splice(
354 $this->_workSheetCollection,
355 $newIndex,
357 $pSheet
359 return $newIndex;
363 * Get sheet count
365 * @return int
367 public function getSheetCount()
369 return count($this->_workSheetCollection);
373 * Get active sheet index
375 * @return int Active sheet index
377 public function getActiveSheetIndex()
379 return $this->_activeSheetIndex;
383 * Set active sheet index
385 * @param int $pIndex Active sheet index
386 * @throws Exception
387 * @return PHPExcel_Worksheet
389 public function setActiveSheetIndex($pIndex = 0)
391 if ($pIndex > count($this->_workSheetCollection) - 1) {
392 throw new Exception("Active sheet index is out of bounds.");
393 } else {
394 $this->_activeSheetIndex = $pIndex;
396 return $this->getActiveSheet();
400 * Set active sheet index by name
402 * @param string $pValue Sheet title
403 * @return PHPExcel_Worksheet
404 * @throws Exception
406 public function setActiveSheetIndexByName($pValue = '')
408 if (($worksheet = $this->getSheetByName($pValue)) instanceof PHPExcel_Worksheet) {
409 $this->setActiveSheetIndex($this->getIndex($worksheet));
410 return $worksheet;
413 throw new Exception('Workbook does not contain sheet:' . $pValue);
417 * Get sheet names
419 * @return string[]
421 public function getSheetNames()
423 $returnValue = array();
424 $worksheetCount = $this->getSheetCount();
425 for ($i = 0; $i < $worksheetCount; ++$i) {
426 $returnValue[] = $this->getSheet($i)->getTitle();
429 return $returnValue;
433 * Add external sheet
435 * @param PHPExcel_Worksheet $pSheet External sheet to add
436 * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
437 * @throws Exception
438 * @return PHPExcel_Worksheet
440 public function addExternalSheet(PHPExcel_Worksheet $pSheet, $iSheetIndex = null) {
441 if ($this->sheetNameExists($pSheet->getTitle())) {
442 throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first.");
445 // count how many cellXfs there are in this workbook currently, we will need this below
446 $countCellXfs = count($this->_cellXfCollection);
448 // copy all the shared cellXfs from the external workbook and append them to the current
449 foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
450 $this->addCellXf(clone $cellXf);
453 // move sheet to this workbook
454 $pSheet->rebindParent($this);
456 // update the cellXfs
457 foreach ($pSheet->getCellCollection(false) as $cellID) {
458 $cell = $pSheet->getCell($cellID);
459 $cell->setXfIndex( $cell->getXfIndex() + $countCellXfs );
462 return $this->addSheet($pSheet, $iSheetIndex);
466 * Get named ranges
468 * @return PHPExcel_NamedRange[]
470 public function getNamedRanges() {
471 return $this->_namedRanges;
475 * Add named range
477 * @param PHPExcel_NamedRange $namedRange
478 * @return PHPExcel
480 public function addNamedRange(PHPExcel_NamedRange $namedRange) {
481 if ($namedRange->getScope() == null) {
482 // global scope
483 $this->_namedRanges[$namedRange->getName()] = $namedRange;
484 } else {
485 // local scope
486 $this->_namedRanges[$namedRange->getScope()->getTitle().'!'.$namedRange->getName()] = $namedRange;
488 return true;
492 * Get named range
494 * @param string $namedRange
495 * @param PHPExcel_Worksheet|null $pSheet Scope. Use null for global scope
496 * @return PHPExcel_NamedRange|null
498 public function getNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) {
499 $returnValue = null;
501 if ($namedRange != '' && ($namedRange !== NULL)) {
502 // first look for global defined name
503 if (isset($this->_namedRanges[$namedRange])) {
504 $returnValue = $this->_namedRanges[$namedRange];
507 // then look for local defined name (has priority over global defined name if both names exist)
508 if (($pSheet !== NULL) && isset($this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
509 $returnValue = $this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange];
513 return $returnValue;
517 * Remove named range
519 * @param string $namedRange
520 * @param PHPExcel_Worksheet|null $pSheet Scope: use null for global scope.
521 * @return PHPExcel
523 public function removeNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) {
524 if ($pSheet === NULL) {
525 if (isset($this->_namedRanges[$namedRange])) {
526 unset($this->_namedRanges[$namedRange]);
528 } else {
529 if (isset($this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
530 unset($this->_namedRanges[$pSheet->getTitle() . '!' . $namedRange]);
533 return $this;
537 * Get worksheet iterator
539 * @return PHPExcel_WorksheetIterator
541 public function getWorksheetIterator() {
542 return new PHPExcel_WorksheetIterator($this);
546 * Copy workbook (!= clone!)
548 * @return PHPExcel
550 public function copy() {
551 $copied = clone $this;
553 $worksheetCount = count($this->_workSheetCollection);
554 for ($i = 0; $i < $worksheetCount; ++$i) {
555 $this->_workSheetCollection[$i] = $this->_workSheetCollection[$i]->copy();
556 $this->_workSheetCollection[$i]->rebindParent($this);
559 return $copied;
563 * Implement PHP __clone to create a deep clone, not just a shallow copy.
565 public function __clone() {
566 foreach($this as $key => $val) {
567 if (is_object($val) || (is_array($val))) {
568 $this->{$key} = unserialize(serialize($val));
574 * Get the workbook collection of cellXfs
576 * @return PHPExcel_Style[]
578 public function getCellXfCollection()
580 return $this->_cellXfCollection;
584 * Get cellXf by index
586 * @param int $pIndex
587 * @return PHPExcel_Style
589 public function getCellXfByIndex($pIndex = 0)
591 return $this->_cellXfCollection[$pIndex];
595 * Get cellXf by hash code
597 * @param string $pValue
598 * @return PHPExcel_Style|false
600 public function getCellXfByHashCode($pValue = '')
602 foreach ($this->_cellXfCollection as $cellXf) {
603 if ($cellXf->getHashCode() == $pValue) {
604 return $cellXf;
607 return false;
611 * Get default style
613 * @return PHPExcel_Style
614 * @throws Exception
616 public function getDefaultStyle()
618 if (isset($this->_cellXfCollection[0])) {
619 return $this->_cellXfCollection[0];
621 throw new Exception('No default style found for this workbook');
625 * Add a cellXf to the workbook
627 * @param PHPExcel_Style $style
629 public function addCellXf(PHPExcel_Style $style)
631 $this->_cellXfCollection[] = $style;
632 $style->setIndex(count($this->_cellXfCollection) - 1);
636 * Remove cellXf by index. It is ensured that all cells get their xf index updated.
638 * @param int $pIndex Index to cellXf
639 * @throws Exception
641 public function removeCellXfByIndex($pIndex = 0)
643 if ($pIndex > count($this->_cellXfCollection) - 1) {
644 throw new Exception("CellXf index is out of bounds.");
645 } else {
646 // first remove the cellXf
647 array_splice($this->_cellXfCollection, $pIndex, 1);
649 // then update cellXf indexes for cells
650 foreach ($this->_workSheetCollection as $worksheet) {
651 foreach ($worksheet->getCellCollection(false) as $cellID) {
652 $cell = $worksheet->getCell($cellID);
653 $xfIndex = $cell->getXfIndex();
654 if ($xfIndex > $pIndex ) {
655 // decrease xf index by 1
656 $cell->setXfIndex($xfIndex - 1);
657 } else if ($xfIndex == $pIndex) {
658 // set to default xf index 0
659 $cell->setXfIndex(0);
667 * Get the cellXf supervisor
669 * @return PHPExcel_Style
671 public function getCellXfSupervisor()
673 return $this->_cellXfSupervisor;
677 * Get the workbook collection of cellStyleXfs
679 * @return PHPExcel_Style[]
681 public function getCellStyleXfCollection()
683 return $this->_cellStyleXfCollection;
687 * Get cellStyleXf by index
689 * @param int $pIndex
690 * @return PHPExcel_Style
692 public function getCellStyleXfByIndex($pIndex = 0)
694 return $this->_cellStyleXfCollection[$pIndex];
698 * Get cellStyleXf by hash code
700 * @param string $pValue
701 * @return PHPExcel_Style|false
703 public function getCellStyleXfByHashCode($pValue = '')
705 foreach ($this->_cellXfStyleCollection as $cellStyleXf) {
706 if ($cellStyleXf->getHashCode() == $pValue) {
707 return $cellStyleXf;
710 return false;
714 * Add a cellStyleXf to the workbook
716 * @param PHPExcel_Style $pStyle
718 public function addCellStyleXf(PHPExcel_Style $pStyle)
720 $this->_cellStyleXfCollection[] = $pStyle;
721 $pStyle->setIndex(count($this->_cellStyleXfCollection) - 1);
725 * Remove cellStyleXf by index
727 * @param int $pIndex
728 * @throws Exception
730 public function removeCellStyleXfByIndex($pIndex = 0)
732 if ($pIndex > count($this->_cellStyleXfCollection) - 1) {
733 throw new Exception("CellStyleXf index is out of bounds.");
734 } else {
735 array_splice($this->_cellStyleXfCollection, $pIndex, 1);
740 * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
741 * and columns in the workbook
743 public function garbageCollect()
745 // how many references are there to each cellXf ?
746 $countReferencesCellXf = array();
747 foreach ($this->_cellXfCollection as $index => $cellXf) {
748 $countReferencesCellXf[$index] = 0;
751 foreach ($this->getWorksheetIterator() as $sheet) {
753 // from cells
754 foreach ($sheet->getCellCollection(false) as $cellID) {
755 $cell = $sheet->getCell($cellID);
756 ++$countReferencesCellXf[$cell->getXfIndex()];
759 // from row dimensions
760 foreach ($sheet->getRowDimensions() as $rowDimension) {
761 if ($rowDimension->getXfIndex() !== null) {
762 ++$countReferencesCellXf[$rowDimension->getXfIndex()];
766 // from column dimensions
767 foreach ($sheet->getColumnDimensions() as $columnDimension) {
768 ++$countReferencesCellXf[$columnDimension->getXfIndex()];
772 // remove cellXfs without references and create mapping so we can update xfIndex
773 // for all cells and columns
774 $countNeededCellXfs = 0;
775 foreach ($this->_cellXfCollection as $index => $cellXf) {
776 if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
777 ++$countNeededCellXfs;
778 } else {
779 unset($this->_cellXfCollection[$index]);
781 $map[$index] = $countNeededCellXfs - 1;
783 $this->_cellXfCollection = array_values($this->_cellXfCollection);
785 // update the index for all cellXfs
786 foreach ($this->_cellXfCollection as $i => $cellXf) {
787 $cellXf->setIndex($i);
790 // make sure there is always at least one cellXf (there should be)
791 if (empty($this->_cellXfCollection)) {
792 $this->_cellXfCollection[] = new PHPExcel_Style();
795 // update the xfIndex for all cells, row dimensions, column dimensions
796 foreach ($this->getWorksheetIterator() as $sheet) {
798 // for all cells
799 foreach ($sheet->getCellCollection(false) as $cellID) {
800 $cell = $sheet->getCell($cellID);
801 $cell->setXfIndex( $map[$cell->getXfIndex()] );
804 // for all row dimensions
805 foreach ($sheet->getRowDimensions() as $rowDimension) {
806 if ($rowDimension->getXfIndex() !== null) {
807 $rowDimension->setXfIndex( $map[$rowDimension->getXfIndex()] );
811 // for all column dimensions
812 foreach ($sheet->getColumnDimensions() as $columnDimension) {
813 $columnDimension->setXfIndex( $map[$columnDimension->getXfIndex()] );
817 // also do garbage collection for all the sheets
818 foreach ($this->getWorksheetIterator() as $sheet) {
819 $sheet->garbageCollect();