diff --git a/includes/PHPExcel/Classes/PHPExcel/Autoloader.php b/includes/PHPExcel/Classes/PHPExcel/Autoloader.php
new file mode 100644
index 0000000..f0b2251
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Autoloader.php
@@ -0,0 +1,89 @@
+= 0) {
+ return spl_autoload_register(array('PHPExcel_Autoloader', 'Load'), true, true);
+ } else {
+ return spl_autoload_register(array('PHPExcel_Autoloader', 'Load'));
+ }
+ } // function Register()
+
+
+ /**
+ * Autoload a class identified by name
+ *
+ * @param string $pClassName Name of the object to load
+ */
+ public static function Load($pClassName){
+ if ((class_exists($pClassName,FALSE)) || (strpos($pClassName, 'PHPExcel') !== 0)) {
+ // Either already loaded, or not a PHPExcel class request
+ return FALSE;
+ }
+
+ $pClassFilePath = PHPEXCEL_ROOT .
+ str_replace('_',DIRECTORY_SEPARATOR,$pClassName) .
+ '.php';
+
+ if ((file_exists($pClassFilePath) === FALSE) || (is_readable($pClassFilePath) === FALSE)) {
+ // Can't load
+ return FALSE;
+ }
+
+ require($pClassFilePath);
+ } // function Load()
+
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/CachedObjectStorageFactory.php b/includes/PHPExcel/Classes/PHPExcel/CachedObjectStorageFactory.php
new file mode 100644
index 0000000..2da9234
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/CachedObjectStorageFactory.php
@@ -0,0 +1,251 @@
+ array(
+ ),
+ self::cache_in_memory_gzip => array(
+ ),
+ self::cache_in_memory_serialized => array(
+ ),
+ self::cache_igbinary => array(
+ ),
+ self::cache_to_phpTemp => array( 'memoryCacheSize' => '1MB'
+ ),
+ self::cache_to_discISAM => array( 'dir' => NULL
+ ),
+ self::cache_to_apc => array( 'cacheTime' => 600
+ ),
+ self::cache_to_memcache => array( 'memcacheServer' => 'localhost',
+ 'memcachePort' => 11211,
+ 'cacheTime' => 600
+ ),
+ self::cache_to_wincache => array( 'cacheTime' => 600
+ ),
+ self::cache_to_sqlite => array(
+ ),
+ self::cache_to_sqlite3 => array(
+ ),
+ );
+
+
+ /**
+ * Arguments for the active cache storage method
+ *
+ * @var array of mixed array
+ */
+ private static $_storageMethodParameters = array();
+
+
+ /**
+ * Return the current cache storage method
+ *
+ * @return string|NULL
+ **/
+ public static function getCacheStorageMethod()
+ {
+ return self::$_cacheStorageMethod;
+ } // function getCacheStorageMethod()
+
+
+ /**
+ * Return the current cache storage class
+ *
+ * @return PHPExcel_CachedObjectStorage_ICache|NULL
+ **/
+ public static function getCacheStorageClass()
+ {
+ return self::$_cacheStorageClass;
+ } // function getCacheStorageClass()
+
+
+ /**
+ * Return the list of all possible cache storage methods
+ *
+ * @return string[]
+ **/
+ public static function getAllCacheStorageMethods()
+ {
+ return self::$_storageMethods;
+ } // function getCacheStorageMethods()
+
+
+ /**
+ * Return the list of all available cache storage methods
+ *
+ * @return string[]
+ **/
+ public static function getCacheStorageMethods()
+ {
+ $activeMethods = array();
+ foreach(self::$_storageMethods as $storageMethod) {
+ $cacheStorageClass = 'PHPExcel_CachedObjectStorage_' . $storageMethod;
+ if (call_user_func(array($cacheStorageClass, 'cacheMethodIsAvailable'))) {
+ $activeMethods[] = $storageMethod;
+ }
+ }
+ return $activeMethods;
+ } // function getCacheStorageMethods()
+
+
+ /**
+ * Identify the cache storage method to use
+ *
+ * @param string $method Name of the method to use for cell cacheing
+ * @param array of mixed $arguments Additional arguments to pass to the cell caching class
+ * when instantiating
+ * @return boolean
+ **/
+ public static function initialize($method = self::cache_in_memory, $arguments = array())
+ {
+ if (!in_array($method,self::$_storageMethods)) {
+ return FALSE;
+ }
+
+ $cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method;
+ if (!call_user_func(array( $cacheStorageClass,
+ 'cacheMethodIsAvailable'))) {
+ return FALSE;
+ }
+
+ self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method];
+ foreach($arguments as $k => $v) {
+ if (array_key_exists($k, self::$_storageMethodParameters[$method])) {
+ self::$_storageMethodParameters[$method][$k] = $v;
+ }
+ }
+
+ if (self::$_cacheStorageMethod === NULL) {
+ self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_' . $method;
+ self::$_cacheStorageMethod = $method;
+ }
+ return TRUE;
+ } // function initialize()
+
+
+ /**
+ * Initialise the cache storage
+ *
+ * @param PHPExcel_Worksheet $parent Enable cell caching for this worksheet
+ * @return PHPExcel_CachedObjectStorage_ICache
+ **/
+ public static function getInstance(PHPExcel_Worksheet $parent)
+ {
+ $cacheMethodIsAvailable = TRUE;
+ if (self::$_cacheStorageMethod === NULL) {
+ $cacheMethodIsAvailable = self::initialize();
+ }
+
+ if ($cacheMethodIsAvailable) {
+ $instance = new self::$_cacheStorageClass( $parent,
+ self::$_storageMethodParameters[self::$_cacheStorageMethod]
+ );
+ if ($instance !== NULL) {
+ return $instance;
+ }
+ }
+
+ return FALSE;
+ } // function getInstance()
+
+
+ /**
+ * Clear the cache storage
+ *
+ **/
+ public static function finalize()
+ {
+ self::$_cacheStorageMethod = NULL;
+ self::$_cacheStorageClass = NULL;
+ self::$_storageMethodParameters = array();
+ }
+
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/Calculation.php b/includes/PHPExcel/Classes/PHPExcel/Calculation.php
new file mode 100644
index 0000000..48fb4a4
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Calculation.php
@@ -0,0 +1,3952 @@
+=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})');
+ // Named Range of cells
+ define('CALCULATION_REGEXP_NAMEDRANGE','((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)');
+ } else {
+ // Cell reference (cell or range of cells, with or without a sheet reference)
+ define('CALCULATION_REGEXP_CELLREF','(((\w*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d+)');
+ // Named Range of cells
+ define('CALCULATION_REGEXP_NAMEDRANGE','(((\w*)|(\'.*\')|(\".*\"))!)?([_A-Z][_A-Z0-9\.]*)');
+ }
+}
+
+
+/**
+ * PHPExcel_Calculation (Multiton)
+ *
+ * @category PHPExcel
+ * @package PHPExcel_Calculation
+ * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
+ */
+class PHPExcel_Calculation {
+
+ /** Constants */
+ /** Regular Expressions */
+ // Numeric operand
+ const CALCULATION_REGEXP_NUMBER = '[-+]?\d*\.?\d+(e[-+]?\d+)?';
+ // String operand
+ const CALCULATION_REGEXP_STRING = '"(?:[^"]|"")*"';
+ // Opening bracket
+ const CALCULATION_REGEXP_OPENBRACE = '\(';
+ // Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
+ const CALCULATION_REGEXP_FUNCTION = '@?([A-Z][A-Z0-9\.]*)[\s]*\(';
+ // Cell reference (cell or range of cells, with or without a sheet reference)
+ const CALCULATION_REGEXP_CELLREF = CALCULATION_REGEXP_CELLREF;
+ // Named Range of cells
+ const CALCULATION_REGEXP_NAMEDRANGE = CALCULATION_REGEXP_NAMEDRANGE;
+ // Error
+ const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?';
+
+
+ /** constants */
+ const RETURN_ARRAY_AS_ERROR = 'error';
+ const RETURN_ARRAY_AS_VALUE = 'value';
+ const RETURN_ARRAY_AS_ARRAY = 'array';
+
+ private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
+
+
+ /**
+ * Instance of this class
+ *
+ * @access private
+ * @var PHPExcel_Calculation
+ */
+ private static $_instance;
+
+
+ /**
+ * Instance of the workbook this Calculation Engine is using
+ *
+ * @access private
+ * @var PHPExcel
+ */
+ private $_workbook;
+
+ /**
+ * List of instances of the calculation engine that we've instantiated for individual workbooks
+ *
+ * @access private
+ * @var PHPExcel_Calculation[]
+ */
+ private static $_workbookSets;
+
+ /**
+ * Calculation cache
+ *
+ * @access private
+ * @var array
+ */
+ private $_calculationCache = array ();
+
+
+ /**
+ * Calculation cache enabled
+ *
+ * @access private
+ * @var boolean
+ */
+ private $_calculationCacheEnabled = TRUE;
+
+
+ /**
+ * List of operators that can be used within formulae
+ * The true/false value indicates whether it is a binary operator or a unary operator
+ *
+ * @access private
+ * @var array
+ */
+ private static $_operators = array('+' => TRUE, '-' => TRUE, '*' => TRUE, '/' => TRUE,
+ '^' => TRUE, '&' => TRUE, '%' => FALSE, '~' => FALSE,
+ '>' => TRUE, '<' => TRUE, '=' => TRUE, '>=' => TRUE,
+ '<=' => TRUE, '<>' => TRUE, '|' => TRUE, ':' => TRUE
+ );
+
+
+ /**
+ * List of binary operators (those that expect two operands)
+ *
+ * @access private
+ * @var array
+ */
+ private static $_binaryOperators = array('+' => TRUE, '-' => TRUE, '*' => TRUE, '/' => TRUE,
+ '^' => TRUE, '&' => TRUE, '>' => TRUE, '<' => TRUE,
+ '=' => TRUE, '>=' => TRUE, '<=' => TRUE, '<>' => TRUE,
+ '|' => TRUE, ':' => TRUE
+ );
+
+ /**
+ * The debug log generated by the calculation engine
+ *
+ * @access private
+ * @var PHPExcel_CalcEngine_Logger
+ *
+ */
+ private $debugLog;
+
+ /**
+ * Flag to determine how formula errors should be handled
+ * If true, then a user error will be triggered
+ * If false, then an exception will be thrown
+ *
+ * @access public
+ * @var boolean
+ *
+ */
+ public $suppressFormulaErrors = FALSE;
+
+ /**
+ * Error message for any error that was raised/thrown by the calculation engine
+ *
+ * @access public
+ * @var string
+ *
+ */
+ public $formulaError = NULL;
+
+ /**
+ * An array of the nested cell references accessed by the calculation engine, used for the debug log
+ *
+ * @access private
+ * @var array of string
+ *
+ */
+ private $_cyclicReferenceStack;
+
+ private $_cellStack = array();
+
+ /**
+ * Current iteration counter for cyclic formulae
+ * If the value is 0 (or less) then cyclic formulae will throw an exception,
+ * otherwise they will iterate to the limit defined here before returning a result
+ *
+ * @var integer
+ *
+ */
+ private $_cyclicFormulaCount = 1;
+
+ private $_cyclicFormulaCell = '';
+
+ /**
+ * Number of iterations for cyclic formulae
+ *
+ * @var integer
+ *
+ */
+ public $cyclicFormulaCount = 1;
+
+ /**
+ * Precision used for calculations
+ *
+ * @var integer
+ *
+ */
+ private $_savedPrecision = 14;
+
+
+ /**
+ * The current locale setting
+ *
+ * @var string
+ *
+ */
+ private static $_localeLanguage = 'en_us'; // US English (default locale)
+
+ /**
+ * List of available locale settings
+ * Note that this is read for the locale subdirectory only when requested
+ *
+ * @var string[]
+ *
+ */
+ private static $_validLocaleLanguages = array( 'en' // English (default language)
+ );
+ /**
+ * Locale-specific argument separator for function arguments
+ *
+ * @var string
+ *
+ */
+ private static $_localeArgumentSeparator = ',';
+ private static $_localeFunctions = array();
+
+ /**
+ * Locale-specific translations for Excel constants (True, False and Null)
+ *
+ * @var string[]
+ *
+ */
+ public static $_localeBoolean = array( 'TRUE' => 'TRUE',
+ 'FALSE' => 'FALSE',
+ 'NULL' => 'NULL'
+ );
+
+
+ /**
+ * Excel constant string translations to their PHP equivalents
+ * Constant conversion from text name/value to actual (datatyped) value
+ *
+ * @var string[]
+ *
+ */
+ private static $_ExcelConstants = array('TRUE' => TRUE,
+ 'FALSE' => FALSE,
+ 'NULL' => NULL
+ );
+
+ // PHPExcel functions
+ private static $_PHPExcelFunctions = array( // PHPExcel functions
+ 'ABS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'abs',
+ 'argumentCount' => '1'
+ ),
+ 'ACCRINT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::ACCRINT',
+ 'argumentCount' => '4-7'
+ ),
+ 'ACCRINTM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::ACCRINTM',
+ 'argumentCount' => '3-5'
+ ),
+ 'ACOS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'acos',
+ 'argumentCount' => '1'
+ ),
+ 'ACOSH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'acosh',
+ 'argumentCount' => '1'
+ ),
+ 'ADDRESS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::CELL_ADDRESS',
+ 'argumentCount' => '2-5'
+ ),
+ 'AMORDEGRC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::AMORDEGRC',
+ 'argumentCount' => '6,7'
+ ),
+ 'AMORLINC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::AMORLINC',
+ 'argumentCount' => '6,7'
+ ),
+ 'AND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_AND',
+ 'argumentCount' => '1+'
+ ),
+ 'AREAS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'ASC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'ASIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'asin',
+ 'argumentCount' => '1'
+ ),
+ 'ASINH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'asinh',
+ 'argumentCount' => '1'
+ ),
+ 'ATAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'atan',
+ 'argumentCount' => '1'
+ ),
+ 'ATAN2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::ATAN2',
+ 'argumentCount' => '2'
+ ),
+ 'ATANH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'atanh',
+ 'argumentCount' => '1'
+ ),
+ 'AVEDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::AVEDEV',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGE',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGEA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGEA',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGEIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGEIF',
+ 'argumentCount' => '2,3'
+ ),
+ 'AVERAGEIFS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3+'
+ ),
+ 'BAHTTEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'BESSELI' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELI',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELJ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELJ',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELK',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELY',
+ 'argumentCount' => '2'
+ ),
+ 'BETADIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::BETADIST',
+ 'argumentCount' => '3-5'
+ ),
+ 'BETAINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::BETAINV',
+ 'argumentCount' => '3-5'
+ ),
+ 'BIN2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BINTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'BIN2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BINTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'BIN2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::BINTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'BINOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::BINOMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'CEILING' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::CEILING',
+ 'argumentCount' => '2'
+ ),
+ 'CELL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1,2'
+ ),
+ 'CHAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::CHARACTER',
+ 'argumentCount' => '1'
+ ),
+ 'CHIDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::CHIDIST',
+ 'argumentCount' => '2'
+ ),
+ 'CHIINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::CHIINV',
+ 'argumentCount' => '2'
+ ),
+ 'CHITEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'CHOOSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::CHOOSE',
+ 'argumentCount' => '2+'
+ ),
+ 'CLEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::TRIMNONPRINTABLE',
+ 'argumentCount' => '1'
+ ),
+ 'CODE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::ASCIICODE',
+ 'argumentCount' => '1'
+ ),
+ 'COLUMN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMN',
+ 'argumentCount' => '-1',
+ 'passByReference' => array(TRUE)
+ ),
+ 'COLUMNS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMNS',
+ 'argumentCount' => '1'
+ ),
+ 'COMBIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::COMBIN',
+ 'argumentCount' => '2'
+ ),
+ 'COMPLEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::COMPLEX',
+ 'argumentCount' => '2,3'
+ ),
+ 'CONCATENATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::CONCATENATE',
+ 'argumentCount' => '1+'
+ ),
+ 'CONFIDENCE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::CONFIDENCE',
+ 'argumentCount' => '3'
+ ),
+ 'CONVERT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::CONVERTUOM',
+ 'argumentCount' => '3'
+ ),
+ 'CORREL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::CORREL',
+ 'argumentCount' => '2'
+ ),
+ 'COS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'cos',
+ 'argumentCount' => '1'
+ ),
+ 'COSH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'cosh',
+ 'argumentCount' => '1'
+ ),
+ 'COUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNT',
+ 'argumentCount' => '1+'
+ ),
+ 'COUNTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTA',
+ 'argumentCount' => '1+'
+ ),
+ 'COUNTBLANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTBLANK',
+ 'argumentCount' => '1'
+ ),
+ 'COUNTIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTIF',
+ 'argumentCount' => '2'
+ ),
+ 'COUNTIFS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'COUPDAYBS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYBS',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPDAYS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYS',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPDAYSNC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYSNC',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPNCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::COUPNCD',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPNUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::COUPNUM',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPPCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::COUPPCD',
+ 'argumentCount' => '3,4'
+ ),
+ 'COVAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::COVAR',
+ 'argumentCount' => '2'
+ ),
+ 'CRITBINOM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::CRITBINOM',
+ 'argumentCount' => '3'
+ ),
+ 'CUBEKPIMEMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBEMEMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBEMEMBERPROPERTY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBERANKEDMEMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBESET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBESETCOUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUBEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'CUMIPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::CUMIPMT',
+ 'argumentCount' => '6'
+ ),
+ 'CUMPRINC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::CUMPRINC',
+ 'argumentCount' => '6'
+ ),
+ 'DATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DATE',
+ 'argumentCount' => '3'
+ ),
+ 'DATEDIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DATEDIF',
+ 'argumentCount' => '2,3'
+ ),
+ 'DATEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DATEVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'DAVERAGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DAVERAGE',
+ 'argumentCount' => '3'
+ ),
+ 'DAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DAYOFMONTH',
+ 'argumentCount' => '1'
+ ),
+ 'DAYS360' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DAYS360',
+ 'argumentCount' => '2,3'
+ ),
+ 'DB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::DB',
+ 'argumentCount' => '4,5'
+ ),
+ 'DCOUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DCOUNT',
+ 'argumentCount' => '3'
+ ),
+ 'DCOUNTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DCOUNTA',
+ 'argumentCount' => '3'
+ ),
+ 'DDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::DDB',
+ 'argumentCount' => '4,5'
+ ),
+ 'DEC2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEC2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEC2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEGREES' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'rad2deg',
+ 'argumentCount' => '1'
+ ),
+ 'DELTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::DELTA',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEVSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::DEVSQ',
+ 'argumentCount' => '1+'
+ ),
+ 'DGET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DGET',
+ 'argumentCount' => '3'
+ ),
+ 'DISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::DISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'DMAX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DMAX',
+ 'argumentCount' => '3'
+ ),
+ 'DMIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DMIN',
+ 'argumentCount' => '3'
+ ),
+ 'DOLLAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::DOLLAR',
+ 'argumentCount' => '1,2'
+ ),
+ 'DOLLARDE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::DOLLARDE',
+ 'argumentCount' => '2'
+ ),
+ 'DOLLARFR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::DOLLARFR',
+ 'argumentCount' => '2'
+ ),
+ 'DPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DPRODUCT',
+ 'argumentCount' => '3'
+ ),
+ 'DSTDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DSTDEV',
+ 'argumentCount' => '3'
+ ),
+ 'DSTDEVP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DSTDEVP',
+ 'argumentCount' => '3'
+ ),
+ 'DSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DSUM',
+ 'argumentCount' => '3'
+ ),
+ 'DURATION' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '5,6'
+ ),
+ 'DVAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DVAR',
+ 'argumentCount' => '3'
+ ),
+ 'DVARP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Database::DVARP',
+ 'argumentCount' => '3'
+ ),
+ 'EDATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::EDATE',
+ 'argumentCount' => '2'
+ ),
+ 'EFFECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::EFFECT',
+ 'argumentCount' => '2'
+ ),
+ 'EOMONTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::EOMONTH',
+ 'argumentCount' => '2'
+ ),
+ 'ERF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::ERF',
+ 'argumentCount' => '1,2'
+ ),
+ 'ERFC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::ERFC',
+ 'argumentCount' => '1'
+ ),
+ 'ERROR.TYPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ERROR_TYPE',
+ 'argumentCount' => '1'
+ ),
+ 'EVEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::EVEN',
+ 'argumentCount' => '1'
+ ),
+ 'EXACT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'EXP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'exp',
+ 'argumentCount' => '1'
+ ),
+ 'EXPONDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::EXPONDIST',
+ 'argumentCount' => '3'
+ ),
+ 'FACT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::FACT',
+ 'argumentCount' => '1'
+ ),
+ 'FACTDOUBLE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::FACTDOUBLE',
+ 'argumentCount' => '1'
+ ),
+ 'FALSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::FALSE',
+ 'argumentCount' => '0'
+ ),
+ 'FDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'FIND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'FINDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'FINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3'
+ ),
+ 'FISHER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::FISHER',
+ 'argumentCount' => '1'
+ ),
+ 'FISHERINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::FISHERINV',
+ 'argumentCount' => '1'
+ ),
+ 'FIXED' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::FIXEDFORMAT',
+ 'argumentCount' => '1-3'
+ ),
+ 'FLOOR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::FLOOR',
+ 'argumentCount' => '2'
+ ),
+ 'FORECAST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::FORECAST',
+ 'argumentCount' => '3'
+ ),
+ 'FREQUENCY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'FTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'FV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::FV',
+ 'argumentCount' => '3-5'
+ ),
+ 'FVSCHEDULE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::FVSCHEDULE',
+ 'argumentCount' => '2'
+ ),
+ 'GAMMADIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMADIST',
+ 'argumentCount' => '4'
+ ),
+ 'GAMMAINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMAINV',
+ 'argumentCount' => '3'
+ ),
+ 'GAMMALN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMALN',
+ 'argumentCount' => '1'
+ ),
+ 'GCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::GCD',
+ 'argumentCount' => '1+'
+ ),
+ 'GEOMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::GEOMEAN',
+ 'argumentCount' => '1+'
+ ),
+ 'GESTEP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::GESTEP',
+ 'argumentCount' => '1,2'
+ ),
+ 'GETPIVOTDATA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'GROWTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::GROWTH',
+ 'argumentCount' => '1-4'
+ ),
+ 'HARMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::HARMEAN',
+ 'argumentCount' => '1+'
+ ),
+ 'HEX2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'HEX2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'HEX2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'HLOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::HLOOKUP',
+ 'argumentCount' => '3,4'
+ ),
+ 'HOUR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::HOUROFDAY',
+ 'argumentCount' => '1'
+ ),
+ 'HYPERLINK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::HYPERLINK',
+ 'argumentCount' => '1,2',
+ 'passCellReference'=> TRUE
+ ),
+ 'HYPGEOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::HYPGEOMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'IF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::STATEMENT_IF',
+ 'argumentCount' => '1-3'
+ ),
+ 'IFERROR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::IFERROR',
+ 'argumentCount' => '2'
+ ),
+ 'IMABS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMABS',
+ 'argumentCount' => '1'
+ ),
+ 'IMAGINARY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMAGINARY',
+ 'argumentCount' => '1'
+ ),
+ 'IMARGUMENT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMARGUMENT',
+ 'argumentCount' => '1'
+ ),
+ 'IMCONJUGATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMCONJUGATE',
+ 'argumentCount' => '1'
+ ),
+ 'IMCOS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMCOS',
+ 'argumentCount' => '1'
+ ),
+ 'IMDIV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMDIV',
+ 'argumentCount' => '2'
+ ),
+ 'IMEXP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMEXP',
+ 'argumentCount' => '1'
+ ),
+ 'IMLN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMLN',
+ 'argumentCount' => '1'
+ ),
+ 'IMLOG10' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMLOG10',
+ 'argumentCount' => '1'
+ ),
+ 'IMLOG2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMLOG2',
+ 'argumentCount' => '1'
+ ),
+ 'IMPOWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMPOWER',
+ 'argumentCount' => '2'
+ ),
+ 'IMPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMPRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'IMREAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMREAL',
+ 'argumentCount' => '1'
+ ),
+ 'IMSIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSIN',
+ 'argumentCount' => '1'
+ ),
+ 'IMSQRT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSQRT',
+ 'argumentCount' => '1'
+ ),
+ 'IMSUB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSUB',
+ 'argumentCount' => '2'
+ ),
+ 'IMSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::IMSUM',
+ 'argumentCount' => '1+'
+ ),
+ 'INDEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::INDEX',
+ 'argumentCount' => '1-4'
+ ),
+ 'INDIRECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::INDIRECT',
+ 'argumentCount' => '1,2',
+ 'passCellReference'=> TRUE
+ ),
+ 'INFO' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'INT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::INT',
+ 'argumentCount' => '1'
+ ),
+ 'INTERCEPT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::INTERCEPT',
+ 'argumentCount' => '2'
+ ),
+ 'INTRATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::INTRATE',
+ 'argumentCount' => '4,5'
+ ),
+ 'IPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::IPMT',
+ 'argumentCount' => '4-6'
+ ),
+ 'IRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::IRR',
+ 'argumentCount' => '1,2'
+ ),
+ 'ISBLANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_BLANK',
+ 'argumentCount' => '1'
+ ),
+ 'ISERR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERR',
+ 'argumentCount' => '1'
+ ),
+ 'ISERROR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERROR',
+ 'argumentCount' => '1'
+ ),
+ 'ISEVEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_EVEN',
+ 'argumentCount' => '1'
+ ),
+ 'ISLOGICAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_LOGICAL',
+ 'argumentCount' => '1'
+ ),
+ 'ISNA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NA',
+ 'argumentCount' => '1'
+ ),
+ 'ISNONTEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NONTEXT',
+ 'argumentCount' => '1'
+ ),
+ 'ISNUMBER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_NUMBER',
+ 'argumentCount' => '1'
+ ),
+ 'ISODD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_ODD',
+ 'argumentCount' => '1'
+ ),
+ 'ISPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::ISPMT',
+ 'argumentCount' => '4'
+ ),
+ 'ISREF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'ISTEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IS_TEXT',
+ 'argumentCount' => '1'
+ ),
+ 'JIS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'KURT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::KURT',
+ 'argumentCount' => '1+'
+ ),
+ 'LARGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::LARGE',
+ 'argumentCount' => '2'
+ ),
+ 'LCM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::LCM',
+ 'argumentCount' => '1+'
+ ),
+ 'LEFT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::LEFT',
+ 'argumentCount' => '1,2'
+ ),
+ 'LEFTB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::LEFT',
+ 'argumentCount' => '1,2'
+ ),
+ 'LEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::STRINGLENGTH',
+ 'argumentCount' => '1'
+ ),
+ 'LENB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::STRINGLENGTH',
+ 'argumentCount' => '1'
+ ),
+ 'LINEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::LINEST',
+ 'argumentCount' => '1-4'
+ ),
+ 'LN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'log',
+ 'argumentCount' => '1'
+ ),
+ 'LOG' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::LOG_BASE',
+ 'argumentCount' => '1,2'
+ ),
+ 'LOG10' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'log10',
+ 'argumentCount' => '1'
+ ),
+ 'LOGEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::LOGEST',
+ 'argumentCount' => '1-4'
+ ),
+ 'LOGINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::LOGINV',
+ 'argumentCount' => '3'
+ ),
+ 'LOGNORMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::LOGNORMDIST',
+ 'argumentCount' => '3'
+ ),
+ 'LOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::LOOKUP',
+ 'argumentCount' => '2,3'
+ ),
+ 'LOWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::LOWERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'MATCH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::MATCH',
+ 'argumentCount' => '2,3'
+ ),
+ 'MAX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MAX',
+ 'argumentCount' => '1+'
+ ),
+ 'MAXA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MAXA',
+ 'argumentCount' => '1+'
+ ),
+ 'MAXIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MAXIF',
+ 'argumentCount' => '2+'
+ ),
+ 'MDETERM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::MDETERM',
+ 'argumentCount' => '1'
+ ),
+ 'MDURATION' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '5,6'
+ ),
+ 'MEDIAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MEDIAN',
+ 'argumentCount' => '1+'
+ ),
+ 'MEDIANIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'MID' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::MID',
+ 'argumentCount' => '3'
+ ),
+ 'MIDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::MID',
+ 'argumentCount' => '3'
+ ),
+ 'MIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MIN',
+ 'argumentCount' => '1+'
+ ),
+ 'MINA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MINA',
+ 'argumentCount' => '1+'
+ ),
+ 'MINIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MINIF',
+ 'argumentCount' => '2+'
+ ),
+ 'MINUTE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::MINUTEOFHOUR',
+ 'argumentCount' => '1'
+ ),
+ 'MINVERSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::MINVERSE',
+ 'argumentCount' => '1'
+ ),
+ 'MIRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::MIRR',
+ 'argumentCount' => '3'
+ ),
+ 'MMULT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::MMULT',
+ 'argumentCount' => '2'
+ ),
+ 'MOD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::MOD',
+ 'argumentCount' => '2'
+ ),
+ 'MODE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::MODE',
+ 'argumentCount' => '1+'
+ ),
+ 'MONTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::MONTHOFYEAR',
+ 'argumentCount' => '1'
+ ),
+ 'MROUND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::MROUND',
+ 'argumentCount' => '2'
+ ),
+ 'MULTINOMIAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::MULTINOMIAL',
+ 'argumentCount' => '1+'
+ ),
+ 'N' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::N',
+ 'argumentCount' => '1'
+ ),
+ 'NA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NA',
+ 'argumentCount' => '0'
+ ),
+ 'NEGBINOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::NEGBINOMDIST',
+ 'argumentCount' => '3'
+ ),
+ 'NETWORKDAYS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::NETWORKDAYS',
+ 'argumentCount' => '2+'
+ ),
+ 'NOMINAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::NOMINAL',
+ 'argumentCount' => '2'
+ ),
+ 'NORMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'NORMINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMINV',
+ 'argumentCount' => '3'
+ ),
+ 'NORMSDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMSDIST',
+ 'argumentCount' => '1'
+ ),
+ 'NORMSINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::NORMSINV',
+ 'argumentCount' => '1'
+ ),
+ 'NOT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::NOT',
+ 'argumentCount' => '1'
+ ),
+ 'NOW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DATETIMENOW',
+ 'argumentCount' => '0'
+ ),
+ 'NPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::NPER',
+ 'argumentCount' => '3-5'
+ ),
+ 'NPV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::NPV',
+ 'argumentCount' => '2+'
+ ),
+ 'OCT2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'OCT2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'OCT2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'ODD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::ODD',
+ 'argumentCount' => '1'
+ ),
+ 'ODDFPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '8,9'
+ ),
+ 'ODDFYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '8,9'
+ ),
+ 'ODDLPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '7,8'
+ ),
+ 'ODDLYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '7,8'
+ ),
+ 'OFFSET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::OFFSET',
+ 'argumentCount' => '3,5',
+ 'passCellReference'=> TRUE,
+ 'passByReference' => array(TRUE)
+ ),
+ 'OR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_OR',
+ 'argumentCount' => '1+'
+ ),
+ 'PEARSON' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::CORREL',
+ 'argumentCount' => '2'
+ ),
+ 'PERCENTILE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::PERCENTILE',
+ 'argumentCount' => '2'
+ ),
+ 'PERCENTRANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::PERCENTRANK',
+ 'argumentCount' => '2,3'
+ ),
+ 'PERMUT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::PERMUT',
+ 'argumentCount' => '2'
+ ),
+ 'PHONETIC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'PI' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'pi',
+ 'argumentCount' => '0'
+ ),
+ 'PMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::PMT',
+ 'argumentCount' => '3-5'
+ ),
+ 'POISSON' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::POISSON',
+ 'argumentCount' => '3'
+ ),
+ 'POWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::POWER',
+ 'argumentCount' => '2'
+ ),
+ 'PPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::PPMT',
+ 'argumentCount' => '4-6'
+ ),
+ 'PRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::PRICE',
+ 'argumentCount' => '6,7'
+ ),
+ 'PRICEDISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::PRICEDISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'PRICEMAT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::PRICEMAT',
+ 'argumentCount' => '5,6'
+ ),
+ 'PROB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'PRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::PRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'PROPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::PROPERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'PV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::PV',
+ 'argumentCount' => '3-5'
+ ),
+ 'QUARTILE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::QUARTILE',
+ 'argumentCount' => '2'
+ ),
+ 'QUOTIENT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::QUOTIENT',
+ 'argumentCount' => '2'
+ ),
+ 'RADIANS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'deg2rad',
+ 'argumentCount' => '1'
+ ),
+ 'RAND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::RAND',
+ 'argumentCount' => '0'
+ ),
+ 'RANDBETWEEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::RAND',
+ 'argumentCount' => '2'
+ ),
+ 'RANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::RANK',
+ 'argumentCount' => '2,3'
+ ),
+ 'RATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::RATE',
+ 'argumentCount' => '3-6'
+ ),
+ 'RECEIVED' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::RECEIVED',
+ 'argumentCount' => '4-5'
+ ),
+ 'REPLACE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::REPLACE',
+ 'argumentCount' => '4'
+ ),
+ 'REPLACEB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::REPLACE',
+ 'argumentCount' => '4'
+ ),
+ 'REPT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'str_repeat',
+ 'argumentCount' => '2'
+ ),
+ 'RIGHT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::RIGHT',
+ 'argumentCount' => '1,2'
+ ),
+ 'RIGHTB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::RIGHT',
+ 'argumentCount' => '1,2'
+ ),
+ 'ROMAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::ROMAN',
+ 'argumentCount' => '1,2'
+ ),
+ 'ROUND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'round',
+ 'argumentCount' => '2'
+ ),
+ 'ROUNDDOWN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::ROUNDDOWN',
+ 'argumentCount' => '2'
+ ),
+ 'ROUNDUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::ROUNDUP',
+ 'argumentCount' => '2'
+ ),
+ 'ROW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::ROW',
+ 'argumentCount' => '-1',
+ 'passByReference' => array(TRUE)
+ ),
+ 'ROWS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::ROWS',
+ 'argumentCount' => '1'
+ ),
+ 'RSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::RSQ',
+ 'argumentCount' => '2'
+ ),
+ 'RTD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1+'
+ ),
+ 'SEARCH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'SEARCHB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'SECOND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::SECONDOFMINUTE',
+ 'argumentCount' => '1'
+ ),
+ 'SERIESSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SERIESSUM',
+ 'argumentCount' => '4'
+ ),
+ 'SIGN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SIGN',
+ 'argumentCount' => '1'
+ ),
+ 'SIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'sin',
+ 'argumentCount' => '1'
+ ),
+ 'SINH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'sinh',
+ 'argumentCount' => '1'
+ ),
+ 'SKEW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::SKEW',
+ 'argumentCount' => '1+'
+ ),
+ 'SLN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::SLN',
+ 'argumentCount' => '3'
+ ),
+ 'SLOPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::SLOPE',
+ 'argumentCount' => '2'
+ ),
+ 'SMALL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::SMALL',
+ 'argumentCount' => '2'
+ ),
+ 'SQRT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'sqrt',
+ 'argumentCount' => '1'
+ ),
+ 'SQRTPI' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SQRTPI',
+ 'argumentCount' => '1'
+ ),
+ 'STANDARDIZE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::STANDARDIZE',
+ 'argumentCount' => '3'
+ ),
+ 'STDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEV',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVA',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVP',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVPA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVPA',
+ 'argumentCount' => '1+'
+ ),
+ 'STEYX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::STEYX',
+ 'argumentCount' => '2'
+ ),
+ 'SUBSTITUTE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::SUBSTITUTE',
+ 'argumentCount' => '3,4'
+ ),
+ 'SUBTOTAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUBTOTAL',
+ 'argumentCount' => '2+'
+ ),
+ 'SUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUM',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMIF',
+ 'argumentCount' => '2,3'
+ ),
+ 'SUMIFS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'SUMPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMPRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMSQ',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMX2MY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMX2MY2',
+ 'argumentCount' => '2'
+ ),
+ 'SUMX2PY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMX2PY2',
+ 'argumentCount' => '2'
+ ),
+ 'SUMXMY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMXMY2',
+ 'argumentCount' => '2'
+ ),
+ 'SYD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::SYD',
+ 'argumentCount' => '4'
+ ),
+ 'T' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::RETURNSTRING',
+ 'argumentCount' => '1'
+ ),
+ 'TAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'tan',
+ 'argumentCount' => '1'
+ ),
+ 'TANH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'tanh',
+ 'argumentCount' => '1'
+ ),
+ 'TBILLEQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::TBILLEQ',
+ 'argumentCount' => '3'
+ ),
+ 'TBILLPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::TBILLPRICE',
+ 'argumentCount' => '3'
+ ),
+ 'TBILLYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::TBILLYIELD',
+ 'argumentCount' => '3'
+ ),
+ 'TDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::TDIST',
+ 'argumentCount' => '3'
+ ),
+ 'TEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::TEXTFORMAT',
+ 'argumentCount' => '2'
+ ),
+ 'TIME' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::TIME',
+ 'argumentCount' => '3'
+ ),
+ 'TIMEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::TIMEVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'TINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::TINV',
+ 'argumentCount' => '2'
+ ),
+ 'TODAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DATENOW',
+ 'argumentCount' => '0'
+ ),
+ 'TRANSPOSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::TRANSPOSE',
+ 'argumentCount' => '1'
+ ),
+ 'TREND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::TREND',
+ 'argumentCount' => '1-4'
+ ),
+ 'TRIM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::TRIMSPACES',
+ 'argumentCount' => '1'
+ ),
+ 'TRIMMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::TRIMMEAN',
+ 'argumentCount' => '2'
+ ),
+ 'TRUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Logical::TRUE',
+ 'argumentCount' => '0'
+ ),
+ 'TRUNC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_MathTrig::TRUNC',
+ 'argumentCount' => '1,2'
+ ),
+ 'TTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '4'
+ ),
+ 'TYPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TYPE',
+ 'argumentCount' => '1'
+ ),
+ 'UPPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::UPPERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'USDOLLAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2'
+ ),
+ 'VALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_TextData::VALUE',
+ 'argumentCount' => '1'
+ ),
+ 'VAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::VARFunc',
+ 'argumentCount' => '1+'
+ ),
+ 'VARA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::VARA',
+ 'argumentCount' => '1+'
+ ),
+ 'VARP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::VARP',
+ 'argumentCount' => '1+'
+ ),
+ 'VARPA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::VARPA',
+ 'argumentCount' => '1+'
+ ),
+ 'VDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '5-7'
+ ),
+ 'VERSION' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VERSION',
+ 'argumentCount' => '0'
+ ),
+ 'VLOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_LookupRef::VLOOKUP',
+ 'argumentCount' => '3,4'
+ ),
+ 'WEEKDAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::DAYOFWEEK',
+ 'argumentCount' => '1,2'
+ ),
+ 'WEEKNUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::WEEKOFYEAR',
+ 'argumentCount' => '1,2'
+ ),
+ 'WEIBULL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::WEIBULL',
+ 'argumentCount' => '4'
+ ),
+ 'WORKDAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::WORKDAY',
+ 'argumentCount' => '2+'
+ ),
+ 'XIRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::XIRR',
+ 'argumentCount' => '2,3'
+ ),
+ 'XNPV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::XNPV',
+ 'argumentCount' => '3'
+ ),
+ 'YEAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::YEAR',
+ 'argumentCount' => '1'
+ ),
+ 'YEARFRAC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_DateTime::YEARFRAC',
+ 'argumentCount' => '2,3'
+ ),
+ 'YIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '6,7'
+ ),
+ 'YIELDDISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::YIELDDISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'YIELDMAT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Financial::YIELDMAT',
+ 'argumentCount' => '5,6'
+ ),
+ 'ZTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Statistical::ZTEST',
+ 'argumentCount' => '2-3'
+ )
+ );
+
+
+ // Internal functions used for special control purposes
+ private static $_controlFunctions = array(
+ 'MKMATRIX' => array('argumentCount' => '*',
+ 'functionCall' => 'self::_mkMatrix'
+ )
+ );
+
+
+
+
+ private function __construct(PHPExcel $workbook = NULL) {
+ $setPrecision = (PHP_INT_SIZE == 4) ? 14 : 16;
+ $this->_savedPrecision = ini_get('precision');
+ if ($this->_savedPrecision < $setPrecision) {
+ ini_set('precision',$setPrecision);
+ }
+ $this->delta = 1 * pow(10, -$setPrecision);
+
+ if ($workbook !== NULL) {
+ self::$_workbookSets[$workbook->getID()] = $this;
+ }
+
+ $this->_workbook = $workbook;
+ $this->_cyclicReferenceStack = new PHPExcel_CalcEngine_CyclicReferenceStack();
+ $this->_debugLog = new PHPExcel_CalcEngine_Logger($this->_cyclicReferenceStack);
+ } // function __construct()
+
+
+ public function __destruct() {
+ if ($this->_savedPrecision != ini_get('precision')) {
+ ini_set('precision',$this->_savedPrecision);
+ }
+ }
+
+ private static function _loadLocales() {
+ $localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
+ foreach (glob($localeFileDirectory.'/*',GLOB_ONLYDIR) as $filename) {
+ $filename = substr($filename,strlen($localeFileDirectory)+1);
+ if ($filename != 'en') {
+ self::$_validLocaleLanguages[] = $filename;
+ }
+ }
+ }
+
+ /**
+ * Get an instance of this class
+ *
+ * @access public
+ * @param PHPExcel $workbook Injected workbook for working with a PHPExcel object,
+ * or NULL to create a standalone claculation engine
+ * @return PHPExcel_Calculation
+ */
+ public static function getInstance(PHPExcel $workbook = NULL) {
+ if ($workbook !== NULL) {
+ if (isset(self::$_workbookSets[$workbook->getID()])) {
+ return self::$_workbookSets[$workbook->getID()];
+ }
+ return new PHPExcel_Calculation($workbook);
+ }
+
+ if (!isset(self::$_instance) || (self::$_instance === NULL)) {
+ self::$_instance = new PHPExcel_Calculation();
+ }
+
+ return self::$_instance;
+ } // function getInstance()
+
+ /**
+ * Unset an instance of this class
+ *
+ * @access public
+ * @param PHPExcel $workbook Injected workbook identifying the instance to unset
+ */
+ public static function unsetInstance(PHPExcel $workbook = NULL) {
+ if ($workbook !== NULL) {
+ if (isset(self::$_workbookSets[$workbook->getID()])) {
+ unset(self::$_workbookSets[$workbook->getID()]);
+ }
+ }
+ }
+
+ /**
+ * Flush the calculation cache for any existing instance of this class
+ * but only if a PHPExcel_Calculation instance exists
+ *
+ * @access public
+ * @return null
+ */
+ public function flushInstance() {
+ $this->clearCalculationCache();
+ } // function flushInstance()
+
+
+ /**
+ * Get the debuglog for this claculation engine instance
+ *
+ * @access public
+ * @return PHPExcel_CalcEngine_Logger
+ */
+ public function getDebugLog() {
+ return $this->_debugLog;
+ }
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ *
+ * @access public
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public final function __clone() {
+ throw new PHPExcel_Calculation_Exception ('Cloning the calculation engine is not allowed!');
+ } // function __clone()
+
+
+ /**
+ * Return the locale-specific translation of TRUE
+ *
+ * @access public
+ * @return string locale-specific translation of TRUE
+ */
+ public static function getTRUE() {
+ return self::$_localeBoolean['TRUE'];
+ }
+
+ /**
+ * Return the locale-specific translation of FALSE
+ *
+ * @access public
+ * @return string locale-specific translation of FALSE
+ */
+ public static function getFALSE() {
+ return self::$_localeBoolean['FALSE'];
+ }
+
+ /**
+ * Set the Array Return Type (Array or Value of first element in the array)
+ *
+ * @access public
+ * @param string $returnType Array return type
+ * @return boolean Success or failure
+ */
+ public static function setArrayReturnType($returnType) {
+ if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
+ ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
+ ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
+ self::$returnArrayAsType = $returnType;
+ return TRUE;
+ }
+ return FALSE;
+ } // function setArrayReturnType()
+
+
+ /**
+ * Return the Array Return Type (Array or Value of first element in the array)
+ *
+ * @access public
+ * @return string $returnType Array return type
+ */
+ public static function getArrayReturnType() {
+ return self::$returnArrayAsType;
+ } // function getArrayReturnType()
+
+
+ /**
+ * Is calculation caching enabled?
+ *
+ * @access public
+ * @return boolean
+ */
+ public function getCalculationCacheEnabled() {
+ return $this->_calculationCacheEnabled;
+ } // function getCalculationCacheEnabled()
+
+ /**
+ * Enable/disable calculation cache
+ *
+ * @access public
+ * @param boolean $pValue
+ */
+ public function setCalculationCacheEnabled($pValue = TRUE) {
+ $this->_calculationCacheEnabled = $pValue;
+ $this->clearCalculationCache();
+ } // function setCalculationCacheEnabled()
+
+
+ /**
+ * Enable calculation cache
+ */
+ public function enableCalculationCache() {
+ $this->setCalculationCacheEnabled(TRUE);
+ } // function enableCalculationCache()
+
+
+ /**
+ * Disable calculation cache
+ */
+ public function disableCalculationCache() {
+ $this->setCalculationCacheEnabled(FALSE);
+ } // function disableCalculationCache()
+
+
+ /**
+ * Clear calculation cache
+ */
+ public function clearCalculationCache() {
+ $this->_calculationCache = array();
+ } // function clearCalculationCache()
+
+ /**
+ * Clear calculation cache for a specified worksheet
+ *
+ * @param string $worksheetName
+ */
+ public function clearCalculationCacheForWorksheet($worksheetName) {
+ if (isset($this->_calculationCache[$worksheetName])) {
+ unset($this->_calculationCache[$worksheetName]);
+ }
+ } // function clearCalculationCacheForWorksheet()
+
+ /**
+ * Rename calculation cache for a specified worksheet
+ *
+ * @param string $fromWorksheetName
+ * @param string $toWorksheetName
+ */
+ public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName) {
+ if (isset($this->_calculationCache[$fromWorksheetName])) {
+ $this->_calculationCache[$toWorksheetName] = &$this->_calculationCache[$fromWorksheetName];
+ unset($this->_calculationCache[$fromWorksheetName]);
+ }
+ } // function renameCalculationCacheForWorksheet()
+
+
+ /**
+ * Get the currently defined locale code
+ *
+ * @return string
+ */
+ public function getLocale() {
+ return self::$_localeLanguage;
+ } // function getLocale()
+
+
+ /**
+ * Set the locale code
+ *
+ * @param string $locale The locale to use for formula translation
+ * @return boolean
+ */
+ public function setLocale($locale = 'en_us') {
+ // Identify our locale and language
+ $language = $locale = strtolower($locale);
+ if (strpos($locale,'_') !== FALSE) {
+ list($language) = explode('_',$locale);
+ }
+
+ if (count(self::$_validLocaleLanguages) == 1)
+ self::_loadLocales();
+
+ // Test whether we have any language data for this language (any locale)
+ if (in_array($language,self::$_validLocaleLanguages)) {
+ // initialise language/locale settings
+ self::$_localeFunctions = array();
+ self::$_localeArgumentSeparator = ',';
+ self::$_localeBoolean = array('TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL');
+ // Default is English, if user isn't requesting english, then read the necessary data from the locale files
+ if ($locale != 'en_us') {
+ // Search for a file with a list of function names for locale
+ $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_',DIRECTORY_SEPARATOR,$locale).DIRECTORY_SEPARATOR.'functions';
+ if (!file_exists($functionNamesFile)) {
+ // If there isn't a locale specific function file, look for a language specific function file
+ $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions';
+ if (!file_exists($functionNamesFile)) {
+ return FALSE;
+ }
+ }
+ // Retrieve the list of locale or language specific function names
+ $localeFunctions = file($functionNamesFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ foreach ($localeFunctions as $localeFunction) {
+ list($localeFunction) = explode('##',$localeFunction); // Strip out comments
+ if (strpos($localeFunction,'=') !== FALSE) {
+ list($fName,$lfName) = explode('=',$localeFunction);
+ $fName = trim($fName);
+ $lfName = trim($lfName);
+ if ((isset(self::$_PHPExcelFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
+ self::$_localeFunctions[$fName] = $lfName;
+ }
+ }
+ }
+ // Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
+ if (isset(self::$_localeFunctions['TRUE'])) { self::$_localeBoolean['TRUE'] = self::$_localeFunctions['TRUE']; }
+ if (isset(self::$_localeFunctions['FALSE'])) { self::$_localeBoolean['FALSE'] = self::$_localeFunctions['FALSE']; }
+
+ $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_',DIRECTORY_SEPARATOR,$locale).DIRECTORY_SEPARATOR.'config';
+ if (!file_exists($configFile)) {
+ $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'config';
+ }
+ if (file_exists($configFile)) {
+ $localeSettings = file($configFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ foreach ($localeSettings as $localeSetting) {
+ list($localeSetting) = explode('##',$localeSetting); // Strip out comments
+ if (strpos($localeSetting,'=') !== FALSE) {
+ list($settingName,$settingValue) = explode('=',$localeSetting);
+ $settingName = strtoupper(trim($settingName));
+ switch ($settingName) {
+ case 'ARGUMENTSEPARATOR' :
+ self::$_localeArgumentSeparator = trim($settingValue);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
+ self::$functionReplaceFromLocale = self::$functionReplaceToLocale = NULL;
+ self::$_localeLanguage = $locale;
+ return TRUE;
+ }
+ return FALSE;
+ } // function setLocale()
+
+
+
+ public static function _translateSeparator($fromSeparator,$toSeparator,$formula,&$inBraces) {
+ $strlen = mb_strlen($formula);
+ for ($i = 0; $i < $strlen; ++$i) {
+ $chr = mb_substr($formula,$i,1);
+ switch ($chr) {
+ case '{' : $inBraces = TRUE;
+ break;
+ case '}' : $inBraces = FALSE;
+ break;
+ case $fromSeparator :
+ if (!$inBraces) {
+ $formula = mb_substr($formula,0,$i).$toSeparator.mb_substr($formula,$i+1);
+ }
+ }
+ }
+ return $formula;
+ }
+
+ private static function _translateFormula($from,$to,$formula,$fromSeparator,$toSeparator) {
+ // Convert any Excel function names to the required language
+ if (self::$_localeLanguage !== 'en_us') {
+ $inBraces = FALSE;
+ // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+ if (strpos($formula,'"') !== FALSE) {
+ // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+ // the formula
+ $temp = explode('"',$formula);
+ $i = FALSE;
+ foreach($temp as &$value) {
+ // Only count/replace in alternating array entries
+ if ($i = !$i) {
+ $value = preg_replace($from,$to,$value);
+ $value = self::_translateSeparator($fromSeparator,$toSeparator,$value,$inBraces);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $formula = implode('"',$temp);
+ } else {
+ // If there's no quoted strings, then we do a simple count/replace
+ $formula = preg_replace($from,$to,$formula);
+ $formula = self::_translateSeparator($fromSeparator,$toSeparator,$formula,$inBraces);
+ }
+ }
+
+ return $formula;
+ }
+
+ private static $functionReplaceFromExcel = NULL;
+ private static $functionReplaceToLocale = NULL;
+
+ public function _translateFormulaToLocale($formula) {
+ if (self::$functionReplaceFromExcel === NULL) {
+ self::$functionReplaceFromExcel = array();
+ foreach(array_keys(self::$_localeFunctions) as $excelFunctionName) {
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui';
+ }
+ foreach(array_keys(self::$_localeBoolean) as $excelBoolean) {
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
+ }
+
+ }
+
+ if (self::$functionReplaceToLocale === NULL) {
+ self::$functionReplaceToLocale = array();
+ foreach(array_values(self::$_localeFunctions) as $localeFunctionName) {
+ self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2';
+ }
+ foreach(array_values(self::$_localeBoolean) as $localeBoolean) {
+ self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2';
+ }
+ }
+
+ return self::_translateFormula(self::$functionReplaceFromExcel,self::$functionReplaceToLocale,$formula,',',self::$_localeArgumentSeparator);
+ } // function _translateFormulaToLocale()
+
+
+ private static $functionReplaceFromLocale = NULL;
+ private static $functionReplaceToExcel = NULL;
+
+ public function _translateFormulaToEnglish($formula) {
+ if (self::$functionReplaceFromLocale === NULL) {
+ self::$functionReplaceFromLocale = array();
+ foreach(array_values(self::$_localeFunctions) as $localeFunctionName) {
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui';
+ }
+ foreach(array_values(self::$_localeBoolean) as $excelBoolean) {
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
+ }
+ }
+
+ if (self::$functionReplaceToExcel === NULL) {
+ self::$functionReplaceToExcel = array();
+ foreach(array_keys(self::$_localeFunctions) as $excelFunctionName) {
+ self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2';
+ }
+ foreach(array_keys(self::$_localeBoolean) as $excelBoolean) {
+ self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2';
+ }
+ }
+
+ return self::_translateFormula(self::$functionReplaceFromLocale,self::$functionReplaceToExcel,$formula,self::$_localeArgumentSeparator,',');
+ } // function _translateFormulaToEnglish()
+
+
+ public static function _localeFunc($function) {
+ if (self::$_localeLanguage !== 'en_us') {
+ $functionName = trim($function,'(');
+ if (isset(self::$_localeFunctions[$functionName])) {
+ $brace = ($functionName != $function);
+ $function = self::$_localeFunctions[$functionName];
+ if ($brace) { $function .= '('; }
+ }
+ }
+ return $function;
+ }
+
+
+
+
+ /**
+ * Wrap string values in quotes
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ public static function _wrapResult($value) {
+ if (is_string($value)) {
+ // Error values cannot be "wrapped"
+ if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) {
+ // Return Excel errors "as is"
+ return $value;
+ }
+ // Return strings wrapped in quotes
+ return '"'.$value.'"';
+ // Convert numeric errors to NaN error
+ } else if((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return $value;
+ } // function _wrapResult()
+
+
+ /**
+ * Remove quotes used as a wrapper to identify string values
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ public static function _unwrapResult($value) {
+ if (is_string($value)) {
+ if ((isset($value{0})) && ($value{0} == '"') && (substr($value,-1) == '"')) {
+ return substr($value,1,-1);
+ }
+ // Convert numeric errors to NaN error
+ } else if((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return $value;
+ } // function _unwrapResult()
+
+
+
+
+ /**
+ * Calculate cell value (using formula from a cell ID)
+ * Retained for backward compatibility
+ *
+ * @access public
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @return mixed
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function calculate(PHPExcel_Cell $pCell = NULL) {
+ try {
+ return $this->calculateCellValue($pCell);
+ } catch (PHPExcel_Exception $e) {
+ throw new PHPExcel_Calculation_Exception($e->getMessage());
+ }
+ } // function calculate()
+
+
+ /**
+ * Calculate the value of a cell formula
+ *
+ * @access public
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @param Boolean $resetLog Flag indicating whether the debug log should be reset or not
+ * @return mixed
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function calculateCellValue(PHPExcel_Cell $pCell = NULL, $resetLog = TRUE) {
+ if ($pCell === NULL) {
+ return NULL;
+ }
+
+ $returnArrayAsType = self::$returnArrayAsType;
+ if ($resetLog) {
+ // Initialise the logging settings if requested
+ $this->formulaError = null;
+ $this->_debugLog->clearLog();
+ $this->_cyclicReferenceStack->clear();
+ $this->_cyclicFormulaCount = 1;
+
+ self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
+ }
+
+ // Execute the calculation for the cell formula
+ $this->_cellStack[] = array(
+ 'sheet' => $pCell->getWorksheet()->getTitle(),
+ 'cell' => $pCell->getCoordinate(),
+ );
+ try {
+ $result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
+ $cellAddress = array_pop($this->_cellStack);
+ $this->_workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
+ } catch (PHPExcel_Exception $e) {
+ $cellAddress = array_pop($this->_cellStack);
+ $this->_workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
+ throw new PHPExcel_Calculation_Exception($e->getMessage());
+ }
+
+ if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+ self::$returnArrayAsType = $returnArrayAsType;
+ $testResult = PHPExcel_Calculation_Functions::flattenArray($result);
+ if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ // If there's only a single cell in the array, then we allow it
+ if (count($testResult) != 1) {
+ // If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
+ $r = array_keys($result);
+ $r = array_shift($r);
+ if (!is_numeric($r)) { return PHPExcel_Calculation_Functions::VALUE(); }
+ if (is_array($result[$r])) {
+ $c = array_keys($result[$r]);
+ $c = array_shift($c);
+ if (!is_numeric($c)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ }
+ $result = array_shift($testResult);
+ }
+ self::$returnArrayAsType = $returnArrayAsType;
+
+
+ if ($result === NULL) {
+ return 0;
+ } elseif((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return $result;
+ } // function calculateCellValue(
+
+
+ /**
+ * Validate and parse a formula string
+ *
+ * @param string $formula Formula to parse
+ * @return array
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function parseFormula($formula) {
+ // Basic validation that this is indeed a formula
+ // We return an empty array if not
+ $formula = trim($formula);
+ if ((!isset($formula{0})) || ($formula{0} != '=')) return array();
+ $formula = ltrim(substr($formula,1));
+ if (!isset($formula{0})) return array();
+
+ // Parse the formula and return the token stack
+ return $this->_parseFormula($formula);
+ } // function parseFormula()
+
+
+ /**
+ * Calculate the value of a formula
+ *
+ * @param string $formula Formula to parse
+ * @param string $cellID Address of the cell to calculate
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @return mixed
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function calculateFormula($formula, $cellID=NULL, PHPExcel_Cell $pCell = NULL) {
+ // Initialise the logging settings
+ $this->formulaError = null;
+ $this->_debugLog->clearLog();
+ $this->_cyclicReferenceStack->clear();
+
+ // Disable calculation cacheing because it only applies to cell calculations, not straight formulae
+ // But don't actually flush any cache
+ $resetCache = $this->getCalculationCacheEnabled();
+ $this->_calculationCacheEnabled = FALSE;
+ // Execute the calculation
+ try {
+ $result = self::_unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
+ } catch (PHPExcel_Exception $e) {
+ throw new PHPExcel_Calculation_Exception($e->getMessage());
+ }
+
+ // Reset calculation cacheing to its previous state
+ $this->_calculationCacheEnabled = $resetCache;
+
+ return $result;
+ } // function calculateFormula()
+
+
+ public function getValueFromCache($cellReference, &$cellValue) {
+ // Is calculation cacheing enabled?
+ // Is the value present in calculation cache?
+ $this->_debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
+ if (($this->_calculationCacheEnabled) && (isset($this->_calculationCache[$cellReference]))) {
+ $this->_debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
+ // Return the cached result
+ $cellValue = $this->_calculationCache[$cellReference];
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ public function saveValueToCache($cellReference, $cellValue) {
+ if ($this->_calculationCacheEnabled) {
+ $this->_calculationCache[$cellReference] = $cellValue;
+ }
+ }
+
+ /**
+ * Parse a cell formula and calculate its value
+ *
+ * @param string $formula The formula to parse and calculate
+ * @param string $cellID The ID (e.g. A3) of the cell that we are calculating
+ * @param PHPExcel_Cell $pCell Cell to calculate
+ * @return mixed
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function _calculateFormulaValue($formula, $cellID=null, PHPExcel_Cell $pCell = null) {
+ $cellValue = null;
+
+ // Basic validation that this is indeed a formula
+ // We simply return the cell value if not
+ $formula = trim($formula);
+ if ($formula{0} != '=') return self::_wrapResult($formula);
+ $formula = ltrim(substr($formula, 1));
+ if (!isset($formula{0})) return self::_wrapResult($formula);
+
+ $pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
+ $wsTitle = ($pCellParent !== NULL) ? $pCellParent->getTitle() : "\x00Wrk";
+ $wsCellReference = $wsTitle . '!' . $cellID;
+
+ if (($cellID !== NULL) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
+ return $cellValue;
+ }
+
+ if (($wsTitle{0} !== "\x00") && ($this->_cyclicReferenceStack->onStack($wsCellReference))) {
+ if ($this->cyclicFormulaCount <= 0) {
+ $this->_cyclicFormulaCell = '';
+ return $this->_raiseFormulaError('Cyclic Reference in Formula');
+ } elseif ($this->_cyclicFormulaCell === $wsCellReference) {
+ ++$this->_cyclicFormulaCount;
+ if ($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) {
+ $this->_cyclicFormulaCell = '';
+ return $cellValue;
+ }
+ } elseif ($this->_cyclicFormulaCell == '') {
+ if ($this->_cyclicFormulaCount >= $this->cyclicFormulaCount) {
+ return $cellValue;
+ }
+ $this->_cyclicFormulaCell = $wsCellReference;
+ }
+ }
+
+ // Parse the formula onto the token stack and calculate the value
+ $this->_cyclicReferenceStack->push($wsCellReference);
+ $cellValue = $this->_processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
+ $this->_cyclicReferenceStack->pop();
+
+ // Save to calculation cache
+ if ($cellID !== NULL) {
+ $this->saveValueToCache($wsCellReference, $cellValue);
+ }
+
+ // Return the calculated value
+ return $cellValue;
+ } // function _calculateFormulaValue()
+
+
+ /**
+ * Ensure that paired matrix operands are both matrices and of the same size
+ *
+ * @param mixed &$operand1 First matrix operand
+ * @param mixed &$operand2 Second matrix operand
+ * @param integer $resize Flag indicating whether the matrices should be resized to match
+ * and (if so), whether the smaller dimension should grow or the
+ * larger should shrink.
+ * 0 = no resize
+ * 1 = shrink to fit
+ * 2 = extend to fit
+ */
+ private static function _checkMatrixOperands(&$operand1,&$operand2,$resize = 1) {
+ // Examine each of the two operands, and turn them into an array if they aren't one already
+ // Note that this function should only be called if one or both of the operand is already an array
+ if (!is_array($operand1)) {
+ list($matrixRows,$matrixColumns) = self::_getMatrixDimensions($operand2);
+ $operand1 = array_fill(0,$matrixRows,array_fill(0,$matrixColumns,$operand1));
+ $resize = 0;
+ } elseif (!is_array($operand2)) {
+ list($matrixRows,$matrixColumns) = self::_getMatrixDimensions($operand1);
+ $operand2 = array_fill(0,$matrixRows,array_fill(0,$matrixColumns,$operand2));
+ $resize = 0;
+ }
+
+ list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($operand1);
+ list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($operand2);
+ if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
+ $resize = 1;
+ }
+
+ if ($resize == 2) {
+ // Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
+ self::_resizeMatricesExtend($operand1,$operand2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns);
+ } elseif ($resize == 1) {
+ // Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
+ self::_resizeMatricesShrink($operand1,$operand2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns);
+ }
+ return array( $matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns);
+ } // function _checkMatrixOperands()
+
+
+ /**
+ * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
+ *
+ * @param mixed &$matrix matrix operand
+ * @return array An array comprising the number of rows, and number of columns
+ */
+ public static function _getMatrixDimensions(&$matrix) {
+ $matrixRows = count($matrix);
+ $matrixColumns = 0;
+ foreach($matrix as $rowKey => $rowValue) {
+ $matrixColumns = max(count($rowValue),$matrixColumns);
+ if (!is_array($rowValue)) {
+ $matrix[$rowKey] = array($rowValue);
+ } else {
+ $matrix[$rowKey] = array_values($rowValue);
+ }
+ }
+ $matrix = array_values($matrix);
+ return array($matrixRows,$matrixColumns);
+ } // function _getMatrixDimensions()
+
+
+ /**
+ * Ensure that paired matrix operands are both matrices of the same size
+ *
+ * @param mixed &$matrix1 First matrix operand
+ * @param mixed &$matrix2 Second matrix operand
+ * @param integer $matrix1Rows Row size of first matrix operand
+ * @param integer $matrix1Columns Column size of first matrix operand
+ * @param integer $matrix2Rows Row size of second matrix operand
+ * @param integer $matrix2Columns Column size of second matrix operand
+ */
+ private static function _resizeMatricesShrink(&$matrix1,&$matrix2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns) {
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Rows < $matrix1Rows) {
+ for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
+ unset($matrix1[$i]);
+ }
+ }
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ unset($matrix1[$i][$j]);
+ }
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Rows < $matrix2Rows) {
+ for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
+ unset($matrix2[$i]);
+ }
+ }
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ unset($matrix2[$i][$j]);
+ }
+ }
+ }
+ }
+ } // function _resizeMatricesShrink()
+
+
+ /**
+ * Ensure that paired matrix operands are both matrices of the same size
+ *
+ * @param mixed &$matrix1 First matrix operand
+ * @param mixed &$matrix2 Second matrix operand
+ * @param integer $matrix1Rows Row size of first matrix operand
+ * @param integer $matrix1Columns Column size of first matrix operand
+ * @param integer $matrix2Rows Row size of second matrix operand
+ * @param integer $matrix2Columns Column size of second matrix operand
+ */
+ private static function _resizeMatricesExtend(&$matrix1,&$matrix2,$matrix1Rows,$matrix1Columns,$matrix2Rows,$matrix2Columns) {
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ $x = $matrix2[$i][$matrix2Columns-1];
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ $matrix2[$i][$j] = $x;
+ }
+ }
+ }
+ if ($matrix2Rows < $matrix1Rows) {
+ $x = $matrix2[$matrix2Rows-1];
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ $matrix2[$i] = $x;
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ $x = $matrix1[$i][$matrix1Columns-1];
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ $matrix1[$i][$j] = $x;
+ }
+ }
+ }
+ if ($matrix1Rows < $matrix2Rows) {
+ $x = $matrix1[$matrix1Rows-1];
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ $matrix1[$i] = $x;
+ }
+ }
+ }
+ } // function _resizeMatricesExtend()
+
+
+ /**
+ * Format details of an operand for display in the log (based on operand type)
+ *
+ * @param mixed $value First matrix operand
+ * @return mixed
+ */
+ private function _showValue($value) {
+ if ($this->_debugLog->getWriteDebugLog()) {
+ $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
+ if (count($testArray) == 1) {
+ $value = array_pop($testArray);
+ }
+
+ if (is_array($value)) {
+ $returnMatrix = array();
+ $pad = $rpad = ', ';
+ foreach($value as $row) {
+ if (is_array($row)) {
+ $returnMatrix[] = implode($pad,array_map(array($this,'_showValue'),$row));
+ $rpad = '; ';
+ } else {
+ $returnMatrix[] = $this->_showValue($row);
+ }
+ }
+ return '{ '.implode($rpad,$returnMatrix).' }';
+ } elseif(is_string($value) && (trim($value,'"') == $value)) {
+ return '"'.$value.'"';
+ } elseif(is_bool($value)) {
+ return ($value) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+ }
+ }
+ return PHPExcel_Calculation_Functions::flattenSingleValue($value);
+ } // function _showValue()
+
+
+ /**
+ * Format type and details of an operand for display in the log (based on operand type)
+ *
+ * @param mixed $value First matrix operand
+ * @return mixed
+ */
+ private function _showTypeDetails($value) {
+ if ($this->_debugLog->getWriteDebugLog()) {
+ $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
+ if (count($testArray) == 1) {
+ $value = array_pop($testArray);
+ }
+
+ if ($value === NULL) {
+ return 'a NULL value';
+ } elseif (is_float($value)) {
+ $typeString = 'a floating point number';
+ } elseif(is_int($value)) {
+ $typeString = 'an integer number';
+ } elseif(is_bool($value)) {
+ $typeString = 'a boolean';
+ } elseif(is_array($value)) {
+ $typeString = 'a matrix';
+ } else {
+ if ($value == '') {
+ return 'an empty string';
+ } elseif ($value{0} == '#') {
+ return 'a '.$value.' error';
+ } else {
+ $typeString = 'a string';
+ }
+ }
+ return $typeString.' with a value of '.$this->_showValue($value);
+ }
+ } // function _showTypeDetails()
+
+
+ private function _convertMatrixReferences($formula) {
+ static $matrixReplaceFrom = array('{',';','}');
+ static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(','),MKMATRIX(','))');
+
+ // Convert any Excel matrix references to the MKMATRIX() function
+ if (strpos($formula,'{') !== FALSE) {
+ // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+ if (strpos($formula,'"') !== FALSE) {
+ // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+ // the formula
+ $temp = explode('"',$formula);
+ // Open and Closed counts used for trapping mismatched braces in the formula
+ $openCount = $closeCount = 0;
+ $i = FALSE;
+ foreach($temp as &$value) {
+ // Only count/replace in alternating array entries
+ if ($i = !$i) {
+ $openCount += substr_count($value,'{');
+ $closeCount += substr_count($value,'}');
+ $value = str_replace($matrixReplaceFrom,$matrixReplaceTo,$value);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $formula = implode('"',$temp);
+ } else {
+ // If there's no quoted strings, then we do a simple count/replace
+ $openCount = substr_count($formula,'{');
+ $closeCount = substr_count($formula,'}');
+ $formula = str_replace($matrixReplaceFrom,$matrixReplaceTo,$formula);
+ }
+ // Trap for mismatched braces and trigger an appropriate error
+ if ($openCount < $closeCount) {
+ if ($openCount > 0) {
+ return $this->_raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected '}' encountered");
+ }
+ } elseif ($openCount > $closeCount) {
+ if ($closeCount > 0) {
+ return $this->_raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected '{' encountered");
+ }
+ }
+ }
+
+ return $formula;
+ } // function _convertMatrixReferences()
+
+
+ private static function _mkMatrix() {
+ return func_get_args();
+ } // function _mkMatrix()
+
+
+ // Binary Operators
+ // These operators always work on two values
+ // Array key is the operator, the value indicates whether this is a left or right associative operator
+ private static $_operatorAssociativity = array(
+ '^' => 0, // Exponentiation
+ '*' => 0, '/' => 0, // Multiplication and Division
+ '+' => 0, '-' => 0, // Addition and Subtraction
+ '&' => 0, // Concatenation
+ '|' => 0, ':' => 0, // Intersect and Range
+ '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison
+ );
+
+ // Comparison (Boolean) Operators
+ // These operators work on two values, but always return a boolean result
+ private static $_comparisonOperators = array('>' => TRUE, '<' => TRUE, '=' => TRUE, '>=' => TRUE, '<=' => TRUE, '<>' => TRUE);
+
+ // Operator Precedence
+ // This list includes all valid operators, whether binary (including boolean) or unary (such as %)
+ // Array key is the operator, the value is its precedence
+ private static $_operatorPrecedence = array(
+ ':' => 8, // Range
+ '|' => 7, // Intersect
+ '~' => 6, // Negation
+ '%' => 5, // Percentage
+ '^' => 4, // Exponentiation
+ '*' => 3, '/' => 3, // Multiplication and Division
+ '+' => 2, '-' => 2, // Addition and Subtraction
+ '&' => 1, // Concatenation
+ '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0 // Comparison
+ );
+
+ // Convert infix to postfix notation
+ private function _parseFormula($formula, PHPExcel_Cell $pCell = NULL) {
+ if (($formula = $this->_convertMatrixReferences(trim($formula))) === FALSE) {
+ return FALSE;
+ }
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
+ // so we store the parent worksheet so that we can re-attach it when necessary
+ $pCellParent = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
+
+ $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
+ '|'.self::CALCULATION_REGEXP_CELLREF.
+ '|'.self::CALCULATION_REGEXP_NUMBER.
+ '|'.self::CALCULATION_REGEXP_STRING.
+ '|'.self::CALCULATION_REGEXP_OPENBRACE.
+ '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
+ '|'.self::CALCULATION_REGEXP_ERROR.
+ ')/si';
+
+ // Start with initialisation
+ $index = 0;
+ $stack = new PHPExcel_Calculation_Token_Stack;
+ $output = array();
+ $expectingOperator = FALSE; // We use this test in syntax-checking the expression to determine when a
+ // - is a negation or + is a positive operator rather than an operation
+ $expectingOperand = FALSE; // We use this test in syntax-checking the expression to determine whether an operand
+ // should be null in a function call
+ // The guts of the lexical parser
+ // Loop through the formula extracting each operator and operand in turn
+ while(TRUE) {
+//echo 'Assessing Expression '.substr($formula, $index),PHP_EOL;
+ $opCharacter = $formula{$index}; // Get the first character of the value at the current index position
+//echo 'Initial character of expression block is '.$opCharacter,PHP_EOL;
+ if ((isset(self::$_comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$_comparisonOperators[$formula{$index+1}]))) {
+ $opCharacter .= $formula{++$index};
+//echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL;
+ }
+
+ // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
+ $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
+//echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL;
+//var_dump($match);
+
+ if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
+//echo 'Element is a Negation operator',PHP_EOL;
+ $stack->push('Unary Operator','~'); // Put a negation on the stack
+ ++$index; // and drop the negation symbol
+ } elseif ($opCharacter == '%' && $expectingOperator) {
+//echo 'Element is a Percentage operator',PHP_EOL;
+ $stack->push('Unary Operator','%'); // Put a percentage on the stack
+ ++$index;
+ } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
+//echo 'Element is a Positive number, not Plus operator',PHP_EOL;
+ ++$index; // Drop the redundant plus symbol
+ } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal
+ return $this->_raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression
+
+ } elseif ((isset(self::$_operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
+//echo 'Element with value '.$opCharacter.' is an Operator',PHP_EOL;
+ while($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ isset(self::$_operators[$o2['value']]) &&
+ @(self::$_operatorAssociativity[$opCharacter] ? self::$_operatorPrecedence[$opCharacter] < self::$_operatorPrecedence[$o2['value']] : self::$_operatorPrecedence[$opCharacter] <= self::$_operatorPrecedence[$o2['value']])) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+ $stack->push('Binary Operator',$opCharacter); // Finally put our current operator onto the stack
+ ++$index;
+ $expectingOperator = FALSE;
+
+ } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
+//echo 'Element is a Closing bracket',PHP_EOL;
+ $expectingOperand = FALSE;
+ while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
+ if ($o2 === NULL) return $this->_raiseFormulaError('Formula Error: Unexpected closing brace ")"');
+ else $output[] = $o2;
+ }
+ $d = $stack->last(2);
+ if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
+ $functionName = $matches[1]; // Get the function name
+//echo 'Closed Function is '.$functionName,PHP_EOL;
+ $d = $stack->pop();
+ $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack)
+//if ($argumentCount == 0) {
+// echo 'With no arguments',PHP_EOL;
+//} elseif ($argumentCount == 1) {
+// echo 'With 1 argument',PHP_EOL;
+//} else {
+// echo 'With '.$argumentCount.' arguments',PHP_EOL;
+//}
+ $output[] = $d; // Dump the argument count on the output
+ $output[] = $stack->pop(); // Pop the function and push onto the output
+ if (isset(self::$_controlFunctions[$functionName])) {
+//echo 'Built-in function '.$functionName,PHP_EOL;
+ $expectedArgumentCount = self::$_controlFunctions[$functionName]['argumentCount'];
+ $functionCall = self::$_controlFunctions[$functionName]['functionCall'];
+ } elseif (isset(self::$_PHPExcelFunctions[$functionName])) {
+//echo 'PHPExcel function '.$functionName,PHP_EOL;
+ $expectedArgumentCount = self::$_PHPExcelFunctions[$functionName]['argumentCount'];
+ $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
+ } else { // did we somehow push a non-function on the stack? this should never happen
+ return $this->_raiseFormulaError("Formula Error: Internal error, non-function on stack");
+ }
+ // Check the argument count
+ $argumentCountError = FALSE;
+ if (is_numeric($expectedArgumentCount)) {
+ if ($expectedArgumentCount < 0) {
+//echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount),PHP_EOL;
+ if ($argumentCount > abs($expectedArgumentCount)) {
+ $argumentCountError = TRUE;
+ $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
+ }
+ } else {
+//echo '$expectedArgumentCount is numeric '.$expectedArgumentCount,PHP_EOL;
+ if ($argumentCount != $expectedArgumentCount) {
+ $argumentCountError = TRUE;
+ $expectedArgumentCountString = $expectedArgumentCount;
+ }
+ }
+ } elseif ($expectedArgumentCount != '*') {
+ $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/',$expectedArgumentCount,$argMatch);
+//print_r($argMatch);
+//echo PHP_EOL;
+ switch ($argMatch[2]) {
+ case '+' :
+ if ($argumentCount < $argMatch[1]) {
+ $argumentCountError = TRUE;
+ $expectedArgumentCountString = $argMatch[1].' or more ';
+ }
+ break;
+ case '-' :
+ if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
+ $argumentCountError = TRUE;
+ $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3];
+ }
+ break;
+ case ',' :
+ if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
+ $argumentCountError = TRUE;
+ $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3];
+ }
+ break;
+ }
+ }
+ if ($argumentCountError) {
+ return $this->_raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, ".$expectedArgumentCountString." expected");
+ }
+ }
+ ++$index;
+
+ } elseif ($opCharacter == ',') { // Is this the separator for function arguments?
+//echo 'Element is a Function argument separator',PHP_EOL;
+ while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
+ if ($o2 === NULL) return $this->_raiseFormulaError("Formula Error: Unexpected ,");
+ else $output[] = $o2; // pop the argument expression stuff and push onto the output
+ }
+ // If we've a comma when we're expecting an operand, then what we actually have is a null operand;
+ // so push a null onto the stack
+ if (($expectingOperand) || (!$expectingOperator)) {
+ $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL);
+ }
+ // make sure there was a function
+ $d = $stack->last(2);
+ if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches))
+ return $this->_raiseFormulaError("Formula Error: Unexpected ,");
+ $d = $stack->pop();
+ $stack->push($d['type'],++$d['value'],$d['reference']); // increment the argument count
+ $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
+ $expectingOperator = FALSE;
+ $expectingOperand = TRUE;
+ ++$index;
+
+ } elseif ($opCharacter == '(' && !$expectingOperator) {
+// echo 'Element is an Opening Bracket
';
+ $stack->push('Brace', '(');
+ ++$index;
+
+ } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
+ $expectingOperator = TRUE;
+ $expectingOperand = FALSE;
+ $val = $match[1];
+ $length = strlen($val);
+// echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function
';
+
+ if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) {
+ $val = preg_replace('/\s/u','',$val);
+// echo 'Element '.$val.' is a Function
';
+ if (isset(self::$_PHPExcelFunctions[strtoupper($matches[1])]) || isset(self::$_controlFunctions[strtoupper($matches[1])])) { // it's a function
+ $stack->push('Function', strtoupper($val));
+ $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index+$length), $amatch);
+ if ($ax) {
+ $stack->push('Operand Count for Function '.strtoupper($val).')', 0);
+ $expectingOperator = TRUE;
+ } else {
+ $stack->push('Operand Count for Function '.strtoupper($val).')', 1);
+ $expectingOperator = FALSE;
+ }
+ $stack->push('Brace', '(');
+ } else { // it's a var w/ implicit multiplication
+ $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => NULL);
+ }
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) {
+// echo 'Element '.$val.' is a Cell reference
';
+ // Watch for this case-change when modifying to allow cell references in different worksheets...
+ // Should only be applied to the actual cell column, not the worksheet name
+
+ // If the last entry on the stack was a : operator, then we have a cell range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp['value'] == ':') {
+ // If we have a worksheet reference, then we're playing with a 3D reference
+ if ($matches[2] == '') {
+ // Otherwise, we 'inherit' the worksheet reference from the start cell reference
+ // The start of the cell range reference should be the last entry in $output
+ $startCellRef = $output[count($output)-1]['value'];
+ preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches);
+ if ($startMatches[2] > '') {
+ $val = $startMatches[2].'!'.$val;
+ }
+ } else {
+ return $this->_raiseFormulaError("3D Range references are not yet supported");
+ }
+ }
+
+ $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val);
+// $expectingOperator = FALSE;
+ } else { // it's a variable, constant, string, number or boolean
+// echo 'Element is a Variable, Constant, String, Number or Boolean
';
+ // If the last entry on the stack was a : operator, then we may have a row or column range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp['value'] == ':') {
+ $startRowColRef = $output[count($output)-1]['value'];
+ $rangeWS1 = '';
+ if (strpos('!',$startRowColRef) !== FALSE) {
+ list($rangeWS1,$startRowColRef) = explode('!',$startRowColRef);
+ }
+ if ($rangeWS1 != '') $rangeWS1 .= '!';
+ $rangeWS2 = $rangeWS1;
+ if (strpos('!',$val) !== FALSE) {
+ list($rangeWS2,$val) = explode('!',$val);
+ }
+ if ($rangeWS2 != '') $rangeWS2 .= '!';
+ if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
+ ($startRowColRef <= 1048576) && ($val <= 1048576)) {
+ // Row range
+ $endRowColRef = ($pCellParent !== NULL) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
+ $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef;
+ $val = $rangeWS2.$endRowColRef.$val;
+ } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
+ (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
+ // Column range
+ $endRowColRef = ($pCellParent !== NULL) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
+ $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1';
+ $val = $rangeWS2.$val.$endRowColRef;
+ }
+ }
+
+ $localeConstant = FALSE;
+ if ($opCharacter == '"') {
+// echo 'Element is a String
';
+ // UnEscape any quotes within the string
+ $val = self::_wrapResult(str_replace('""','"',self::_unwrapResult($val)));
+ } elseif (is_numeric($val)) {
+// echo 'Element is a Number
';
+ if ((strpos($val,'.') !== FALSE) || (stripos($val,'e') !== FALSE) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+// echo 'Casting '.$val.' to float
';
+ $val = (float) $val;
+ } else {
+// echo 'Casting '.$val.' to integer
';
+ $val = (integer) $val;
+ }
+ } elseif (isset(self::$_ExcelConstants[trim(strtoupper($val))])) {
+ $excelConstant = trim(strtoupper($val));
+// echo 'Element '.$excelConstant.' is an Excel Constant
';
+ $val = self::$_ExcelConstants[$excelConstant];
+ } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$_localeBoolean)) !== FALSE) {
+// echo 'Element '.$localeConstant.' is an Excel Constant
';
+ $val = self::$_ExcelConstants[$localeConstant];
+ }
+ $details = array('type' => 'Value', 'value' => $val, 'reference' => NULL);
+ if ($localeConstant) { $details['localeValue'] = $localeConstant; }
+ $output[] = $details;
+ }
+ $index += $length;
+
+ } elseif ($opCharacter == '$') { // absolute row or column range
+ ++$index;
+ } elseif ($opCharacter == ')') { // miscellaneous error checking
+ if ($expectingOperand) {
+ $output[] = array('type' => 'NULL Value', 'value' => self::$_ExcelConstants['NULL'], 'reference' => NULL);
+ $expectingOperand = FALSE;
+ $expectingOperator = TRUE;
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected ')'");
+ }
+ } elseif (isset(self::$_operators[$opCharacter]) && !$expectingOperator) {
+ return $this->_raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
+ } else { // I don't even want to know what you did to get here
+ return $this->_raiseFormulaError("Formula Error: An unexpected error occured");
+ }
+ // Test for end of formula string
+ if ($index == strlen($formula)) {
+ // Did we end with an operator?.
+ // Only valid for the % unary operator
+ if ((isset(self::$_operators[$opCharacter])) && ($opCharacter != '%')) {
+ return $this->_raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
+ } else {
+ break;
+ }
+ }
+ // Ignore white space
+ while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
+ ++$index;
+ }
+ if ($formula{$index} == ' ') {
+ while ($formula{$index} == ' ') {
+ ++$index;
+ }
+ // If we're expecting an operator, but only have a space between the previous and next operands (and both are
+ // Cell References) then we have an INTERSECTION operator
+// echo 'Possible Intersect Operator
';
+ if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) &&
+ ($output[count($output)-1]['type'] == 'Cell Reference')) {
+// echo 'Element is an Intersect Operator
';
+ while($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ isset(self::$_operators[$o2['value']]) &&
+ @(self::$_operatorAssociativity[$opCharacter] ? self::$_operatorPrecedence[$opCharacter] < self::$_operatorPrecedence[$o2['value']] : self::$_operatorPrecedence[$opCharacter] <= self::$_operatorPrecedence[$o2['value']])) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+ $stack->push('Binary Operator','|'); // Put an Intersect Operator on the stack
+ $expectingOperator = FALSE;
+ }
+ }
+ }
+
+ while (($op = $stack->pop()) !== NULL) { // pop everything off the stack and push onto output
+ if ((is_array($op) && $op['value'] == '(') || ($op === '('))
+ return $this->_raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
+ $output[] = $op;
+ }
+ return $output;
+ } // function _parseFormula()
+
+
+ private static function _dataTestReference(&$operandData)
+ {
+ $operand = $operandData['value'];
+ if (($operandData['reference'] === NULL) && (is_array($operand))) {
+ $rKeys = array_keys($operand);
+ $rowKey = array_shift($rKeys);
+ $cKeys = array_keys(array_keys($operand[$rowKey]));
+ $colKey = array_shift($cKeys);
+ if (ctype_upper($colKey)) {
+ $operandData['reference'] = $colKey.$rowKey;
+ }
+ }
+ return $operand;
+ }
+
+ // evaluate postfix notation
+ private function _processTokenStack($tokens, $cellID = NULL, PHPExcel_Cell $pCell = NULL) {
+ if ($tokens == FALSE) return FALSE;
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
+ // so we store the parent cell collection so that we can re-attach it when necessary
+ $pCellWorksheet = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
+ $pCellParent = ($pCell !== NULL) ? $pCell->getParent() : null;
+ $stack = new PHPExcel_Calculation_Token_Stack;
+
+ // Loop through each token in turn
+ foreach ($tokens as $tokenData) {
+// print_r($tokenData);
+// echo '
';
+ $token = $tokenData['value'];
+// echo 'Token is '.$token.'
';
+ // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
+ if (isset(self::$_binaryOperators[$token])) {
+// echo 'Token is a binary operator
';
+ // We must have two operands, error if we don't
+ if (($operand2Data = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ if (($operand1Data = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+
+ $operand1 = self::_dataTestReference($operand1Data);
+ $operand2 = self::_dataTestReference($operand2Data);
+
+ // Log what we're doing
+ if ($token == ':') {
+ $this->_debugLog->writeDebugLog('Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference']));
+ } else {
+ $this->_debugLog->writeDebugLog('Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2));
+ }
+
+ // Process the operation in the appropriate manner
+ switch ($token) {
+ // Comparison (Boolean) Operators
+ case '>' : // Greater than
+ case '<' : // Less than
+ case '>=' : // Greater than or Equal to
+ case '<=' : // Less than or Equal to
+ case '=' : // Equality
+ case '<>' : // Inequality
+ $this->_executeBinaryComparisonOperation($cellID,$operand1,$operand2,$token,$stack);
+ break;
+ // Binary Operators
+ case ':' : // Range
+ $sheet1 = $sheet2 = '';
+ if (strpos($operand1Data['reference'],'!') !== FALSE) {
+ list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']);
+ } else {
+ $sheet1 = ($pCellParent !== NULL) ? $pCellWorksheet->getTitle() : '';
+ }
+ if (strpos($operand2Data['reference'],'!') !== FALSE) {
+ list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']);
+ } else {
+ $sheet2 = $sheet1;
+ }
+ if ($sheet1 == $sheet2) {
+ if ($operand1Data['reference'] === NULL) {
+ if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
+ $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
+ } elseif (trim($operand1Data['reference']) == '') {
+ $operand1Data['reference'] = $pCell->getCoordinate();
+ } else {
+ $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
+ }
+ }
+ if ($operand2Data['reference'] === NULL) {
+ if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
+ $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
+ } elseif (trim($operand2Data['reference']) == '') {
+ $operand2Data['reference'] = $pCell->getCoordinate();
+ } else {
+ $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
+ }
+ }
+
+ $oData = array_merge(explode(':',$operand1Data['reference']),explode(':',$operand2Data['reference']));
+ $oCol = $oRow = array();
+ foreach($oData as $oDatum) {
+ $oCR = PHPExcel_Cell::coordinateFromString($oDatum);
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
+ $oRow[] = $oCR[1];
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ if ($pCellParent !== NULL) {
+ $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($sheet1), FALSE);
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $stack->push('Cell Reference',$cellValue,$cellRef);
+ } else {
+ $stack->push('Error',PHPExcel_Calculation_Functions::REF(),NULL);
+ }
+
+ break;
+ case '+' : // Addition
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'plusEquals',$stack);
+ break;
+ case '-' : // Subtraction
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'minusEquals',$stack);
+ break;
+ case '*' : // Multiplication
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayTimesEquals',$stack);
+ break;
+ case '/' : // Division
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayRightDivide',$stack);
+ break;
+ case '^' : // Exponential
+ $this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'power',$stack);
+ break;
+ case '&' : // Concatenation
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ if (is_bool($operand1)) {
+ $operand1 = ($operand1) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+ }
+ if (is_bool($operand2)) {
+ $operand2 = ($operand2) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
+ }
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ // Ensure that both operands are arrays/matrices
+ self::_checkMatrixOperands($operand1,$operand2,2);
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->concat($operand2);
+ $result = $matrixResult->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ $result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"';
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result));
+ $stack->push('Value',$result);
+ break;
+ case '|' : // Intersect
+ $rowIntersect = array_intersect_key($operand1,$operand2);
+ $cellIntersect = $oCol = $oRow = array();
+ foreach(array_keys($rowIntersect) as $row) {
+ $oRow[] = $row;
+ foreach($rowIntersect[$row] as $col => $data) {
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
+ $cellIntersect[$row] = array_intersect_key($operand1[$row],$operand2[$row]);
+ }
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($cellIntersect));
+ $stack->push('Value',$cellIntersect,$cellRef);
+ break;
+ }
+
+ // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
+ } elseif (($token === '~') || ($token === '%')) {
+// echo 'Token is a unary operator
';
+ if (($arg = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ $arg = $arg['value'];
+ if ($token === '~') {
+// echo 'Token is a negation operator
';
+ $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->_showValue($arg));
+ $multiplier = -1;
+ } else {
+// echo 'Token is a percentile operator
';
+ $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->_showValue($arg));
+ $multiplier = 0.01;
+ }
+ if (is_array($arg)) {
+ self::_checkMatrixOperands($arg,$multiplier,2);
+ try {
+ $matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg);
+ $matrixResult = $matrix1->arrayTimesEquals($multiplier);
+ $result = $matrixResult->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
+ $result = '#VALUE!';
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result));
+ $stack->push('Value',$result);
+ } else {
+ $this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack);
+ }
+
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
+ $cellRef = NULL;
+// echo 'Element '.$token.' is a Cell reference
';
+ if (isset($matches[8])) {
+// echo 'Reference is a Range of cells
';
+ if ($pCell === NULL) {
+// We can't access the range, so return a REF error
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ } else {
+ $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2],"\"'");
+ if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) {
+ // It's a Reference to an external workbook (not currently supported)
+ return $this->_raiseFormulaError('Unable to access External Workbook');
+ }
+ $matches[2] = trim($matches[2],"\"'");
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
+ if ($pCellParent !== NULL) {
+ $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE);
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue));
+// $cellRef = $matches[2].'!'.$cellRef;
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
+ if ($pCellParent !== NULL) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE);
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue));
+ }
+ }
+ } else {
+// echo 'Reference is a single Cell
';
+ if ($pCell === NULL) {
+// We can't access the cell, so return a REF error
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ } else {
+ $cellRef = $matches[6].$matches[7];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2],"\"'");
+ if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) {
+ // It's a Reference to an external workbook (not currently supported)
+ return $this->_raiseFormulaError('Unable to access External Workbook');
+ }
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
+ if ($pCellParent !== NULL) {
+ $cellSheet = $this->_workbook->getSheetByName($matches[2]);
+ if ($cellSheet && $cellSheet->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE);
+ $pCell->attach($pCellParent);
+ } else {
+ $cellValue = NULL;
+ }
+ } else {
+ return $this->_raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue));
+// $cellRef = $matches[2].'!'.$cellRef;
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
+ if ($pCellParent->isDataSet($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE);
+ $pCell->attach($pCellParent);
+ } else {
+ $cellValue = NULL;
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue));
+ }
+ }
+ }
+ $stack->push('Value',$cellValue,$cellRef);
+
+ // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
+// echo 'Token is a function
';
+ $functionName = $matches[1];
+ $argCount = $stack->pop();
+ $argCount = $argCount['value'];
+ if ($functionName != 'MKMATRIX') {
+ $this->_debugLog->writeDebugLog('Evaluating Function ', self::_localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
+ }
+ if ((isset(self::$_PHPExcelFunctions[$functionName])) || (isset(self::$_controlFunctions[$functionName]))) { // function
+ if (isset(self::$_PHPExcelFunctions[$functionName])) {
+ $functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$_PHPExcelFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$_PHPExcelFunctions[$functionName]['passCellReference']);
+ } elseif (isset(self::$_controlFunctions[$functionName])) {
+ $functionCall = self::$_controlFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$_controlFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$_controlFunctions[$functionName]['passCellReference']);
+ }
+ // get the arguments for this function
+// echo 'Function '.$functionName.' expects '.$argCount.' arguments
';
+ $args = $argArrayVals = array();
+ for ($i = 0; $i < $argCount; ++$i) {
+ $arg = $stack->pop();
+ $a = $argCount - $i - 1;
+ if (($passByReference) &&
+ (isset(self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
+ (self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) {
+ if ($arg['reference'] === NULL) {
+ $args[] = $cellID;
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($cellID); }
+ } else {
+ $args[] = $arg['reference'];
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($arg['reference']); }
+ }
+ } else {
+ $args[] = self::_unwrapResult($arg['value']);
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($arg['value']); }
+ }
+ }
+ // Reverse the order of the arguments
+ krsort($args);
+ if (($passByReference) && ($argCount == 0)) {
+ $args[] = $cellID;
+ $argArrayVals[] = $this->_showValue($cellID);
+ }
+// echo 'Arguments are: ';
+// print_r($args);
+// echo '
';
+ if ($functionName != 'MKMATRIX') {
+ if ($this->_debugLog->getWriteDebugLog()) {
+ krsort($argArrayVals);
+ $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )');
+ }
+ }
+ // Process each argument in turn, building the return value as an array
+// if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
+// $operand1 = $args[1];
+// $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->_showValue($operand1));
+// $result = array();
+// $row = 0;
+// foreach($operand1 as $args) {
+// if (is_array($args)) {
+// foreach($args as $arg) {
+// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )');
+// $r = call_user_func_array($functionCall,$arg);
+// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
+// $result[$row][] = $r;
+// }
+// ++$row;
+// } else {
+// $this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )');
+// $r = call_user_func_array($functionCall,$args);
+// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
+// $result[] = $r;
+// }
+// }
+// } else {
+ // Process the argument with the appropriate function call
+ if ($passCellReference) {
+ $args[] = $pCell;
+ }
+ if (strpos($functionCall,'::') !== FALSE) {
+ $result = call_user_func_array(explode('::',$functionCall),$args);
+ } else {
+ foreach($args as &$arg) {
+ $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
+ }
+ unset($arg);
+ $result = call_user_func_array($functionCall,$args);
+ }
+// }
+ if ($functionName != 'MKMATRIX') {
+ $this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result));
+ }
+ $stack->push('Value',self::_wrapResult($result));
+ }
+
+ } else {
+ // if the token is a number, boolean, string or an Excel error, push it onto the stack
+ if (isset(self::$_ExcelConstants[strtoupper($token)])) {
+ $excelConstant = strtoupper($token);
+// echo 'Token is a PHPExcel constant: '.$excelConstant.'
';
+ $stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]);
+ $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant]));
+ } elseif ((is_numeric($token)) || ($token === NULL) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
+// echo 'Token is a number, boolean, string, null or an Excel error
';
+ $stack->push('Value',$token);
+ // if the token is a named range, push the named range name onto the stack
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
+// echo 'Token is a named range
';
+ $namedRange = $matches[6];
+// echo 'Named Range is '.$namedRange.'
';
+ $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
+ $cellValue = $this->extractNamedRange($namedRange, ((NULL !== $pCell) ? $pCellWorksheet : NULL), FALSE);
+ $pCell->attach($pCellParent);
+ $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue));
+ $stack->push('Named Range',$cellValue,$namedRange);
+ } else {
+ return $this->_raiseFormulaError("undefined variable '$token'");
+ }
+ }
+ }
+ // when we're out of tokens, the stack should have a single element, the final result
+ if ($stack->count() != 1) return $this->_raiseFormulaError("internal error");
+ $output = $stack->pop();
+ $output = $output['value'];
+
+// if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+// return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
+// }
+ return $output;
+ } // function _processTokenStack()
+
+
+ private function _validateBinaryOperand($cellID, &$operand, &$stack) {
+ if (is_array($operand)) {
+ if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
+ do {
+ $operand = array_pop($operand);
+ } while (is_array($operand));
+ }
+ }
+ // Numbers, matrices and booleans can pass straight through, as they're already valid
+ if (is_string($operand)) {
+ // We only need special validations for the operand if it is a string
+ // Start by stripping off the quotation marks we use to identify true excel string values internally
+ if ($operand > '' && $operand{0} == '"') { $operand = self::_unwrapResult($operand); }
+ // If the string is a numeric value, we treat it as a numeric, so no further testing
+ if (!is_numeric($operand)) {
+ // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
+ if ($operand > '' && $operand{0} == '#') {
+ $stack->push('Value', $operand);
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($operand));
+ return FALSE;
+ } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) {
+ // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
+ $stack->push('Value', '#VALUE!');
+ $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->_showTypeDetails('#VALUE!'));
+ return FALSE;
+ }
+ }
+ }
+
+ // return a true if the value of the operand is one that we can use in normal binary operations
+ return TRUE;
+ } // function _validateBinaryOperand()
+
+
+ private function _executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays=FALSE) {
+ // If we're dealing with matrix operations, we want a matrix result
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ $result = array();
+ if ((is_array($operand1)) && (!is_array($operand2))) {
+ foreach($operand1 as $x => $operandData) {
+ $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2));
+ $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2,$operation,$stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } elseif ((!is_array($operand1)) && (is_array($operand2))) {
+ foreach($operand2 as $x => $operandData) {
+ $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operand1), ' ', $operation, ' ', $this->_showValue($operandData));
+ $this->_executeBinaryComparisonOperation($cellID,$operand1,$operandData,$operation,$stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } else {
+ if (!$recursingArrays) { self::_checkMatrixOperands($operand1,$operand2,2); }
+ foreach($operand1 as $x => $operandData) {
+ $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->_showValue($operandData), ' ', $operation, ' ', $this->_showValue($operand2[$x]));
+ $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,TRUE);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Array',$result);
+ return TRUE;
+ }
+
+ // Simple validate the two operands if they are string values
+ if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') { $operand1 = self::_unwrapResult($operand1); }
+ if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') { $operand2 = self::_unwrapResult($operand2); }
+
+ // Use case insensitive comparaison if not OpenOffice mode
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE)
+ {
+ if (is_string($operand1)) {
+ $operand1 = strtoupper($operand1);
+ }
+
+ if (is_string($operand2)) {
+ $operand2 = strtoupper($operand2);
+ }
+ }
+
+ $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE;
+
+ // execute the necessary operation
+ switch ($operation) {
+ // Greater than
+ case '>':
+ if ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
+ } else {
+ $result = ($operand1 > $operand2);
+ }
+ break;
+ // Less than
+ case '<':
+ if ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
+ } else {
+ $result = ($operand1 < $operand2);
+ }
+ break;
+ // Equality
+ case '=':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = (abs($operand1 - $operand2) < $this->delta);
+ } else {
+ $result = strcmp($operand1, $operand2) == 0;
+ }
+ break;
+ // Greater than or equal
+ case '>=':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
+ } elseif ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
+ } else {
+ $result = strcmp($operand1, $operand2) >= 0;
+ }
+ break;
+ // Less than or equal
+ case '<=':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
+ } elseif ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
+ } else {
+ $result = strcmp($operand1, $operand2) <= 0;
+ }
+ break;
+ // Inequality
+ case '<>':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = (abs($operand1 - $operand2) > 1E-14);
+ } else {
+ $result = strcmp($operand1, $operand2) != 0;
+ }
+ break;
+ }
+
+ // Log the result details
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value',$result);
+ return true;
+ }
+
+ /**
+ * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters
+ * @param string $str1 First string value for the comparison
+ * @param string $str2 Second string value for the comparison
+ * @return integer
+ */
+ private function strcmpLowercaseFirst($str1, $str2)
+ {
+ $inversedStr1 = PHPExcel_Shared_String::StrCaseReverse($str1);
+ $inversedStr2 = PHPExcel_Shared_String::StrCaseReverse($str2);
+
+ return strcmp($inversedStr1, $inversedStr2);
+ }
+
+ private function _executeNumericBinaryOperation($cellID,$operand1,$operand2,$operation,$matrixFunction,&$stack) {
+ // Validate the two operands
+ if (!$this->_validateBinaryOperand($cellID,$operand1,$stack)) return FALSE;
+ if (!$this->_validateBinaryOperand($cellID,$operand2,$stack)) return FALSE;
+
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ // Ensure that both operands are arrays/matrices of the same size
+ self::_checkMatrixOperands($operand1, $operand2, 2);
+
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->$matrixFunction($operand2);
+ $result = $matrixResult->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ if ((PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) &&
+ ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1)>0) ||
+ (is_string($operand2) && !is_numeric($operand2) && strlen($operand2)>0))) {
+ $result = PHPExcel_Calculation_Functions::VALUE();
+ } else {
+ // If we're dealing with non-matrix operations, execute the necessary operation
+ switch ($operation) {
+ // Addition
+ case '+':
+ $result = $operand1 + $operand2;
+ break;
+ // Subtraction
+ case '-':
+ $result = $operand1 - $operand2;
+ break;
+ // Multiplication
+ case '*':
+ $result = $operand1 * $operand2;
+ break;
+ // Division
+ case '/':
+ if ($operand2 == 0) {
+ // Trap for Divide by Zero error
+ $stack->push('Value','#DIV/0!');
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails('#DIV/0!'));
+ return FALSE;
+ } else {
+ $result = $operand1 / $operand2;
+ }
+ break;
+ // Power
+ case '^':
+ $result = pow($operand1, $operand2);
+ break;
+ }
+ }
+ }
+
+ // Log the result details
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value',$result);
+ return TRUE;
+ } // function _executeNumericBinaryOperation()
+
+
+ // trigger an error, but nicely, if need be
+ protected function _raiseFormulaError($errorMessage) {
+ $this->formulaError = $errorMessage;
+ $this->_cyclicReferenceStack->clear();
+ if (!$this->suppressFormulaErrors) throw new PHPExcel_Calculation_Exception($errorMessage);
+ trigger_error($errorMessage, E_USER_ERROR);
+ } // function _raiseFormulaError()
+
+
+ /**
+ * Extract range values
+ *
+ * @param string &$pRange String based range representation
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @param boolean $resetLog Flag indicating whether calculation log should be reset or not
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = NULL, $resetLog = TRUE) {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractCellRange('.$pRange.')',PHP_EOL;
+ if ($pSheet !== NULL) {
+ $pSheetName = $pSheet->getTitle();
+// echo 'Passed sheet name is '.$pSheetName.PHP_EOL;
+// echo 'Range reference is '.$pRange.PHP_EOL;
+ if (strpos ($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference',PHP_EOL;
+ list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+// echo 'New sheet name is '.$pSheetName,PHP_EOL;
+// echo 'Adjusted Range reference is '.$pRange,PHP_EOL;
+ $pSheet = $this->_workbook->getSheetByName($pSheetName);
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+ $pRange = $pSheetName.'!'.$pRange;
+ if (!isset($aReferences[1])) {
+ // Single cell in range
+ sscanf($aReferences[0],'%[A-Z]%d', $currentCol, $currentRow);
+ $cellValue = NULL;
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ } else {
+ // Extract cell data for all cells in the range
+ foreach ($aReferences as $reference) {
+ // Extract range
+ sscanf($reference,'%[A-Z]%d', $currentCol, $currentRow);
+ $cellValue = NULL;
+ if ($pSheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ }
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function extractCellRange()
+
+
+ /**
+ * Extract range values
+ *
+ * @param string &$pRange String based range representation
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @param boolean $resetLog Flag indicating whether calculation log should be reset or not
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = NULL, $resetLog = TRUE) {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractNamedRange('.$pRange.')
';
+ if ($pSheet !== NULL) {
+ $pSheetName = $pSheet->getTitle();
+// echo 'Current sheet name is '.$pSheetName.'
';
+// echo 'Range reference is '.$pRange.'
';
+ if (strpos ($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference',PHP_EOL;
+ list($pSheetName,$pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+// echo 'New sheet name is '.$pSheetName,PHP_EOL;
+// echo 'Adjusted Range reference is '.$pRange,PHP_EOL;
+ $pSheet = $this->_workbook->getSheetByName($pSheetName);
+ }
+
+ // Named range?
+ $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet);
+ if ($namedRange !== NULL) {
+ $pSheet = $namedRange->getWorksheet();
+// echo 'Named Range '.$pRange.' (';
+ $pRange = $namedRange->getRange();
+ $splitRange = PHPExcel_Cell::splitRange($pRange);
+ // Convert row and column references
+ if (ctype_alpha($splitRange[0][0])) {
+ $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
+ } elseif(ctype_digit($splitRange[0][0])) {
+ $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
+ }
+// echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'
';
+
+// if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) {
+// if (!$namedRange->getLocalOnly()) {
+// $pSheet = $namedRange->getWorksheet();
+// } else {
+// return $returnValue;
+// }
+// }
+ } else {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+// var_dump($aReferences);
+ if (!isset($aReferences[1])) {
+ // Single cell (or single column or row) in range
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
+ $cellValue = NULL;
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ } else {
+ // Extract cell data for all cells in the range
+ foreach ($aReferences as $reference) {
+ // Extract range
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference);
+// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'
';
+ $cellValue = NULL;
+ if ($pSheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ }
+ }
+// print_r($returnValue);
+// echo '
';
+ }
+
+ // Return
+ return $returnValue;
+ } // function extractNamedRange()
+
+
+ /**
+ * Is a specific function implemented?
+ *
+ * @param string $pFunction Function Name
+ * @return boolean
+ */
+ public function isImplemented($pFunction = '') {
+ $pFunction = strtoupper ($pFunction);
+ if (isset(self::$_PHPExcelFunctions[$pFunction])) {
+ return (self::$_PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY');
+ } else {
+ return FALSE;
+ }
+ } // function isImplemented()
+
+
+ /**
+ * Get a list of all implemented functions as an array of function objects
+ *
+ * @return array of PHPExcel_Calculation_Function
+ */
+ public function listFunctions() {
+ // Return value
+ $returnValue = array();
+ // Loop functions
+ foreach(self::$_PHPExcelFunctions as $functionName => $function) {
+ if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
+ $returnValue[$functionName] = new PHPExcel_Calculation_Function($function['category'],
+ $functionName,
+ $function['functionCall']
+ );
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function listFunctions()
+
+
+ /**
+ * Get a list of all Excel function names
+ *
+ * @return array
+ */
+ public function listAllFunctionNames() {
+ return array_keys(self::$_PHPExcelFunctions);
+ } // function listAllFunctionNames()
+
+ /**
+ * Get a list of implemented Excel function names
+ *
+ * @return array
+ */
+ public function listFunctionNames() {
+ // Return value
+ $returnValue = array();
+ // Loop functions
+ foreach(self::$_PHPExcelFunctions as $functionName => $function) {
+ if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
+ $returnValue[] = $functionName;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function listFunctionNames()
+
+} // class PHPExcel_Calculation
+
diff --git a/includes/PHPExcel/Classes/PHPExcel/Cell.php b/includes/PHPExcel/Classes/PHPExcel/Cell.php
new file mode 100644
index 0000000..2d5f910
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Cell.php
@@ -0,0 +1,1022 @@
+_parent->updateCacheData($this);
+
+ return $this;
+ }
+
+ public function detach() {
+ $this->_parent = NULL;
+ }
+
+ public function attach(PHPExcel_CachedObjectStorage_CacheBase $parent) {
+ $this->_parent = $parent;
+ }
+
+
+ /**
+ * Create a new Cell
+ *
+ * @param mixed $pValue
+ * @param string $pDataType
+ * @param PHPExcel_Worksheet $pSheet
+ * @throws PHPExcel_Exception
+ */
+ public function __construct($pValue = NULL, $pDataType = NULL, PHPExcel_Worksheet $pSheet = NULL)
+ {
+ // Initialise cell value
+ $this->_value = $pValue;
+
+ // Set worksheet cache
+ $this->_parent = $pSheet->getCellCacheController();
+
+ // Set datatype?
+ if ($pDataType !== NULL) {
+ if ($pDataType == PHPExcel_Cell_DataType::TYPE_STRING2)
+ $pDataType = PHPExcel_Cell_DataType::TYPE_STRING;
+ $this->_dataType = $pDataType;
+ } elseif (!self::getValueBinder()->bindValue($this, $pValue)) {
+ throw new PHPExcel_Exception("Value could not be bound to cell.");
+ }
+ }
+
+ /**
+ * Get cell coordinate column
+ *
+ * @return string
+ */
+ public function getColumn()
+ {
+ return $this->_parent->getCurrentColumn();
+ }
+
+ /**
+ * Get cell coordinate row
+ *
+ * @return int
+ */
+ public function getRow()
+ {
+ return $this->_parent->getCurrentRow();
+ }
+
+ /**
+ * Get cell coordinate
+ *
+ * @return string
+ */
+ public function getCoordinate()
+ {
+ return $this->_parent->getCurrentAddress();
+ }
+
+ /**
+ * Get cell value
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * Get cell value with formatting
+ *
+ * @return string
+ */
+ public function getFormattedValue()
+ {
+ return (string) PHPExcel_Style_NumberFormat::toFormattedString(
+ $this->getCalculatedValue(),
+ $this->getStyle()
+ ->getNumberFormat()->getFormatCode()
+ );
+ }
+
+ /**
+ * Set cell value
+ *
+ * Sets the value for a cell, automatically determining the datatype using the value binder
+ *
+ * @param mixed $pValue Value
+ * @return PHPExcel_Cell
+ * @throws PHPExcel_Exception
+ */
+ public function setValue($pValue = NULL)
+ {
+ if (!self::getValueBinder()->bindValue($this, $pValue)) {
+ throw new PHPExcel_Exception("Value could not be bound to cell.");
+ }
+ return $this;
+ }
+
+ /**
+ * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder)
+ *
+ * @param mixed $pValue Value
+ * @param string $pDataType Explicit data type
+ * @return PHPExcel_Cell
+ * @throws PHPExcel_Exception
+ */
+ public function setValueExplicit($pValue = NULL, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING)
+ {
+ // set the value according to data type
+ switch ($pDataType) {
+ case PHPExcel_Cell_DataType::TYPE_NULL:
+ $this->_value = $pValue;
+ break;
+ case PHPExcel_Cell_DataType::TYPE_STRING2:
+ $pDataType = PHPExcel_Cell_DataType::TYPE_STRING;
+ case PHPExcel_Cell_DataType::TYPE_STRING:
+ case PHPExcel_Cell_DataType::TYPE_INLINE:
+ $this->_value = PHPExcel_Cell_DataType::checkString($pValue);
+ break;
+ case PHPExcel_Cell_DataType::TYPE_NUMERIC:
+ $this->_value = (float)$pValue;
+ break;
+ case PHPExcel_Cell_DataType::TYPE_FORMULA:
+ $this->_value = (string)$pValue;
+ break;
+ case PHPExcel_Cell_DataType::TYPE_BOOL:
+ $this->_value = (bool)$pValue;
+ break;
+ case PHPExcel_Cell_DataType::TYPE_ERROR:
+ $this->_value = PHPExcel_Cell_DataType::checkErrorCode($pValue);
+ break;
+ default:
+ throw new PHPExcel_Exception('Invalid datatype: ' . $pDataType);
+ break;
+ }
+
+ // set the datatype
+ $this->_dataType = $pDataType;
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Get calculated cell value
+ *
+ * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling
+ *
+ * @param boolean $resetLog Whether the calculation engine logger should be reset or not
+ * @return mixed
+ * @throws PHPExcel_Exception
+ */
+ public function getCalculatedValue($resetLog = TRUE)
+ {
+//echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().PHP_EOL;
+ if ($this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ try {
+//echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value'.PHP_EOL;
+ $result = PHPExcel_Calculation::getInstance(
+ $this->getWorksheet()->getParent()
+ )->calculateCellValue($this,$resetLog);
+//echo $this->getCoordinate().' calculation result is '.$result.PHP_EOL;
+ // We don't yet handle array returns
+ if (is_array($result)) {
+ while (is_array($result)) {
+ $result = array_pop($result);
+ }
+ }
+ } catch ( PHPExcel_Exception $ex ) {
+ if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->_calculatedValue !== NULL)) {
+//echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL;
+ return $this->_calculatedValue; // Fallback for calculations referencing external files.
+ }
+//echo 'Calculation Exception: '.$ex->getMessage().PHP_EOL;
+ $result = '#N/A';
+ throw new PHPExcel_Calculation_Exception(
+ $this->getWorksheet()->getTitle().'!'.$this->getCoordinate().' -> '.$ex->getMessage()
+ );
+ }
+
+ if ($result === '#Not Yet Implemented') {
+//echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().PHP_EOL;
+ return $this->_calculatedValue; // Fallback if calculation engine does not support the formula.
+ }
+//echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().PHP_EOL;
+ return $result;
+ } elseif($this->_value instanceof PHPExcel_RichText) {
+// echo 'Cell value for '.$this->getCoordinate().' is rich text: Returning data value of '.$this->_value.'
';
+ return $this->_value->getPlainText();
+ }
+// echo 'Cell value for '.$this->getCoordinate().' is not a formula: Returning data value of '.$this->_value.'
';
+ return $this->_value;
+ }
+
+ /**
+ * Set old calculated value (cached)
+ *
+ * @param mixed $pValue Value
+ * @return PHPExcel_Cell
+ */
+ public function setCalculatedValue($pValue = NULL)
+ {
+ if ($pValue !== NULL) {
+ $this->_calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue;
+ }
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Get old calculated value (cached)
+ * This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
+ * create the original spreadsheet file.
+ * Note that this value is not guaranteed to refelect the actual calculated value because it is
+ * possible that auto-calculation was disabled in the original spreadsheet, and underlying data
+ * values used by the formula have changed since it was last calculated.
+ *
+ * @return mixed
+ */
+ public function getOldCalculatedValue()
+ {
+ return $this->_calculatedValue;
+ }
+
+ /**
+ * Get cell data type
+ *
+ * @return string
+ */
+ public function getDataType()
+ {
+ return $this->_dataType;
+ }
+
+ /**
+ * Set cell data type
+ *
+ * @param string $pDataType
+ * @return PHPExcel_Cell
+ */
+ public function setDataType($pDataType = PHPExcel_Cell_DataType::TYPE_STRING)
+ {
+ if ($pDataType == PHPExcel_Cell_DataType::TYPE_STRING2)
+ $pDataType = PHPExcel_Cell_DataType::TYPE_STRING;
+
+ $this->_dataType = $pDataType;
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Identify if the cell contains a formula
+ *
+ * @return boolean
+ */
+ public function isFormula()
+ {
+ return $this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA;
+ }
+
+ /**
+ * Does this cell contain Data validation rules?
+ *
+ * @return boolean
+ * @throws PHPExcel_Exception
+ */
+ public function hasDataValidation()
+ {
+ if (!isset($this->_parent)) {
+ throw new PHPExcel_Exception('Cannot check for data validation when cell is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Data validation rules
+ *
+ * @return PHPExcel_Cell_DataValidation
+ * @throws PHPExcel_Exception
+ */
+ public function getDataValidation()
+ {
+ if (!isset($this->_parent)) {
+ throw new PHPExcel_Exception('Cannot get data validation for cell that is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->getDataValidation($this->getCoordinate());
+ }
+
+ /**
+ * Set Data validation rules
+ *
+ * @param PHPExcel_Cell_DataValidation $pDataValidation
+ * @return PHPExcel_Cell
+ * @throws PHPExcel_Exception
+ */
+ public function setDataValidation(PHPExcel_Cell_DataValidation $pDataValidation = NULL)
+ {
+ if (!isset($this->_parent)) {
+ throw new PHPExcel_Exception('Cannot set data validation for cell that is not bound to a worksheet');
+ }
+
+ $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation);
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Does this cell contain a Hyperlink?
+ *
+ * @return boolean
+ * @throws PHPExcel_Exception
+ */
+ public function hasHyperlink()
+ {
+ if (!isset($this->_parent)) {
+ throw new PHPExcel_Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Hyperlink
+ *
+ * @return PHPExcel_Cell_Hyperlink
+ * @throws PHPExcel_Exception
+ */
+ public function getHyperlink()
+ {
+ if (!isset($this->_parent)) {
+ throw new PHPExcel_Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->getHyperlink($this->getCoordinate());
+ }
+
+ /**
+ * Set Hyperlink
+ *
+ * @param PHPExcel_Cell_Hyperlink $pHyperlink
+ * @return PHPExcel_Cell
+ * @throws PHPExcel_Exception
+ */
+ public function setHyperlink(PHPExcel_Cell_Hyperlink $pHyperlink = NULL)
+ {
+ if (!isset($this->_parent)) {
+ throw new PHPExcel_Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
+ }
+
+ $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink);
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Get parent worksheet
+ *
+ * @return PHPExcel_CachedObjectStorage_CacheBase
+ */
+ public function getParent() {
+ return $this->_parent;
+ }
+
+ /**
+ * Get parent worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getWorksheet() {
+ return $this->_parent->getParent();
+ }
+
+ /**
+ * Is this cell in a merge range
+ *
+ * @return boolean
+ */
+ public function isInMergeRange() {
+ return (boolean) $this->getMergeRange();
+ }
+
+ /**
+ * Is this cell the master (top left cell) in a merge range (that holds the actual data value)
+ *
+ * @return boolean
+ */
+ public function isMergeRangeValueCell() {
+ if ($mergeRange = $this->getMergeRange()) {
+ $mergeRange = PHPExcel_Cell::splitRange($mergeRange);
+ list($startCell) = $mergeRange[0];
+ if ($this->getCoordinate() === $startCell) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If this cell is in a merge range, then return the range
+ *
+ * @return string
+ */
+ public function getMergeRange() {
+ foreach($this->getWorksheet()->getMergeCells() as $mergeRange) {
+ if ($this->isInRange($mergeRange)) {
+ return $mergeRange;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get cell style
+ *
+ * @return PHPExcel_Style
+ */
+ public function getStyle()
+ {
+ return $this->getWorksheet()->getStyle($this->getCoordinate());
+ }
+
+ /**
+ * Re-bind parent
+ *
+ * @param PHPExcel_Worksheet $parent
+ * @return PHPExcel_Cell
+ */
+ public function rebindParent(PHPExcel_Worksheet $parent) {
+ $this->_parent = $parent->getCellCacheController();
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * Is cell in a specific range?
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return boolean
+ */
+ public function isInRange($pRange = 'A1:A1')
+ {
+ list($rangeStart,$rangeEnd) = self::rangeBoundaries($pRange);
+
+ // Translate properties
+ $myColumn = self::columnIndexFromString($this->getColumn());
+ $myRow = $this->getRow();
+
+ // Verify if cell is in range
+ return (($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
+ ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow)
+ );
+ }
+
+ /**
+ * Coordinate from string
+ *
+ * @param string $pCoordinateString
+ * @return array Array containing column and row (indexes 0 and 1)
+ * @throws PHPExcel_Exception
+ */
+ public static function coordinateFromString($pCoordinateString = 'A1')
+ {
+ if (preg_match("/^([$]?[A-Z]{1,3})([$]?\d{1,7})$/", $pCoordinateString, $matches)) {
+ return array($matches[1],$matches[2]);
+ } elseif ((strpos($pCoordinateString,':') !== FALSE) || (strpos($pCoordinateString,',') !== FALSE)) {
+ throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells');
+ } elseif ($pCoordinateString == '') {
+ throw new PHPExcel_Exception('Cell coordinate can not be zero-length string');
+ }
+
+ throw new PHPExcel_Exception('Invalid cell coordinate '.$pCoordinateString);
+ }
+
+ /**
+ * Make string row, column or cell coordinate absolute
+ *
+ * @param string $pCoordinateString e.g. 'A' or '1' or 'A1'
+ * Note that this value can be a row or column reference as well as a cell reference
+ * @return string Absolute coordinate e.g. '$A' or '$1' or '$A$1'
+ * @throws PHPExcel_Exception
+ */
+ public static function absoluteReference($pCoordinateString = 'A1')
+ {
+ if (strpos($pCoordinateString,':') === FALSE && strpos($pCoordinateString,',') === FALSE) {
+ // Split out any worksheet name from the reference
+ $worksheet = '';
+ $cellAddress = explode('!',$pCoordinateString);
+ if (count($cellAddress) > 1) {
+ list($worksheet,$pCoordinateString) = $cellAddress;
+ }
+ if ($worksheet > '') $worksheet .= '!';
+
+ // Create absolute coordinate
+ if (ctype_digit($pCoordinateString)) {
+ return $worksheet . '$' . $pCoordinateString;
+ } elseif (ctype_alpha($pCoordinateString)) {
+ return $worksheet . '$' . strtoupper($pCoordinateString);
+ }
+ return $worksheet . self::absoluteCoordinate($pCoordinateString);
+ }
+
+ throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells');
+ }
+
+ /**
+ * Make string coordinate absolute
+ *
+ * @param string $pCoordinateString e.g. 'A1'
+ * @return string Absolute coordinate e.g. '$A$1'
+ * @throws PHPExcel_Exception
+ */
+ public static function absoluteCoordinate($pCoordinateString = 'A1')
+ {
+ if (strpos($pCoordinateString,':') === FALSE && strpos($pCoordinateString,',') === FALSE) {
+ // Split out any worksheet name from the coordinate
+ $worksheet = '';
+ $cellAddress = explode('!',$pCoordinateString);
+ if (count($cellAddress) > 1) {
+ list($worksheet,$pCoordinateString) = $cellAddress;
+ }
+ if ($worksheet > '') $worksheet .= '!';
+
+ // Create absolute coordinate
+ list($column, $row) = self::coordinateFromString($pCoordinateString);
+ $column = ltrim($column,'$');
+ $row = ltrim($row,'$');
+ return $worksheet . '$' . $column . '$' . $row;
+ }
+
+ throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells');
+ }
+
+ /**
+ * Split range into coordinate strings
+ *
+ * @param string $pRange e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4'
+ * @return array Array containg one or more arrays containing one or two coordinate strings
+ * e.g. array('B4','D9') or array(array('B4','D9'),array('H2','O11'))
+ * or array('B4')
+ */
+ public static function splitRange($pRange = 'A1:A1')
+ {
+ // Ensure $pRange is a valid range
+ if(empty($pRange)) {
+ $pRange = self::DEFAULT_RANGE;
+ }
+
+ $exploded = explode(',', $pRange);
+ $counter = count($exploded);
+ for ($i = 0; $i < $counter; ++$i) {
+ $exploded[$i] = explode(':', $exploded[$i]);
+ }
+ return $exploded;
+ }
+
+ /**
+ * Build range from coordinate strings
+ *
+ * @param array $pRange Array containg one or more arrays containing one or two coordinate strings
+ * @return string String representation of $pRange
+ * @throws PHPExcel_Exception
+ */
+ public static function buildRange($pRange)
+ {
+ // Verify range
+ if (!is_array($pRange) || empty($pRange) || !is_array($pRange[0])) {
+ throw new PHPExcel_Exception('Range does not contain any information');
+ }
+
+ // Build range
+ $imploded = array();
+ $counter = count($pRange);
+ for ($i = 0; $i < $counter; ++$i) {
+ $pRange[$i] = implode(':', $pRange[$i]);
+ }
+ $imploded = implode(',', $pRange);
+
+ return $imploded;
+ }
+
+ /**
+ * Calculate range boundaries
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return array Range coordinates array(Start Cell, End Cell)
+ * where Start Cell and End Cell are arrays (Column Number, Row Number)
+ */
+ public static function rangeBoundaries($pRange = 'A1:A1')
+ {
+ // Ensure $pRange is a valid range
+ if(empty($pRange)) {
+ $pRange = self::DEFAULT_RANGE;
+ }
+
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Extract range
+ if (strpos($pRange, ':') === FALSE) {
+ $rangeA = $rangeB = $pRange;
+ } else {
+ list($rangeA, $rangeB) = explode(':', $pRange);
+ }
+
+ // Calculate range outer borders
+ $rangeStart = self::coordinateFromString($rangeA);
+ $rangeEnd = self::coordinateFromString($rangeB);
+
+ // Translate column into index
+ $rangeStart[0] = self::columnIndexFromString($rangeStart[0]);
+ $rangeEnd[0] = self::columnIndexFromString($rangeEnd[0]);
+
+ return array($rangeStart, $rangeEnd);
+ }
+
+ /**
+ * Calculate range dimension
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return array Range dimension (width, height)
+ */
+ public static function rangeDimension($pRange = 'A1:A1')
+ {
+ // Calculate range outer borders
+ list($rangeStart,$rangeEnd) = self::rangeBoundaries($pRange);
+
+ return array( ($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1) );
+ }
+
+ /**
+ * Calculate range boundaries
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return array Range coordinates array(Start Cell, End Cell)
+ * where Start Cell and End Cell are arrays (Column ID, Row Number)
+ */
+ public static function getRangeBoundaries($pRange = 'A1:A1')
+ {
+ // Ensure $pRange is a valid range
+ if(empty($pRange)) {
+ $pRange = self::DEFAULT_RANGE;
+ }
+
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Extract range
+ if (strpos($pRange, ':') === FALSE) {
+ $rangeA = $rangeB = $pRange;
+ } else {
+ list($rangeA, $rangeB) = explode(':', $pRange);
+ }
+
+ return array( self::coordinateFromString($rangeA), self::coordinateFromString($rangeB));
+ }
+
+ /**
+ * Column index from string
+ *
+ * @param string $pString
+ * @return int Column index (base 1 !!!)
+ */
+ public static function columnIndexFromString($pString = 'A')
+ {
+ // Using a lookup cache adds a slight memory overhead, but boosts speed
+ // caching using a static within the method is faster than a class static,
+ // though it's additional memory overhead
+ static $_indexCache = array();
+
+ if (isset($_indexCache[$pString]))
+ return $_indexCache[$pString];
+
+ // It's surprising how costly the strtoupper() and ord() calls actually are, so we use a lookup array rather than use ord()
+ // and make it case insensitive to get rid of the strtoupper() as well. Because it's a static, there's no significant
+ // memory overhead either
+ static $_columnLookup = array(
+ '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,
+ '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,
+ '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,
+ '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
+ );
+
+ // We also use the language construct isset() rather than the more costly strlen() function to match the length of $pString
+ // for improved performance
+ if (isset($pString{0})) {
+ if (!isset($pString{1})) {
+ $_indexCache[$pString] = $_columnLookup[$pString];
+ return $_indexCache[$pString];
+ } elseif(!isset($pString{2})) {
+ $_indexCache[$pString] = $_columnLookup[$pString{0}] * 26 + $_columnLookup[$pString{1}];
+ return $_indexCache[$pString];
+ } elseif(!isset($pString{3})) {
+ $_indexCache[$pString] = $_columnLookup[$pString{0}] * 676 + $_columnLookup[$pString{1}] * 26 + $_columnLookup[$pString{2}];
+ return $_indexCache[$pString];
+ }
+ }
+ throw new PHPExcel_Exception("Column string index can not be " . ((isset($pString{0})) ? "longer than 3 characters" : "empty"));
+ }
+
+ /**
+ * String from columnindex
+ *
+ * @param int $pColumnIndex Column index (base 0 !!!)
+ * @return string
+ */
+ public static function stringFromColumnIndex($pColumnIndex = 0)
+ {
+ // Using a lookup cache adds a slight memory overhead, but boosts speed
+ // caching using a static within the method is faster than a class static,
+ // though it's additional memory overhead
+ static $_indexCache = array();
+
+ if (!isset($_indexCache[$pColumnIndex])) {
+ // Determine column string
+ if ($pColumnIndex < 26) {
+ $_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex);
+ } elseif ($pColumnIndex < 702) {
+ $_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) .
+ chr(65 + $pColumnIndex % 26);
+ } else {
+ $_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) .
+ chr(65 + ((($pColumnIndex - 26) % 676) / 26)) .
+ chr(65 + $pColumnIndex % 26);
+ }
+ }
+ return $_indexCache[$pColumnIndex];
+ }
+
+ /**
+ * Extract all cell references in range
+ *
+ * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25)
+ * @return array Array containing single cell references
+ */
+ public static function extractAllCellReferencesInRange($pRange = 'A1') {
+ // Returnvalue
+ $returnValue = array();
+
+ // Explode spaces
+ $cellBlocks = explode(' ', str_replace('$', '', strtoupper($pRange)));
+ foreach ($cellBlocks as $cellBlock) {
+ // Single cell?
+ if (strpos($cellBlock,':') === FALSE && strpos($cellBlock,',') === FALSE) {
+ $returnValue[] = $cellBlock;
+ continue;
+ }
+
+ // Range...
+ $ranges = self::splitRange($cellBlock);
+ foreach($ranges as $range) {
+ // Single cell?
+ if (!isset($range[1])) {
+ $returnValue[] = $range[0];
+ continue;
+ }
+
+ // Range...
+ list($rangeStart, $rangeEnd) = $range;
+ sscanf($rangeStart,'%[A-Z]%d', $startCol, $startRow);
+ sscanf($rangeEnd,'%[A-Z]%d', $endCol, $endRow);
+ $endCol++;
+
+ // Current data
+ $currentCol = $startCol;
+ $currentRow = $startRow;
+
+ // Loop cells
+ while ($currentCol != $endCol) {
+ while ($currentRow <= $endRow) {
+ $returnValue[] = $currentCol.$currentRow;
+ ++$currentRow;
+ }
+ ++$currentCol;
+ $currentRow = $startRow;
+ }
+ }
+ }
+
+ // Sort the result by column and row
+ $sortKeys = array();
+ foreach (array_unique($returnValue) as $coord) {
+ sscanf($coord,'%[A-Z]%d', $column, $row);
+ $sortKeys[sprintf('%3s%09d',$column,$row)] = $coord;
+ }
+ ksort($sortKeys);
+
+ // Return value
+ return array_values($sortKeys);
+ }
+
+ /**
+ * Compare 2 cells
+ *
+ * @param PHPExcel_Cell $a Cell a
+ * @param PHPExcel_Cell $b Cell b
+ * @return int Result of comparison (always -1 or 1, never zero!)
+ */
+ public static function compareCells(PHPExcel_Cell $a, PHPExcel_Cell $b)
+ {
+ if ($a->getRow() < $b->getRow()) {
+ return -1;
+ } elseif ($a->getRow() > $b->getRow()) {
+ return 1;
+ } elseif (self::columnIndexFromString($a->getColumn()) < self::columnIndexFromString($b->getColumn())) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Get value binder to use
+ *
+ * @return PHPExcel_Cell_IValueBinder
+ */
+ public static function getValueBinder() {
+ if (self::$_valueBinder === NULL) {
+ self::$_valueBinder = new PHPExcel_Cell_DefaultValueBinder();
+ }
+
+ return self::$_valueBinder;
+ }
+
+ /**
+ * Set value binder to use
+ *
+ * @param PHPExcel_Cell_IValueBinder $binder
+ * @throws PHPExcel_Exception
+ */
+ public static function setValueBinder(PHPExcel_Cell_IValueBinder $binder = NULL) {
+ if ($binder === NULL) {
+ throw new PHPExcel_Exception("A PHPExcel_Cell_IValueBinder is required for PHPExcel to function correctly.");
+ }
+
+ self::$_valueBinder = $binder;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if ((is_object($value)) && ($key != '_parent')) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ /**
+ * Get index to cellXf
+ *
+ * @return int
+ */
+ public function getXfIndex()
+ {
+ return $this->_xfIndex;
+ }
+
+ /**
+ * Set index to cellXf
+ *
+ * @param int $pValue
+ * @return PHPExcel_Cell
+ */
+ public function setXfIndex($pValue = 0)
+ {
+ $this->_xfIndex = $pValue;
+
+ return $this->notifyCacheController();
+ }
+
+ /**
+ * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling
+ */
+ public function setFormulaAttributes($pAttributes)
+ {
+ $this->_formulaAttributes = $pAttributes;
+ return $this;
+ }
+
+ /**
+ * @deprecated Since version 1.7.8 for planned changes to cell for array formula handling
+ */
+ public function getFormulaAttributes()
+ {
+ return $this->_formulaAttributes;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->getValue();
+ }
+
+}
+
diff --git a/includes/PHPExcel/Classes/PHPExcel/Chart.php b/includes/PHPExcel/Classes/PHPExcel/Chart.php
new file mode 100644
index 0000000..9bf72db
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Chart.php
@@ -0,0 +1,635 @@
+_name = $name;
+ $this->_title = $title;
+ $this->_legend = $legend;
+ $this->_xAxisLabel = $xAxisLabel;
+ $this->_yAxisLabel = $yAxisLabel;
+ $this->_plotArea = $plotArea;
+ $this->_plotVisibleOnly = $plotVisibleOnly;
+ $this->_displayBlanksAs = $displayBlanksAs;
+ $this->_xAxis = $xAxis;
+ $this->_yAxis = $yAxis;
+ $this->_majorGridlines = $majorGridlines;
+ $this->_minorGridlines = $minorGridlines;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->_name;
+ }
+
+ /**
+ * Get Worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getWorksheet() {
+ return $this->_worksheet;
+ }
+
+ /**
+ * Set Worksheet
+ *
+ * @param PHPExcel_Worksheet $pValue
+ * @throws PHPExcel_Chart_Exception
+ * @return PHPExcel_Chart
+ */
+ public function setWorksheet(PHPExcel_Worksheet $pValue = null) {
+ $this->_worksheet = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get Title
+ *
+ * @return PHPExcel_Chart_Title
+ */
+ public function getTitle() {
+ return $this->_title;
+ }
+
+ /**
+ * Set Title
+ *
+ * @param PHPExcel_Chart_Title $title
+ * @return PHPExcel_Chart
+ */
+ public function setTitle(PHPExcel_Chart_Title $title) {
+ $this->_title = $title;
+
+ return $this;
+ }
+
+ /**
+ * Get Legend
+ *
+ * @return PHPExcel_Chart_Legend
+ */
+ public function getLegend() {
+ return $this->_legend;
+ }
+
+ /**
+ * Set Legend
+ *
+ * @param PHPExcel_Chart_Legend $legend
+ * @return PHPExcel_Chart
+ */
+ public function setLegend(PHPExcel_Chart_Legend $legend) {
+ $this->_legend = $legend;
+
+ return $this;
+ }
+
+ /**
+ * Get X-Axis Label
+ *
+ * @return PHPExcel_Chart_Title
+ */
+ public function getXAxisLabel() {
+ return $this->_xAxisLabel;
+ }
+
+ /**
+ * Set X-Axis Label
+ *
+ * @param PHPExcel_Chart_Title $label
+ * @return PHPExcel_Chart
+ */
+ public function setXAxisLabel(PHPExcel_Chart_Title $label) {
+ $this->_xAxisLabel = $label;
+
+ return $this;
+ }
+
+ /**
+ * Get Y-Axis Label
+ *
+ * @return PHPExcel_Chart_Title
+ */
+ public function getYAxisLabel() {
+ return $this->_yAxisLabel;
+ }
+
+ /**
+ * Set Y-Axis Label
+ *
+ * @param PHPExcel_Chart_Title $label
+ * @return PHPExcel_Chart
+ */
+ public function setYAxisLabel(PHPExcel_Chart_Title $label) {
+ $this->_yAxisLabel = $label;
+
+ return $this;
+ }
+
+ /**
+ * Get Plot Area
+ *
+ * @return PHPExcel_Chart_PlotArea
+ */
+ public function getPlotArea() {
+ return $this->_plotArea;
+ }
+
+ /**
+ * Get Plot Visible Only
+ *
+ * @return boolean
+ */
+ public function getPlotVisibleOnly() {
+ return $this->_plotVisibleOnly;
+ }
+
+ /**
+ * Set Plot Visible Only
+ *
+ * @param boolean $plotVisibleOnly
+ * @return PHPExcel_Chart
+ */
+ public function setPlotVisibleOnly($plotVisibleOnly = true) {
+ $this->_plotVisibleOnly = $plotVisibleOnly;
+
+ return $this;
+ }
+
+ /**
+ * Get Display Blanks as
+ *
+ * @return string
+ */
+ public function getDisplayBlanksAs() {
+ return $this->_displayBlanksAs;
+ }
+
+ /**
+ * Set Display Blanks as
+ *
+ * @param string $displayBlanksAs
+ * @return PHPExcel_Chart
+ */
+ public function setDisplayBlanksAs($displayBlanksAs = '0') {
+ $this->_displayBlanksAs = $displayBlanksAs;
+ }
+
+
+ /**
+ * Get yAxis
+ *
+ * @return PHPExcel_Chart_Axis
+ */
+ public function getChartAxisY() {
+ if($this->_yAxis !== NULL){
+ return $this->_yAxis;
+ }
+
+ return new PHPExcel_Chart_Axis();
+ }
+
+ /**
+ * Get xAxis
+ *
+ * @return PHPExcel_Chart_Axis
+ */
+ public function getChartAxisX() {
+ if($this->_xAxis !== NULL){
+ return $this->_xAxis;
+ }
+
+ return new PHPExcel_Chart_Axis();
+ }
+
+ /**
+ * Get Major Gridlines
+ *
+ * @return PHPExcel_Chart_GridLines
+ */
+ public function getMajorGridlines() {
+ if($this->_majorGridlines !== NULL){
+ return $this->_majorGridlines;
+ }
+
+ return new PHPExcel_Chart_GridLines();
+ }
+
+ /**
+ * Get Minor Gridlines
+ *
+ * @return PHPExcel_Chart_GridLines
+ */
+ public function getMinorGridlines() {
+ if($this->_minorGridlines !== NULL){
+ return $this->_minorGridlines;
+ }
+
+ return new PHPExcel_Chart_GridLines();
+ }
+
+
+ /**
+ * Set the Top Left position for the chart
+ *
+ * @param string $cell
+ * @param integer $xOffset
+ * @param integer $yOffset
+ * @return PHPExcel_Chart
+ */
+ public function setTopLeftPosition($cell, $xOffset=null, $yOffset=null) {
+ $this->_topLeftCellRef = $cell;
+ if (!is_null($xOffset))
+ $this->setTopLeftXOffset($xOffset);
+ if (!is_null($yOffset))
+ $this->setTopLeftYOffset($yOffset);
+
+ return $this;
+ }
+
+ /**
+ * Get the top left position of the chart
+ *
+ * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
+ */
+ public function getTopLeftPosition() {
+ return array( 'cell' => $this->_topLeftCellRef,
+ 'xOffset' => $this->_topLeftXOffset,
+ 'yOffset' => $this->_topLeftYOffset
+ );
+ }
+
+ /**
+ * Get the cell address where the top left of the chart is fixed
+ *
+ * @return string
+ */
+ public function getTopLeftCell() {
+ return $this->_topLeftCellRef;
+ }
+
+ /**
+ * Set the Top Left cell position for the chart
+ *
+ * @param string $cell
+ * @return PHPExcel_Chart
+ */
+ public function setTopLeftCell($cell) {
+ $this->_topLeftCellRef = $cell;
+
+ return $this;
+ }
+
+ /**
+ * Set the offset position within the Top Left cell for the chart
+ *
+ * @param integer $xOffset
+ * @param integer $yOffset
+ * @return PHPExcel_Chart
+ */
+ public function setTopLeftOffset($xOffset=null,$yOffset=null) {
+ if (!is_null($xOffset))
+ $this->setTopLeftXOffset($xOffset);
+ if (!is_null($yOffset))
+ $this->setTopLeftYOffset($yOffset);
+
+ return $this;
+ }
+
+ /**
+ * Get the offset position within the Top Left cell for the chart
+ *
+ * @return integer[]
+ */
+ public function getTopLeftOffset() {
+ return array( 'X' => $this->_topLeftXOffset,
+ 'Y' => $this->_topLeftYOffset
+ );
+ }
+
+ public function setTopLeftXOffset($xOffset) {
+ $this->_topLeftXOffset = $xOffset;
+
+ return $this;
+ }
+
+ public function getTopLeftXOffset() {
+ return $this->_topLeftXOffset;
+ }
+
+ public function setTopLeftYOffset($yOffset) {
+ $this->_topLeftYOffset = $yOffset;
+
+ return $this;
+ }
+
+ public function getTopLeftYOffset() {
+ return $this->_topLeftYOffset;
+ }
+
+ /**
+ * Set the Bottom Right position of the chart
+ *
+ * @param string $cell
+ * @param integer $xOffset
+ * @param integer $yOffset
+ * @return PHPExcel_Chart
+ */
+ public function setBottomRightPosition($cell, $xOffset=null, $yOffset=null) {
+ $this->_bottomRightCellRef = $cell;
+ if (!is_null($xOffset))
+ $this->setBottomRightXOffset($xOffset);
+ if (!is_null($yOffset))
+ $this->setBottomRightYOffset($yOffset);
+
+ return $this;
+ }
+
+ /**
+ * Get the bottom right position of the chart
+ *
+ * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
+ */
+ public function getBottomRightPosition() {
+ return array( 'cell' => $this->_bottomRightCellRef,
+ 'xOffset' => $this->_bottomRightXOffset,
+ 'yOffset' => $this->_bottomRightYOffset
+ );
+ }
+
+ public function setBottomRightCell($cell) {
+ $this->_bottomRightCellRef = $cell;
+
+ return $this;
+ }
+
+ /**
+ * Get the cell address where the bottom right of the chart is fixed
+ *
+ * @return string
+ */
+ public function getBottomRightCell() {
+ return $this->_bottomRightCellRef;
+ }
+
+ /**
+ * Set the offset position within the Bottom Right cell for the chart
+ *
+ * @param integer $xOffset
+ * @param integer $yOffset
+ * @return PHPExcel_Chart
+ */
+ public function setBottomRightOffset($xOffset=null,$yOffset=null) {
+ if (!is_null($xOffset))
+ $this->setBottomRightXOffset($xOffset);
+ if (!is_null($yOffset))
+ $this->setBottomRightYOffset($yOffset);
+
+ return $this;
+ }
+
+ /**
+ * Get the offset position within the Bottom Right cell for the chart
+ *
+ * @return integer[]
+ */
+ public function getBottomRightOffset() {
+ return array( 'X' => $this->_bottomRightXOffset,
+ 'Y' => $this->_bottomRightYOffset
+ );
+ }
+
+ public function setBottomRightXOffset($xOffset) {
+ $this->_bottomRightXOffset = $xOffset;
+
+ return $this;
+ }
+
+ public function getBottomRightXOffset() {
+ return $this->_bottomRightXOffset;
+ }
+
+ public function setBottomRightYOffset($yOffset) {
+ $this->_bottomRightYOffset = $yOffset;
+
+ return $this;
+ }
+
+ public function getBottomRightYOffset() {
+ return $this->_bottomRightYOffset;
+ }
+
+
+ public function refresh() {
+ if ($this->_worksheet !== NULL) {
+ $this->_plotArea->refresh($this->_worksheet);
+ }
+ }
+
+ public function render($outputDestination = null) {
+ $libraryName = PHPExcel_Settings::getChartRendererName();
+ if (is_null($libraryName)) {
+ return false;
+ }
+ // Ensure that data series values are up-to-date before we render
+ $this->refresh();
+
+ $libraryPath = PHPExcel_Settings::getChartRendererPath();
+ $includePath = str_replace('\\','/',get_include_path());
+ $rendererPath = str_replace('\\','/',$libraryPath);
+ if (strpos($rendererPath,$includePath) === false) {
+ set_include_path(get_include_path() . PATH_SEPARATOR . $libraryPath);
+ }
+
+ $rendererName = 'PHPExcel_Chart_Renderer_'.$libraryName;
+ $renderer = new $rendererName($this);
+
+ if ($outputDestination == 'php://output') {
+ $outputDestination = null;
+ }
+ return $renderer->render($outputDestination);
+ }
+
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/Comment.php b/includes/PHPExcel/Classes/PHPExcel/Comment.php
new file mode 100644
index 0000000..8b8cfdc
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Comment.php
@@ -0,0 +1,327 @@
+_author = 'Author';
+ $this->_text = new PHPExcel_RichText();
+ $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1');
+ $this->_alignment = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL;
+ }
+
+ /**
+ * Get Author
+ *
+ * @return string
+ */
+ public function getAuthor() {
+ return $this->_author;
+ }
+
+ /**
+ * Set Author
+ *
+ * @param string $pValue
+ * @return PHPExcel_Comment
+ */
+ public function setAuthor($pValue = '') {
+ $this->_author = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Rich text comment
+ *
+ * @return PHPExcel_RichText
+ */
+ public function getText() {
+ return $this->_text;
+ }
+
+ /**
+ * Set Rich text comment
+ *
+ * @param PHPExcel_RichText $pValue
+ * @return PHPExcel_Comment
+ */
+ public function setText(PHPExcel_RichText $pValue) {
+ $this->_text = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get comment width (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getWidth() {
+ return $this->_width;
+ }
+
+ /**
+ * Set comment width (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setWidth($value = '96pt') {
+ $this->_width = $value;
+ return $this;
+ }
+
+ /**
+ * Get comment height (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getHeight() {
+ return $this->_height;
+ }
+
+ /**
+ * Set comment height (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setHeight($value = '55.5pt') {
+ $this->_height = $value;
+ return $this;
+ }
+
+ /**
+ * Get left margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getMarginLeft() {
+ return $this->_marginLeft;
+ }
+
+ /**
+ * Set left margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setMarginLeft($value = '59.25pt') {
+ $this->_marginLeft = $value;
+ return $this;
+ }
+
+ /**
+ * Get top margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @return string
+ */
+ public function getMarginTop() {
+ return $this->_marginTop;
+ }
+
+ /**
+ * Set top margin (CSS style, i.e. XXpx or YYpt)
+ *
+ * @param string $value
+ * @return PHPExcel_Comment
+ */
+ public function setMarginTop($value = '1.5pt') {
+ $this->_marginTop = $value;
+ return $this;
+ }
+
+ /**
+ * Is the comment visible by default?
+ *
+ * @return boolean
+ */
+ public function getVisible() {
+ return $this->_visible;
+ }
+
+ /**
+ * Set comment default visibility
+ *
+ * @param boolean $value
+ * @return PHPExcel_Comment
+ */
+ public function setVisible($value = false) {
+ $this->_visible = $value;
+ return $this;
+ }
+
+ /**
+ * Get fill color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getFillColor() {
+ return $this->_fillColor;
+ }
+
+ /**
+ * Set Alignment
+ *
+ * @param string $pValue
+ * @return PHPExcel_Comment
+ */
+ public function setAlignment($pValue = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL) {
+ $this->_alignment = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Alignment
+ *
+ * @return string
+ */
+ public function getAlignment() {
+ return $this->_alignment;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_author
+ . $this->_text->getHashCode()
+ . $this->_width
+ . $this->_height
+ . $this->_marginLeft
+ . $this->_marginTop
+ . ($this->_visible ? 1 : 0)
+ . $this->_fillColor->getHashCode()
+ . $this->_alignment
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->_text->getPlainText();
+ }
+
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/DocumentProperties.php b/includes/PHPExcel/Classes/PHPExcel/DocumentProperties.php
new file mode 100644
index 0000000..7b4a0e4
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/DocumentProperties.php
@@ -0,0 +1,587 @@
+_lastModifiedBy = $this->_creator;
+ $this->_created = time();
+ $this->_modified = time();
+ }
+
+ /**
+ * Get Creator
+ *
+ * @return string
+ */
+ public function getCreator() {
+ return $this->_creator;
+ }
+
+ /**
+ * Set Creator
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCreator($pValue = '') {
+ $this->_creator = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Last Modified By
+ *
+ * @return string
+ */
+ public function getLastModifiedBy() {
+ return $this->_lastModifiedBy;
+ }
+
+ /**
+ * Set Last Modified By
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setLastModifiedBy($pValue = '') {
+ $this->_lastModifiedBy = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Created
+ *
+ * @return datetime
+ */
+ public function getCreated() {
+ return $this->_created;
+ }
+
+ /**
+ * Set Created
+ *
+ * @param datetime $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCreated($pValue = null) {
+ if ($pValue === NULL) {
+ $pValue = time();
+ } elseif (is_string($pValue)) {
+ if (is_numeric($pValue)) {
+ $pValue = intval($pValue);
+ } else {
+ $pValue = strtotime($pValue);
+ }
+ }
+
+ $this->_created = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Modified
+ *
+ * @return datetime
+ */
+ public function getModified() {
+ return $this->_modified;
+ }
+
+ /**
+ * Set Modified
+ *
+ * @param datetime $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setModified($pValue = null) {
+ if ($pValue === NULL) {
+ $pValue = time();
+ } elseif (is_string($pValue)) {
+ if (is_numeric($pValue)) {
+ $pValue = intval($pValue);
+ } else {
+ $pValue = strtotime($pValue);
+ }
+ }
+
+ $this->_modified = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Title
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->_title;
+ }
+
+ /**
+ * Set Title
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setTitle($pValue = '') {
+ $this->_title = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Description
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->_description;
+ }
+
+ /**
+ * Set Description
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setDescription($pValue = '') {
+ $this->_description = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Subject
+ *
+ * @return string
+ */
+ public function getSubject() {
+ return $this->_subject;
+ }
+
+ /**
+ * Set Subject
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setSubject($pValue = '') {
+ $this->_subject = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Keywords
+ *
+ * @return string
+ */
+ public function getKeywords() {
+ return $this->_keywords;
+ }
+
+ /**
+ * Set Keywords
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setKeywords($pValue = '') {
+ $this->_keywords = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Category
+ *
+ * @return string
+ */
+ public function getCategory() {
+ return $this->_category;
+ }
+
+ /**
+ * Set Category
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCategory($pValue = '') {
+ $this->_category = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Company
+ *
+ * @return string
+ */
+ public function getCompany() {
+ return $this->_company;
+ }
+
+ /**
+ * Set Company
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCompany($pValue = '') {
+ $this->_company = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Manager
+ *
+ * @return string
+ */
+ public function getManager() {
+ return $this->_manager;
+ }
+
+ /**
+ * Set Manager
+ *
+ * @param string $pValue
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setManager($pValue = '') {
+ $this->_manager = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get a List of Custom Property Names
+ *
+ * @return array of string
+ */
+ public function getCustomProperties() {
+ return array_keys($this->_customProperties);
+ }
+
+ /**
+ * Check if a Custom Property is defined
+ *
+ * @param string $propertyName
+ * @return boolean
+ */
+ public function isCustomPropertySet($propertyName) {
+ return isset($this->_customProperties[$propertyName]);
+ }
+
+ /**
+ * Get a Custom Property Value
+ *
+ * @param string $propertyName
+ * @return string
+ */
+ public function getCustomPropertyValue($propertyName) {
+ if (isset($this->_customProperties[$propertyName])) {
+ return $this->_customProperties[$propertyName]['value'];
+ }
+
+ }
+
+ /**
+ * Get a Custom Property Type
+ *
+ * @param string $propertyName
+ * @return string
+ */
+ public function getCustomPropertyType($propertyName) {
+ if (isset($this->_customProperties[$propertyName])) {
+ return $this->_customProperties[$propertyName]['type'];
+ }
+
+ }
+
+ /**
+ * Set a Custom Property
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @param string $propertyType
+ * 'i' : Integer
+ * 'f' : Floating Point
+ * 's' : String
+ * 'd' : Date/Time
+ * 'b' : Boolean
+ * @return PHPExcel_DocumentProperties
+ */
+ public function setCustomProperty($propertyName,$propertyValue='',$propertyType=NULL) {
+ if (($propertyType === NULL) || (!in_array($propertyType,array(self::PROPERTY_TYPE_INTEGER,
+ self::PROPERTY_TYPE_FLOAT,
+ self::PROPERTY_TYPE_STRING,
+ self::PROPERTY_TYPE_DATE,
+ self::PROPERTY_TYPE_BOOLEAN)))) {
+ if ($propertyValue === NULL) {
+ $propertyType = self::PROPERTY_TYPE_STRING;
+ } elseif (is_float($propertyValue)) {
+ $propertyType = self::PROPERTY_TYPE_FLOAT;
+ } elseif(is_int($propertyValue)) {
+ $propertyType = self::PROPERTY_TYPE_INTEGER;
+ } elseif (is_bool($propertyValue)) {
+ $propertyType = self::PROPERTY_TYPE_BOOLEAN;
+ } else {
+ $propertyType = self::PROPERTY_TYPE_STRING;
+ }
+ }
+
+ $this->_customProperties[$propertyName] = array('value' => $propertyValue, 'type' => $propertyType);
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ public static function convertProperty($propertyValue,$propertyType) {
+ switch ($propertyType) {
+ case 'empty' : // Empty
+ return '';
+ break;
+ case 'null' : // Null
+ return NULL;
+ break;
+ case 'i1' : // 1-Byte Signed Integer
+ case 'i2' : // 2-Byte Signed Integer
+ case 'i4' : // 4-Byte Signed Integer
+ case 'i8' : // 8-Byte Signed Integer
+ case 'int' : // Integer
+ return (int) $propertyValue;
+ break;
+ case 'ui1' : // 1-Byte Unsigned Integer
+ case 'ui2' : // 2-Byte Unsigned Integer
+ case 'ui4' : // 4-Byte Unsigned Integer
+ case 'ui8' : // 8-Byte Unsigned Integer
+ case 'uint' : // Unsigned Integer
+ return abs((int) $propertyValue);
+ break;
+ case 'r4' : // 4-Byte Real Number
+ case 'r8' : // 8-Byte Real Number
+ case 'decimal' : // Decimal
+ return (float) $propertyValue;
+ break;
+ case 'lpstr' : // LPSTR
+ case 'lpwstr' : // LPWSTR
+ case 'bstr' : // Basic String
+ return $propertyValue;
+ break;
+ case 'date' : // Date and Time
+ case 'filetime' : // File Time
+ return strtotime($propertyValue);
+ break;
+ case 'bool' : // Boolean
+ return ($propertyValue == 'true') ? True : False;
+ break;
+ case 'cy' : // Currency
+ case 'error' : // Error Status Code
+ case 'vector' : // Vector
+ case 'array' : // Array
+ case 'blob' : // Binary Blob
+ case 'oblob' : // Binary Blob Object
+ case 'stream' : // Binary Stream
+ case 'ostream' : // Binary Stream Object
+ case 'storage' : // Binary Storage
+ case 'ostorage' : // Binary Storage Object
+ case 'vstream' : // Binary Versioned Stream
+ case 'clsid' : // Class ID
+ case 'cf' : // Clipboard Data
+ return $propertyValue;
+ break;
+ }
+ return $propertyValue;
+ }
+
+ public static function convertPropertyType($propertyType) {
+ switch ($propertyType) {
+ case 'i1' : // 1-Byte Signed Integer
+ case 'i2' : // 2-Byte Signed Integer
+ case 'i4' : // 4-Byte Signed Integer
+ case 'i8' : // 8-Byte Signed Integer
+ case 'int' : // Integer
+ case 'ui1' : // 1-Byte Unsigned Integer
+ case 'ui2' : // 2-Byte Unsigned Integer
+ case 'ui4' : // 4-Byte Unsigned Integer
+ case 'ui8' : // 8-Byte Unsigned Integer
+ case 'uint' : // Unsigned Integer
+ return self::PROPERTY_TYPE_INTEGER;
+ break;
+ case 'r4' : // 4-Byte Real Number
+ case 'r8' : // 8-Byte Real Number
+ case 'decimal' : // Decimal
+ return self::PROPERTY_TYPE_FLOAT;
+ break;
+ case 'empty' : // Empty
+ case 'null' : // Null
+ case 'lpstr' : // LPSTR
+ case 'lpwstr' : // LPWSTR
+ case 'bstr' : // Basic String
+ return self::PROPERTY_TYPE_STRING;
+ break;
+ case 'date' : // Date and Time
+ case 'filetime' : // File Time
+ return self::PROPERTY_TYPE_DATE;
+ break;
+ case 'bool' : // Boolean
+ return self::PROPERTY_TYPE_BOOLEAN;
+ break;
+ case 'cy' : // Currency
+ case 'error' : // Error Status Code
+ case 'vector' : // Vector
+ case 'array' : // Array
+ case 'blob' : // Binary Blob
+ case 'oblob' : // Binary Blob Object
+ case 'stream' : // Binary Stream
+ case 'ostream' : // Binary Stream Object
+ case 'storage' : // Binary Storage
+ case 'ostorage' : // Binary Storage Object
+ case 'vstream' : // Binary Versioned Stream
+ case 'clsid' : // Class ID
+ case 'cf' : // Clipboard Data
+ return self::PROPERTY_TYPE_UNKNOWN;
+ break;
+ }
+ return self::PROPERTY_TYPE_UNKNOWN;
+ }
+
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/DocumentSecurity.php b/includes/PHPExcel/Classes/PHPExcel/DocumentSecurity.php
new file mode 100644
index 0000000..cf7ffb5
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/DocumentSecurity.php
@@ -0,0 +1,218 @@
+_lockRevision = false;
+ $this->_lockStructure = false;
+ $this->_lockWindows = false;
+ $this->_revisionsPassword = '';
+ $this->_workbookPassword = '';
+ }
+
+ /**
+ * Is some sort of dcument security enabled?
+ *
+ * @return boolean
+ */
+ function isSecurityEnabled() {
+ return $this->_lockRevision ||
+ $this->_lockStructure ||
+ $this->_lockWindows;
+ }
+
+ /**
+ * Get LockRevision
+ *
+ * @return boolean
+ */
+ function getLockRevision() {
+ return $this->_lockRevision;
+ }
+
+ /**
+ * Set LockRevision
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setLockRevision($pValue = false) {
+ $this->_lockRevision = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get LockStructure
+ *
+ * @return boolean
+ */
+ function getLockStructure() {
+ return $this->_lockStructure;
+ }
+
+ /**
+ * Set LockStructure
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setLockStructure($pValue = false) {
+ $this->_lockStructure = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get LockWindows
+ *
+ * @return boolean
+ */
+ function getLockWindows() {
+ return $this->_lockWindows;
+ }
+
+ /**
+ * Set LockWindows
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setLockWindows($pValue = false) {
+ $this->_lockWindows = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get RevisionsPassword (hashed)
+ *
+ * @return string
+ */
+ function getRevisionsPassword() {
+ return $this->_revisionsPassword;
+ }
+
+ /**
+ * Set RevisionsPassword
+ *
+ * @param string $pValue
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setRevisionsPassword($pValue = '', $pAlreadyHashed = false) {
+ if (!$pAlreadyHashed) {
+ $pValue = PHPExcel_Shared_PasswordHasher::hashPassword($pValue);
+ }
+ $this->_revisionsPassword = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get WorkbookPassword (hashed)
+ *
+ * @return string
+ */
+ function getWorkbookPassword() {
+ return $this->_workbookPassword;
+ }
+
+ /**
+ * Set WorkbookPassword
+ *
+ * @param string $pValue
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @return PHPExcel_DocumentSecurity
+ */
+ function setWorkbookPassword($pValue = '', $pAlreadyHashed = false) {
+ if (!$pAlreadyHashed) {
+ $pValue = PHPExcel_Shared_PasswordHasher::hashPassword($pValue);
+ }
+ $this->_workbookPassword = $pValue;
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/Exception.php b/includes/PHPExcel/Classes/PHPExcel/Exception.php
new file mode 100644
index 0000000..578b9ee
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Exception.php
@@ -0,0 +1,52 @@
+line = $line;
+ $e->file = $file;
+ throw $e;
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/HashTable.php b/includes/PHPExcel/Classes/PHPExcel/HashTable.php
new file mode 100644
index 0000000..77106e1
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/HashTable.php
@@ -0,0 +1,202 @@
+addFromSource($pSource);
+ }
+ }
+
+ /**
+ * Add HashTable items from source
+ *
+ * @param PHPExcel_IComparable[] $pSource Source array to create HashTable from
+ * @throws PHPExcel_Exception
+ */
+ public function addFromSource($pSource = null) {
+ // Check if an array was passed
+ if ($pSource == null) {
+ return;
+ } else if (!is_array($pSource)) {
+ throw new PHPExcel_Exception('Invalid array parameter passed.');
+ }
+
+ foreach ($pSource as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Add HashTable item
+ *
+ * @param PHPExcel_IComparable $pSource Item to add
+ * @throws PHPExcel_Exception
+ */
+ public function add(PHPExcel_IComparable $pSource = null) {
+ $hash = $pSource->getHashCode();
+ if (!isset($this->_items[$hash])) {
+ $this->_items[$hash] = $pSource;
+ $this->_keyMap[count($this->_items) - 1] = $hash;
+ }
+ }
+
+ /**
+ * Remove HashTable item
+ *
+ * @param PHPExcel_IComparable $pSource Item to remove
+ * @throws PHPExcel_Exception
+ */
+ public function remove(PHPExcel_IComparable $pSource = null) {
+ $hash = $pSource->getHashCode();
+ if (isset($this->_items[$hash])) {
+ unset($this->_items[$hash]);
+
+ $deleteKey = -1;
+ foreach ($this->_keyMap as $key => $value) {
+ if ($deleteKey >= 0) {
+ $this->_keyMap[$key - 1] = $value;
+ }
+
+ if ($value == $hash) {
+ $deleteKey = $key;
+ }
+ }
+ unset($this->_keyMap[count($this->_keyMap) - 1]);
+ }
+ }
+
+ /**
+ * Clear HashTable
+ *
+ */
+ public function clear() {
+ $this->_items = array();
+ $this->_keyMap = array();
+ }
+
+ /**
+ * Count
+ *
+ * @return int
+ */
+ public function count() {
+ return count($this->_items);
+ }
+
+ /**
+ * Get index for hash code
+ *
+ * @param string $pHashCode
+ * @return int Index
+ */
+ public function getIndexForHashCode($pHashCode = '') {
+ return array_search($pHashCode, $this->_keyMap);
+ }
+
+ /**
+ * Get by index
+ *
+ * @param int $pIndex
+ * @return PHPExcel_IComparable
+ *
+ */
+ public function getByIndex($pIndex = 0) {
+ if (isset($this->_keyMap[$pIndex])) {
+ return $this->getByHashCode( $this->_keyMap[$pIndex] );
+ }
+
+ return null;
+ }
+
+ /**
+ * Get by hashcode
+ *
+ * @param string $pHashCode
+ * @return PHPExcel_IComparable
+ *
+ */
+ public function getByHashCode($pHashCode = '') {
+ if (isset($this->_items[$pHashCode])) {
+ return $this->_items[$pHashCode];
+ }
+
+ return null;
+ }
+
+ /**
+ * HashTable to array
+ *
+ * @return PHPExcel_IComparable[]
+ */
+ public function toArray() {
+ return $this->_items;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ }
+ }
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/IComparable.php b/includes/PHPExcel/Classes/PHPExcel/IComparable.php
new file mode 100644
index 0000000..adb9a01
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/IComparable.php
@@ -0,0 +1,43 @@
+ 'IWriter', 'path' => 'PHPExcel/Writer/{0}.php', 'class' => 'PHPExcel_Writer_{0}' ),
+ array( 'type' => 'IReader', 'path' => 'PHPExcel/Reader/{0}.php', 'class' => 'PHPExcel_Reader_{0}' )
+ );
+
+ /**
+ * Autoresolve classes
+ *
+ * @var array
+ * @access private
+ * @static
+ */
+ private static $_autoResolveClasses = array(
+ 'Excel2007',
+ 'Excel5',
+ 'Excel2003XML',
+ 'OOCalc',
+ 'SYLK',
+ 'Gnumeric',
+ 'HTML',
+ 'CSV',
+ );
+
+ /**
+ * Private constructor for PHPExcel_IOFactory
+ */
+ private function __construct() { }
+
+ /**
+ * Get search locations
+ *
+ * @static
+ * @access public
+ * @return array
+ */
+ public static function getSearchLocations() {
+ return self::$_searchLocations;
+ } // function getSearchLocations()
+
+ /**
+ * Set search locations
+ *
+ * @static
+ * @access public
+ * @param array $value
+ * @throws PHPExcel_Reader_Exception
+ */
+ public static function setSearchLocations($value) {
+ if (is_array($value)) {
+ self::$_searchLocations = $value;
+ } else {
+ throw new PHPExcel_Reader_Exception('Invalid parameter passed.');
+ }
+ } // function setSearchLocations()
+
+ /**
+ * Add search location
+ *
+ * @static
+ * @access public
+ * @param string $type Example: IWriter
+ * @param string $location Example: PHPExcel/Writer/{0}.php
+ * @param string $classname Example: PHPExcel_Writer_{0}
+ */
+ public static function addSearchLocation($type = '', $location = '', $classname = '') {
+ self::$_searchLocations[] = array( 'type' => $type, 'path' => $location, 'class' => $classname );
+ } // function addSearchLocation()
+
+ /**
+ * Create PHPExcel_Writer_IWriter
+ *
+ * @static
+ * @access public
+ * @param PHPExcel $phpExcel
+ * @param string $writerType Example: Excel2007
+ * @return PHPExcel_Writer_IWriter
+ * @throws PHPExcel_Reader_Exception
+ */
+ public static function createWriter(PHPExcel $phpExcel, $writerType = '') {
+ // Search type
+ $searchType = 'IWriter';
+
+ // Include class
+ foreach (self::$_searchLocations as $searchLocation) {
+ if ($searchLocation['type'] == $searchType) {
+ $className = str_replace('{0}', $writerType, $searchLocation['class']);
+
+ $instance = new $className($phpExcel);
+ if ($instance !== NULL) {
+ return $instance;
+ }
+ }
+ }
+
+ // Nothing found...
+ throw new PHPExcel_Reader_Exception("No $searchType found for type $writerType");
+ } // function createWriter()
+
+ /**
+ * Create PHPExcel_Reader_IReader
+ *
+ * @static
+ * @access public
+ * @param string $readerType Example: Excel2007
+ * @return PHPExcel_Reader_IReader
+ * @throws PHPExcel_Reader_Exception
+ */
+ public static function createReader($readerType = '') {
+ // Search type
+ $searchType = 'IReader';
+
+ // Include class
+ foreach (self::$_searchLocations as $searchLocation) {
+ if ($searchLocation['type'] == $searchType) {
+ $className = str_replace('{0}', $readerType, $searchLocation['class']);
+
+ $instance = new $className();
+ if ($instance !== NULL) {
+ return $instance;
+ }
+ }
+ }
+
+ // Nothing found...
+ throw new PHPExcel_Reader_Exception("No $searchType found for type $readerType");
+ } // function createReader()
+
+ /**
+ * Loads PHPExcel from file using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @static
+ * @access public
+ * @param string $pFilename The name of the spreadsheet file
+ * @return PHPExcel
+ * @throws PHPExcel_Reader_Exception
+ */
+ public static function load($pFilename) {
+ $reader = self::createReaderForFile($pFilename);
+ return $reader->load($pFilename);
+ } // function load()
+
+ /**
+ * Identify file type using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @static
+ * @access public
+ * @param string $pFilename The name of the spreadsheet file to identify
+ * @return string
+ * @throws PHPExcel_Reader_Exception
+ */
+ public static function identify($pFilename) {
+ $reader = self::createReaderForFile($pFilename);
+ $className = get_class($reader);
+ $classType = explode('_',$className);
+ unset($reader);
+ return array_pop($classType);
+ } // function identify()
+
+ /**
+ * Create PHPExcel_Reader_IReader for file using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @static
+ * @access public
+ * @param string $pFilename The name of the spreadsheet file
+ * @return PHPExcel_Reader_IReader
+ * @throws PHPExcel_Reader_Exception
+ */
+ public static function createReaderForFile($pFilename) {
+
+ // First, lucky guess by inspecting file extension
+ $pathinfo = pathinfo($pFilename);
+
+ $extensionType = NULL;
+ if (isset($pathinfo['extension'])) {
+ switch (strtolower($pathinfo['extension'])) {
+ case 'xlsx': // Excel (OfficeOpenXML) Spreadsheet
+ case 'xlsm': // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
+ case 'xltx': // Excel (OfficeOpenXML) Template
+ case 'xltm': // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
+ $extensionType = 'Excel2007';
+ break;
+ case 'xls': // Excel (BIFF) Spreadsheet
+ case 'xlt': // Excel (BIFF) Template
+ $extensionType = 'Excel5';
+ break;
+ case 'ods': // Open/Libre Offic Calc
+ case 'ots': // Open/Libre Offic Calc Template
+ $extensionType = 'OOCalc';
+ break;
+ case 'slk':
+ $extensionType = 'SYLK';
+ break;
+ case 'xml': // Excel 2003 SpreadSheetML
+ $extensionType = 'Excel2003XML';
+ break;
+ case 'gnumeric':
+ $extensionType = 'Gnumeric';
+ break;
+ case 'htm':
+ case 'html':
+ $extensionType = 'HTML';
+ break;
+ case 'csv':
+ // Do nothing
+ // We must not try to use CSV reader since it loads
+ // all files including Excel files etc.
+ break;
+ default:
+ break;
+ }
+
+ if ($extensionType !== NULL) {
+ $reader = self::createReader($extensionType);
+ // Let's see if we are lucky
+ if (isset($reader) && $reader->canRead($pFilename)) {
+ return $reader;
+ }
+ }
+ }
+
+ // If we reach here then "lucky guess" didn't give any result
+ // Try walking through all the options in self::$_autoResolveClasses
+ foreach (self::$_autoResolveClasses as $autoResolveClass) {
+ // Ignore our original guess, we know that won't work
+ if ($autoResolveClass !== $extensionType) {
+ $reader = self::createReader($autoResolveClass);
+ if ($reader->canRead($pFilename)) {
+ return $reader;
+ }
+ }
+ }
+
+ throw new PHPExcel_Reader_Exception('Unable to identify a reader for this file');
+ } // function createReaderForFile()
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/NamedRange.php b/includes/PHPExcel/Classes/PHPExcel/NamedRange.php
new file mode 100644
index 0000000..fe245e3
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/NamedRange.php
@@ -0,0 +1,246 @@
+_worksheet)
+ *
+ * @var bool
+ */
+ private $_localOnly;
+
+ /**
+ * Scope
+ *
+ * @var PHPExcel_Worksheet
+ */
+ private $_scope;
+
+ /**
+ * Create a new NamedRange
+ *
+ * @param string $pName
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @param string $pRange
+ * @param bool $pLocalOnly
+ * @param PHPExcel_Worksheet|null $pScope Scope. Only applies when $pLocalOnly = true. Null for global scope.
+ * @throws PHPExcel_Exception
+ */
+ public function __construct($pName = null, PHPExcel_Worksheet $pWorksheet, $pRange = 'A1', $pLocalOnly = false, $pScope = null)
+ {
+ // Validate data
+ if (($pName === NULL) || ($pWorksheet === NULL) || ($pRange === NULL)) {
+ throw new PHPExcel_Exception('Parameters can not be null.');
+ }
+
+ // Set local members
+ $this->_name = $pName;
+ $this->_worksheet = $pWorksheet;
+ $this->_range = $pRange;
+ $this->_localOnly = $pLocalOnly;
+ $this->_scope = ($pLocalOnly == true) ?
+ (($pScope == null) ? $pWorksheet : $pScope) : null;
+ }
+
+ /**
+ * Get name
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->_name;
+ }
+
+ /**
+ * Set name
+ *
+ * @param string $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setName($value = null) {
+ if ($value !== NULL) {
+ // Old title
+ $oldTitle = $this->_name;
+
+ // Re-attach
+ if ($this->_worksheet !== NULL) {
+ $this->_worksheet->getParent()->removeNamedRange($this->_name,$this->_worksheet);
+ }
+ $this->_name = $value;
+
+ if ($this->_worksheet !== NULL) {
+ $this->_worksheet->getParent()->addNamedRange($this);
+ }
+
+ // New title
+ $newTitle = $this->_name;
+ PHPExcel_ReferenceHelper::getInstance()->updateNamedFormulas($this->_worksheet->getParent(), $oldTitle, $newTitle);
+ }
+ return $this;
+ }
+
+ /**
+ * Get worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getWorksheet() {
+ return $this->_worksheet;
+ }
+
+ /**
+ * Set worksheet
+ *
+ * @param PHPExcel_Worksheet $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setWorksheet(PHPExcel_Worksheet $value = null) {
+ if ($value !== NULL) {
+ $this->_worksheet = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Get range
+ *
+ * @return string
+ */
+ public function getRange() {
+ return $this->_range;
+ }
+
+ /**
+ * Set range
+ *
+ * @param string $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setRange($value = null) {
+ if ($value !== NULL) {
+ $this->_range = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Get localOnly
+ *
+ * @return bool
+ */
+ public function getLocalOnly() {
+ return $this->_localOnly;
+ }
+
+ /**
+ * Set localOnly
+ *
+ * @param bool $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setLocalOnly($value = false) {
+ $this->_localOnly = $value;
+ $this->_scope = $value ? $this->_worksheet : null;
+ return $this;
+ }
+
+ /**
+ * Get scope
+ *
+ * @return PHPExcel_Worksheet|null
+ */
+ public function getScope() {
+ return $this->_scope;
+ }
+
+ /**
+ * Set scope
+ *
+ * @param PHPExcel_Worksheet|null $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setScope(PHPExcel_Worksheet $value = null) {
+ $this->_scope = $value;
+ $this->_localOnly = ($value == null) ? false : true;
+ return $this;
+ }
+
+ /**
+ * Resolve a named range to a regular cell range
+ *
+ * @param string $pNamedRange Named range
+ * @param PHPExcel_Worksheet|null $pSheet Scope. Use null for global scope
+ * @return PHPExcel_NamedRange
+ */
+ public static function resolveRange($pNamedRange = '', PHPExcel_Worksheet $pSheet) {
+ return $pSheet->getParent()->getNamedRange($pNamedRange, $pSheet);
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/ReferenceHelper.php b/includes/PHPExcel/Classes/PHPExcel/ReferenceHelper.php
new file mode 100644
index 0000000..9eadab4
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/ReferenceHelper.php
@@ -0,0 +1,922 @@
+= ($beforeRow + $pNumRows)) &&
+ ($cellRow < $beforeRow)) {
+ return TRUE;
+ } elseif ($pNumCols < 0 &&
+ ($cellColumnIndex >= ($beforeColumnIndex + $pNumCols)) &&
+ ($cellColumnIndex < $beforeColumnIndex)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Update page breaks when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustPageBreaks(PHPExcel_Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aBreaks = $pSheet->getBreaks();
+ ($pNumCols > 0 || $pNumRows > 0) ?
+ uksort($aBreaks, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
+ uksort($aBreaks, array('PHPExcel_ReferenceHelper','cellSort'));
+
+ foreach ($aBreaks as $key => $value) {
+ if (self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
+ // If we're deleting, then clear any defined breaks that are within the range
+ // of rows/columns that we're deleting
+ $pSheet->setBreak($key, PHPExcel_Worksheet::BREAK_NONE);
+ } else {
+ // Otherwise update any affected breaks by inserting a new break at the appropriate point
+ // and removing the old affected break
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->setBreak($newReference, $value)
+ ->setBreak($key, PHPExcel_Worksheet::BREAK_NONE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update cell comments when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aComments = $pSheet->getComments();
+ $aNewComments = array(); // the new array of all comments
+
+ foreach ($aComments as $key => &$value) {
+ // Any comments inside a deleted range will be ignored
+ if (!self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
+ // Otherwise build a new array of comments indexed by the adjusted cell reference
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ $aNewComments[$newReference] = $value;
+ }
+ }
+ // Replace the comments array with the new set of comments
+ $pSheet->setComments($aNewComments);
+ }
+
+ /**
+ * Update hyperlinks when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aHyperlinkCollection = $pSheet->getHyperlinkCollection();
+ ($pNumCols > 0 || $pNumRows > 0) ?
+ uksort($aHyperlinkCollection, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
+ uksort($aHyperlinkCollection, array('PHPExcel_ReferenceHelper','cellSort'));
+
+ foreach ($aHyperlinkCollection as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->setHyperlink( $newReference, $value );
+ $pSheet->setHyperlink( $key, null );
+ }
+ }
+ }
+
+ /**
+ * Update data validations when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aDataValidationCollection = $pSheet->getDataValidationCollection();
+ ($pNumCols > 0 || $pNumRows > 0) ?
+ uksort($aDataValidationCollection, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
+ uksort($aDataValidationCollection, array('PHPExcel_ReferenceHelper','cellSort'));
+ foreach ($aDataValidationCollection as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->setDataValidation( $newReference, $value );
+ $pSheet->setDataValidation( $key, null );
+ }
+ }
+ }
+
+ /**
+ * Update merged cells when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aMergeCells = $pSheet->getMergeCells();
+ $aNewMergeCells = array(); // the new array of all merge cells
+ foreach ($aMergeCells as $key => &$value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ $aNewMergeCells[$newReference] = $newReference;
+ }
+ $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
+ }
+
+ /**
+ * Update protected cells when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aProtectedCells = $pSheet->getProtectedCells();
+ ($pNumCols > 0 || $pNumRows > 0) ?
+ uksort($aProtectedCells, array('PHPExcel_ReferenceHelper','cellReverseSort')) :
+ uksort($aProtectedCells, array('PHPExcel_ReferenceHelper','cellSort'));
+ foreach ($aProtectedCells as $key => $value) {
+ $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
+ if ($key != $newReference) {
+ $pSheet->protectCells( $newReference, $value, true );
+ $pSheet->unprotectCells( $key );
+ }
+ }
+ }
+
+ /**
+ * Update column dimensions when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
+ if (!empty($aColumnDimensions)) {
+ foreach ($aColumnDimensions as $objColumnDimension) {
+ $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
+ list($newReference) = PHPExcel_Cell::coordinateFromString($newReference);
+ if ($objColumnDimension->getColumnIndex() != $newReference) {
+ $objColumnDimension->setColumnIndex($newReference);
+ }
+ }
+ $pSheet->refreshColumnDimensions();
+ }
+ }
+
+ /**
+ * Update row dimensions when inserting/deleting rows/columns
+ *
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
+ * @param integer $beforeColumnIndex Index number of the column we're inserting/deleting before
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $beforeRow Number of the row we're inserting/deleting before
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function _adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
+ {
+ $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
+ if (!empty($aRowDimensions)) {
+ foreach ($aRowDimensions as $objRowDimension) {
+ $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
+ list(, $newReference) = PHPExcel_Cell::coordinateFromString($newReference);
+ if ($objRowDimension->getRowIndex() != $newReference) {
+ $objRowDimension->setRowIndex($newReference);
+ }
+ }
+ $pSheet->refreshRowDimensions();
+
+ $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
+ for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) {
+ $newDimension = $pSheet->getRowDimension($i);
+ $newDimension->setRowHeight($copyDimension->getRowHeight());
+ $newDimension->setVisible($copyDimension->getVisible());
+ $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
+ $newDimension->setCollapsed($copyDimension->getCollapsed());
+ }
+ }
+ }
+
+ /**
+ * Insert a new column or row, updating all possible related data
+ *
+ * @param string $pBefore Insert before this cell address (e.g. 'A1')
+ * @param integer $pNumCols Number of columns to insert/delete (negative values indicate deletion)
+ * @param integer $pNumRows Number of rows to insert/delete (negative values indicate deletion)
+ * @param PHPExcel_Worksheet $pSheet The worksheet that we're editing
+ * @throws PHPExcel_Exception
+ */
+ public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, PHPExcel_Worksheet $pSheet = NULL)
+ {
+ $remove = ($pNumCols < 0 || $pNumRows < 0);
+ $aCellCollection = $pSheet->getCellCollection();
+
+ // Get coordinates of $pBefore
+ $beforeColumn = 'A';
+ $beforeRow = 1;
+ list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString($pBefore);
+ $beforeColumnIndex = PHPExcel_Cell::columnIndexFromString($beforeColumn);
+
+ // Clear cells if we are removing columns or rows
+ $highestColumn = $pSheet->getHighestColumn();
+ $highestRow = $pSheet->getHighestRow();
+
+ // 1. Clear column strips if we are removing columns
+ if ($pNumCols < 0 && $beforeColumnIndex - 2 + $pNumCols > 0) {
+ for ($i = 1; $i <= $highestRow - 1; ++$i) {
+ for ($j = $beforeColumnIndex - 1 + $pNumCols; $j <= $beforeColumnIndex - 2; ++$j) {
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($j) . $i;
+ $pSheet->removeConditionalStyles($coordinate);
+ if ($pSheet->cellExists($coordinate)) {
+ $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
+ $pSheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
+ }
+ }
+
+ // 2. Clear row strips if we are removing rows
+ if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
+ for ($i = $beforeColumnIndex - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
+ for ($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++$j) {
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . $j;
+ $pSheet->removeConditionalStyles($coordinate);
+ if ($pSheet->cellExists($coordinate)) {
+ $pSheet->getCell($coordinate)->setValueExplicit('', PHPExcel_Cell_DataType::TYPE_NULL);
+ $pSheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
+ }
+ }
+
+ // Loop through cells, bottom-up, and change cell coordinates
+ if($remove) {
+ // It's faster to reverse and pop than to use unshift, especially with large cell collections
+ $aCellCollection = array_reverse($aCellCollection);
+ }
+ while ($cellID = array_pop($aCellCollection)) {
+ $cell = $pSheet->getCell($cellID);
+ $cellIndex = PHPExcel_Cell::columnIndexFromString($cell->getColumn());
+
+ if ($cellIndex-1 + $pNumCols < 0) {
+ continue;
+ }
+
+ // New coordinates
+ $newCoordinates = PHPExcel_Cell::stringFromColumnIndex($cellIndex-1 + $pNumCols) . ($cell->getRow() + $pNumRows);
+
+ // Should the cell be updated? Move value and cellXf index from one cell to another.
+ if (($cellIndex >= $beforeColumnIndex) &&
+ ($cell->getRow() >= $beforeRow)) {
+
+ // Update cell styles
+ $pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex());
+
+ // Insert this cell at its new location
+ if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ // Formula should be adjusted
+ $pSheet->getCell($newCoordinates)
+ ->setValue($this->updateFormulaReferences($cell->getValue(),
+ $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
+ } else {
+ // Formula should not be adjusted
+ $pSheet->getCell($newCoordinates)->setValue($cell->getValue());
+ }
+
+ // Clear the original cell
+ $pSheet->getCellCacheController()->deleteCacheData($cellID);
+
+ } else {
+ /* We don't need to update styles for rows/columns before our insertion position,
+ but we do still need to adjust any formulae in those cells */
+ if ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ // Formula should be adjusted
+ $cell->setValue($this->updateFormulaReferences($cell->getValue(),
+ $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
+ }
+
+ }
+ }
+
+ // Duplicate styles for the newly inserted cells
+ $highestColumn = $pSheet->getHighestColumn();
+ $highestRow = $pSheet->getHighestRow();
+
+ if ($pNumCols > 0 && $beforeColumnIndex - 2 > 0) {
+ for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
+
+ // Style
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex( $beforeColumnIndex - 2 ) . $i;
+ if ($pSheet->cellExists($coordinate)) {
+ $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
+ $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
+ $pSheet->getConditionalStyles($coordinate) : false;
+ for ($j = $beforeColumnIndex - 1; $j <= $beforeColumnIndex - 2 + $pNumCols; ++$j) {
+ $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
+ if ($conditionalStyles) {
+ $cloned = array();
+ foreach ($conditionalStyles as $conditionalStyle) {
+ $cloned[] = clone $conditionalStyle;
+ }
+ $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($j) . $i, $cloned);
+ }
+ }
+ }
+
+ }
+ }
+
+ if ($pNumRows > 0 && $beforeRow - 1 > 0) {
+ for ($i = $beforeColumnIndex - 1; $i <= PHPExcel_Cell::columnIndexFromString($highestColumn) - 1; ++$i) {
+
+ // Style
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($i) . ($beforeRow - 1);
+ if ($pSheet->cellExists($coordinate)) {
+ $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
+ $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
+ $pSheet->getConditionalStyles($coordinate) : false;
+ for ($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++$j) {
+ $pSheet->getCell(PHPExcel_Cell::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+ if ($conditionalStyles) {
+ $cloned = array();
+ foreach ($conditionalStyles as $conditionalStyle) {
+ $cloned[] = clone $conditionalStyle;
+ }
+ $pSheet->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($i) . $j, $cloned);
+ }
+ }
+ }
+ }
+ }
+
+ // Update worksheet: column dimensions
+ $this->_adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: row dimensions
+ $this->_adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: page breaks
+ $this->_adjustPageBreaks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: comments
+ $this->_adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: hyperlinks
+ $this->_adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: data validations
+ $this->_adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: merge cells
+ $this->_adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: protected cells
+ $this->_adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);
+
+ // Update worksheet: autofilter
+ $autoFilter = $pSheet->getAutoFilter();
+ $autoFilterRange = $autoFilter->getRange();
+ if (!empty($autoFilterRange)) {
+ if ($pNumCols != 0) {
+ $autoFilterColumns = array_keys($autoFilter->getColumns());
+ if (count($autoFilterColumns) > 0) {
+ sscanf($pBefore,'%[A-Z]%d', $column, $row);
+ $columnIndex = PHPExcel_Cell::columnIndexFromString($column);
+ list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($autoFilterRange);
+ if ($columnIndex <= $rangeEnd[0]) {
+ if ($pNumCols < 0) {
+ // If we're actually deleting any columns that fall within the autofilter range,
+ // then we delete any rules for those columns
+ $deleteColumn = $columnIndex + $pNumCols - 1;
+ $deleteCount = abs($pNumCols);
+ for ($i = 1; $i <= $deleteCount; ++$i) {
+ if (in_array(PHPExcel_Cell::stringFromColumnIndex($deleteColumn),$autoFilterColumns)) {
+ $autoFilter->clearColumn(PHPExcel_Cell::stringFromColumnIndex($deleteColumn));
+ }
+ ++$deleteColumn;
+ }
+ }
+ $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
+
+ // Shuffle columns in autofilter range
+ if ($pNumCols > 0) {
+ // For insert, we shuffle from end to beginning to avoid overwriting
+ $startColID = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
+ $toColID = PHPExcel_Cell::stringFromColumnIndex($startCol+$pNumCols-1);
+ $endColID = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0]);
+
+ $startColRef = $startCol;
+ $endColRef = $rangeEnd[0];
+ $toColRef = $rangeEnd[0]+$pNumCols;
+
+ do {
+ $autoFilter->shiftColumn(PHPExcel_Cell::stringFromColumnIndex($endColRef-1),PHPExcel_Cell::stringFromColumnIndex($toColRef-1));
+ --$endColRef;
+ --$toColRef;
+ } while ($startColRef <= $endColRef);
+ } else {
+ // For delete, we shuffle from beginning to end to avoid overwriting
+ $startColID = PHPExcel_Cell::stringFromColumnIndex($startCol-1);
+ $toColID = PHPExcel_Cell::stringFromColumnIndex($startCol+$pNumCols-1);
+ $endColID = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0]);
+ do {
+ $autoFilter->shiftColumn($startColID,$toColID);
+ ++$startColID;
+ ++$toColID;
+ } while ($startColID != $endColID);
+ }
+ }
+ }
+ }
+ $pSheet->setAutoFilter( $this->updateCellReference($autoFilterRange, $pBefore, $pNumCols, $pNumRows) );
+ }
+
+ // Update worksheet: freeze pane
+ if ($pSheet->getFreezePane() != '') {
+ $pSheet->freezePane( $this->updateCellReference($pSheet->getFreezePane(), $pBefore, $pNumCols, $pNumRows) );
+ }
+
+ // Page setup
+ if ($pSheet->getPageSetup()->isPrintAreaSet()) {
+ $pSheet->getPageSetup()->setPrintArea( $this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows) );
+ }
+
+ // Update worksheet: drawings
+ $aDrawings = $pSheet->getDrawingCollection();
+ foreach ($aDrawings as $objDrawing) {
+ $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
+ if ($objDrawing->getCoordinates() != $newReference) {
+ $objDrawing->setCoordinates($newReference);
+ }
+ }
+
+ // Update workbook: named ranges
+ if (count($pSheet->getParent()->getNamedRanges()) > 0) {
+ foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
+ if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
+ $namedRange->setRange(
+ $this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows)
+ );
+ }
+ }
+ }
+
+ // Garbage collect
+ $pSheet->garbageCollect();
+ }
+
+ /**
+ * Update references within formulas
+ *
+ * @param string $pFormula Formula to update
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to insert
+ * @param int $pNumRows Number of rows to insert
+ * @param string $sheetName Worksheet name/title
+ * @return string Updated formula
+ * @throws PHPExcel_Exception
+ */
+ public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '') {
+ // Update cell references in the formula
+ $formulaBlocks = explode('"',$pFormula);
+ $i = false;
+ foreach($formulaBlocks as &$formulaBlock) {
+ // Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
+ if ($i = !$i) {
+ $adjustCount = 0;
+ $newCellTokens = $cellTokens = array();
+ // Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
+ $matchCount = preg_match_all('/'.self::REFHELPER_REGEXP_ROWRANGE.'/i', ' '.$formulaBlock.' ', $matches, PREG_SET_ORDER);
+ if ($matchCount > 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3].':'.$match[4];
+ $modified3 = substr($this->updateCellReference('$A'.$match[3],$pBefore,$pNumCols,$pNumRows),2);
+ $modified4 = substr($this->updateCellReference('$A'.$match[4],$pBefore,$pNumCols,$pNumRows),2);
+
+ if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3.':'.$modified4;
+ // 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
+ $column = 100000;
+ $row = 10000000+trim($match[3],'$');
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3].':'.$match[4];
+ $modified3 = substr($this->updateCellReference($match[3].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
+ $modified4 = substr($this->updateCellReference($match[4].'$1',$pBefore,$pNumCols,$pNumRows),0,-2);
+
+ if ($match[3].':'.$match[4] !== $modified3.':'.$modified4) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3.':'.$modified4;
+ // 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
+ $column = PHPExcel_Cell::columnIndexFromString(trim($match[3],'$')) + 100000;
+ $row = 10000000;
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3].':'.$match[4];
+ $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
+ $modified4 = $this->updateCellReference($match[4],$pBefore,$pNumCols,$pNumRows);
+
+ if ($match[3].$match[4] !== $modified3.$modified4) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3.':'.$modified4;
+ list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
+ // 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
+ $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
+ $row = trim($row,'$') + 10000000;
+ $cellIndex = $column.$row;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2].'!' : '';
+ $fromString .= $match[3];
+
+ $modified3 = $this->updateCellReference($match[3],$pBefore,$pNumCols,$pNumRows);
+ if ($match[3] !== $modified3) {
+ if (($match[2] == '') || (trim($match[2],"'") == $sheetName)) {
+ $toString = ($match[2] > '') ? $match[2].'!' : '';
+ $toString .= $modified3;
+ list($column,$row) = PHPExcel_Cell::coordinateFromString($match[3]);
+ // 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
+ $column = PHPExcel_Cell::columnIndexFromString(trim($column,'$')) + 100000;
+ $row = trim($row,'$') + 10000000;
+ $cellIndex = $row . $column;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString);
+ $cellTokens[$cellIndex] = '/(? 0) {
+ if ($pNumCols > 0 || $pNumRows > 0) {
+ krsort($cellTokens);
+ krsort($newCellTokens);
+ } else {
+ ksort($cellTokens);
+ ksort($newCellTokens);
+ } // Update cell references in the formula
+ $formulaBlock = str_replace('\\','',preg_replace($cellTokens,$newCellTokens,$formulaBlock));
+ }
+ }
+ }
+ unset($formulaBlock);
+
+ // Then rebuild the formula string
+ return implode('"',$formulaBlocks);
+ }
+
+ /**
+ * Update cell reference
+ *
+ * @param string $pCellRange Cell range
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to increment
+ * @param int $pNumRows Number of rows to increment
+ * @return string Updated cell range
+ * @throws PHPExcel_Exception
+ */
+ public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
+ // Is it in another worksheet? Will not have to update anything.
+ if (strpos($pCellRange, "!") !== false) {
+ return $pCellRange;
+ // Is it a range or a single cell?
+ } elseif (strpos($pCellRange, ':') === false && strpos($pCellRange, ',') === false) {
+ // Single cell
+ return $this->_updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
+ } elseif (strpos($pCellRange, ':') !== false || strpos($pCellRange, ',') !== false) {
+ // Range
+ return $this->_updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
+ } else {
+ // Return original
+ return $pCellRange;
+ }
+ }
+
+ /**
+ * Update named formulas (i.e. containing worksheet references / named ranges)
+ *
+ * @param PHPExcel $pPhpExcel Object to update
+ * @param string $oldName Old name (name to replace)
+ * @param string $newName New name
+ */
+ public function updateNamedFormulas(PHPExcel $pPhpExcel, $oldName = '', $newName = '') {
+ if ($oldName == '') {
+ return;
+ }
+
+ foreach ($pPhpExcel->getWorksheetIterator() as $sheet) {
+ foreach ($sheet->getCellCollection(false) as $cellID) {
+ $cell = $sheet->getCell($cellID);
+ if (($cell !== NULL) && ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_FORMULA)) {
+ $formula = $cell->getValue();
+ if (strpos($formula, $oldName) !== false) {
+ $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
+ $formula = str_replace($oldName . "!", $newName . "!", $formula);
+ $cell->setValueExplicit($formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Update cell range
+ *
+ * @param string $pCellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to increment
+ * @param int $pNumRows Number of rows to increment
+ * @return string Updated cell range
+ * @throws PHPExcel_Exception
+ */
+ private function _updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
+ if (strpos($pCellRange,':') !== false || strpos($pCellRange, ',') !== false) {
+ // Update range
+ $range = PHPExcel_Cell::splitRange($pCellRange);
+ $ic = count($range);
+ for ($i = 0; $i < $ic; ++$i) {
+ $jc = count($range[$i]);
+ for ($j = 0; $j < $jc; ++$j) {
+ if (ctype_alpha($range[$i][$j])) {
+ $r = PHPExcel_Cell::coordinateFromString($this->_updateSingleCellReference($range[$i][$j].'1', $pBefore, $pNumCols, $pNumRows));
+ $range[$i][$j] = $r[0];
+ } elseif(ctype_digit($range[$i][$j])) {
+ $r = PHPExcel_Cell::coordinateFromString($this->_updateSingleCellReference('A'.$range[$i][$j], $pBefore, $pNumCols, $pNumRows));
+ $range[$i][$j] = $r[1];
+ } else {
+ $range[$i][$j] = $this->_updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
+ }
+ }
+ }
+
+ // Recreate range string
+ return PHPExcel_Cell::buildRange($range);
+ } else {
+ throw new PHPExcel_Exception("Only cell ranges may be passed to this method.");
+ }
+ }
+
+ /**
+ * Update single cell reference
+ *
+ * @param string $pCellReference Single cell reference
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to increment
+ * @param int $pNumRows Number of rows to increment
+ * @return string Updated cell reference
+ * @throws PHPExcel_Exception
+ */
+ private function _updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) {
+ if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) {
+ // Get coordinates of $pBefore
+ list($beforeColumn, $beforeRow) = PHPExcel_Cell::coordinateFromString( $pBefore );
+
+ // Get coordinates of $pCellReference
+ list($newColumn, $newRow) = PHPExcel_Cell::coordinateFromString( $pCellReference );
+
+ // Verify which parts should be updated
+ $updateColumn = (($newColumn{0} != '$') && ($beforeColumn{0} != '$') &&
+ PHPExcel_Cell::columnIndexFromString($newColumn) >= PHPExcel_Cell::columnIndexFromString($beforeColumn));
+ $updateRow = (($newRow{0} != '$') && ($beforeRow{0} != '$') &&
+ $newRow >= $beforeRow);
+
+ // Create new column reference
+ if ($updateColumn) {
+ $newColumn = PHPExcel_Cell::stringFromColumnIndex( PHPExcel_Cell::columnIndexFromString($newColumn) - 1 + $pNumCols );
+ }
+
+ // Create new row reference
+ if ($updateRow) {
+ $newRow = $newRow + $pNumRows;
+ }
+
+ // Return new reference
+ return $newColumn . $newRow;
+ } else {
+ throw new PHPExcel_Exception("Only single cell references may be passed to this method.");
+ }
+ }
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ *
+ * @throws PHPExcel_Exception
+ */
+ public final function __clone() {
+ throw new PHPExcel_Exception("Cloning a Singleton is not allowed!");
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/RichText.php b/includes/PHPExcel/Classes/PHPExcel/RichText.php
new file mode 100644
index 0000000..1932631
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/RichText.php
@@ -0,0 +1,199 @@
+_richTextElements = array();
+
+ // Rich-Text string attached to cell?
+ if ($pCell !== NULL) {
+ // Add cell text and style
+ if ($pCell->getValue() != "") {
+ $objRun = new PHPExcel_RichText_Run($pCell->getValue());
+ $objRun->setFont(clone $pCell->getParent()->getStyle($pCell->getCoordinate())->getFont());
+ $this->addText($objRun);
+ }
+
+ // Set parent value
+ $pCell->setValueExplicit($this, PHPExcel_Cell_DataType::TYPE_STRING);
+ }
+ }
+
+ /**
+ * Add text
+ *
+ * @param PHPExcel_RichText_ITextElement $pText Rich text element
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_RichText
+ */
+ public function addText(PHPExcel_RichText_ITextElement $pText = null)
+ {
+ $this->_richTextElements[] = $pText;
+ return $this;
+ }
+
+ /**
+ * Create text
+ *
+ * @param string $pText Text
+ * @return PHPExcel_RichText_TextElement
+ * @throws PHPExcel_Exception
+ */
+ public function createText($pText = '')
+ {
+ $objText = new PHPExcel_RichText_TextElement($pText);
+ $this->addText($objText);
+ return $objText;
+ }
+
+ /**
+ * Create text run
+ *
+ * @param string $pText Text
+ * @return PHPExcel_RichText_Run
+ * @throws PHPExcel_Exception
+ */
+ public function createTextRun($pText = '')
+ {
+ $objText = new PHPExcel_RichText_Run($pText);
+ $this->addText($objText);
+ return $objText;
+ }
+
+ /**
+ * Get plain text
+ *
+ * @return string
+ */
+ public function getPlainText()
+ {
+ // Return value
+ $returnValue = '';
+
+ // Loop through all PHPExcel_RichText_ITextElement
+ foreach ($this->_richTextElements as $text) {
+ $returnValue .= $text->getText();
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getPlainText();
+ }
+
+ /**
+ * Get Rich Text elements
+ *
+ * @return PHPExcel_RichText_ITextElement[]
+ */
+ public function getRichTextElements()
+ {
+ return $this->_richTextElements;
+ }
+
+ /**
+ * Set Rich Text elements
+ *
+ * @param PHPExcel_RichText_ITextElement[] $pElements Array of elements
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_RichText
+ */
+ public function setRichTextElements($pElements = null)
+ {
+ if (is_array($pElements)) {
+ $this->_richTextElements = $pElements;
+ } else {
+ throw new PHPExcel_Exception("Invalid PHPExcel_RichText_ITextElement[] array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ $hashElements = '';
+ foreach ($this->_richTextElements as $element) {
+ $hashElements .= $element->getHashCode();
+ }
+
+ return md5(
+ $hashElements
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/Settings.php b/includes/PHPExcel/Classes/PHPExcel/Settings.php
new file mode 100644
index 0000000..c78d935
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Settings.php
@@ -0,0 +1,391 @@
+setLocale($locale);
+ } // function setLocale()
+
+
+ /**
+ * Set details of the external library that PHPExcel should use for rendering charts
+ *
+ * @param string $libraryName Internal reference name of the library
+ * e.g. PHPExcel_Settings::CHART_RENDERER_JPGRAPH
+ * @param string $libraryBaseDir Directory path to the library's base folder
+ *
+ * @return boolean Success or failure
+ */
+ public static function setChartRenderer($libraryName, $libraryBaseDir)
+ {
+ if (!self::setChartRendererName($libraryName))
+ return FALSE;
+ return self::setChartRendererPath($libraryBaseDir);
+ } // function setChartRenderer()
+
+
+ /**
+ * Identify to PHPExcel the external library to use for rendering charts
+ *
+ * @param string $libraryName Internal reference name of the library
+ * e.g. PHPExcel_Settings::CHART_RENDERER_JPGRAPH
+ *
+ * @return boolean Success or failure
+ */
+ public static function setChartRendererName($libraryName)
+ {
+ if (!in_array($libraryName,self::$_chartRenderers)) {
+ return FALSE;
+ }
+
+ self::$_chartRendererName = $libraryName;
+
+ return TRUE;
+ } // function setChartRendererName()
+
+
+ /**
+ * Tell PHPExcel where to find the external library to use for rendering charts
+ *
+ * @param string $libraryBaseDir Directory path to the library's base folder
+ * @return boolean Success or failure
+ */
+ public static function setChartRendererPath($libraryBaseDir)
+ {
+ if ((file_exists($libraryBaseDir) === false) || (is_readable($libraryBaseDir) === false)) {
+ return FALSE;
+ }
+ self::$_chartRendererPath = $libraryBaseDir;
+
+ return TRUE;
+ } // function setChartRendererPath()
+
+
+ /**
+ * Return the Chart Rendering Library that PHPExcel is currently configured to use (e.g. jpgraph)
+ *
+ * @return string|NULL Internal reference name of the Chart Rendering Library that PHPExcel is
+ * currently configured to use
+ * e.g. PHPExcel_Settings::CHART_RENDERER_JPGRAPH
+ */
+ public static function getChartRendererName()
+ {
+ return self::$_chartRendererName;
+ } // function getChartRendererName()
+
+
+ /**
+ * Return the directory path to the Chart Rendering Library that PHPExcel is currently configured to use
+ *
+ * @return string|NULL Directory Path to the Chart Rendering Library that PHPExcel is
+ * currently configured to use
+ */
+ public static function getChartRendererPath()
+ {
+ return self::$_chartRendererPath;
+ } // function getChartRendererPath()
+
+
+ /**
+ * Set details of the external library that PHPExcel should use for rendering PDF files
+ *
+ * @param string $libraryName Internal reference name of the library
+ * e.g. PHPExcel_Settings::PDF_RENDERER_TCPDF,
+ * PHPExcel_Settings::PDF_RENDERER_DOMPDF
+ * or PHPExcel_Settings::PDF_RENDERER_MPDF
+ * @param string $libraryBaseDir Directory path to the library's base folder
+ *
+ * @return boolean Success or failure
+ */
+ public static function setPdfRenderer($libraryName, $libraryBaseDir)
+ {
+ if (!self::setPdfRendererName($libraryName))
+ return FALSE;
+ return self::setPdfRendererPath($libraryBaseDir);
+ } // function setPdfRenderer()
+
+
+ /**
+ * Identify to PHPExcel the external library to use for rendering PDF files
+ *
+ * @param string $libraryName Internal reference name of the library
+ * e.g. PHPExcel_Settings::PDF_RENDERER_TCPDF,
+ * PHPExcel_Settings::PDF_RENDERER_DOMPDF
+ * or PHPExcel_Settings::PDF_RENDERER_MPDF
+ *
+ * @return boolean Success or failure
+ */
+ public static function setPdfRendererName($libraryName)
+ {
+ if (!in_array($libraryName,self::$_pdfRenderers)) {
+ return FALSE;
+ }
+
+ self::$_pdfRendererName = $libraryName;
+
+ return TRUE;
+ } // function setPdfRendererName()
+
+
+ /**
+ * Tell PHPExcel where to find the external library to use for rendering PDF files
+ *
+ * @param string $libraryBaseDir Directory path to the library's base folder
+ * @return boolean Success or failure
+ */
+ public static function setPdfRendererPath($libraryBaseDir)
+ {
+ if ((file_exists($libraryBaseDir) === false) || (is_readable($libraryBaseDir) === false)) {
+ return FALSE;
+ }
+ self::$_pdfRendererPath = $libraryBaseDir;
+
+ return TRUE;
+ } // function setPdfRendererPath()
+
+
+ /**
+ * Return the PDF Rendering Library that PHPExcel is currently configured to use (e.g. dompdf)
+ *
+ * @return string|NULL Internal reference name of the PDF Rendering Library that PHPExcel is
+ * currently configured to use
+ * e.g. PHPExcel_Settings::PDF_RENDERER_TCPDF,
+ * PHPExcel_Settings::PDF_RENDERER_DOMPDF
+ * or PHPExcel_Settings::PDF_RENDERER_MPDF
+ */
+ public static function getPdfRendererName()
+ {
+ return self::$_pdfRendererName;
+ } // function getPdfRendererName()
+
+ /**
+ * Return the directory path to the PDF Rendering Library that PHPExcel is currently configured to use
+ *
+ * @return string|NULL Directory Path to the PDF Rendering Library that PHPExcel is
+ * currently configured to use
+ */
+ public static function getPdfRendererPath()
+ {
+ return self::$_pdfRendererPath;
+ } // function getPdfRendererPath()
+
+ /**
+ * Set default options for libxml loader
+ *
+ * @param int $options Default options for libxml loader
+ */
+ public static function setLibXmlLoaderOptions($options = null)
+ {
+ if (is_null($options) && defined(LIBXML_DTDLOAD)) {
+ $options = LIBXML_DTDLOAD | LIBXML_DTDATTR;
+ }
+ if (version_compare(PHP_VERSION, '5.2.11') >= 0) {
+ @libxml_disable_entity_loader($options == (LIBXML_DTDLOAD | LIBXML_DTDATTR));
+ }
+ self::$_libXmlLoaderOptions = $options;
+ } // function setLibXmlLoaderOptions
+
+ /**
+ * Get default options for libxml loader.
+ * Defaults to LIBXML_DTDLOAD | LIBXML_DTDATTR when not set explicitly.
+ *
+ * @return int Default options for libxml loader
+ */
+ public static function getLibXmlLoaderOptions()
+ {
+ if (is_null(self::$_libXmlLoaderOptions) && defined(LIBXML_DTDLOAD)) {
+ self::setLibXmlLoaderOptions(LIBXML_DTDLOAD | LIBXML_DTDATTR);
+ }
+ if (version_compare(PHP_VERSION, '5.2.11') >= 0) {
+ @libxml_disable_entity_loader(self::$_libXmlLoaderOptions == (LIBXML_DTDLOAD | LIBXML_DTDATTR));
+ }
+ return self::$_libXmlLoaderOptions;
+ } // function getLibXmlLoaderOptions
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/Style.php b/includes/PHPExcel/Classes/PHPExcel/Style.php
new file mode 100644
index 0000000..9c29320
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Style.php
@@ -0,0 +1,668 @@
+_isSupervisor = $isSupervisor;
+
+ // Initialise values
+ $this->_conditionalStyles = array();
+ $this->_font = new PHPExcel_Style_Font($isSupervisor, $isConditional);
+ $this->_fill = new PHPExcel_Style_Fill($isSupervisor, $isConditional);
+ $this->_borders = new PHPExcel_Style_Borders($isSupervisor, $isConditional);
+ $this->_alignment = new PHPExcel_Style_Alignment($isSupervisor, $isConditional);
+ $this->_numberFormat = new PHPExcel_Style_NumberFormat($isSupervisor, $isConditional);
+ $this->_protection = new PHPExcel_Style_Protection($isSupervisor, $isConditional);
+
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->_font->bindParent($this);
+ $this->_fill->bindParent($this);
+ $this->_borders->bindParent($this);
+ $this->_alignment->bindParent($this);
+ $this->_numberFormat->bindParent($this);
+ $this->_protection->bindParent($this);
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style
+ */
+ public function getSharedComponent()
+ {
+ $activeSheet = $this->getActiveSheet();
+ $selectedCell = $this->getActiveCell(); // e.g. 'A1'
+
+ if ($activeSheet->cellExists($selectedCell)) {
+ $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
+ } else {
+ $xfIndex = 0;
+ }
+
+ return $this->_parent->getCellXfByIndex($xfIndex);
+ }
+
+ /**
+ * Get parent. Only used for style supervisor
+ *
+ * @return PHPExcel
+ */
+ public function getParent()
+ {
+ return $this->_parent;
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('quotePrefix' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->applyFromArray(
+ * array(
+ * 'font' => array(
+ * 'name' => 'Arial',
+ * 'bold' => true,
+ * 'italic' => false,
+ * 'underline' => PHPExcel_Style_Font::UNDERLINE_DOUBLE,
+ * 'strike' => false,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * ),
+ * 'borders' => array(
+ * 'bottom' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * ),
+ * 'top' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * )
+ * ),
+ * 'quotePrefix' => true
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @param boolean $pAdvanced Advanced mode for setting borders.
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style
+ */
+ public function applyFromArray($pStyles = null, $pAdvanced = true)
+ {
+ if (is_array($pStyles)) {
+ if ($this->_isSupervisor) {
+
+ $pRange = $this->getSelectedCells();
+
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Is it a cell range or a single cell?
+ if (strpos($pRange, ':') === false) {
+ $rangeA = $pRange;
+ $rangeB = $pRange;
+ } else {
+ list($rangeA, $rangeB) = explode(':', $pRange);
+ }
+
+ // Calculate range outer borders
+ $rangeStart = PHPExcel_Cell::coordinateFromString($rangeA);
+ $rangeEnd = PHPExcel_Cell::coordinateFromString($rangeB);
+
+ // Translate column into index
+ $rangeStart[0] = PHPExcel_Cell::columnIndexFromString($rangeStart[0]) - 1;
+ $rangeEnd[0] = PHPExcel_Cell::columnIndexFromString($rangeEnd[0]) - 1;
+
+ // Make sure we can loop upwards on rows and columns
+ if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+ $tmp = $rangeStart;
+ $rangeStart = $rangeEnd;
+ $rangeEnd = $tmp;
+ }
+
+ // ADVANCED MODE:
+
+ if ($pAdvanced && isset($pStyles['borders'])) {
+
+ // 'allborders' is a shorthand property for 'outline' and 'inside' and
+ // it applies to components that have not been set explicitly
+ if (isset($pStyles['borders']['allborders'])) {
+ foreach (array('outline', 'inside') as $component) {
+ if (!isset($pStyles['borders'][$component])) {
+ $pStyles['borders'][$component] = $pStyles['borders']['allborders'];
+ }
+ }
+ unset($pStyles['borders']['allborders']); // not needed any more
+ }
+
+ // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
+ // it applies to components that have not been set explicitly
+ if (isset($pStyles['borders']['outline'])) {
+ foreach (array('top', 'right', 'bottom', 'left') as $component) {
+ if (!isset($pStyles['borders'][$component])) {
+ $pStyles['borders'][$component] = $pStyles['borders']['outline'];
+ }
+ }
+ unset($pStyles['borders']['outline']); // not needed any more
+ }
+
+ // 'inside' is a shorthand property for 'vertical' and 'horizontal'
+ // it applies to components that have not been set explicitly
+ if (isset($pStyles['borders']['inside'])) {
+ foreach (array('vertical', 'horizontal') as $component) {
+ if (!isset($pStyles['borders'][$component])) {
+ $pStyles['borders'][$component] = $pStyles['borders']['inside'];
+ }
+ }
+ unset($pStyles['borders']['inside']); // not needed any more
+ }
+
+ // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
+ $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
+ $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);
+
+ // loop through up to 3 x 3 = 9 regions
+ for ($x = 1; $x <= $xMax; ++$x) {
+ // start column index for region
+ $colStart = ($x == 3) ?
+ PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0])
+ : PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] + $x - 1);
+
+ // end column index for region
+ $colEnd = ($x == 1) ?
+ PHPExcel_Cell::stringFromColumnIndex($rangeStart[0])
+ : PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
+
+ for ($y = 1; $y <= $yMax; ++$y) {
+
+ // which edges are touching the region
+ $edges = array();
+
+ // are we at left edge
+ if ($x == 1) {
+ $edges[] = 'left';
+ }
+
+ // are we at right edge
+ if ($x == $xMax) {
+ $edges[] = 'right';
+ }
+
+ // are we at top edge?
+ if ($y == 1) {
+ $edges[] = 'top';
+ }
+
+ // are we at bottom edge?
+ if ($y == $yMax) {
+ $edges[] = 'bottom';
+ }
+
+ // start row index for region
+ $rowStart = ($y == 3) ?
+ $rangeEnd[1] : $rangeStart[1] + $y - 1;
+
+ // end row index for region
+ $rowEnd = ($y == 1) ?
+ $rangeStart[1] : $rangeEnd[1] - $yMax + $y;
+
+ // build range for region
+ $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
+
+ // retrieve relevant style array for region
+ $regionStyles = $pStyles;
+ unset($regionStyles['borders']['inside']);
+
+ // what are the inner edges of the region when looking at the selection
+ $innerEdges = array_diff( array('top', 'right', 'bottom', 'left'), $edges );
+
+ // inner edges that are not touching the region should take the 'inside' border properties if they have been set
+ foreach ($innerEdges as $innerEdge) {
+ switch ($innerEdge) {
+ case 'top':
+ case 'bottom':
+ // should pick up 'horizontal' border property if set
+ if (isset($pStyles['borders']['horizontal'])) {
+ $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
+ } else {
+ unset($regionStyles['borders'][$innerEdge]);
+ }
+ break;
+ case 'left':
+ case 'right':
+ // should pick up 'vertical' border property if set
+ if (isset($pStyles['borders']['vertical'])) {
+ $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
+ } else {
+ unset($regionStyles['borders'][$innerEdge]);
+ }
+ break;
+ }
+ }
+
+ // apply region style to region by calling applyFromArray() in simple mode
+ $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
+ }
+ }
+ return $this;
+ }
+
+ // SIMPLE MODE:
+
+ // Selection type, inspect
+ if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
+ $selectionType = 'COLUMN';
+ } else if (preg_match('/^A[0-9]+:XFD[0-9]+$/', $pRange)) {
+ $selectionType = 'ROW';
+ } else {
+ $selectionType = 'CELL';
+ }
+
+ // First loop through columns, rows, or cells to find out which styles are affected by this operation
+ switch ($selectionType) {
+ case 'COLUMN':
+ $oldXfIndexes = array();
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
+ }
+ break;
+
+ case 'ROW':
+ $oldXfIndexes = array();
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
+ $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
+ } else {
+ $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
+ }
+ }
+ break;
+
+ case 'CELL':
+ $oldXfIndexes = array();
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
+ }
+ }
+ break;
+ }
+
+ // clone each of the affected styles, apply the style array, and add the new styles to the workbook
+ $workbook = $this->getActiveSheet()->getParent();
+ foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
+ $style = $workbook->getCellXfByIndex($oldXfIndex);
+ $newStyle = clone $style;
+ $newStyle->applyFromArray($pStyles);
+
+ if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
+ // there is already such cell Xf in our collection
+ $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
+ } else {
+ // we don't have such a cell Xf, need to add
+ $workbook->addCellXf($newStyle);
+ $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
+ }
+ }
+
+ // Loop through columns, rows, or cells again and update the XF index
+ switch ($selectionType) {
+ case 'COLUMN':
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
+ $oldXfIndex = $columnDimension->getXfIndex();
+ $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
+ }
+ break;
+
+ case 'ROW':
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $rowDimension = $this->getActiveSheet()->getRowDimension($row);
+ $oldXfIndex = $rowDimension->getXfIndex() === null ?
+ 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
+ $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
+ }
+ break;
+
+ case 'CELL':
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
+ $oldXfIndex = $cell->getXfIndex();
+ $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
+ }
+ }
+ break;
+ }
+
+ } else {
+ // not a supervisor, just apply the style array directly on style object
+ if (array_key_exists('fill', $pStyles)) {
+ $this->getFill()->applyFromArray($pStyles['fill']);
+ }
+ if (array_key_exists('font', $pStyles)) {
+ $this->getFont()->applyFromArray($pStyles['font']);
+ }
+ if (array_key_exists('borders', $pStyles)) {
+ $this->getBorders()->applyFromArray($pStyles['borders']);
+ }
+ if (array_key_exists('alignment', $pStyles)) {
+ $this->getAlignment()->applyFromArray($pStyles['alignment']);
+ }
+ if (array_key_exists('numberformat', $pStyles)) {
+ $this->getNumberFormat()->applyFromArray($pStyles['numberformat']);
+ }
+ if (array_key_exists('protection', $pStyles)) {
+ $this->getProtection()->applyFromArray($pStyles['protection']);
+ }
+ if (array_key_exists('quotePrefix', $pStyles)) {
+ $this->_quotePrefix = $pStyles['quotePrefix'];
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Fill
+ *
+ * @return PHPExcel_Style_Fill
+ */
+ public function getFill()
+ {
+ return $this->_fill;
+ }
+
+ /**
+ * Get Font
+ *
+ * @return PHPExcel_Style_Font
+ */
+ public function getFont()
+ {
+ return $this->_font;
+ }
+
+ /**
+ * Set font
+ *
+ * @param PHPExcel_Style_Font $font
+ * @return PHPExcel_Style
+ */
+ public function setFont(PHPExcel_Style_Font $font)
+ {
+ $this->_font = $font;
+ return $this;
+ }
+
+ /**
+ * Get Borders
+ *
+ * @return PHPExcel_Style_Borders
+ */
+ public function getBorders()
+ {
+ return $this->_borders;
+ }
+
+ /**
+ * Get Alignment
+ *
+ * @return PHPExcel_Style_Alignment
+ */
+ public function getAlignment()
+ {
+ return $this->_alignment;
+ }
+
+ /**
+ * Get Number Format
+ *
+ * @return PHPExcel_Style_NumberFormat
+ */
+ public function getNumberFormat()
+ {
+ return $this->_numberFormat;
+ }
+
+ /**
+ * Get Conditional Styles. Only used on supervisor.
+ *
+ * @return PHPExcel_Style_Conditional[]
+ */
+ public function getConditionalStyles()
+ {
+ return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
+ }
+
+ /**
+ * Set Conditional Styles. Only used on supervisor.
+ *
+ * @param PHPExcel_Style_Conditional[] $pValue Array of condtional styles
+ * @return PHPExcel_Style
+ */
+ public function setConditionalStyles($pValue = null)
+ {
+ if (is_array($pValue)) {
+ $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
+ }
+ return $this;
+ }
+
+ /**
+ * Get Protection
+ *
+ * @return PHPExcel_Style_Protection
+ */
+ public function getProtection()
+ {
+ return $this->_protection;
+ }
+
+ /**
+ * Get quote prefix
+ *
+ * @return boolean
+ */
+ public function getQuotePrefix()
+ {
+ if ($this->_isSupervisor) {
+ return $this->getSharedComponent()->getQuotePrefix();
+ }
+ return $this->_quotePrefix;
+ }
+
+ /**
+ * Set quote prefix
+ *
+ * @param boolean $pValue
+ */
+ public function setQuotePrefix($pValue)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->_isSupervisor) {
+ $styleArray = array('quotePrefix' => $pValue);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->_quotePrefix = (boolean) $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ $hashConditionals = '';
+ foreach ($this->_conditionalStyles as $conditional) {
+ $hashConditionals .= $conditional->getHashCode();
+ }
+
+ return md5(
+ $this->_fill->getHashCode()
+ . $this->_font->getHashCode()
+ . $this->_borders->getHashCode()
+ . $this->_alignment->getHashCode()
+ . $this->_numberFormat->getHashCode()
+ . $hashConditionals
+ . $this->_protection->getHashCode()
+ . ($this->_quotePrefix ? 't' : 'f')
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Get own index in style collection
+ *
+ * @return int
+ */
+ public function getIndex()
+ {
+ return $this->_index;
+ }
+
+ /**
+ * Set own index in style collection
+ *
+ * @param int $pValue
+ */
+ public function setIndex($pValue)
+ {
+ $this->_index = $pValue;
+ }
+
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/Worksheet.php b/includes/PHPExcel/Classes/PHPExcel/Worksheet.php
new file mode 100644
index 0000000..2b0b57a
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/Worksheet.php
@@ -0,0 +1,2945 @@
+_parent = $pParent;
+ $this->setTitle($pTitle, FALSE);
+ // setTitle can change $pTitle
+ $this->setCodeName($this->getTitle());
+ $this->setSheetState(PHPExcel_Worksheet::SHEETSTATE_VISIBLE);
+
+ $this->_cellCollection = PHPExcel_CachedObjectStorageFactory::getInstance($this);
+
+ // Set page setup
+ $this->_pageSetup = new PHPExcel_Worksheet_PageSetup();
+
+ // Set page margins
+ $this->_pageMargins = new PHPExcel_Worksheet_PageMargins();
+
+ // Set page header/footer
+ $this->_headerFooter = new PHPExcel_Worksheet_HeaderFooter();
+
+ // Set sheet view
+ $this->_sheetView = new PHPExcel_Worksheet_SheetView();
+
+ // Drawing collection
+ $this->_drawingCollection = new ArrayObject();
+
+ // Chart collection
+ $this->_chartCollection = new ArrayObject();
+
+ // Protection
+ $this->_protection = new PHPExcel_Worksheet_Protection();
+
+ // Default row dimension
+ $this->_defaultRowDimension = new PHPExcel_Worksheet_RowDimension(NULL);
+
+ // Default column dimension
+ $this->_defaultColumnDimension = new PHPExcel_Worksheet_ColumnDimension(NULL);
+
+ $this->_autoFilter = new PHPExcel_Worksheet_AutoFilter(NULL, $this);
+ }
+
+
+ /**
+ * Disconnect all cells from this PHPExcel_Worksheet object,
+ * typically so that the worksheet object can be unset
+ *
+ */
+ public function disconnectCells() {
+ if ( $this->_cellCollection !== NULL){
+ $this->_cellCollection->unsetWorksheetCells();
+ $this->_cellCollection = NULL;
+ }
+ // detach ourself from the workbook, so that it can then delete this worksheet successfully
+ $this->_parent = null;
+ }
+
+ /**
+ * Code to execute when this worksheet is unset()
+ *
+ */
+ function __destruct() {
+ PHPExcel_Calculation::getInstance($this->_parent)
+ ->clearCalculationCacheForWorksheet($this->_title);
+
+ $this->disconnectCells();
+ }
+
+ /**
+ * Return the cache controller for the cell collection
+ *
+ * @return PHPExcel_CachedObjectStorage_xxx
+ */
+ public function getCellCacheController() {
+ return $this->_cellCollection;
+ } // function getCellCacheController()
+
+
+ /**
+ * Get array of invalid characters for sheet title
+ *
+ * @return array
+ */
+ public static function getInvalidCharacters()
+ {
+ return self::$_invalidCharacters;
+ }
+
+ /**
+ * Check sheet code name for valid Excel syntax
+ *
+ * @param string $pValue The string to check
+ * @return string The valid string
+ * @throws Exception
+ */
+ private static function _checkSheetCodeName($pValue)
+ {
+ $CharCount = PHPExcel_Shared_String::CountCharacters($pValue);
+ if ($CharCount == 0) {
+ throw new PHPExcel_Exception('Sheet code name cannot be empty.');
+ }
+ // Some of the printable ASCII characters are invalid: * : / \ ? [ ] and first and last characters cannot be a "'"
+ if ((str_replace(self::$_invalidCharacters, '', $pValue) !== $pValue) ||
+ (PHPExcel_Shared_String::Substring($pValue,-1,1)=='\'') ||
+ (PHPExcel_Shared_String::Substring($pValue,0,1)=='\'')) {
+ throw new PHPExcel_Exception('Invalid character found in sheet code name');
+ }
+
+ // Maximum 31 characters allowed for sheet title
+ if ($CharCount > 31) {
+ throw new PHPExcel_Exception('Maximum 31 characters allowed in sheet code name.');
+ }
+
+ return $pValue;
+ }
+
+ /**
+ * Check sheet title for valid Excel syntax
+ *
+ * @param string $pValue The string to check
+ * @return string The valid string
+ * @throws PHPExcel_Exception
+ */
+ private static function _checkSheetTitle($pValue)
+ {
+ // Some of the printable ASCII characters are invalid: * : / \ ? [ ]
+ if (str_replace(self::$_invalidCharacters, '', $pValue) !== $pValue) {
+ throw new PHPExcel_Exception('Invalid character found in sheet title');
+ }
+
+ // Maximum 31 characters allowed for sheet title
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 31) {
+ throw new PHPExcel_Exception('Maximum 31 characters allowed in sheet title.');
+ }
+
+ return $pValue;
+ }
+
+ /**
+ * Get collection of cells
+ *
+ * @param boolean $pSorted Also sort the cell collection?
+ * @return PHPExcel_Cell[]
+ */
+ public function getCellCollection($pSorted = true)
+ {
+ if ($pSorted) {
+ // Re-order cell collection
+ return $this->sortCellCollection();
+ }
+ if ($this->_cellCollection !== NULL) {
+ return $this->_cellCollection->getCellList();
+ }
+ return array();
+ }
+
+ /**
+ * Sort collection of cells
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function sortCellCollection()
+ {
+ if ($this->_cellCollection !== NULL) {
+ return $this->_cellCollection->getSortedCellList();
+ }
+ return array();
+ }
+
+ /**
+ * Get collection of row dimensions
+ *
+ * @return PHPExcel_Worksheet_RowDimension[]
+ */
+ public function getRowDimensions()
+ {
+ return $this->_rowDimensions;
+ }
+
+ /**
+ * Get default row dimension
+ *
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function getDefaultRowDimension()
+ {
+ return $this->_defaultRowDimension;
+ }
+
+ /**
+ * Get collection of column dimensions
+ *
+ * @return PHPExcel_Worksheet_ColumnDimension[]
+ */
+ public function getColumnDimensions()
+ {
+ return $this->_columnDimensions;
+ }
+
+ /**
+ * Get default column dimension
+ *
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function getDefaultColumnDimension()
+ {
+ return $this->_defaultColumnDimension;
+ }
+
+ /**
+ * Get collection of drawings
+ *
+ * @return PHPExcel_Worksheet_BaseDrawing[]
+ */
+ public function getDrawingCollection()
+ {
+ return $this->_drawingCollection;
+ }
+
+ /**
+ * Get collection of charts
+ *
+ * @return PHPExcel_Chart[]
+ */
+ public function getChartCollection()
+ {
+ return $this->_chartCollection;
+ }
+
+ /**
+ * Add chart
+ *
+ * @param PHPExcel_Chart $pChart
+ * @param int|null $iChartIndex Index where chart should go (0,1,..., or null for last)
+ * @return PHPExcel_Chart
+ */
+ public function addChart(PHPExcel_Chart $pChart = null, $iChartIndex = null)
+ {
+ $pChart->setWorksheet($this);
+ if (is_null($iChartIndex)) {
+ $this->_chartCollection[] = $pChart;
+ } else {
+ // Insert the chart at the requested index
+ array_splice($this->_chartCollection, $iChartIndex, 0, array($pChart));
+ }
+
+ return $pChart;
+ }
+
+ /**
+ * Return the count of charts on this worksheet
+ *
+ * @return int The number of charts
+ */
+ public function getChartCount()
+ {
+ return count($this->_chartCollection);
+ }
+
+ /**
+ * Get a chart by its index position
+ *
+ * @param string $index Chart index position
+ * @return false|PHPExcel_Chart
+ * @throws PHPExcel_Exception
+ */
+ public function getChartByIndex($index = null)
+ {
+ $chartCount = count($this->_chartCollection);
+ if ($chartCount == 0) {
+ return false;
+ }
+ if (is_null($index)) {
+ $index = --$chartCount;
+ }
+ if (!isset($this->_chartCollection[$index])) {
+ return false;
+ }
+
+ return $this->_chartCollection[$index];
+ }
+
+ /**
+ * Return an array of the names of charts on this worksheet
+ *
+ * @return string[] The names of charts
+ * @throws PHPExcel_Exception
+ */
+ public function getChartNames()
+ {
+ $chartNames = array();
+ foreach($this->_chartCollection as $chart) {
+ $chartNames[] = $chart->getName();
+ }
+ return $chartNames;
+ }
+
+ /**
+ * Get a chart by name
+ *
+ * @param string $chartName Chart name
+ * @return false|PHPExcel_Chart
+ * @throws PHPExcel_Exception
+ */
+ public function getChartByName($chartName = '')
+ {
+ $chartCount = count($this->_chartCollection);
+ if ($chartCount == 0) {
+ return false;
+ }
+ foreach($this->_chartCollection as $index => $chart) {
+ if ($chart->getName() == $chartName) {
+ return $this->_chartCollection[$index];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Refresh column dimensions
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function refreshColumnDimensions()
+ {
+ $currentColumnDimensions = $this->getColumnDimensions();
+ $newColumnDimensions = array();
+
+ foreach ($currentColumnDimensions as $objColumnDimension) {
+ $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
+ }
+
+ $this->_columnDimensions = $newColumnDimensions;
+
+ return $this;
+ }
+
+ /**
+ * Refresh row dimensions
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function refreshRowDimensions()
+ {
+ $currentRowDimensions = $this->getRowDimensions();
+ $newRowDimensions = array();
+
+ foreach ($currentRowDimensions as $objRowDimension) {
+ $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
+ }
+
+ $this->_rowDimensions = $newRowDimensions;
+
+ return $this;
+ }
+
+ /**
+ * Calculate worksheet dimension
+ *
+ * @return string String containing the dimension of this worksheet
+ */
+ public function calculateWorksheetDimension()
+ {
+ // Return
+ return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
+ }
+
+ /**
+ * Calculate worksheet data dimension
+ *
+ * @return string String containing the dimension of this worksheet that actually contain data
+ */
+ public function calculateWorksheetDataDimension()
+ {
+ // Return
+ return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
+ }
+
+ /**
+ * Calculate widths for auto-size columns
+ *
+ * @param boolean $calculateMergeCells Calculate merge cell width
+ * @return PHPExcel_Worksheet;
+ */
+ public function calculateColumnWidths($calculateMergeCells = false)
+ {
+ // initialize $autoSizes array
+ $autoSizes = array();
+ foreach ($this->getColumnDimensions() as $colDimension) {
+ if ($colDimension->getAutoSize()) {
+ $autoSizes[$colDimension->getColumnIndex()] = -1;
+ }
+ }
+
+ // There is only something to do if there are some auto-size columns
+ if (!empty($autoSizes)) {
+
+ // build list of cells references that participate in a merge
+ $isMergeCell = array();
+ foreach ($this->getMergeCells() as $cells) {
+ foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cells) as $cellReference) {
+ $isMergeCell[$cellReference] = true;
+ }
+ }
+
+ // loop through all cells in the worksheet
+ foreach ($this->getCellCollection(false) as $cellID) {
+ $cell = $this->getCell($cellID);
+ if (isset($autoSizes[$this->_cellCollection->getCurrentColumn()])) {
+ // Determine width if cell does not participate in a merge
+ if (!isset($isMergeCell[$this->_cellCollection->getCurrentAddress()])) {
+ // Calculated value
+ // To formatted string
+ $cellValue = PHPExcel_Style_NumberFormat::toFormattedString(
+ $cell->getCalculatedValue(),
+ $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
+ );
+
+ $autoSizes[$this->_cellCollection->getCurrentColumn()] = max(
+ (float) $autoSizes[$this->_cellCollection->getCurrentColumn()],
+ (float)PHPExcel_Shared_Font::calculateColumnWidth(
+ $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
+ $cellValue,
+ $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
+ $this->getDefaultStyle()->getFont()
+ )
+ );
+ }
+ }
+ }
+
+ // adjust column widths
+ foreach ($autoSizes as $columnIndex => $width) {
+ if ($width == -1) $width = $this->getDefaultColumnDimension()->getWidth();
+ $this->getColumnDimension($columnIndex)->setWidth($width);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get parent
+ *
+ * @return PHPExcel
+ */
+ public function getParent() {
+ return $this->_parent;
+ }
+
+ /**
+ * Re-bind parent
+ *
+ * @param PHPExcel $parent
+ * @return PHPExcel_Worksheet
+ */
+ public function rebindParent(PHPExcel $parent) {
+ if ($this->_parent !== null) {
+ $namedRanges = $this->_parent->getNamedRanges();
+ foreach ($namedRanges as $namedRange) {
+ $parent->addNamedRange($namedRange);
+ }
+
+ $this->_parent->removeSheetByIndex(
+ $this->_parent->getIndex($this)
+ );
+ }
+ $this->_parent = $parent;
+
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->_title;
+ }
+
+ /**
+ * Set title
+ *
+ * @param string $pValue String containing the dimension of this worksheet
+ * @param string $updateFormulaCellReferences boolean Flag indicating whether cell references in formulae should
+ * be updated to reflect the new sheet name.
+ * This should be left as the default true, unless you are
+ * certain that no formula cells on any worksheet contain
+ * references to this worksheet
+ * @return PHPExcel_Worksheet
+ */
+ public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
+ {
+ // Is this a 'rename' or not?
+ if ($this->getTitle() == $pValue) {
+ return $this;
+ }
+
+ // Syntax check
+ self::_checkSheetTitle($pValue);
+
+ // Old title
+ $oldTitle = $this->getTitle();
+
+ if ($this->_parent) {
+ // Is there already such sheet name?
+ if ($this->_parent->sheetNameExists($pValue)) {
+ // Use name, but append with lowest possible integer
+
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 29) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue,0,29);
+ }
+ $i = 1;
+ while ($this->_parent->sheetNameExists($pValue . ' ' . $i)) {
+ ++$i;
+ if ($i == 10) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 28) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue,0,28);
+ }
+ } elseif ($i == 100) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 27) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue,0,27);
+ }
+ }
+ }
+
+ $altTitle = $pValue . ' ' . $i;
+ return $this->setTitle($altTitle,$updateFormulaCellReferences);
+ }
+ }
+
+ // Set title
+ $this->_title = $pValue;
+ $this->_dirty = true;
+
+ if ($this->_parent) {
+ // New title
+ $newTitle = $this->getTitle();
+ PHPExcel_Calculation::getInstance($this->_parent)
+ ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
+ if ($updateFormulaCellReferences)
+ PHPExcel_ReferenceHelper::getInstance()->updateNamedFormulas($this->_parent, $oldTitle, $newTitle);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get sheet state
+ *
+ * @return string Sheet state (visible, hidden, veryHidden)
+ */
+ public function getSheetState() {
+ return $this->_sheetState;
+ }
+
+ /**
+ * Set sheet state
+ *
+ * @param string $value Sheet state (visible, hidden, veryHidden)
+ * @return PHPExcel_Worksheet
+ */
+ public function setSheetState($value = PHPExcel_Worksheet::SHEETSTATE_VISIBLE) {
+ $this->_sheetState = $value;
+ return $this;
+ }
+
+ /**
+ * Get page setup
+ *
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function getPageSetup()
+ {
+ return $this->_pageSetup;
+ }
+
+ /**
+ * Set page setup
+ *
+ * @param PHPExcel_Worksheet_PageSetup $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setPageSetup(PHPExcel_Worksheet_PageSetup $pValue)
+ {
+ $this->_pageSetup = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get page margins
+ *
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function getPageMargins()
+ {
+ return $this->_pageMargins;
+ }
+
+ /**
+ * Set page margins
+ *
+ * @param PHPExcel_Worksheet_PageMargins $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setPageMargins(PHPExcel_Worksheet_PageMargins $pValue)
+ {
+ $this->_pageMargins = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get page header/footer
+ *
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function getHeaderFooter()
+ {
+ return $this->_headerFooter;
+ }
+
+ /**
+ * Set page header/footer
+ *
+ * @param PHPExcel_Worksheet_HeaderFooter $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setHeaderFooter(PHPExcel_Worksheet_HeaderFooter $pValue)
+ {
+ $this->_headerFooter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get sheet view
+ *
+ * @return PHPExcel_Worksheet_SheetView
+ */
+ public function getSheetView()
+ {
+ return $this->_sheetView;
+ }
+
+ /**
+ * Set sheet view
+ *
+ * @param PHPExcel_Worksheet_SheetView $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setSheetView(PHPExcel_Worksheet_SheetView $pValue)
+ {
+ $this->_sheetView = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Protection
+ *
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function getProtection()
+ {
+ return $this->_protection;
+ }
+
+ /**
+ * Set Protection
+ *
+ * @param PHPExcel_Worksheet_Protection $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setProtection(PHPExcel_Worksheet_Protection $pValue)
+ {
+ $this->_protection = $pValue;
+ $this->_dirty = true;
+
+ return $this;
+ }
+
+ /**
+ * Get highest worksheet column
+ *
+ * @param string $row Return the data highest column for the specified row,
+ * or the highest column of any row if no row number is passed
+ * @return string Highest column name
+ */
+ public function getHighestColumn($row = null)
+ {
+ if ($row == null) {
+ return $this->_cachedHighestColumn;
+ }
+ return $this->getHighestDataColumn($row);
+ }
+
+ /**
+ * Get highest worksheet column that contains data
+ *
+ * @param string $row Return the highest data column for the specified row,
+ * or the highest data column of any row if no row number is passed
+ * @return string Highest column name that contains data
+ */
+ public function getHighestDataColumn($row = null)
+ {
+ return $this->_cellCollection->getHighestColumn($row);
+ }
+
+ /**
+ * Get highest worksheet row
+ *
+ * @param string $column Return the highest data row for the specified column,
+ * or the highest row of any column if no column letter is passed
+ * @return int Highest row number
+ */
+ public function getHighestRow($column = null)
+ {
+ if ($column == null) {
+ return $this->_cachedHighestRow;
+ }
+ return $this->getHighestDataRow($column);
+ }
+
+ /**
+ * Get highest worksheet row that contains data
+ *
+ * @param string $column Return the highest data row for the specified column,
+ * or the highest data row of any column if no column letter is passed
+ * @return string Highest row number that contains data
+ */
+ public function getHighestDataRow($column = null)
+ {
+ return $this->_cellCollection->getHighestRow($column);
+ }
+
+ /**
+ * Get highest worksheet column and highest row that have cell records
+ *
+ * @return array Highest column name and highest row number
+ */
+ public function getHighestRowAndColumn()
+ {
+ return $this->_cellCollection->getHighestRowAndColumn();
+ }
+
+ /**
+ * Set a cell value
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValue($pCoordinate = 'A1', $pValue = null, $returnCell = false)
+ {
+ $cell = $this->getCell(strtoupper($pCoordinate))->setValue($pValue);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Set a cell value by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell (A = 0)
+ * @param string $pRow Numeric row coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValueByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $returnCell = false)
+ {
+ $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Set a cell value
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param string $pDataType Explicit data type
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValueExplicit($pCoordinate = 'A1', $pValue = null, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING, $returnCell = false)
+ {
+ // Set value
+ $cell = $this->getCell(strtoupper($pCoordinate))->setValueExplicit($pValue, $pDataType);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Set a cell value by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @param string $pRow Numeric row coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param string $pDataType Explicit data type
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValueExplicitByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING, $returnCell = false)
+ {
+ $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Cell Cell that was found
+ */
+ public function getCell($pCoordinate = 'A1')
+ {
+ $pCoordinate = strtoupper($pCoordinate);
+ // Check cell collection
+ if ($this->_cellCollection->isDataSet($pCoordinate)) {
+ return $this->_cellCollection->getCacheData($pCoordinate);
+ }
+
+ // Worksheet reference?
+ if (strpos($pCoordinate, '!') !== false) {
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true);
+ return $this->_parent->getSheetByName($worksheetReference[0])->getCell($worksheetReference[1]);
+ }
+
+ // Named range?
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $pCoordinate, $matches)) &&
+ (preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $pCoordinate, $matches))) {
+ $namedRange = PHPExcel_NamedRange::resolveRange($pCoordinate, $this);
+ if ($namedRange !== NULL) {
+ $pCoordinate = $namedRange->getRange();
+ return $namedRange->getWorksheet()->getCell($pCoordinate);
+ }
+ }
+
+ // Uppercase coordinate
+ $pCoordinate = strtoupper($pCoordinate);
+
+ if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate can not be a range of cells.');
+ } elseif (strpos($pCoordinate, '$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate must not be absolute.');
+ }
+
+ // Create new cell object
+ return $this->_createNewCell($pCoordinate);
+ }
+
+ /**
+ * Get cell at a specific coordinate by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @param string $pRow Numeric row coordinate of the cell
+ * @return PHPExcel_Cell Cell that was found
+ */
+ public function getCellByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($pColumn);
+ $coordinate = $columnLetter . $pRow;
+
+ if ($this->_cellCollection->isDataSet($coordinate)) {
+ return $this->_cellCollection->getCacheData($coordinate);
+ }
+
+ return $this->_createNewCell($coordinate);
+ }
+
+ /**
+ * Create a new cell at the specified coordinate
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @return PHPExcel_Cell Cell that was created
+ */
+ private function _createNewCell($pCoordinate)
+ {
+ $cell = $this->_cellCollection->addCacheData(
+ $pCoordinate,
+ new PHPExcel_Cell(
+ NULL,
+ PHPExcel_Cell_DataType::TYPE_NULL,
+ $this
+ )
+ );
+ $this->_cellCollectionIsSorted = false;
+
+ // Coordinates
+ $aCoordinates = PHPExcel_Cell::coordinateFromString($pCoordinate);
+ if (PHPExcel_Cell::columnIndexFromString($this->_cachedHighestColumn) < PHPExcel_Cell::columnIndexFromString($aCoordinates[0]))
+ $this->_cachedHighestColumn = $aCoordinates[0];
+ $this->_cachedHighestRow = max($this->_cachedHighestRow, $aCoordinates[1]);
+
+ // Cell needs appropriate xfIndex from dimensions records
+ // but don't create dimension records if they don't already exist
+ $rowDimension = $this->getRowDimension($aCoordinates[1], FALSE);
+ $columnDimension = $this->getColumnDimension($aCoordinates[0], FALSE);
+
+ if ($rowDimension !== NULL && $rowDimension->getXfIndex() > 0) {
+ // then there is a row dimension with explicit style, assign it to the cell
+ $cell->setXfIndex($rowDimension->getXfIndex());
+ } elseif ($columnDimension !== NULL && $columnDimension->getXfIndex() > 0) {
+ // then there is a column dimension, assign it to the cell
+ $cell->setXfIndex($columnDimension->getXfIndex());
+ }
+
+ return $cell;
+ }
+
+ /**
+ * Does the cell at a specific coordinate exist?
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return boolean
+ */
+ public function cellExists($pCoordinate = 'A1')
+ {
+ // Worksheet reference?
+ if (strpos($pCoordinate, '!') !== false) {
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true);
+ return $this->_parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
+ }
+
+ // Named range?
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $pCoordinate, $matches)) &&
+ (preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $pCoordinate, $matches))) {
+ $namedRange = PHPExcel_NamedRange::resolveRange($pCoordinate, $this);
+ if ($namedRange !== NULL) {
+ $pCoordinate = $namedRange->getRange();
+ if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
+ if (!$namedRange->getLocalOnly()) {
+ return $namedRange->getWorksheet()->cellExists($pCoordinate);
+ } else {
+ throw new PHPExcel_Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
+ }
+ }
+ }
+ else { return false; }
+ }
+
+ // Uppercase coordinate
+ $pCoordinate = strtoupper($pCoordinate);
+
+ if (strpos($pCoordinate,':') !== false || strpos($pCoordinate,',') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate can not be a range of cells.');
+ } elseif (strpos($pCoordinate,'$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate must not be absolute.');
+ } else {
+ // Coordinates
+ $aCoordinates = PHPExcel_Cell::coordinateFromString($pCoordinate);
+
+ // Cell exists?
+ return $this->_cellCollection->isDataSet($pCoordinate);
+ }
+ }
+
+ /**
+ * Cell at a specific coordinate by using numeric cell coordinates exists?
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @param string $pRow Numeric row coordinate of the cell
+ * @return boolean
+ */
+ public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->cellExists(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Get row dimension at a specific row
+ *
+ * @param int $pRow Numeric index of the row
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function getRowDimension($pRow = 1, $create = TRUE)
+ {
+ // Found
+ $found = null;
+
+ // Get row dimension
+ if (!isset($this->_rowDimensions[$pRow])) {
+ if (!$create)
+ return NULL;
+ $this->_rowDimensions[$pRow] = new PHPExcel_Worksheet_RowDimension($pRow);
+
+ $this->_cachedHighestRow = max($this->_cachedHighestRow,$pRow);
+ }
+ return $this->_rowDimensions[$pRow];
+ }
+
+ /**
+ * Get column dimension at a specific column
+ *
+ * @param string $pColumn String index of the column
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function getColumnDimension($pColumn = 'A', $create = TRUE)
+ {
+ // Uppercase coordinate
+ $pColumn = strtoupper($pColumn);
+
+ // Fetch dimensions
+ if (!isset($this->_columnDimensions[$pColumn])) {
+ if (!$create)
+ return NULL;
+ $this->_columnDimensions[$pColumn] = new PHPExcel_Worksheet_ColumnDimension($pColumn);
+
+ if (PHPExcel_Cell::columnIndexFromString($this->_cachedHighestColumn) < PHPExcel_Cell::columnIndexFromString($pColumn))
+ $this->_cachedHighestColumn = $pColumn;
+ }
+ return $this->_columnDimensions[$pColumn];
+ }
+
+ /**
+ * Get column dimension at a specific column by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function getColumnDimensionByColumn($pColumn = 0)
+ {
+ return $this->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($pColumn));
+ }
+
+ /**
+ * Get styles
+ *
+ * @return PHPExcel_Style[]
+ */
+ public function getStyles()
+ {
+ return $this->_styles;
+ }
+
+ /**
+ * Get default style of workbook.
+ *
+ * @deprecated
+ * @return PHPExcel_Style
+ * @throws PHPExcel_Exception
+ */
+ public function getDefaultStyle()
+ {
+ return $this->_parent->getDefaultStyle();
+ }
+
+ /**
+ * Set default style - should only be used by PHPExcel_IReader implementations!
+ *
+ * @deprecated
+ * @param PHPExcel_Style $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setDefaultStyle(PHPExcel_Style $pValue)
+ {
+ $this->_parent->getDefaultStyle()->applyFromArray(array(
+ 'font' => array(
+ 'name' => $pValue->getFont()->getName(),
+ 'size' => $pValue->getFont()->getSize(),
+ ),
+ ));
+ return $this;
+ }
+
+ /**
+ * Get style for cell
+ *
+ * @param string $pCellCoordinate Cell coordinate (or range) to get style for
+ * @return PHPExcel_Style
+ * @throws PHPExcel_Exception
+ */
+ public function getStyle($pCellCoordinate = 'A1')
+ {
+ // set this sheet as active
+ $this->_parent->setActiveSheetIndex($this->_parent->getIndex($this));
+
+ // set cell coordinate as active
+ $this->setSelectedCells(strtoupper($pCellCoordinate));
+
+ return $this->_parent->getCellXfSupervisor();
+ }
+
+ /**
+ * Get conditional styles for a cell
+ *
+ * @param string $pCoordinate
+ * @return PHPExcel_Style_Conditional[]
+ */
+ public function getConditionalStyles($pCoordinate = 'A1')
+ {
+ $pCoordinate = strtoupper($pCoordinate);
+ if (!isset($this->_conditionalStylesCollection[$pCoordinate])) {
+ $this->_conditionalStylesCollection[$pCoordinate] = array();
+ }
+ return $this->_conditionalStylesCollection[$pCoordinate];
+ }
+
+ /**
+ * Do conditional styles exist for this cell?
+ *
+ * @param string $pCoordinate
+ * @return boolean
+ */
+ public function conditionalStylesExists($pCoordinate = 'A1')
+ {
+ if (isset($this->_conditionalStylesCollection[strtoupper($pCoordinate)])) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes conditional styles for a cell
+ *
+ * @param string $pCoordinate
+ * @return PHPExcel_Worksheet
+ */
+ public function removeConditionalStyles($pCoordinate = 'A1')
+ {
+ unset($this->_conditionalStylesCollection[strtoupper($pCoordinate)]);
+ return $this;
+ }
+
+ /**
+ * Get collection of conditional styles
+ *
+ * @return array
+ */
+ public function getConditionalStylesCollection()
+ {
+ return $this->_conditionalStylesCollection;
+ }
+
+ /**
+ * Set conditional styles
+ *
+ * @param $pCoordinate string E.g. 'A1'
+ * @param $pValue PHPExcel_Style_Conditional[]
+ * @return PHPExcel_Worksheet
+ */
+ public function setConditionalStyles($pCoordinate = 'A1', $pValue)
+ {
+ $this->_conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get style for cell by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @param int pColumn2 Numeric column coordinate of the range cell
+ * @param int pRow2 Numeric row coordinate of the range cell
+ * @return PHPExcel_Style
+ */
+ public function getStyleByColumnAndRow($pColumn = 0, $pRow = 1, $pColumn2 = null, $pRow2 = null)
+ {
+ if (!is_null($pColumn2) && !is_null($pRow2)) {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow . ':' .
+ PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->getStyle($cellRange);
+ }
+
+ return $this->getStyle(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Set shared cell style to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range!
+ *
+ * @deprecated
+ * @param PHPExcel_Style $pSharedCellStyle Cell style to share
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setSharedStyle(PHPExcel_Style $pSharedCellStyle = null, $pRange = '')
+ {
+ $this->duplicateStyle($pSharedCellStyle, $pRange);
+ return $this;
+ }
+
+ /**
+ * Duplicate cell style to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range!
+ *
+ * @param PHPExcel_Style $pCellStyle Cell style to duplicate
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function duplicateStyle(PHPExcel_Style $pCellStyle = null, $pRange = '')
+ {
+ // make sure we have a real style and not supervisor
+ $style = $pCellStyle->getIsSupervisor() ? $pCellStyle->getSharedComponent() : $pCellStyle;
+
+ // Add the style to the workbook if necessary
+ $workbook = $this->_parent;
+ if ($existingStyle = $this->_parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
+ // there is already such cell Xf in our collection
+ $xfIndex = $existingStyle->getIndex();
+ } else {
+ // we don't have such a cell Xf, need to add
+ $workbook->addCellXf($pCellStyle);
+ $xfIndex = $pCellStyle->getIndex();
+ }
+
+ // Calculate range outer borders
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange . ':' . $pRange);
+
+ // Make sure we can loop upwards on rows and columns
+ if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+ $tmp = $rangeStart;
+ $rangeStart = $rangeEnd;
+ $rangeEnd = $tmp;
+ }
+
+ // Loop through cells and apply styles
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $this->getCell(PHPExcel_Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Duplicate conditional style to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range!
+ *
+ * @param array of PHPExcel_Style_Conditional $pCellStyle Cell style to duplicate
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '')
+ {
+ foreach($pCellStyle as $cellStyle) {
+ if (!($cellStyle instanceof PHPExcel_Style_Conditional)) {
+ throw new PHPExcel_Exception('Style is not a conditional style');
+ }
+ }
+
+ // Calculate range outer borders
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange . ':' . $pRange);
+
+ // Make sure we can loop upwards on rows and columns
+ if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+ $tmp = $rangeStart;
+ $rangeStart = $rangeEnd;
+ $rangeEnd = $tmp;
+ }
+
+ // Loop through cells and apply styles
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $this->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Duplicate cell style array to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range,
+ * if they are in the styles array. For example, if you decide to set a range of
+ * cells to font bold, only include font bold in the styles array.
+ *
+ * @deprecated
+ * @param array $pStyles Array containing style information
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @param boolean $pAdvanced Advanced mode for setting borders.
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function duplicateStyleArray($pStyles = null, $pRange = '', $pAdvanced = true)
+ {
+ $this->getStyle($pRange)->applyFromArray($pStyles, $pAdvanced);
+ return $this;
+ }
+
+ /**
+ * Set break on a cell
+ *
+ * @param string $pCell Cell coordinate (e.g. A1)
+ * @param int $pBreak Break type (type of PHPExcel_Worksheet::BREAK_*)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setBreak($pCell = 'A1', $pBreak = PHPExcel_Worksheet::BREAK_NONE)
+ {
+ // Uppercase coordinate
+ $pCell = strtoupper($pCell);
+
+ if ($pCell != '') {
+ if ($pBreak == PHPExcel_Worksheet::BREAK_NONE) {
+ if (isset($this->_breaks[$pCell])) {
+ unset($this->_breaks[$pCell]);
+ }
+ } else {
+ $this->_breaks[$pCell] = $pBreak;
+ }
+ } else {
+ throw new PHPExcel_Exception('No cell coordinate specified.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set break on a cell by using numeric cell coordinates
+ *
+ * @param integer $pColumn Numeric column coordinate of the cell
+ * @param integer $pRow Numeric row coordinate of the cell
+ * @param integer $pBreak Break type (type of PHPExcel_Worksheet::BREAK_*)
+ * @return PHPExcel_Worksheet
+ */
+ public function setBreakByColumnAndRow($pColumn = 0, $pRow = 1, $pBreak = PHPExcel_Worksheet::BREAK_NONE)
+ {
+ return $this->setBreak(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak);
+ }
+
+ /**
+ * Get breaks
+ *
+ * @return array[]
+ */
+ public function getBreaks()
+ {
+ return $this->_breaks;
+ }
+
+ /**
+ * Set merge on a cell range
+ *
+ * @param string $pRange Cell range (e.g. A1:E1)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function mergeCells($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (strpos($pRange,':') !== false) {
+ $this->_mergeCells[$pRange] = $pRange;
+
+ // make sure cells are created
+
+ // get the cells in the range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+
+ // create upper left cell if it does not already exist
+ $upperLeft = $aReferences[0];
+ if (!$this->cellExists($upperLeft)) {
+ $this->getCell($upperLeft)->setValueExplicit(null, PHPExcel_Cell_DataType::TYPE_NULL);
+ }
+
+ // create or blank out the rest of the cells in the range
+ $count = count($aReferences);
+ for ($i = 1; $i < $count; $i++) {
+ $this->getCell($aReferences[$i])->setValueExplicit(null, PHPExcel_Cell_DataType::TYPE_NULL);
+ }
+
+ } else {
+ throw new PHPExcel_Exception('Merge must be set on a range of cells.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set merge on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function mergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->mergeCells($cellRange);
+ }
+
+ /**
+ * Remove merge on a cell range
+ *
+ * @param string $pRange Cell range (e.g. A1:E1)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unmergeCells($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (strpos($pRange,':') !== false) {
+ if (isset($this->_mergeCells[$pRange])) {
+ unset($this->_mergeCells[$pRange]);
+ } else {
+ throw new PHPExcel_Exception('Cell range ' . $pRange . ' not known as merged.');
+ }
+ } else {
+ throw new PHPExcel_Exception('Merge can only be removed from a range of cells.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Remove merge on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unmergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->unmergeCells($cellRange);
+ }
+
+ /**
+ * Get merge cells array.
+ *
+ * @return array[]
+ */
+ public function getMergeCells()
+ {
+ return $this->_mergeCells;
+ }
+
+ /**
+ * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
+ * a single cell range.
+ *
+ * @param array
+ */
+ public function setMergeCells($pValue = array())
+ {
+ $this->_mergeCells = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Set protection on a cell range
+ *
+ * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
+ * @param string $pPassword Password to unlock the protection
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function protectCells($pRange = 'A1', $pPassword = '', $pAlreadyHashed = false)
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (!$pAlreadyHashed) {
+ $pPassword = PHPExcel_Shared_PasswordHasher::hashPassword($pPassword);
+ }
+ $this->_protectedCells[$pRange] = $pPassword;
+
+ return $this;
+ }
+
+ /**
+ * Set protection on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @param string $pPassword Password to unlock the protection
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function protectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->protectCells($cellRange, $pPassword, $pAlreadyHashed);
+ }
+
+ /**
+ * Remove protection on a cell range
+ *
+ * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unprotectCells($pRange = 'A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (isset($this->_protectedCells[$pRange])) {
+ unset($this->_protectedCells[$pRange]);
+ } else {
+ throw new PHPExcel_Exception('Cell range ' . $pRange . ' not known as protected.');
+ }
+ return $this;
+ }
+
+ /**
+ * Remove protection on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @param string $pPassword Password to unlock the protection
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unprotectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->unprotectCells($cellRange, $pPassword, $pAlreadyHashed);
+ }
+
+ /**
+ * Get protected cells
+ *
+ * @return array[]
+ */
+ public function getProtectedCells()
+ {
+ return $this->_protectedCells;
+ }
+
+ /**
+ * Get Autofilter
+ *
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function getAutoFilter()
+ {
+ return $this->_autoFilter;
+ }
+
+ /**
+ * Set AutoFilter
+ *
+ * @param PHPExcel_Worksheet_AutoFilter|string $pValue
+ * A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setAutoFilter($pValue)
+ {
+ $pRange = strtoupper($pValue);
+
+ if (is_string($pValue)) {
+ $this->_autoFilter->setRange($pValue);
+ } elseif(is_object($pValue) && ($pValue instanceof PHPExcel_Worksheet_AutoFilter)) {
+ $this->_autoFilter = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Set Autofilter Range by using numeric cell coordinates
+ *
+ * @param integer $pColumn1 Numeric column coordinate of the first cell
+ * @param integer $pRow1 Numeric row coordinate of the first cell
+ * @param integer $pColumn2 Numeric column coordinate of the second cell
+ * @param integer $pRow2 Numeric row coordinate of the second cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setAutoFilterByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
+ {
+ return $this->setAutoFilter(
+ PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1
+ . ':' .
+ PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2
+ );
+ }
+
+ /**
+ * Remove autofilter
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function removeAutoFilter()
+ {
+ $this->_autoFilter->setRange(NULL);
+ return $this;
+ }
+
+ /**
+ * Get Freeze Pane
+ *
+ * @return string
+ */
+ public function getFreezePane()
+ {
+ return $this->_freezePane;
+ }
+
+ /**
+ * Freeze Pane
+ *
+ * @param string $pCell Cell (i.e. A2)
+ * Examples:
+ * A2 will freeze the rows above cell A2 (i.e row 1)
+ * B1 will freeze the columns to the left of cell B1 (i.e column A)
+ * B2 will freeze the rows above and to the left of cell A2
+ * (i.e row 1 and column A)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function freezePane($pCell = '')
+ {
+ // Uppercase coordinate
+ $pCell = strtoupper($pCell);
+
+ if (strpos($pCell,':') === false && strpos($pCell,',') === false) {
+ $this->_freezePane = $pCell;
+ } else {
+ throw new PHPExcel_Exception('Freeze pane can not be set on a range of cells.');
+ }
+ return $this;
+ }
+
+ /**
+ * Freeze Pane by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function freezePaneByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->freezePane(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Unfreeze Pane
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function unfreezePane()
+ {
+ return $this->freezePane('');
+ }
+
+ /**
+ * Insert a new row, updating all possible related data
+ *
+ * @param int $pBefore Insert before this one
+ * @param int $pNumRows Number of rows to insert
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function insertNewRowBefore($pBefore = 1, $pNumRows = 1) {
+ if ($pBefore >= 1) {
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
+ } else {
+ throw new PHPExcel_Exception("Rows can only be inserted before at least row 1.");
+ }
+ return $this;
+ }
+
+ /**
+ * Insert a new column, updating all possible related data
+ *
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to insert
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function insertNewColumnBefore($pBefore = 'A', $pNumCols = 1) {
+ if (!is_numeric($pBefore)) {
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
+ } else {
+ throw new PHPExcel_Exception("Column references should not be numeric.");
+ }
+ return $this;
+ }
+
+ /**
+ * Insert a new column, updating all possible related data
+ *
+ * @param int $pBefore Insert before this one (numeric column coordinate of the cell)
+ * @param int $pNumCols Number of columns to insert
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function insertNewColumnBeforeByIndex($pBefore = 0, $pNumCols = 1) {
+ if ($pBefore >= 0) {
+ return $this->insertNewColumnBefore(PHPExcel_Cell::stringFromColumnIndex($pBefore), $pNumCols);
+ } else {
+ throw new PHPExcel_Exception("Columns can only be inserted before at least column A (0).");
+ }
+ }
+
+ /**
+ * Delete a row, updating all possible related data
+ *
+ * @param int $pRow Remove starting with this one
+ * @param int $pNumRows Number of rows to remove
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function removeRow($pRow = 1, $pNumRows = 1) {
+ if ($pRow >= 1) {
+ $highestRow = $this->getHighestDataRow();
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
+ for($r = 0; $r < $pNumRows; ++$r) {
+ $this->getCellCacheController()->removeRow($highestRow);
+ --$highestRow;
+ }
+ } else {
+ throw new PHPExcel_Exception("Rows to be deleted should at least start from row 1.");
+ }
+ return $this;
+ }
+
+ /**
+ * Remove a column, updating all possible related data
+ *
+ * @param string $pColumn Remove starting with this one
+ * @param int $pNumCols Number of columns to remove
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function removeColumn($pColumn = 'A', $pNumCols = 1) {
+ if (!is_numeric($pColumn)) {
+ $highestColumn = $this->getHighestDataColumn();
+ $pColumn = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
+ for($c = 0; $c < $pNumCols; ++$c) {
+ $this->getCellCacheController()->removeColumn($highestColumn);
+ $highestColumn = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($highestColumn) - 2);
+ }
+ } else {
+ throw new PHPExcel_Exception("Column references should not be numeric.");
+ }
+ return $this;
+ }
+
+ /**
+ * Remove a column, updating all possible related data
+ *
+ * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell)
+ * @param int $pNumCols Number of columns to remove
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function removeColumnByIndex($pColumn = 0, $pNumCols = 1) {
+ if ($pColumn >= 0) {
+ return $this->removeColumn(PHPExcel_Cell::stringFromColumnIndex($pColumn), $pNumCols);
+ } else {
+ throw new PHPExcel_Exception("Columns to be deleted should at least start from column 0");
+ }
+ }
+
+ /**
+ * Show gridlines?
+ *
+ * @return boolean
+ */
+ public function getShowGridlines() {
+ return $this->_showGridlines;
+ }
+
+ /**
+ * Set show gridlines
+ *
+ * @param boolean $pValue Show gridlines (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowGridlines($pValue = false) {
+ $this->_showGridlines = $pValue;
+ return $this;
+ }
+
+ /**
+ * Print gridlines?
+ *
+ * @return boolean
+ */
+ public function getPrintGridlines() {
+ return $this->_printGridlines;
+ }
+
+ /**
+ * Set print gridlines
+ *
+ * @param boolean $pValue Print gridlines (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setPrintGridlines($pValue = false) {
+ $this->_printGridlines = $pValue;
+ return $this;
+ }
+
+ /**
+ * Show row and column headers?
+ *
+ * @return boolean
+ */
+ public function getShowRowColHeaders() {
+ return $this->_showRowColHeaders;
+ }
+
+ /**
+ * Set show row and column headers
+ *
+ * @param boolean $pValue Show row and column headers (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowRowColHeaders($pValue = false) {
+ $this->_showRowColHeaders = $pValue;
+ return $this;
+ }
+
+ /**
+ * Show summary below? (Row/Column outlining)
+ *
+ * @return boolean
+ */
+ public function getShowSummaryBelow() {
+ return $this->_showSummaryBelow;
+ }
+
+ /**
+ * Set show summary below
+ *
+ * @param boolean $pValue Show summary below (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowSummaryBelow($pValue = true) {
+ $this->_showSummaryBelow = $pValue;
+ return $this;
+ }
+
+ /**
+ * Show summary right? (Row/Column outlining)
+ *
+ * @return boolean
+ */
+ public function getShowSummaryRight() {
+ return $this->_showSummaryRight;
+ }
+
+ /**
+ * Set show summary right
+ *
+ * @param boolean $pValue Show summary right (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowSummaryRight($pValue = true) {
+ $this->_showSummaryRight = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get comments
+ *
+ * @return PHPExcel_Comment[]
+ */
+ public function getComments()
+ {
+ return $this->_comments;
+ }
+
+ /**
+ * Set comments array for the entire sheet.
+ *
+ * @param array of PHPExcel_Comment
+ * @return PHPExcel_Worksheet
+ */
+ public function setComments($pValue = array())
+ {
+ $this->_comments = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get comment for cell
+ *
+ * @param string $pCellCoordinate Cell coordinate to get comment for
+ * @return PHPExcel_Comment
+ * @throws PHPExcel_Exception
+ */
+ public function getComment($pCellCoordinate = 'A1')
+ {
+ // Uppercase coordinate
+ $pCellCoordinate = strtoupper($pCellCoordinate);
+
+ if (strpos($pCellCoordinate,':') !== false || strpos($pCellCoordinate,',') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells.');
+ } else if (strpos($pCellCoordinate,'$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate string must not be absolute.');
+ } else if ($pCellCoordinate == '') {
+ throw new PHPExcel_Exception('Cell coordinate can not be zero-length string.');
+ } else {
+ // Check if we already have a comment for this cell.
+ // If not, create a new comment.
+ if (isset($this->_comments[$pCellCoordinate])) {
+ return $this->_comments[$pCellCoordinate];
+ } else {
+ $newComment = new PHPExcel_Comment();
+ $this->_comments[$pCellCoordinate] = $newComment;
+ return $newComment;
+ }
+ }
+ }
+
+ /**
+ * Get comment for cell by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @return PHPExcel_Comment
+ */
+ public function getCommentByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->getComment(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Get selected cell
+ *
+ * @deprecated
+ * @return string
+ */
+ public function getSelectedCell()
+ {
+ return $this->getSelectedCells();
+ }
+
+ /**
+ * Get active cell
+ *
+ * @return string Example: 'A1'
+ */
+ public function getActiveCell()
+ {
+ return $this->_activeCell;
+ }
+
+ /**
+ * Get selected cells
+ *
+ * @return string
+ */
+ public function getSelectedCells()
+ {
+ return $this->_selectedCells;
+ }
+
+ /**
+ * Selected cell
+ *
+ * @param string $pCoordinate Cell (i.e. A1)
+ * @return PHPExcel_Worksheet
+ */
+ public function setSelectedCell($pCoordinate = 'A1')
+ {
+ return $this->setSelectedCells($pCoordinate);
+ }
+
+ /**
+ * Select a range of cells.
+ *
+ * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setSelectedCells($pCoordinate = 'A1')
+ {
+ // Uppercase coordinate
+ $pCoordinate = strtoupper($pCoordinate);
+
+ // Convert 'A' to 'A:A'
+ $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
+
+ // Convert '1' to '1:1'
+ $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
+
+ // Convert 'A:C' to 'A1:C1048576'
+ $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
+
+ // Convert '1:3' to 'A1:XFD3'
+ $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
+
+ if (strpos($pCoordinate,':') !== false || strpos($pCoordinate,',') !== false) {
+ list($first, ) = PHPExcel_Cell::splitRange($pCoordinate);
+ $this->_activeCell = $first[0];
+ } else {
+ $this->_activeCell = $pCoordinate;
+ }
+ $this->_selectedCells = $pCoordinate;
+ return $this;
+ }
+
+ /**
+ * Selected cell by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setSelectedCellByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->setSelectedCells(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Get right-to-left
+ *
+ * @return boolean
+ */
+ public function getRightToLeft() {
+ return $this->_rightToLeft;
+ }
+
+ /**
+ * Set right-to-left
+ *
+ * @param boolean $value Right-to-left true/false
+ * @return PHPExcel_Worksheet
+ */
+ public function setRightToLeft($value = false) {
+ $this->_rightToLeft = $value;
+ return $this;
+ }
+
+ /**
+ * Fill worksheet from values in array
+ *
+ * @param array $source Source array
+ * @param mixed $nullValue Value in source array that stands for blank cell
+ * @param string $startCell Insert array starting from this cell address as the top left coordinate
+ * @param boolean $strictNullComparison Apply strict comparison when testing for null values in the array
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function fromArray($source = null, $nullValue = null, $startCell = 'A1', $strictNullComparison = false) {
+ if (is_array($source)) {
+ // Convert a 1-D array to 2-D (for ease of looping)
+ if (!is_array(end($source))) {
+ $source = array($source);
+ }
+
+ // start coordinate
+ list ($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($startCell);
+
+ // Loop through $source
+ foreach ($source as $rowData) {
+ $currentColumn = $startColumn;
+ foreach($rowData as $cellValue) {
+ if ($strictNullComparison) {
+ if ($cellValue !== $nullValue) {
+ // Set cell value
+ $this->getCell($currentColumn . $startRow)->setValue($cellValue);
+ }
+ } else {
+ if ($cellValue != $nullValue) {
+ // Set cell value
+ $this->getCell($currentColumn . $startRow)->setValue($cellValue);
+ }
+ }
+ ++$currentColumn;
+ }
+ ++$startRow;
+ }
+ } else {
+ throw new PHPExcel_Exception("Parameter \$source should be an array.");
+ }
+ return $this;
+ }
+
+ /**
+ * Create array from a range of cells
+ *
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+ * @param boolean $calculateFormulas Should formulas be calculated?
+ * @param boolean $formatData Should formatting be applied to cell values?
+ * @param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+ * True - Return rows and columns indexed by their actual row and column IDs
+ * @return array
+ */
+ public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) {
+ // Returnvalue
+ $returnValue = array();
+ // Identify the range that we need to extract from the worksheet
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange);
+ $minCol = PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] -1);
+ $minRow = $rangeStart[1];
+ $maxCol = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] -1);
+ $maxRow = $rangeEnd[1];
+
+ $maxCol++;
+ // Loop through rows
+ $r = -1;
+ for ($row = $minRow; $row <= $maxRow; ++$row) {
+ $rRef = ($returnCellRef) ? $row : ++$r;
+ $c = -1;
+ // Loop through columns in the current row
+ for ($col = $minCol; $col != $maxCol; ++$col) {
+ $cRef = ($returnCellRef) ? $col : ++$c;
+ // Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
+ // so we test and retrieve directly against _cellCollection
+ if ($this->_cellCollection->isDataSet($col.$row)) {
+ // Cell exists
+ $cell = $this->_cellCollection->getCacheData($col.$row);
+ if ($cell->getValue() !== null) {
+ if ($cell->getValue() instanceof PHPExcel_RichText) {
+ $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
+ } else {
+ if ($calculateFormulas) {
+ $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
+ } else {
+ $returnValue[$rRef][$cRef] = $cell->getValue();
+ }
+ }
+
+ if ($formatData) {
+ $style = $this->_parent->getCellXfByIndex($cell->getXfIndex());
+ $returnValue[$rRef][$cRef] = PHPExcel_Style_NumberFormat::toFormattedString(
+ $returnValue[$rRef][$cRef],
+ ($style && $style->getNumberFormat()) ?
+ $style->getNumberFormat()->getFormatCode() :
+ PHPExcel_Style_NumberFormat::FORMAT_GENERAL
+ );
+ }
+ } else {
+ // Cell holds a NULL
+ $returnValue[$rRef][$cRef] = $nullValue;
+ }
+ } else {
+ // Cell doesn't exist
+ $returnValue[$rRef][$cRef] = $nullValue;
+ }
+ }
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+
+ /**
+ * Create array from a range of cells
+ *
+ * @param string $pNamedRange Name of the Named Range
+ * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+ * @param boolean $calculateFormulas Should formulas be calculated?
+ * @param boolean $formatData Should formatting be applied to cell values?
+ * @param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+ * True - Return rows and columns indexed by their actual row and column IDs
+ * @return array
+ * @throws PHPExcel_Exception
+ */
+ public function namedRangeToArray($pNamedRange = '', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) {
+ $namedRange = PHPExcel_NamedRange::resolveRange($pNamedRange, $this);
+ if ($namedRange !== NULL) {
+ $pWorkSheet = $namedRange->getWorksheet();
+ $pCellRange = $namedRange->getRange();
+
+ return $pWorkSheet->rangeToArray( $pCellRange,
+ $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+ }
+
+ throw new PHPExcel_Exception('Named Range '.$pNamedRange.' does not exist.');
+ }
+
+
+ /**
+ * Create array from worksheet
+ *
+ * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+ * @param boolean $calculateFormulas Should formulas be calculated?
+ * @param boolean $formatData Should formatting be applied to cell values?
+ * @param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+ * True - Return rows and columns indexed by their actual row and column IDs
+ * @return array
+ */
+ public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) {
+ // Garbage collect...
+ $this->garbageCollect();
+
+ // Identify the range that we need to extract from the worksheet
+ $maxCol = $this->getHighestColumn();
+ $maxRow = $this->getHighestRow();
+ // Return
+ return $this->rangeToArray( 'A1:'.$maxCol.$maxRow,
+ $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+ }
+
+ /**
+ * Get row iterator
+ *
+ * @param integer $startRow The row number at which to start iterating
+ * @param integer $endRow The row number at which to stop iterating
+ *
+ * @return PHPExcel_Worksheet_RowIterator
+ */
+ public function getRowIterator($startRow = 1, $endRow = null) {
+ return new PHPExcel_Worksheet_RowIterator($this, $startRow, $endRow);
+ }
+
+ /**
+ * Get column iterator
+ *
+ * @param string $startColumn The column address at which to start iterating
+ * @param string $endColumn The column address at which to stop iterating
+ *
+ * @return PHPExcel_Worksheet_ColumnIterator
+ */
+ public function getColumnIterator($startColumn = 'A', $endColumn = null) {
+ return new PHPExcel_Worksheet_ColumnIterator($this, $startColumn, $endColumn);
+ }
+
+ /**
+ * Run PHPExcel garabage collector.
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function garbageCollect() {
+ // Flush cache
+ $this->_cellCollection->getCacheData('A1');
+ // Build a reference table from images
+// $imageCoordinates = array();
+// $iterator = $this->getDrawingCollection()->getIterator();
+// while ($iterator->valid()) {
+// $imageCoordinates[$iterator->current()->getCoordinates()] = true;
+//
+// $iterator->next();
+// }
+//
+ // Lookup highest column and highest row if cells are cleaned
+ $colRow = $this->_cellCollection->getHighestRowAndColumn();
+ $highestRow = $colRow['row'];
+ $highestColumn = PHPExcel_Cell::columnIndexFromString($colRow['column']);
+
+ // Loop through column dimensions
+ foreach ($this->_columnDimensions as $dimension) {
+ $highestColumn = max($highestColumn,PHPExcel_Cell::columnIndexFromString($dimension->getColumnIndex()));
+ }
+
+ // Loop through row dimensions
+ foreach ($this->_rowDimensions as $dimension) {
+ $highestRow = max($highestRow,$dimension->getRowIndex());
+ }
+
+ // Cache values
+ if ($highestColumn < 0) {
+ $this->_cachedHighestColumn = 'A';
+ } else {
+ $this->_cachedHighestColumn = PHPExcel_Cell::stringFromColumnIndex(--$highestColumn);
+ }
+ $this->_cachedHighestRow = $highestRow;
+
+ // Return
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ if ($this->_dirty) {
+ $this->_hash = md5( $this->_title .
+ $this->_autoFilter .
+ ($this->_protection->isProtectionEnabled() ? 't' : 'f') .
+ __CLASS__
+ );
+ $this->_dirty = false;
+ }
+ return $this->_hash;
+ }
+
+ /**
+ * Extract worksheet title from range.
+ *
+ * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
+ * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
+ *
+ * @param string $pRange Range to extract title from
+ * @param bool $returnRange Return range? (see example)
+ * @return mixed
+ */
+ public static function extractSheetTitle($pRange, $returnRange = false) {
+ // Sheet title included?
+ if (($sep = strpos($pRange, '!')) === false) {
+ return '';
+ }
+
+ if ($returnRange) {
+ return array( trim(substr($pRange, 0, $sep),"'"),
+ substr($pRange, $sep + 1)
+ );
+ }
+
+ return substr($pRange, $sep + 1);
+ }
+
+ /**
+ * Get hyperlink
+ *
+ * @param string $pCellCoordinate Cell coordinate to get hyperlink for
+ */
+ public function getHyperlink($pCellCoordinate = 'A1')
+ {
+ // return hyperlink if we already have one
+ if (isset($this->_hyperlinkCollection[$pCellCoordinate])) {
+ return $this->_hyperlinkCollection[$pCellCoordinate];
+ }
+
+ // else create hyperlink
+ $this->_hyperlinkCollection[$pCellCoordinate] = new PHPExcel_Cell_Hyperlink();
+ return $this->_hyperlinkCollection[$pCellCoordinate];
+ }
+
+ /**
+ * Set hyperlnk
+ *
+ * @param string $pCellCoordinate Cell coordinate to insert hyperlink
+ * @param PHPExcel_Cell_Hyperlink $pHyperlink
+ * @return PHPExcel_Worksheet
+ */
+ public function setHyperlink($pCellCoordinate = 'A1', PHPExcel_Cell_Hyperlink $pHyperlink = null)
+ {
+ if ($pHyperlink === null) {
+ unset($this->_hyperlinkCollection[$pCellCoordinate]);
+ } else {
+ $this->_hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
+ }
+ return $this;
+ }
+
+ /**
+ * Hyperlink at a specific coordinate exists?
+ *
+ * @param string $pCoordinate
+ * @return boolean
+ */
+ public function hyperlinkExists($pCoordinate = 'A1')
+ {
+ return isset($this->_hyperlinkCollection[$pCoordinate]);
+ }
+
+ /**
+ * Get collection of hyperlinks
+ *
+ * @return PHPExcel_Cell_Hyperlink[]
+ */
+ public function getHyperlinkCollection()
+ {
+ return $this->_hyperlinkCollection;
+ }
+
+ /**
+ * Get data validation
+ *
+ * @param string $pCellCoordinate Cell coordinate to get data validation for
+ */
+ public function getDataValidation($pCellCoordinate = 'A1')
+ {
+ // return data validation if we already have one
+ if (isset($this->_dataValidationCollection[$pCellCoordinate])) {
+ return $this->_dataValidationCollection[$pCellCoordinate];
+ }
+
+ // else create data validation
+ $this->_dataValidationCollection[$pCellCoordinate] = new PHPExcel_Cell_DataValidation();
+ return $this->_dataValidationCollection[$pCellCoordinate];
+ }
+
+ /**
+ * Set data validation
+ *
+ * @param string $pCellCoordinate Cell coordinate to insert data validation
+ * @param PHPExcel_Cell_DataValidation $pDataValidation
+ * @return PHPExcel_Worksheet
+ */
+ public function setDataValidation($pCellCoordinate = 'A1', PHPExcel_Cell_DataValidation $pDataValidation = null)
+ {
+ if ($pDataValidation === null) {
+ unset($this->_dataValidationCollection[$pCellCoordinate]);
+ } else {
+ $this->_dataValidationCollection[$pCellCoordinate] = $pDataValidation;
+ }
+ return $this;
+ }
+
+ /**
+ * Data validation at a specific coordinate exists?
+ *
+ * @param string $pCoordinate
+ * @return boolean
+ */
+ public function dataValidationExists($pCoordinate = 'A1')
+ {
+ return isset($this->_dataValidationCollection[$pCoordinate]);
+ }
+
+ /**
+ * Get collection of data validations
+ *
+ * @return PHPExcel_Cell_DataValidation[]
+ */
+ public function getDataValidationCollection()
+ {
+ return $this->_dataValidationCollection;
+ }
+
+ /**
+ * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet
+ *
+ * @param string $range
+ * @return string Adjusted range value
+ */
+ public function shrinkRangeToFit($range) {
+ $maxCol = $this->getHighestColumn();
+ $maxRow = $this->getHighestRow();
+ $maxCol = PHPExcel_Cell::columnIndexFromString($maxCol);
+
+ $rangeBlocks = explode(' ',$range);
+ foreach ($rangeBlocks as &$rangeSet) {
+ $rangeBoundaries = PHPExcel_Cell::getRangeBoundaries($rangeSet);
+
+ if (PHPExcel_Cell::columnIndexFromString($rangeBoundaries[0][0]) > $maxCol) { $rangeBoundaries[0][0] = PHPExcel_Cell::stringFromColumnIndex($maxCol); }
+ if ($rangeBoundaries[0][1] > $maxRow) { $rangeBoundaries[0][1] = $maxRow; }
+ if (PHPExcel_Cell::columnIndexFromString($rangeBoundaries[1][0]) > $maxCol) { $rangeBoundaries[1][0] = PHPExcel_Cell::stringFromColumnIndex($maxCol); }
+ if ($rangeBoundaries[1][1] > $maxRow) { $rangeBoundaries[1][1] = $maxRow; }
+ $rangeSet = $rangeBoundaries[0][0].$rangeBoundaries[0][1].':'.$rangeBoundaries[1][0].$rangeBoundaries[1][1];
+ }
+ unset($rangeSet);
+ $stRange = implode(' ',$rangeBlocks);
+
+ return $stRange;
+ }
+
+ /**
+ * Get tab color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getTabColor()
+ {
+ if ($this->_tabColor === NULL)
+ $this->_tabColor = new PHPExcel_Style_Color();
+
+ return $this->_tabColor;
+ }
+
+ /**
+ * Reset tab color
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function resetTabColor()
+ {
+ $this->_tabColor = null;
+ unset($this->_tabColor);
+
+ return $this;
+ }
+
+ /**
+ * Tab color set?
+ *
+ * @return boolean
+ */
+ public function isTabColorSet()
+ {
+ return ($this->_tabColor !== NULL);
+ }
+
+ /**
+ * Copy worksheet (!= clone!)
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function copy() {
+ $copied = clone $this;
+
+ return $copied;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ foreach ($this as $key => $val) {
+ if ($key == '_parent') {
+ continue;
+ }
+
+ if (is_object($val) || (is_array($val))) {
+ if ($key == '_cellCollection') {
+ $newCollection = clone $this->_cellCollection;
+ $newCollection->copyCellCollection($this);
+ $this->_cellCollection = $newCollection;
+ } elseif ($key == '_drawingCollection') {
+ $newCollection = clone $this->_drawingCollection;
+ $this->_drawingCollection = $newCollection;
+ } elseif (($key == '_autoFilter') && ($this->_autoFilter instanceof PHPExcel_Worksheet_AutoFilter)) {
+ $newAutoFilter = clone $this->_autoFilter;
+ $this->_autoFilter = $newAutoFilter;
+ $this->_autoFilter->setParent($this);
+ } else {
+ $this->{$key} = unserialize(serialize($val));
+ }
+ }
+ }
+ }
+/**
+ * Define the code name of the sheet
+ *
+ * @param null|string Same rule as Title minus space not allowed (but, like Excel, change silently space to underscore)
+ * @return objWorksheet
+ * @throws PHPExcel_Exception
+ */
+ public function setCodeName($pValue=null){
+ // Is this a 'rename' or not?
+ if ($this->getCodeName() == $pValue) {
+ return $this;
+ }
+ $pValue = str_replace(' ', '_', $pValue);//Excel does this automatically without flinching, we are doing the same
+ // Syntax check
+ // throw an exception if not valid
+ self::_checkSheetCodeName($pValue);
+
+ // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
+
+ if ($this->getParent()) {
+ // Is there already such sheet name?
+ if ($this->getParent()->sheetCodeNameExists($pValue)) {
+ // Use name, but append with lowest possible integer
+
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 29) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue,0,29);
+ }
+ $i = 1;
+ while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) {
+ ++$i;
+ if ($i == 10) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 28) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue,0,28);
+ }
+ } elseif ($i == 100) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 27) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue,0,27);
+ }
+ }
+ }
+
+ $pValue = $pValue . '_' . $i;// ok, we have a valid name
+ //codeName is'nt used in formula : no need to call for an update
+ //return $this->setTitle($altTitle,$updateFormulaCellReferences);
+ }
+ }
+
+ $this->_codeName=$pValue;
+ return $this;
+ }
+ /**
+ * Return the code name of the sheet
+ *
+ * @return null|string
+ */
+ public function getCodeName(){
+ return $this->_codeName;
+ }
+ /**
+ * Sheet has a code name ?
+ * @return boolean
+ */
+ public function hasCodeName(){
+ return !(is_null($this->_codeName));
+ }
+}
diff --git a/includes/PHPExcel/Classes/PHPExcel/WorksheetIterator.php b/includes/PHPExcel/Classes/PHPExcel/WorksheetIterator.php
new file mode 100644
index 0000000..ad17fd9
--- /dev/null
+++ b/includes/PHPExcel/Classes/PHPExcel/WorksheetIterator.php
@@ -0,0 +1,118 @@
+_subject = $subject;
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->_subject);
+ }
+
+ /**
+ * Rewind iterator
+ */
+ public function rewind()
+ {
+ $this->_position = 0;
+ }
+
+ /**
+ * Current PHPExcel_Worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function current()
+ {
+ return $this->_subject->getSheet($this->_position);
+ }
+
+ /**
+ * Current key
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->_position;
+ }
+
+ /**
+ * Next value
+ */
+ public function next()
+ {
+ ++$this->_position;
+ }
+
+ /**
+ * More PHPExcel_Worksheet instances available?
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->_position < $this->_subject->getSheetCount();
+ }
+}