FirePHP ERROR: Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.
';
+ } else {
+ throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
+ }
+ }
+
+ $Type = null;
+ $Label = null;
+ $Options = array();
+
+ if(func_num_args()==1) {
+ } else
+ if(func_num_args()==2) {
+ switch(func_get_arg(1)) {
+ case self::LOG:
+ case self::INFO:
+ case self::WARN:
+ case self::ERROR:
+ case self::DUMP:
+ case self::TRACE:
+ case self::EXCEPTION:
+ case self::TABLE:
+ case self::GROUP_START:
+ case self::GROUP_END:
+ $Type = func_get_arg(1);
+ break;
+ default:
+ $Label = func_get_arg(1);
+ break;
+ }
+ } else
+ if(func_num_args()==3) {
+ $Type = func_get_arg(2);
+ $Label = func_get_arg(1);
+ } else
+ if(func_num_args()==4) {
+ $Type = func_get_arg(2);
+ $Label = func_get_arg(1);
+ $Options = func_get_arg(3);
+ } else {
+ throw $this->newException('Wrong number of arguments to fb() function!');
+ }
+
+
+ if(!$this->detectClientExtension()) {
+ return false;
+ }
+
+ $meta = array();
+ $skipFinalObjectEncode = false;
+
+ if($Object instanceof Exception) {
+
+ $meta['file'] = $this->_escapeTraceFile($Object->getFile());
+ $meta['line'] = $Object->getLine();
+
+ $trace = $Object->getTrace();
+ if($Object instanceof ErrorException
+ && isset($trace[0]['function'])
+ && $trace[0]['function']=='errorHandler'
+ && isset($trace[0]['class'])
+ && $trace[0]['class']=='FirePHP') {
+
+ $severity = false;
+ switch($Object->getSeverity()) {
+ case E_WARNING: $severity = 'E_WARNING'; break;
+ case E_NOTICE: $severity = 'E_NOTICE'; break;
+ case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
+ case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
+ case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
+ case E_STRICT: $severity = 'E_STRICT'; break;
+ case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
+ case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
+ case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
+ }
+
+ $Object = array('Class'=>get_class($Object),
+ 'Message'=>$severity.': '.$Object->getMessage(),
+ 'File'=>$this->_escapeTraceFile($Object->getFile()),
+ 'Line'=>$Object->getLine(),
+ 'Type'=>'trigger',
+ 'Trace'=>$this->_escapeTrace(array_splice($trace,2)));
+ $skipFinalObjectEncode = true;
+ } else {
+ $Object = array('Class'=>get_class($Object),
+ 'Message'=>$Object->getMessage(),
+ 'File'=>$this->_escapeTraceFile($Object->getFile()),
+ 'Line'=>$Object->getLine(),
+ 'Type'=>'throw',
+ 'Trace'=>$this->_escapeTrace($trace));
+ $skipFinalObjectEncode = true;
+ }
+ $Type = self::EXCEPTION;
+
+ } else
+ if($Type==self::TRACE) {
+
+ $trace = debug_backtrace();
+ if(!$trace) return false;
+ for( $i=0 ; $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+ } else
+ if(isset($trace[$i]['class'])
+ && isset($trace[$i+1]['file'])
+ && $trace[$i]['class']=='FirePHP'
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
+ /* Skip fb() */
+ } else
+ if($trace[$i]['function']=='fb'
+ || $trace[$i]['function']=='trace'
+ || $trace[$i]['function']=='send') {
+ $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
+ 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
+ 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
+ 'Message'=>$trace[$i]['args'][0],
+ 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
+ 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
+ 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
+ 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
+
+ $skipFinalObjectEncode = true;
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
+ break;
+ }
+ }
+
+ } else
+ if($Type==self::TABLE) {
+
+ if(isset($Object[0]) && is_string($Object[0])) {
+ $Object[1] = $this->encodeTable($Object[1]);
+ } else {
+ $Object = $this->encodeTable($Object);
+ }
+
+ $skipFinalObjectEncode = true;
+
+ } else
+ if($Type==self::GROUP_START) {
+
+ if(!$Label) {
+ throw $this->newException('You must specify a label for the group!');
+ }
+
+ } else {
+ if($Type===null) {
+ $Type = self::LOG;
+ }
+ }
+
+ if($this->options['includeLineNumbers']) {
+ if(!isset($meta['file']) || !isset($meta['line'])) {
+
+ $trace = debug_backtrace();
+ for( $i=0 ; $trace && $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+ } else
+ if(isset($trace[$i]['class'])
+ && isset($trace[$i+1]['file'])
+ && $trace[$i]['class']=='FirePHP'
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
+ /* Skip fb() */
+ } else
+ if(isset($trace[$i]['file'])
+ && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
+ /* Skip FB::fb() */
+ } else {
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
+ break;
+ }
+ }
+
+ }
+ } else {
+ unset($meta['file']);
+ unset($meta['line']);
+ }
+
+ $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
+ $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION);
+
+ $structure_index = 1;
+ if($Type==self::DUMP) {
+ $structure_index = 2;
+ $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
+ } else {
+ $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
+ }
+
+ if($Type==self::DUMP) {
+ $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
+ } else {
+ $msg_meta = $Options;
+ $msg_meta['Type'] = $Type;
+ if($Label!==null) {
+ $msg_meta['Label'] = $Label;
+ }
+ if(isset($meta['file']) && !isset($msg_meta['File'])) {
+ $msg_meta['File'] = $meta['file'];
+ }
+ if(isset($meta['line']) && !isset($msg_meta['Line'])) {
+ $msg_meta['Line'] = $meta['line'];
+ }
+ $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
+ }
+
+ $parts = explode("\n",chunk_split($msg, 5000, "\n"));
+
+ for( $i=0 ; $i2) {
+ // Message needs to be split into multiple parts
+ $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
+ (($i==0)?strlen($msg):'')
+ . '|' . $part . '|'
+ . (($isetHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
+ strlen($part) . '|' . $part . '|');
+ }
+
+ $this->messageIndex++;
+
+ if ($this->messageIndex > 99999) {
+ throw $this->newException('Maximum number (99,999) of messages reached!');
+ }
+ }
+ }
+
+ $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
+
+ return true;
+ }
+
+ /**
+ * Standardizes path for windows systems.
+ *
+ * @param string $Path
+ * @return string
+ */
+ protected function _standardizePath($Path) {
+ return preg_replace('/\\\\+/','/',$Path);
+ }
+
+ /**
+ * Escape trace path for windows systems
+ *
+ * @param array $Trace
+ * @return array
+ */
+ protected function _escapeTrace($Trace) {
+ if(!$Trace) return $Trace;
+ for( $i=0 ; $i_escapeTraceFile($Trace[$i]['file']);
+ }
+ if(isset($Trace[$i]['args'])) {
+ $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
+ }
+ }
+ return $Trace;
+ }
+
+ /**
+ * Escape file information of trace for windows systems
+ *
+ * @param string $File
+ * @return string
+ */
+ protected function _escapeTraceFile($File) {
+ /* Check if we have a windows filepath */
+ if(strpos($File,'\\')) {
+ /* First strip down to single \ */
+
+ $file = preg_replace('/\\\\+/','\\',$File);
+
+ return $file;
+ }
+ return $File;
+ }
+
+ /**
+ * Send header
+ *
+ * @param string $Name
+ * @param string_type $Value
+ */
+ protected function setHeader($Name, $Value) {
+ return header($Name.': '.$Value);
+ }
+
+ /**
+ * Get user agent
+ *
+ * @return string|false
+ */
+ protected function getUserAgent() {
+ if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
+ return $_SERVER['HTTP_USER_AGENT'];
+ }
+
+ /**
+ * Returns a new exception
+ *
+ * @param string $Message
+ * @return Exception
+ */
+ protected function newException($Message) {
+ return new Exception($Message);
+ }
+
+ /**
+ * Encode an object into a JSON string
+ *
+ * Uses PHP's jeson_encode() if available
+ *
+ * @param object $Object The object to be encoded
+ * @return string The JSON string
+ */
+ public function jsonEncode($Object, $skipObjectEncode=false)
+ {
+ if(!$skipObjectEncode) {
+ $Object = $this->encodeObject($Object);
+ }
+
+ if(function_exists('json_encode')
+ && $this->options['useNativeJsonEncode']!=false) {
+
+ return json_encode($Object);
+ } else {
+ return $this->json_encode($Object);
+ }
+ }
+
+ /**
+ * Encodes a table by encoding each row and column with encodeObject()
+ *
+ * @param array $Table The table to be encoded
+ * @return array
+ */
+ protected function encodeTable($Table) {
+
+ if(!$Table) return $Table;
+
+ $new_table = array();
+ foreach($Table as $row) {
+
+ if(is_array($row)) {
+ $new_row = array();
+
+ foreach($row as $item) {
+ $new_row[] = $this->encodeObject($item);
+ }
+
+ $new_table[] = $new_row;
+ }
+ }
+
+ return $new_table;
+ }
+
+ /**
+ * Encodes an object including members with
+ * protected and private visibility
+ *
+ * @param Object $Object The object to be encoded
+ * @param int $Depth The current traversal depth
+ * @return array All members of the object
+ */
+ protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
+ {
+ $return = array();
+
+ if (is_resource($Object)) {
+
+ return '** '.(string)$Object.' **';
+
+ } else
+ if (is_object($Object)) {
+
+ if ($ObjectDepth > $this->options['maxObjectDepth']) {
+ return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
+ }
+
+ foreach ($this->objectStack as $refVal) {
+ if ($refVal === $Object) {
+ return '** Recursion ('.get_class($Object).') **';
+ }
+ }
+ array_push($this->objectStack, $Object);
+
+ $return['__className'] = $class = get_class($Object);
+
+ $reflectionClass = new ReflectionClass($class);
+ $properties = array();
+ foreach( $reflectionClass->getProperties() as $property) {
+ $properties[$property->getName()] = $property;
+ }
+
+ $members = (array)$Object;
+
+ foreach( $properties as $raw_name => $property ) {
+
+ $name = $raw_name;
+ if($property->isStatic()) {
+ $name = 'static:'.$name;
+ }
+ if($property->isPublic()) {
+ $name = 'public:'.$name;
+ } else
+ if($property->isPrivate()) {
+ $name = 'private:'.$name;
+ $raw_name = "\0".$class."\0".$raw_name;
+ } else
+ if($property->isProtected()) {
+ $name = 'protected:'.$name;
+ $raw_name = "\0".'*'."\0".$raw_name;
+ }
+
+ if(!(isset($this->objectFilters[$class])
+ && is_array($this->objectFilters[$class])
+ && in_array($raw_name,$this->objectFilters[$class]))) {
+
+ if(array_key_exists($raw_name,$members)
+ && !$property->isStatic()) {
+
+ $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1);
+
+ } else {
+ if(method_exists($property,'setAccessible')) {
+ $property->setAccessible(true);
+ $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
+ } else
+ if($property->isPublic()) {
+ $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
+ } else {
+ $return[$name] = '** Need PHP 5.3 to get value **';
+ }
+ }
+ } else {
+ $return[$name] = '** Excluded by Filter **';
+ }
+ }
+
+ // Include all members that are not defined in the class
+ // but exist in the object
+ foreach( $members as $raw_name => $value ) {
+
+ $name = $raw_name;
+
+ if ($name{0} == "\0") {
+ $parts = explode("\0", $name);
+ $name = $parts[2];
+ }
+
+ if(!isset($properties[$name])) {
+ $name = 'undeclared:'.$name;
+
+ if(!(isset($this->objectFilters[$class])
+ && is_array($this->objectFilters[$class])
+ && in_array($raw_name,$this->objectFilters[$class]))) {
+
+ $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
+ } else {
+ $return[$name] = '** Excluded by Filter **';
+ }
+ }
+ }
+
+ array_pop($this->objectStack);
+
+ } elseif (is_array($Object)) {
+
+ if ($ArrayDepth > $this->options['maxArrayDepth']) {
+ return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
+ }
+
+ foreach ($Object as $key => $val) {
+
+ // Encoding the $GLOBALS PHP array causes an infinite loop
+ // if the recursion is not reset here as it contains
+ // a reference to itself. This is the only way I have come up
+ // with to stop infinite recursion in this case.
+ if($key=='GLOBALS'
+ && is_array($val)
+ && array_key_exists('GLOBALS',$val)) {
+ $val['GLOBALS'] = '** Recursion (GLOBALS) **';
+ }
+
+ $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
+ }
+ } else {
+ if(self::is_utf8($Object)) {
+ return $Object;
+ } else {
+ return utf8_encode($Object);
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * Returns true if $string is valid UTF-8 and false otherwise.
+ *
+ * @param mixed $str String to be tested
+ * @return boolean
+ */
+ protected static function is_utf8($str) {
+ $c=0; $b=0;
+ $bits=0;
+ $len=strlen($str);
+ for($i=0; $i<$len; $i++){
+ $c=ord($str[$i]);
+ if($c > 128){
+ if(($c >= 254)) return false;
+ elseif($c >= 252) $bits=6;
+ elseif($c >= 248) $bits=5;
+ elseif($c >= 240) $bits=4;
+ elseif($c >= 224) $bits=3;
+ elseif($c >= 192) $bits=2;
+ else return false;
+ if(($i+$bits) > $len) return false;
+ while($bits > 1){
+ $i++;
+ $b=ord($str[$i]);
+ if($b < 128 || $b > 191) return false;
+ $bits--;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Converts to and from JSON format.
+ *
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
+ * format. It is easy for humans to read and write. It is easy for machines
+ * to parse and generate. It is based on a subset of the JavaScript
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
+ * This feature can also be found in Python. JSON is a text format that is
+ * completely language independent but uses conventions that are familiar
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
+ * ideal data-interchange language.
+ *
+ * This package provides a simple encoder and decoder for JSON notation. It
+ * is intended for use with client-side Javascript applications that make
+ * use of HTTPRequest to perform server communication functions - data can
+ * be encoded into JSON notation for use in a client-side javascript, or
+ * decoded from incoming Javascript requests. JSON format is native to
+ * Javascript, and can be directly eval()'ed with no further parsing
+ * overhead
+ *
+ * All strings should be in ASCII or UTF-8 format!
+ *
+ * LICENSE: Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met: Redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * @category
+ * @package Services_JSON
+ * @author Michal Migurski
+ * @author Matt Knapp
+ * @author Brett Stimmerman
+ * @author Christoph Dorn
+ * @copyright 2005 Michal Migurski
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+ */
+
+
+ /**
+ * Keep a list of objects as we descend into the array so we can detect recursion.
+ */
+ private $json_objectStack = array();
+
+
+ /**
+ * convert a string from one UTF-8 char to one UTF-16 char
+ *
+ * Normally should be handled by mb_convert_encoding, but
+ * provides a slower PHP-only method for installations
+ * that lack the multibye string extension.
+ *
+ * @param string $utf8 UTF-8 character
+ * @return string UTF-16 character
+ * @access private
+ */
+ private function json_utf82utf16($utf8)
+ {
+ // oh please oh please oh please oh please oh please
+ if(function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+ }
+
+ switch(strlen($utf8)) {
+ case 1:
+ // this case should never be reached, because we are in ASCII range
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return $utf8;
+
+ case 2:
+ // return a UTF-16 character from a 2-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0x07 & (ord($utf8{0}) >> 2))
+ . chr((0xC0 & (ord($utf8{0}) << 6))
+ | (0x3F & ord($utf8{1})));
+
+ case 3:
+ // return a UTF-16 character from a 3-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr((0xF0 & (ord($utf8{0}) << 4))
+ | (0x0F & (ord($utf8{1}) >> 2)))
+ . chr((0xC0 & (ord($utf8{1}) << 6))
+ | (0x7F & ord($utf8{2})));
+ }
+
+ // ignoring UTF-32 for now, sorry
+ return '';
+ }
+
+ /**
+ * encodes an arbitrary variable into JSON format
+ *
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
+ * if var is a strng, note that encode() always expects it
+ * to be in ASCII or UTF-8 format!
+ *
+ * @return mixed JSON string representation of input var or an error if a problem occurs
+ * @access public
+ */
+ private function json_encode($var)
+ {
+
+ if(is_object($var)) {
+ if(in_array($var,$this->json_objectStack)) {
+ return '"** Recursion **"';
+ }
+ }
+
+ switch (gettype($var)) {
+ case 'boolean':
+ return $var ? 'true' : 'false';
+
+ case 'NULL':
+ return 'null';
+
+ case 'integer':
+ return (int) $var;
+
+ case 'double':
+ case 'float':
+ return (float) $var;
+
+ case 'string':
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+ $ascii = '';
+ $strlen_var = strlen($var);
+
+ /*
+ * Iterate over every character in the string,
+ * escaping with a slash or encoding to UTF-8 where necessary
+ */
+ for ($c = 0; $c < $strlen_var; ++$c) {
+
+ $ord_var_c = ord($var{$c});
+
+ switch (true) {
+ case $ord_var_c == 0x08:
+ $ascii .= '\b';
+ break;
+ case $ord_var_c == 0x09:
+ $ascii .= '\t';
+ break;
+ case $ord_var_c == 0x0A:
+ $ascii .= '\n';
+ break;
+ case $ord_var_c == 0x0C:
+ $ascii .= '\f';
+ break;
+ case $ord_var_c == 0x0D:
+ $ascii .= '\r';
+ break;
+
+ case $ord_var_c == 0x22:
+ case $ord_var_c == 0x2F:
+ case $ord_var_c == 0x5C:
+ // double quote, slash, slosh
+ $ascii .= '\\'.$var{$c};
+ break;
+
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+ // characters U-00000000 - U-0000007F (same as ASCII)
+ $ascii .= $var{$c};
+ break;
+
+ case (($ord_var_c & 0xE0) == 0xC0):
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+ $c += 1;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF0) == 0xE0):
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}));
+ $c += 2;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF8) == 0xF0):
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}));
+ $c += 3;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFC) == 0xF8):
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}),
+ ord($var{$c + 4}));
+ $c += 4;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFE) == 0xFC):
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}),
+ ord($var{$c + 4}),
+ ord($var{$c + 5}));
+ $c += 5;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+ }
+ }
+
+ return '"'.$ascii.'"';
+
+ case 'array':
+ /*
+ * As per JSON spec if any array key is not an integer
+ * we must treat the the whole array as an object. We
+ * also try to catch a sparsely populated associative
+ * array with numeric keys here because some JS engines
+ * will create an array with empty indexes up to
+ * max_index which can cause memory issues and because
+ * the keys, which may be relevant, will be remapped
+ * otherwise.
+ *
+ * As per the ECMA and JSON specification an object may
+ * have any string as a property. Unfortunately due to
+ * a hole in the ECMA specification if the key is a
+ * ECMA reserved word or starts with a digit the
+ * parameter is only accessible using ECMAScript's
+ * bracket notation.
+ */
+
+ // treat as a JSON object
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+
+ $this->json_objectStack[] = $var;
+
+ $properties = array_map(array($this, 'json_name_value'),
+ array_keys($var),
+ array_values($var));
+
+ array_pop($this->json_objectStack);
+
+ foreach($properties as $property) {
+ if($property instanceof Exception) {
+ return $property;
+ }
+ }
+
+ return '{' . join(',', $properties) . '}';
+ }
+
+ $this->json_objectStack[] = $var;
+
+ // treat it like a regular array
+ $elements = array_map(array($this, 'json_encode'), $var);
+
+ array_pop($this->json_objectStack);
+
+ foreach($elements as $element) {
+ if($element instanceof Exception) {
+ return $element;
+ }
+ }
+
+ return '[' . join(',', $elements) . ']';
+
+ case 'object':
+ $vars = self::encodeObject($var);
+
+ $this->json_objectStack[] = $var;
+
+ $properties = array_map(array($this, 'json_name_value'),
+ array_keys($vars),
+ array_values($vars));
+
+ array_pop($this->json_objectStack);
+
+ foreach($properties as $property) {
+ if($property instanceof Exception) {
+ return $property;
+ }
+ }
+
+ return '{' . join(',', $properties) . '}';
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * array-walking function for use in generating JSON-formatted name-value pairs
+ *
+ * @param string $name name of key to use
+ * @param mixed $value reference to an array element to be encoded
+ *
+ * @return string JSON-formatted name-value pair, like '"name":value'
+ * @access private
+ */
+ private function json_name_value($name, $value)
+ {
+ // Encoding the $GLOBALS PHP array causes an infinite loop
+ // if the recursion is not reset here as it contains
+ // a reference to itself. This is the only way I have come up
+ // with to stop infinite recursion in this case.
+ if($name=='GLOBALS'
+ && is_array($value)
+ && array_key_exists('GLOBALS',$value)) {
+ $value['GLOBALS'] = '** Recursion **';
+ }
+
+ $encoded_value = $this->json_encode($value);
+
+ if($encoded_value instanceof Exception) {
+ return $encoded_value;
+ }
+
+ return $this->json_encode(strval($name)) . ':' . $encoded_value;
+ }
+}
diff --git a/flaxil/inc/cube/externals/FirePHPCore/FirePHP.class.php4 b/flaxil/inc/cube/externals/FirePHPCore/FirePHP.class.php4
new file mode 100644
index 0000000..3e20120
--- /dev/null
+++ b/flaxil/inc/cube/externals/FirePHPCore/FirePHP.class.php4
@@ -0,0 +1,1291 @@
+
+ * @author Michael Day
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @package FirePHP
+ */
+
+/**
+ * FirePHP version
+ *
+ * @var string
+ */
+define('FirePHP_VERSION', '0.3');
+
+/**
+ * Firebug LOG level
+ *
+ * Logs a message to firebug console
+ *
+ * @var string
+ */
+define('FirePHP_LOG', 'LOG');
+
+/**
+ * Firebug INFO level
+ *
+ * Logs a message to firebug console and displays an info icon before the message
+ *
+ * @var string
+ */
+define('FirePHP_INFO', 'INFO');
+
+/**
+ * Firebug WARN level
+ *
+ * Logs a message to firebug console, displays a warning icon before the message and colors the line turquoise
+ *
+ * @var string
+ */
+define('FirePHP_WARN', 'WARN');
+
+/**
+ * Firebug ERROR level
+ *
+ * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
+ *
+ * @var string
+ */
+define('FirePHP_ERROR', 'ERROR');
+
+/**
+ * Dumps a variable to firebug's server panel
+ *
+ * @var string
+ */
+define('FirePHP_DUMP', 'DUMP');
+
+/**
+ * Displays a stack trace in firebug console
+ *
+ * @var string
+ */
+define('FirePHP_TRACE', 'TRACE');
+
+/**
+ * Displays a table in firebug console
+ *
+ * @var string
+ */
+define('FirePHP_TABLE', 'TABLE');
+
+/**
+ * Starts a group in firebug console
+ *
+ * @var string
+ */
+define('FirePHP_GROUP_START', 'GROUP_START');
+
+/**
+ * Ends a group in firebug console
+ *
+ * @var string
+ */
+define('FirePHP_GROUP_END', 'GROUP_END');
+
+/**
+ * Sends the given data to the FirePHP Firefox Extension.
+ * The data can be displayed in the Firebug Console or in the
+ * "Server" request tab.
+ *
+ * For more information see: http://www.firephp.org/
+ *
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
+ * @author Christoph Dorn
+ * @author Michael Day
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @package FirePHP
+ */
+class FirePHP {
+ /**
+ * Wildfire protocol message index
+ *
+ * @var int
+ */
+ var $messageIndex = 1;
+
+ /**
+ * Options for the library
+ *
+ * @var array
+ */
+ var $options = array('maxObjectDepth' => 10,
+ 'maxArrayDepth' => 20,
+ 'useNativeJsonEncode' => true,
+ 'includeLineNumbers' => true);
+
+ /**
+ * Filters used to exclude object members when encoding
+ *
+ * @var array
+ */
+ var $objectFilters = array();
+
+ /**
+ * A stack of objects used to detect recursion during object encoding
+ *
+ * @var object
+ */
+ var $objectStack = array();
+
+ /**
+ * Flag to enable/disable logging
+ *
+ * @var boolean
+ */
+ var $enabled = true;
+
+ /**
+ * The object constructor
+ */
+ function FirePHP() {
+ }
+
+
+ /**
+ * When the object gets serialized only include specific object members.
+ *
+ * @return array
+ */
+ function __sleep() {
+ return array('options','objectFilters','enabled');
+ }
+
+ /**
+ * Gets singleton instance of FirePHP
+ *
+ * @param boolean $AutoCreate
+ * @return FirePHP
+ */
+ function &getInstance($AutoCreate=false) {
+ global $FirePHP_Instance;
+
+ if($AutoCreate===true && !$FirePHP_Instance) {
+ $FirePHP_Instance = new FirePHP();
+ }
+
+ return $FirePHP_Instance;
+ }
+
+ /**
+ * Enable and disable logging to Firebug
+ *
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
+ * @return void
+ */
+ function setEnabled($Enabled) {
+ $this->enabled = $Enabled;
+ }
+
+ /**
+ * Check if logging is enabled
+ *
+ * @return boolean TRUE if enabled
+ */
+ function getEnabled() {
+ return $this->enabled;
+ }
+
+ /**
+ * Specify a filter to be used when encoding an object
+ *
+ * Filters are used to exclude object members.
+ *
+ * @param string $Class The class name of the object
+ * @param array $Filter An array of members to exclude
+ * @return void
+ */
+ function setObjectFilter($Class, $Filter) {
+ $this->objectFilters[$Class] = $Filter;
+ }
+
+ /**
+ * Set some options for the library
+ *
+ * Options:
+ * - maxObjectDepth: The maximum depth to traverse objects (default: 10)
+ * - maxArrayDepth: The maximum depth to traverse arrays (default: 20)
+ * - useNativeJsonEncode: If true will use json_encode() (default: true)
+ * - includeLineNumbers: If true will include line numbers and filenames (default: true)
+ *
+ * @param array $Options The options to be set
+ * @return void
+ */
+ function setOptions($Options) {
+ $this->options = array_merge($this->options,$Options);
+ }
+
+ /**
+ * Get options from the library
+ *
+ * @return array The currently set options
+ */
+ function getOptions() {
+ return $this->options;
+ }
+
+ /**
+ * Register FirePHP as your error handler
+ *
+ * Will use FirePHP to log each php error.
+ *
+ * @return mixed Returns a string containing the previously defined error handler (if any)
+ */
+ function registerErrorHandler()
+ {
+ //NOTE: The following errors will not be caught by this error handler:
+ // E_ERROR, E_PARSE, E_CORE_ERROR,
+ // E_CORE_WARNING, E_COMPILE_ERROR,
+ // E_COMPILE_WARNING, E_STRICT
+
+ return set_error_handler(array($this,'errorHandler'));
+ }
+
+ /**
+ * FirePHP's error handler
+ *
+ * Logs each php error that will occur.
+ *
+ * @param int $errno
+ * @param string $errstr
+ * @param string $errfile
+ * @param int $errline
+ * @param array $errcontext
+ */
+ function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
+ {
+ global $FirePHP_Instance;
+ // Don't log error if error reporting is switched off
+ if (error_reporting() == 0) {
+ return;
+ }
+ // Only log error for errors we are asking for
+ if (error_reporting() & $errno) {
+ $FirePHP_Instance->group($errstr);
+ $FirePHP_Instance->error("{$errfile}, line $errline");
+ $FirePHP_Instance->groupEnd();
+ }
+ }
+
+ /**
+ * Register FirePHP driver as your assert callback
+ *
+ * @return mixed Returns the original setting
+ */
+ function registerAssertionHandler()
+ {
+ return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
+ }
+
+ /**
+ * FirePHP's assertion handler
+ *
+ * Logs all assertions to your firebug console and then stops the script.
+ *
+ * @param string $file File source of assertion
+ * @param int $line Line source of assertion
+ * @param mixed $code Assertion code
+ */
+ function assertionHandler($file, $line, $code)
+ {
+ $this->fb($code, 'Assertion Failed', FirePHP_ERROR, array('File'=>$file,'Line'=>$line));
+ }
+
+ /**
+ * Set custom processor url for FirePHP
+ *
+ * @param string $URL
+ */
+ function setProcessorUrl($URL)
+ {
+ $this->setHeader('X-FirePHP-ProcessorURL', $URL);
+ }
+
+ /**
+ * Set custom renderer url for FirePHP
+ *
+ * @param string $URL
+ */
+ function setRendererUrl($URL)
+ {
+ $this->setHeader('X-FirePHP-RendererURL', $URL);
+ }
+
+ /**
+ * Start a group for following messages.
+ *
+ * Options:
+ * Collapsed: [true|false]
+ * Color: [#RRGGBB|ColorName]
+ *
+ * @param string $Name
+ * @param array $Options OPTIONAL Instructions on how to log the group
+ * @return true
+ * @throws Exception
+ */
+ function group($Name, $Options=null) {
+
+ if(!$Name) {
+ trigger_error('You must specify a label for the group!');
+ }
+
+ if($Options) {
+ if(!is_array($Options)) {
+ trigger_error('Options must be defined as an array!');
+ }
+ if(array_key_exists('Collapsed', $Options)) {
+ $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false';
+ }
+ }
+
+ return $this->fb(null, $Name, FirePHP_GROUP_START, $Options);
+ }
+
+ /**
+ * Ends a group you have started before
+ *
+ * @return true
+ * @throws Exception
+ */
+ function groupEnd() {
+ return $this->fb(null, null, FirePHP_GROUP_END);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::LOG
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ function log($Object, $Label=null) {
+ return $this->fb($Object, $Label, FirePHP_LOG);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::INFO
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ function info($Object, $Label=null) {
+ return $this->fb($Object, $Label, FirePHP_INFO);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::WARN
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ function warn($Object, $Label=null) {
+ return $this->fb($Object, $Label, FirePHP_WARN);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::ERROR
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ function error($Object, $Label=null) {
+ return $this->fb($Object, $Label, FirePHP_ERROR);
+ }
+
+ /**
+ * Dumps key and variable to firebug server panel
+ *
+ * @see FirePHP::DUMP
+ * @param string $Key
+ * @param mixed $Variable
+ * @return true
+ * @throws Exception
+ */
+ function dump($Key, $Variable) {
+ return $this->fb($Variable, $Key, FirePHP_DUMP);
+ }
+
+ /**
+ * Log a trace in the firebug console
+ *
+ * @see FirePHP::TRACE
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ function trace($Label) {
+ return $this->fb($Label, FirePHP_TRACE);
+ }
+
+ /**
+ * Log a table in the firebug console
+ *
+ * @see FirePHP::TABLE
+ * @param string $Label
+ * @param string $Table
+ * @return true
+ * @throws Exception
+ */
+ function table($Label, $Table) {
+ return $this->fb($Table, $Label, FirePHP_TABLE);
+ }
+
+ /**
+ * Check if FirePHP is installed on client
+ *
+ * @return boolean
+ */
+ function detectClientExtension() {
+ /* Check if FirePHP is installed on client */
+ if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) ||
+ !version_compare($m[1][0],'0.0.6','>=')) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Log varible to Firebug
+ *
+ * @see http://www.firephp.org/Wiki/Reference/Fb
+ * @param mixed $Object The variable to be logged
+ * @return true Return TRUE if message was added to headers, FALSE otherwise
+ * @throws Exception
+ */
+ function fb($Object) {
+
+ if(!$this->enabled) {
+ return false;
+ }
+
+ if (headers_sent($filename, $linenum)) {
+ trigger_error('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
+ }
+
+ $Type = null;
+ $Label = null;
+ $Options = array();
+
+ if(func_num_args()==1) {
+ } else
+ if(func_num_args()==2) {
+ switch(func_get_arg(1)) {
+ case FirePHP_LOG:
+ case FirePHP_INFO:
+ case FirePHP_WARN:
+ case FirePHP_ERROR:
+ case FirePHP_DUMP:
+ case FirePHP_TRACE:
+ case FirePHP_TABLE:
+ case FirePHP_GROUP_START:
+ case FirePHP_GROUP_END:
+ $Type = func_get_arg(1);
+ break;
+ default:
+ $Label = func_get_arg(1);
+ break;
+ }
+ } else
+ if(func_num_args()==3) {
+ $Type = func_get_arg(2);
+ $Label = func_get_arg(1);
+ } else
+ if(func_num_args()==4) {
+ $Type = func_get_arg(2);
+ $Label = func_get_arg(1);
+ $Options = func_get_arg(3);
+ } else {
+ trigger_error('Wrong number of arguments to fb() function!');
+ }
+
+
+ if(!$this->detectClientExtension()) {
+ return false;
+ }
+
+ $meta = array();
+ $skipFinalObjectEncode = false;
+
+ if($Type==FirePHP_TRACE) {
+
+ $trace = debug_backtrace();
+ if(!$trace) return false;
+ for( $i=0 ; $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+ } else
+ if(isset($trace[$i]['class'])
+ && isset($trace[$i+1]['file'])
+ && $trace[$i]['class']=='FirePHP'
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
+ /* Skip fb() */
+ } else
+ if($trace[$i]['function']=='fb'
+ || $trace[$i]['function']=='trace'
+ || $trace[$i]['function']=='send') {
+ $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
+ 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
+ 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
+ 'Message'=>$trace[$i]['args'][0],
+ 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
+ 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
+ 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
+ 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
+
+ $skipFinalObjectEncode = true;
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
+ break;
+ }
+ }
+
+ } else
+ if($Type==FirePHP_TABLE) {
+
+ if(isset($Object[0]) && is_string($Object[0])) {
+ $Object[1] = $this->encodeTable($Object[1]);
+ } else {
+ $Object = $this->encodeTable($Object);
+ }
+
+ $skipFinalObjectEncode = true;
+
+ } else
+ if($Type==FirePHP_GROUP_START) {
+
+ if(!$Label) {
+ trigger_error('You must specify a label for the group!');
+ }
+ } else {
+ if($Type===null) {
+ $Type = FirePHP_LOG;
+ }
+ }
+
+ if($this->options['includeLineNumbers']) {
+ if(!isset($meta['file']) || !isset($meta['line'])) {
+
+ $trace = debug_backtrace();
+ for( $i=0 ; $trace && $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+ } else
+ if(isset($trace[$i]['class'])
+ && isset($trace[$i+1]['file'])
+ && $trace[$i]['class']=='FirePHP'
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
+ /* Skip fb() */
+ } else
+ if(isset($trace[$i]['file'])
+ && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
+ /* Skip FB::fb() */
+ } else {
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
+ break;
+ }
+ }
+
+ }
+ } else {
+ unset($meta['file']);
+ unset($meta['line']);
+ }
+
+ $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
+ $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.FirePHP_VERSION);
+
+ $structure_index = 1;
+ if($Type==FirePHP_DUMP) {
+ $structure_index = 2;
+ $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
+ } else {
+ $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
+ }
+
+ if($Type==FirePHP_DUMP) {
+ $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
+ } else {
+ $msg_meta = $Options;
+ $msg_meta['Type'] = $Type;
+ if($Label!==null) {
+ $msg_meta['Label'] = $Label;
+ }
+ if(isset($meta['file']) && !isset($msg_meta['File'])) {
+ $msg_meta['File'] = $meta['file'];
+ }
+ if(isset($meta['line']) && !isset($msg_meta['Line'])) {
+ $msg_meta['Line'] = $meta['line'];
+ }
+ $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
+ }
+
+ $parts = explode("\n",chunk_split($msg, 5000, "\n"));
+
+ for( $i=0 ; $i2) {
+ // Message needs to be split into multiple parts
+ $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
+ (($i==0)?strlen($msg):'')
+ . '|' . $part . '|'
+ . (($isetHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
+ strlen($part) . '|' . $part . '|');
+ }
+
+ $this->messageIndex++;
+
+ if ($this->messageIndex > 99999) {
+ trigger_error('Maximum number (99,999) of messages reached!');
+ }
+ }
+ }
+
+ $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
+
+ return true;
+ }
+
+
+ /**
+ * Standardizes path for windows systems.
+ *
+ * @param string $Path
+ * @return string
+ */
+ function _standardizePath($Path) {
+ return preg_replace('/\\\\+/','/',$Path);
+ }
+
+ /**
+ * Escape trace path for windows systems
+ *
+ * @param array $Trace
+ * @return array
+ */
+ function _escapeTrace($Trace) {
+ if(!$Trace) return $Trace;
+ for( $i=0 ; $i_escapeTraceFile($Trace[$i]['file']);
+ }
+ if(isset($Trace[$i]['args'])) {
+ $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
+ }
+ }
+ return $Trace;
+ }
+
+ /**
+ * Escape file information of trace for windows systems
+ *
+ * @param string $File
+ * @return string
+ */
+ function _escapeTraceFile($File) {
+ /* Check if we have a windows filepath */
+ if(strpos($File,'\\')) {
+ /* First strip down to single \ */
+
+ $file = preg_replace('/\\\\+/','\\',$File);
+
+ return $file;
+ }
+ return $File;
+ }
+
+ /**
+ * Send header
+ *
+ * @param string $Name
+ * @param string_type $Value
+ */
+ function setHeader($Name, $Value) {
+ return header($Name.': '.$Value);
+ }
+
+ /**
+ * Get user agent
+ *
+ * @return string|false
+ */
+ function getUserAgent() {
+ if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
+ return $_SERVER['HTTP_USER_AGENT'];
+ }
+
+ /**
+ * Encode an object into a JSON string
+ *
+ * Uses PHP's jeson_encode() if available
+ *
+ * @param object $Object The object to be encoded
+ * @return string The JSON string
+ */
+ function jsonEncode($Object, $skipObjectEncode=false)
+ {
+ if(!$skipObjectEncode) {
+ $Object = $this->encodeObject($Object);
+ }
+
+ if(function_exists('json_encode')
+ && $this->options['useNativeJsonEncode']!=false) {
+
+ return json_encode($Object);
+ } else {
+ return $this->json_encode($Object);
+ }
+ }
+
+ /**
+ * Encodes a table by encoding each row and column with encodeObject()
+ *
+ * @param array $Table The table to be encoded
+ * @return array
+ */
+ function encodeTable($Table) {
+
+ if(!$Table) return $Table;
+
+ $new_table = array();
+ foreach($Table as $row) {
+
+ if(is_array($row)) {
+ $new_row = array();
+
+ foreach($row as $item) {
+ $new_row[] = $this->encodeObject($item);
+ }
+
+ $new_table[] = $new_row;
+ }
+ }
+
+ return $new_table;
+ }
+
+ /**
+ * Encodes an object
+ *
+ * @param Object $Object The object to be encoded
+ * @param int $Depth The current traversal depth
+ * @return array All members of the object
+ */
+ function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
+ {
+ $return = array();
+
+ if (is_resource($Object)) {
+
+ return '** '.(string)$Object.' **';
+
+ } else
+ if (is_object($Object)) {
+
+ if ($ObjectDepth > $this->options['maxObjectDepth']) {
+ return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
+ }
+
+ foreach ($this->objectStack as $refVal) {
+ if ($refVal === $Object) {
+ return '** Recursion ('.get_class($Object).') **';
+ }
+ }
+ array_push($this->objectStack, $Object);
+
+ $return['__className'] = $class = get_class($Object);
+
+ $members = (array)$Object;
+
+ // Include all members that are not defined in the class
+ // but exist in the object
+ foreach( $members as $raw_name => $value ) {
+
+ $name = $raw_name;
+
+ if ($name{0} == "\0") {
+ $parts = explode("\0", $name);
+ $name = $parts[2];
+ }
+
+ if(!isset($properties[$name])) {
+ $name = 'undeclared:'.$name;
+
+ if(!(isset($this->objectFilters[$class])
+ && is_array($this->objectFilters[$class])
+ && in_array($raw_name,$this->objectFilters[$class]))) {
+
+ $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
+ } else {
+ $return[$name] = '** Excluded by Filter **';
+ }
+ }
+ }
+
+ array_pop($this->objectStack);
+
+ } elseif (is_array($Object)) {
+
+ if ($ArrayDepth > $this->options['maxArrayDepth']) {
+ return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
+ }
+
+ foreach ($Object as $key => $val) {
+
+ // Encoding the $GLOBALS PHP array causes an infinite loop
+ // if the recursion is not reset here as it contains
+ // a reference to itself. This is the only way I have come up
+ // with to stop infinite recursion in this case.
+ if($key=='GLOBALS'
+ && is_array($val)
+ && array_key_exists('GLOBALS',$val)) {
+ $val['GLOBALS'] = '** Recursion (GLOBALS) **';
+ }
+
+ $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
+ }
+ } else {
+ if($this->is_utf8($Object)) {
+ return $Object;
+ } else {
+ return utf8_encode($Object);
+ }
+ }
+ return $return;
+
+ }
+
+ /**
+ * Returns true if $string is valid UTF-8 and false otherwise.
+ *
+ * @param mixed $str String to be tested
+ * @return boolean
+ */
+ function is_utf8($str) {
+ $c=0; $b=0;
+ $bits=0;
+ $len=strlen($str);
+ for($i=0; $i<$len; $i++){
+ $c=ord($str[$i]);
+ if($c > 128){
+ if(($c >= 254)) return false;
+ elseif($c >= 252) $bits=6;
+ elseif($c >= 248) $bits=5;
+ elseif($c >= 240) $bits=4;
+ elseif($c >= 224) $bits=3;
+ elseif($c >= 192) $bits=2;
+ else return false;
+ if(($i+$bits) > $len) return false;
+ while($bits > 1){
+ $i++;
+ $b=ord($str[$i]);
+ if($b < 128 || $b > 191) return false;
+ $bits--;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Converts to and from JSON format.
+ *
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
+ * format. It is easy for humans to read and write. It is easy for machines
+ * to parse and generate. It is based on a subset of the JavaScript
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
+ * This feature can also be found in Python. JSON is a text format that is
+ * completely language independent but uses conventions that are familiar
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
+ * ideal data-interchange language.
+ *
+ * This package provides a simple encoder and decoder for JSON notation. It
+ * is intended for use with client-side Javascript applications that make
+ * use of HTTPRequest to perform server communication functions - data can
+ * be encoded into JSON notation for use in a client-side javascript, or
+ * decoded from incoming Javascript requests. JSON format is native to
+ * Javascript, and can be directly eval()'ed with no further parsing
+ * overhead
+ *
+ * All strings should be in ASCII or UTF-8 format!
+ *
+ * LICENSE: Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met: Redistributions of source code must retain the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * @category
+ * @package Services_JSON
+ * @author Michal Migurski
+ * @author Matt Knapp
+ * @author Brett Stimmerman
+ * @author Christoph Dorn
+ * @copyright 2005 Michal Migurski
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+ */
+
+
+ /**
+ * Keep a list of objects as we descend into the array so we can detect recursion.
+ */
+ var $json_objectStack = array();
+
+
+ /**
+ * convert a string from one UTF-8 char to one UTF-16 char
+ *
+ * Normally should be handled by mb_convert_encoding, but
+ * provides a slower PHP-only method for installations
+ * that lack the multibye string extension.
+ *
+ * @param string $utf8 UTF-8 character
+ * @return string UTF-16 character
+ * @access private
+ */
+ function json_utf82utf16($utf8)
+ {
+ // oh please oh please oh please oh please oh please
+ if(function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+ }
+
+ switch(strlen($utf8)) {
+ case 1:
+ // this case should never be reached, because we are in ASCII range
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return $utf8;
+
+ case 2:
+ // return a UTF-16 character from a 2-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr(0x07 & (ord($utf8{0}) >> 2))
+ . chr((0xC0 & (ord($utf8{0}) << 6))
+ | (0x3F & ord($utf8{1})));
+
+ case 3:
+ // return a UTF-16 character from a 3-byte UTF-8 char
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ return chr((0xF0 & (ord($utf8{0}) << 4))
+ | (0x0F & (ord($utf8{1}) >> 2)))
+ . chr((0xC0 & (ord($utf8{1}) << 6))
+ | (0x7F & ord($utf8{2})));
+ }
+
+ // ignoring UTF-32 for now, sorry
+ return '';
+ }
+
+ /**
+ * encodes an arbitrary variable into JSON format
+ *
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
+ * if var is a strng, note that encode() always expects it
+ * to be in ASCII or UTF-8 format!
+ *
+ * @return mixed JSON string representation of input var or an error if a problem occurs
+ * @access public
+ */
+ function json_encode($var)
+ {
+
+ if(is_object($var)) {
+ if(in_array($var,$this->json_objectStack)) {
+ return '"** Recursion **"';
+ }
+ }
+
+ switch (gettype($var)) {
+ case 'boolean':
+ return $var ? 'true' : 'false';
+
+ case 'NULL':
+ return 'null';
+
+ case 'integer':
+ return (int) $var;
+
+ case 'double':
+ case 'float':
+ return (float) $var;
+
+ case 'string':
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+ $ascii = '';
+ $strlen_var = strlen($var);
+
+ /*
+ * Iterate over every character in the string,
+ * escaping with a slash or encoding to UTF-8 where necessary
+ */
+ for ($c = 0; $c < $strlen_var; ++$c) {
+
+ $ord_var_c = ord($var{$c});
+
+ switch (true) {
+ case $ord_var_c == 0x08:
+ $ascii .= '\b';
+ break;
+ case $ord_var_c == 0x09:
+ $ascii .= '\t';
+ break;
+ case $ord_var_c == 0x0A:
+ $ascii .= '\n';
+ break;
+ case $ord_var_c == 0x0C:
+ $ascii .= '\f';
+ break;
+ case $ord_var_c == 0x0D:
+ $ascii .= '\r';
+ break;
+
+ case $ord_var_c == 0x22:
+ case $ord_var_c == 0x2F:
+ case $ord_var_c == 0x5C:
+ // double quote, slash, slosh
+ $ascii .= '\\'.$var{$c};
+ break;
+
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+ // characters U-00000000 - U-0000007F (same as ASCII)
+ $ascii .= $var{$c};
+ break;
+
+ case (($ord_var_c & 0xE0) == 0xC0):
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+ $c += 1;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF0) == 0xE0):
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}));
+ $c += 2;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF8) == 0xF0):
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}));
+ $c += 3;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFC) == 0xF8):
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}),
+ ord($var{$c + 4}));
+ $c += 4;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFE) == 0xFC):
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c + 1}),
+ ord($var{$c + 2}),
+ ord($var{$c + 3}),
+ ord($var{$c + 4}),
+ ord($var{$c + 5}));
+ $c += 5;
+ $utf16 = $this->json_utf82utf16($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+ }
+ }
+
+ return '"'.$ascii.'"';
+
+ case 'array':
+ /*
+ * As per JSON spec if any array key is not an integer
+ * we must treat the the whole array as an object. We
+ * also try to catch a sparsely populated associative
+ * array with numeric keys here because some JS engines
+ * will create an array with empty indexes up to
+ * max_index which can cause memory issues and because
+ * the keys, which may be relevant, will be remapped
+ * otherwise.
+ *
+ * As per the ECMA and JSON specification an object may
+ * have any string as a property. Unfortunately due to
+ * a hole in the ECMA specification if the key is a
+ * ECMA reserved word or starts with a digit the
+ * parameter is only accessible using ECMAScript's
+ * bracket notation.
+ */
+
+ // treat as a JSON object
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+
+ $this->json_objectStack[] = $var;
+
+ $properties = array_map(array($this, 'json_name_value'),
+ array_keys($var),
+ array_values($var));
+
+ array_pop($this->json_objectStack);
+
+ return '{' . join(',', $properties) . '}';
+ }
+
+ $this->json_objectStack[] = $var;
+
+ // treat it like a regular array
+ $elements = array_map(array($this, 'json_encode'), $var);
+
+ array_pop($this->json_objectStack);
+
+ return '[' . join(',', $elements) . ']';
+
+ case 'object':
+ $vars = FirePHP::encodeObject($var);
+
+ $this->json_objectStack[] = $var;
+
+ $properties = array_map(array($this, 'json_name_value'),
+ array_keys($vars),
+ array_values($vars));
+
+ array_pop($this->json_objectStack);
+
+ return '{' . join(',', $properties) . '}';
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * array-walking function for use in generating JSON-formatted name-value pairs
+ *
+ * @param string $name name of key to use
+ * @param mixed $value reference to an array element to be encoded
+ *
+ * @return string JSON-formatted name-value pair, like '"name":value'
+ * @access private
+ */
+ function json_name_value($name, $value)
+ {
+ // Encoding the $GLOBALS PHP array causes an infinite loop
+ // if the recursion is not reset here as it contains
+ // a reference to itself. This is the only way I have come up
+ // with to stop infinite recursion in this case.
+ if($name=='GLOBALS'
+ && is_array($value)
+ && array_key_exists('GLOBALS',$value)) {
+ $value['GLOBALS'] = '** Recursion **';
+ }
+
+ $encoded_value = $this->json_encode($value);
+
+ return $this->json_encode(strval($name)) . ':' . $encoded_value;
+ }
+}
+
diff --git a/flaxil/inc/cube/externals/FirePHPCore/LICENSE b/flaxil/inc/cube/externals/FirePHPCore/LICENSE
new file mode 100644
index 0000000..3e390f9
--- /dev/null
+++ b/flaxil/inc/cube/externals/FirePHPCore/LICENSE
@@ -0,0 +1,29 @@
+Software License Agreement (New BSD License)
+
+Copyright (c) 2006-2009, Christoph Dorn
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of Christoph Dorn nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/flaxil/inc/cube/externals/FirePHPCore/fb.php b/flaxil/inc/cube/externals/FirePHPCore/fb.php
new file mode 100644
index 0000000..9d1857c
--- /dev/null
+++ b/flaxil/inc/cube/externals/FirePHPCore/fb.php
@@ -0,0 +1,261 @@
+
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @package FirePHP
+ */
+
+require_once dirname(__FILE__).'/FirePHP.class.php';
+
+/**
+ * Sends the given data to the FirePHP Firefox Extension.
+ * The data can be displayed in the Firebug Console or in the
+ * "Server" request tab.
+ *
+ * @see http://www.firephp.org/Wiki/Reference/Fb
+ * @param mixed $Object
+ * @return true
+ * @throws Exception
+ */
+function fb()
+{
+ $instance = FirePHP::getInstance(true);
+
+ $args = func_get_args();
+ return call_user_func_array(array($instance,'fb'),$args);
+}
+
+
+class FB
+{
+ /**
+ * Enable and disable logging to Firebug
+ *
+ * @see FirePHP->setEnabled()
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
+ * @return void
+ */
+ public static function setEnabled($Enabled) {
+ $instance = FirePHP::getInstance(true);
+ $instance->setEnabled($Enabled);
+ }
+
+ /**
+ * Check if logging is enabled
+ *
+ * @see FirePHP->getEnabled()
+ * @return boolean TRUE if enabled
+ */
+ public static function getEnabled() {
+ $instance = FirePHP::getInstance(true);
+ return $instance->getEnabled();
+ }
+
+ /**
+ * Specify a filter to be used when encoding an object
+ *
+ * Filters are used to exclude object members.
+ *
+ * @see FirePHP->setObjectFilter()
+ * @param string $Class The class name of the object
+ * @param array $Filter An array or members to exclude
+ * @return void
+ */
+ public static function setObjectFilter($Class, $Filter) {
+ $instance = FirePHP::getInstance(true);
+ $instance->setObjectFilter($Class, $Filter);
+ }
+
+ /**
+ * Set some options for the library
+ *
+ * @see FirePHP->setOptions()
+ * @param array $Options The options to be set
+ * @return void
+ */
+ public static function setOptions($Options) {
+ $instance = FirePHP::getInstance(true);
+ $instance->setOptions($Options);
+ }
+
+ /**
+ * Get options for the library
+ *
+ * @see FirePHP->getOptions()
+ * @return array The options
+ */
+ public static function getOptions() {
+ $instance = FirePHP::getInstance(true);
+ return $instance->getOptions();
+ }
+
+ /**
+ * Log object to firebug
+ *
+ * @see http://www.firephp.org/Wiki/Reference/Fb
+ * @param mixed $Object
+ * @return true
+ * @throws Exception
+ */
+ public static function send()
+ {
+ $instance = FirePHP::getInstance(true);
+ $args = func_get_args();
+ return call_user_func_array(array($instance,'fb'),$args);
+ }
+
+ /**
+ * Start a group for following messages
+ *
+ * Options:
+ * Collapsed: [true|false]
+ * Color: [#RRGGBB|ColorName]
+ *
+ * @param string $Name
+ * @param array $Options OPTIONAL Instructions on how to log the group
+ * @return true
+ */
+ public static function group($Name, $Options=null) {
+ $instance = FirePHP::getInstance(true);
+ return $instance->group($Name, $Options);
+ }
+
+ /**
+ * Ends a group you have started before
+ *
+ * @return true
+ * @throws Exception
+ */
+ public static function groupEnd() {
+ return self::send(null, null, FirePHP::GROUP_END);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::LOG
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ public static function log($Object, $Label=null) {
+ return self::send($Object, $Label, FirePHP::LOG);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::INFO
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ public static function info($Object, $Label=null) {
+ return self::send($Object, $Label, FirePHP::INFO);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::WARN
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ public static function warn($Object, $Label=null) {
+ return self::send($Object, $Label, FirePHP::WARN);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::ERROR
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ public static function error($Object, $Label=null) {
+ return self::send($Object, $Label, FirePHP::ERROR);
+ }
+
+ /**
+ * Dumps key and variable to firebug server panel
+ *
+ * @see FirePHP::DUMP
+ * @param string $Key
+ * @param mixed $Variable
+ * @return true
+ * @throws Exception
+ */
+ public static function dump($Key, $Variable) {
+ return self::send($Variable, $Key, FirePHP::DUMP);
+ }
+
+ /**
+ * Log a trace in the firebug console
+ *
+ * @see FirePHP::TRACE
+ * @param string $Label
+ * @return true
+ * @throws Exception
+ */
+ public static function trace($Label) {
+ return self::send($Label, FirePHP::TRACE);
+ }
+
+ /**
+ * Log a table in the firebug console
+ *
+ * @see FirePHP::TABLE
+ * @param string $Label
+ * @param string $Table
+ * @return true
+ * @throws Exception
+ */
+ public static function table($Label, $Table) {
+ return self::send($Table, $Label, FirePHP::TABLE);
+ }
+
+}
+
diff --git a/flaxil/inc/cube/externals/FirePHPCore/fb.php4 b/flaxil/inc/cube/externals/FirePHPCore/fb.php4
new file mode 100644
index 0000000..5b69e34
--- /dev/null
+++ b/flaxil/inc/cube/externals/FirePHPCore/fb.php4
@@ -0,0 +1,251 @@
+
+ * @author Michael Day
+ * @license http://www.opensource.org/licenses/bsd-license.php
+ * @package FirePHP
+ */
+
+require_once dirname(__FILE__).'/FirePHP.class.php4';
+
+/**
+ * Sends the given data to the FirePHP Firefox Extension.
+ * The data can be displayed in the Firebug Console or in the
+ * "Server" request tab.
+ *
+ * @see http://www.firephp.org/Wiki/Reference/Fb
+ * @param mixed $Object
+ * @return true
+ * @throws Exception
+ */
+function fb()
+{
+ $instance =& FirePHP::getInstance(true);
+
+ $args = func_get_args();
+ return call_user_func_array(array(&$instance,'fb'),$args);
+}
+
+
+class FB
+{
+ /**
+ * Enable and disable logging to Firebug
+ *
+ * @see FirePHP->setEnabled()
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
+ * @return void
+ */
+ function setEnabled($Enabled) {
+ $instance =& FirePHP::getInstance(true);
+ $instance->setEnabled($Enabled);
+ }
+
+ /**
+ * Check if logging is enabled
+ *
+ * @see FirePHP->getEnabled()
+ * @return boolean TRUE if enabled
+ */
+ function getEnabled() {
+ $instance =& FirePHP::getInstance(true);
+ return $instance->getEnabled();
+ }
+
+ /**
+ * Specify a filter to be used when encoding an object
+ *
+ * Filters are used to exclude object members.
+ *
+ * @see FirePHP->setObjectFilter()
+ * @param string $Class The class name of the object
+ * @param array $Filter An array or members to exclude
+ * @return void
+ */
+ function setObjectFilter($Class, $Filter) {
+ $instance =& FirePHP::getInstance(true);
+ $instance->setObjectFilter($Class, $Filter);
+ }
+
+ /**
+ * Set some options for the library
+ *
+ * @see FirePHP->setOptions()
+ * @param array $Options The options to be set
+ * @return void
+ */
+ function setOptions($Options) {
+ $instance =& FirePHP::getInstance(true);
+ $instance->setOptions($Options);
+ }
+
+ /**
+ * Get options for the library
+ *
+ * @see FirePHP->getOptions()
+ * @return array The options
+ */
+ function getOptions() {
+ $instance =& FirePHP::getInstance(true);
+ return $instance->getOptions();
+ }
+
+ /**
+ * Log object to firebug
+ *
+ * @see http://www.firephp.org/Wiki/Reference/Fb
+ * @param mixed $Object
+ * @return true
+ */
+ function send()
+ {
+ $instance =& FirePHP::getInstance(true);
+ $args = func_get_args();
+ return call_user_func_array(array(&$instance,'fb'),$args);
+ }
+
+ /**
+ * Start a group for following messages
+ *
+ * Options:
+ * Collapsed: [true|false]
+ * Color: [#RRGGBB|ColorName]
+ *
+ * @param string $Name
+ * @param array $Options OPTIONAL Instructions on how to log the group
+ * @return true
+ */
+ function group($Name, $Options=null) {
+ $instance =& FirePHP::getInstance(true);
+ return $instance->group($Name, $Options);
+ }
+
+ /**
+ * Ends a group you have started before
+ *
+ * @return true
+ */
+ function groupEnd() {
+ return FB::send(null, null, FirePHP_GROUP_END);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::LOG
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ */
+ function log($Object, $Label=null) {
+ return FB::send($Object, $Label, FirePHP_LOG);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::INFO
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ */
+ function info($Object, $Label=null) {
+ return FB::send($Object, $Label, FirePHP_INFO);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::WARN
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ */
+ function warn($Object, $Label=null) {
+ return FB::send($Object, $Label, FirePHP_WARN);
+ }
+
+ /**
+ * Log object with label to firebug console
+ *
+ * @see FirePHP::ERROR
+ * @param mixes $Object
+ * @param string $Label
+ * @return true
+ */
+ function error($Object, $Label=null) {
+ return FB::send($Object, $Label, FirePHP_ERROR);
+ }
+
+ /**
+ * Dumps key and variable to firebug server panel
+ *
+ * @see FirePHP::DUMP
+ * @param string $Key
+ * @param mixed $Variable
+ * @return true
+ */
+ function dump($Key, $Variable) {
+ return FB::send($Variable, $Key, FirePHP_DUMP);
+ }
+
+ /**
+ * Log a trace in the firebug console
+ *
+ * @see FirePHP::TRACE
+ * @param string $Label
+ * @return true
+ */
+ function trace($Label) {
+ return FB::send($Label, FirePHP_TRACE);
+ }
+
+ /**
+ * Log a table in the firebug console
+ *
+ * @see FirePHP::TABLE
+ * @param string $Label
+ * @param string $Table
+ * @return true
+ */
+ function table($Label, $Table) {
+ return FB::send($Table, $Label, FirePHP_TABLE);
+ }
+}
diff --git a/flaxil/inc/cube/externals/JSMin/jsmin-1.1.1.php b/flaxil/inc/cube/externals/JSMin/jsmin-1.1.1.php
new file mode 100644
index 0000000..e06129c
--- /dev/null
+++ b/flaxil/inc/cube/externals/JSMin/jsmin-1.1.1.php
@@ -0,0 +1,291 @@
+
+ * @copyright 2002 Douglas Crockford (jsmin.c)
+ * @copyright 2008 Ryan Grove (PHP port)
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 1.1.1 (2008-03-02)
+ * @link http://code.google.com/p/jsmin-php/
+ */
+
+class JSMin {
+ const ORD_LF = 10;
+ const ORD_SPACE = 32;
+
+ protected $a = '';
+ protected $b = '';
+ protected $input = '';
+ protected $inputIndex = 0;
+ protected $inputLength = 0;
+ protected $lookAhead = null;
+ protected $output = '';
+
+ // -- Public Static Methods --------------------------------------------------
+
+ public static function minify($js) {
+ $jsmin = new JSMin($js);
+ return $jsmin->min();
+ }
+
+ // -- Public Instance Methods ------------------------------------------------
+
+ public function __construct($input) {
+ $this->input = str_replace("\r\n", "\n", $input);
+ $this->inputLength = strlen($this->input);
+ }
+
+ // -- Protected Instance Methods ---------------------------------------------
+
+ protected function action($d) {
+ switch($d) {
+ case 1:
+ $this->output .= $this->a;
+
+ case 2:
+ $this->a = $this->b;
+
+ if ($this->a === "'" || $this->a === '"') {
+ for (;;) {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+
+ if ($this->a === $this->b) {
+ break;
+ }
+
+ if (ord($this->a) <= self::ORD_LF) {
+ throw new JSMinException('Unterminated string literal.');
+ }
+
+ if ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ }
+ }
+ }
+
+ case 3:
+ $this->b = $this->next();
+
+ if ($this->b === '/' && (
+ $this->a === '(' || $this->a === ',' || $this->a === '=' ||
+ $this->a === ':' || $this->a === '[' || $this->a === '!' ||
+ $this->a === '&' || $this->a === '|' || $this->a === '?')) {
+
+ $this->output .= $this->a . $this->b;
+
+ for (;;) {
+ $this->a = $this->get();
+
+ if ($this->a === '/') {
+ break;
+ } elseif ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ } elseif (ord($this->a) <= self::ORD_LF) {
+ throw new JSMinException('Unterminated regular expression '.
+ 'literal.');
+ }
+
+ $this->output .= $this->a;
+ }
+
+ $this->b = $this->next();
+ }
+ }
+ }
+
+ protected function get() {
+ $c = $this->lookAhead;
+ $this->lookAhead = null;
+
+ if ($c === null) {
+ if ($this->inputIndex < $this->inputLength) {
+ $c = $this->input[$this->inputIndex];
+ $this->inputIndex += 1;
+ } else {
+ $c = null;
+ }
+ }
+
+ if ($c === "\r") {
+ return "\n";
+ }
+
+ if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
+ return $c;
+ }
+
+ return ' ';
+ }
+
+ protected function isAlphaNum($c) {
+ return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
+ }
+
+ protected function min() {
+ $this->a = "\n";
+ $this->action(3);
+
+ while ($this->a !== null) {
+ switch ($this->a) {
+ case ' ':
+ if ($this->isAlphaNum($this->b)) {
+ $this->action(1);
+ } else {
+ $this->action(2);
+ }
+ break;
+
+ case "\n":
+ switch ($this->b) {
+ case '{':
+ case '[':
+ case '(':
+ case '+':
+ case '-':
+ $this->action(1);
+ break;
+
+ case ' ':
+ $this->action(3);
+ break;
+
+ default:
+ if ($this->isAlphaNum($this->b)) {
+ $this->action(1);
+ }
+ else {
+ $this->action(2);
+ }
+ }
+ break;
+
+ default:
+ switch ($this->b) {
+ case ' ':
+ if ($this->isAlphaNum($this->a)) {
+ $this->action(1);
+ break;
+ }
+
+ $this->action(3);
+ break;
+
+ case "\n":
+ switch ($this->a) {
+ case '}':
+ case ']':
+ case ')':
+ case '+':
+ case '-':
+ case '"':
+ case "'":
+ $this->action(1);
+ break;
+
+ default:
+ if ($this->isAlphaNum($this->a)) {
+ $this->action(1);
+ }
+ else {
+ $this->action(3);
+ }
+ }
+ break;
+
+ default:
+ $this->action(1);
+ break;
+ }
+ }
+ }
+
+ return $this->output;
+ }
+
+ protected function next() {
+ $c = $this->get();
+
+ if ($c === '/') {
+ switch($this->peek()) {
+ case '/':
+ for (;;) {
+ $c = $this->get();
+
+ if (ord($c) <= self::ORD_LF) {
+ return $c;
+ }
+ }
+
+ case '*':
+ $this->get();
+
+ for (;;) {
+ switch($this->get()) {
+ case '*':
+ if ($this->peek() === '/') {
+ $this->get();
+ return ' ';
+ }
+ break;
+
+ case null:
+ throw new JSMinException('Unterminated comment.');
+ }
+ }
+
+ default:
+ return $c;
+ }
+ }
+
+ return $c;
+ }
+
+ protected function peek() {
+ $this->lookAhead = $this->get();
+ return $this->lookAhead;
+ }
+}
+
+// -- Exceptions ---------------------------------------------------------------
+class JSMinException extends Exception {}
+?>
\ No newline at end of file
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel.php
new file mode 100644
index 0000000..a7ff765
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel.php
@@ -0,0 +1,794 @@
+_workSheetCollection = array();
+ $this->_workSheetCollection[] = new PHPExcel_Worksheet($this);
+ $this->_activeSheetIndex = 0;
+
+ // Create document properties
+ $this->_properties = new PHPExcel_DocumentProperties();
+
+ // Create document security
+ $this->_security = new PHPExcel_DocumentSecurity();
+
+ // Set named ranges
+ $this->_namedRanges = array();
+
+ // Create the cellXf supervisor
+ $this->_cellXfSupervisor = new PHPExcel_Style(true);
+ $this->_cellXfSupervisor->bindParent($this);
+
+ // Create the default style
+ $this->addCellXf(new PHPExcel_Style);
+ $this->addCellStyleXf(new PHPExcel_Style);
+ }
+
+ /**
+ * Get properties
+ *
+ * @return PHPExcel_DocumentProperties
+ */
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+
+ /**
+ * Set properties
+ *
+ * @param PHPExcel_DocumentProperties $pValue
+ */
+ public function setProperties(PHPExcel_DocumentProperties $pValue)
+ {
+ $this->_properties = $pValue;
+ }
+
+ /**
+ * Get security
+ *
+ * @return PHPExcel_DocumentSecurity
+ */
+ public function getSecurity()
+ {
+ return $this->_security;
+ }
+
+ /**
+ * Set security
+ *
+ * @param PHPExcel_DocumentSecurity $pValue
+ */
+ public function setSecurity(PHPExcel_DocumentSecurity $pValue)
+ {
+ $this->_security = $pValue;
+ }
+
+ /**
+ * Get active sheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getActiveSheet()
+ {
+ return $this->_workSheetCollection[$this->_activeSheetIndex];
+ }
+
+ /**
+ * Create sheet and add it to this workbook
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function createSheet($iSheetIndex = null)
+ {
+ $newSheet = new PHPExcel_Worksheet($this);
+ $this->addSheet($newSheet, $iSheetIndex);
+ return $newSheet;
+ }
+
+ /**
+ * Add sheet
+ *
+ * @param PHPExcel_Worksheet $pSheet
+ * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function addSheet(PHPExcel_Worksheet $pSheet = null, $iSheetIndex = null)
+ {
+ if(is_null($iSheetIndex))
+ {
+ $this->_workSheetCollection[] = $pSheet;
+ }
+ else
+ {
+ // Insert the sheet at the requested index
+ array_splice(
+ $this->_workSheetCollection,
+ $iSheetIndex,
+ 0,
+ array($pSheet)
+ );
+
+ // Adjust active sheet index if necessary
+ if ($this->_activeSheetIndex >= $iSheetIndex) {
+ ++$this->_activeSheetIndex;
+ }
+
+ }
+ return $pSheet;
+ }
+
+ /**
+ * Remove sheet by index
+ *
+ * @param int $pIndex Active sheet index
+ * @throws Exception
+ */
+ public function removeSheetByIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_workSheetCollection) - 1) {
+ throw new Exception("Sheet index is out of bounds.");
+ } else {
+ array_splice($this->_workSheetCollection, $pIndex, 1);
+ }
+ }
+
+ /**
+ * Get sheet by index
+ *
+ * @param int $pIndex Sheet index
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function getSheet($pIndex = 0)
+ {
+ if ($pIndex > count($this->_workSheetCollection) - 1) {
+ throw new Exception("Sheet index is out of bounds.");
+ } else {
+ return $this->_workSheetCollection[$pIndex];
+ }
+ }
+
+ /**
+ * Get all sheets
+ *
+ * @return PHPExcel_Worksheet[]
+ */
+ public function getAllSheets()
+ {
+ return $this->_workSheetCollection;
+ }
+
+ /**
+ * Get sheet by name
+ *
+ * @param string $pName Sheet name
+ * @return PHPExcel_Worksheet
+ * @throws Exception
+ */
+ public function getSheetByName($pName = '')
+ {
+ $worksheetCount = count($this->_workSheetCollection);
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ if ($this->_workSheetCollection[$i]->getTitle() == $pName) {
+ return $this->_workSheetCollection[$i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get index for sheet
+ *
+ * @param PHPExcel_Worksheet $pSheet
+ * @return Sheet index
+ * @throws Exception
+ */
+ public function getIndex(PHPExcel_Worksheet $pSheet)
+ {
+ foreach ($this->_workSheetCollection as $key => $value) {
+ if ($value->getHashCode() == $pSheet->getHashCode()) {
+ return $key;
+ }
+ }
+ }
+
+ /**
+ * Set index for sheet by sheet name.
+ *
+ * @param string $sheetName Sheet name to modify index for
+ * @param int $newIndex New index for the sheet
+ * @return New sheet index
+ * @throws Exception
+ */
+ public function setIndexByName($sheetName, $newIndex)
+ {
+ $oldIndex = $this->getIndex($this->getSheetByName($sheetName));
+ $pSheet = array_splice(
+ $this->_workSheetCollection,
+ $oldIndex,
+ 1
+ );
+ array_splice(
+ $this->_workSheetCollection,
+ $newIndex,
+ 0,
+ $pSheet
+ );
+ return $newIndex;
+ }
+
+ /**
+ * Get sheet count
+ *
+ * @return int
+ */
+ public function getSheetCount()
+ {
+ return count($this->_workSheetCollection);
+ }
+
+ /**
+ * Get active sheet index
+ *
+ * @return int Active sheet index
+ */
+ public function getActiveSheetIndex()
+ {
+ return $this->_activeSheetIndex;
+ }
+
+ /**
+ * Set active sheet index
+ *
+ * @param int $pIndex Active sheet index
+ * @throws Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setActiveSheetIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_workSheetCollection) - 1) {
+ throw new Exception("Active sheet index is out of bounds.");
+ } else {
+ $this->_activeSheetIndex = $pIndex;
+ }
+ return $this->getActiveSheet();
+ }
+
+ /**
+ * Get sheet names
+ *
+ * @return string[]
+ */
+ public function getSheetNames()
+ {
+ $returnValue = array();
+ $worksheetCount = $this->getSheetCount();
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ array_push($returnValue, $this->getSheet($i)->getTitle());
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Add external sheet
+ *
+ * @param PHPExcel_Worksheet $pSheet External sheet to add
+ * @param int|null $iSheetIndex Index where sheet should go (0,1,..., or null for last)
+ * @throws Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function addExternalSheet(PHPExcel_Worksheet $pSheet, $iSheetIndex = null) {
+ if (!is_null($this->getSheetByName($pSheet->getTitle()))) {
+ throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first.");
+ }
+
+ // count how many cellXfs there are in this workbook currently, we will need this below
+ $countCellXfs = count($this->_cellXfCollection);
+
+ // copy all the shared cellXfs from the external workbook and append them to the current
+ foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
+ $this->addCellXf(clone $cellXf);
+ }
+
+ // move sheet to this workbook
+ $pSheet->rebindParent($this);
+
+ // update the cellXfs
+ foreach ($pSheet->getCellCollection(false) as $cell) {
+ $cell->setXfIndex( $cell->getXfIndex() + $countCellXfs );
+ }
+
+ return $this->addSheet($pSheet, $iSheetIndex);
+ }
+
+ /**
+ * Get named ranges
+ *
+ * @return PHPExcel_NamedRange[]
+ */
+ public function getNamedRanges() {
+ return $this->_namedRanges;
+ }
+
+ /**
+ * Add named range
+ *
+ * @param PHPExcel_NamedRange $namedRange
+ * @return PHPExcel
+ */
+ public function addNamedRange(PHPExcel_NamedRange $namedRange) {
+ $this->_namedRanges[$namedRange->getWorksheet()->getTitle().'!'.$namedRange->getName()] = $namedRange;
+ return true;
+ }
+
+ /**
+ * Get named range
+ *
+ * @param string $namedRange
+ */
+ public function getNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) {
+ if ($namedRange != '' && !is_null($namedRange)) {
+ if (!is_null($pSheet)) {
+ $key = $pSheet->getTitle().'!'.$namedRange;
+ if (isset($this->_namedRanges[$key])) {
+ return $this->_namedRanges[$key];
+ }
+ }
+ $returnCount = 0;
+ foreach($this->_namedRanges as $_namedRange) {
+ if ($_namedRange->getName() == $namedRange) {
+ if ((!is_null($pSheet)) && ($_namedRange->getWorksheet()->getTitle() == $pSheet->getTitle())) {
+ return $_namedRange;
+ } else {
+ $returnCount++;
+ $returnValue = $_namedRange;
+ }
+ }
+ }
+ if ($returnCount == 1) {
+ return $returnValue;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Remove named range
+ *
+ * @param string $namedRange
+ * @return PHPExcel
+ */
+ public function removeNamedRange($namedRange, PHPExcel_Worksheet $pSheet = null) {
+ if ($namedRange != '' && !is_null($namedRange)) {
+ if (!is_null($pSheet)) {
+ $key = $pSheet->getTitle().'!'.$namedRange;
+ if (isset($this->_namedRanges[$key])) {
+ unset($this->_namedRanges[$key]);
+ }
+ }
+ foreach($this->_namedRanges as $_namedRange) {
+ if ($_namedRange->getName() == $namedRange) {
+ if ((!is_null($pSheet)) && ($_namedRange->getWorksheet()->getTitle() == $pSheet->getTitle())) {
+ $key = $pSheet->getTitle().'!'.$namedRange;
+ if (isset($this->_namedRanges[$key])) {
+ unset($this->_namedRanges[$key]);
+ }
+ }
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get worksheet iterator
+ *
+ * @return PHPExcel_WorksheetIterator
+ */
+ public function getWorksheetIterator() {
+ return new PHPExcel_WorksheetIterator($this);
+ }
+
+ /**
+ * Copy workbook (!= clone!)
+ *
+ * @return PHPExcel
+ */
+ public function copy() {
+ $copied = clone $this;
+
+ $worksheetCount = count($this->_workSheetCollection);
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ $this->_workSheetCollection[$i] = $this->_workSheetCollection[$i]->copy();
+ $this->_workSheetCollection[$i]->rebindParent($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 (is_object($val) || (is_array($val))) {
+ $this->{$key} = unserialize(serialize($val));
+ }
+ }
+ }
+
+ /**
+ * Get the workbook collection of cellXfs
+ *
+ * @return PHPExcel_Style[]
+ */
+ public function getCellXfCollection()
+ {
+ return $this->_cellXfCollection;
+ }
+
+ /**
+ * Get cellXf by index
+ *
+ * @param int $index
+ * @return PHPExcel_Style
+ */
+ public function getCellXfByIndex($pIndex = 0)
+ {
+ return $this->_cellXfCollection[$pIndex];
+ }
+
+ /**
+ * Get cellXf by hash code
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style|false
+ */
+ public function getCellXfByHashCode($pValue = '')
+ {
+ foreach ($this->_cellXfCollection as $cellXf) {
+ if ($cellXf->getHashCode() == $pValue) {
+ return $cellXf;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get default style
+ *
+ * @return PHPExcel_Style
+ * @throws Exception
+ */
+ public function getDefaultStyle()
+ {
+ if (isset($this->_cellXfCollection[0])) {
+ return $this->_cellXfCollection[0];
+ }
+ throw new Exception('No default style found for this workbook');
+ }
+
+ /**
+ * Add a cellXf to the workbook
+ *
+ * @param PHPExcel_Style
+ */
+ public function addCellXf(PHPExcel_Style $style)
+ {
+ $this->_cellXfCollection[] = $style;
+ $style->setIndex(count($this->_cellXfCollection) - 1);
+ }
+
+ /**
+ * Remove cellXf by index. It is ensured that all cells get their xf index updated.
+ *
+ * @param int $pIndex Index to cellXf
+ * @throws Exception
+ */
+ public function removeCellXfByIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_cellXfCollection) - 1) {
+ throw new Exception("CellXf index is out of bounds.");
+ } else {
+ // first remove the cellXf
+ array_splice($this->_cellXfCollection, $pIndex, 1);
+
+ // then update cellXf indexes for cells
+ foreach ($this->_workSheetCollection as $worksheet) {
+ foreach ($worksheet->getCellCollection(false) as $cell) {
+ $xfIndex = $cell->getXfIndex();
+ if ($xfIndex > $pIndex ) {
+ // decrease xf index by 1
+ $cell->setXfIndex($xfIndex - 1);
+ } else if ($xfIndex == $pIndex) {
+ // set to default xf index 0
+ $cell->setXfIndex(0);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the cellXf supervisor
+ *
+ * @return PHPExcel_Style
+ */
+ public function getCellXfSupervisor()
+ {
+ return $this->_cellXfSupervisor;
+ }
+
+ /**
+ * Get the workbook collection of cellStyleXfs
+ *
+ * @return PHPExcel_Style[]
+ */
+ public function getCellStyleXfCollection()
+ {
+ return $this->_cellStyleXfCollection;
+ }
+
+ /**
+ * Get cellStyleXf by index
+ *
+ * @param int $pIndex
+ * @return PHPExcel_Style
+ */
+ public function getCellStyleXfByIndex($pIndex = 0)
+ {
+ return $this->_cellStyleXfCollection[$pIndex];
+ }
+
+ /**
+ * Get cellStyleXf by hash code
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style|false
+ */
+ public function getCellStyleXfByHashCode($pValue = '')
+ {
+ foreach ($this->_cellXfStyleCollection as $cellStyleXf) {
+ if ($cellStyleXf->getHashCode() == $pValue) {
+ return $cellStyleXf;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a cellStyleXf to the workbook
+ *
+ * @param PHPExcel_Style $pStyle
+ */
+ public function addCellStyleXf(PHPExcel_Style $pStyle)
+ {
+ $this->_cellStyleXfCollection[] = $pStyle;
+ $pStyle->setIndex(count($this->_cellStyleXfCollection) - 1);
+ }
+
+ /**
+ * Remove cellStyleXf by index
+ *
+ * @param int $pIndex
+ * @throws Exception
+ */
+ public function removeCellStyleXfByIndex($pIndex = 0)
+ {
+ if ($pIndex > count($this->_cellStyleXfCollection) - 1) {
+ throw new Exception("CellStyleXf index is out of bounds.");
+ } else {
+ array_splice($this->_cellStyleXfCollection, $pIndex, 1);
+ }
+ }
+
+ /**
+ * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
+ * and columns in the workbook
+ */
+ public function garbageCollect()
+ {
+ // how many references are there to each cellXf ?
+ $countReferencesCellXf = array();
+ foreach ($this->_cellXfCollection as $index => $cellXf) {
+ $countReferencesCellXf[$index] = 0;
+ }
+
+ foreach ($this->getWorksheetIterator() as $sheet) {
+
+ // from cells
+ foreach ($sheet->getCellCollection(false) as $cell) {
+ ++$countReferencesCellXf[$cell->getXfIndex()];
+ }
+
+ // from row dimensions
+ foreach ($sheet->getRowDimensions() as $rowDimension) {
+ if ($rowDimension->getXfIndex() !== null) {
+ ++$countReferencesCellXf[$rowDimension->getXfIndex()];
+ }
+ }
+
+ // from column dimensions
+ foreach ($sheet->getColumnDimensions() as $columnDimension) {
+ ++$countReferencesCellXf[$columnDimension->getXfIndex()];
+ }
+ }
+
+ // remove cellXfs without references and create mapping so we can update xfIndex
+ // for all cells and columns
+ $countNeededCellXfs = 0;
+ foreach ($this->_cellXfCollection as $index => $cellXf) {
+ if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
+ ++$countNeededCellXfs;
+ } else {
+ unset($this->_cellXfCollection[$index]);
+ }
+ $map[$index] = $countNeededCellXfs - 1;
+ }
+ $this->_cellXfCollection = array_values($this->_cellXfCollection);
+
+ // update the index for all cellXfs
+ foreach ($this->_cellXfCollection as $i => $cellXf) {
+ echo $cellXf->setIndex($i);
+ }
+
+ // make sure there is always at least one cellXf (there should be)
+ if (count($this->_cellXfCollection) == 0) {
+ $this->_cellXfCollection[] = new PHPExcel_Style();
+ }
+
+ // update the xfIndex for all cells, row dimensions, column dimensions
+ foreach ($this->getWorksheetIterator() as $sheet) {
+
+ // for all cells
+ foreach ($sheet->getCellCollection(false) as $cell) {
+ $cell->setXfIndex( $map[$cell->getXfIndex()] );
+ }
+
+ // for all row dimensions
+ foreach ($sheet->getRowDimensions() as $rowDimension) {
+ if ($rowDimension->getXfIndex() !== null) {
+ $rowDimension->setXfIndex( $map[$rowDimension->getXfIndex()] );
+ }
+ }
+
+ // for all column dimensions
+ foreach ($sheet->getColumnDimensions() as $columnDimension) {
+ $columnDimension->setXfIndex( $map[$columnDimension->getXfIndex()] );
+ }
+ }
+
+ // also do garbage collection for all the sheets
+ foreach ($this->getWorksheetIterator() as $sheet) {
+ $sheet->garbageCollect();
+ }
+ }
+
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation.php
new file mode 100644
index 0000000..793734f
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation.php
@@ -0,0 +1,3292 @@
+', '<', '=', '>=', '<=', '<>', '|', ':');
+
+
+ /**
+ * List of binary operators (those that expect two operands)
+ *
+ * @access private
+ * @var array
+ */
+ private $_binaryOperators = array('+', '-', '*', '/', '^', '&', '>', '<', '=', '>=', '<=', '<>', '|', ':');
+
+ public $suppressFormulaErrors = false;
+ public $formulaError = null;
+ public $writeDebugLog = false;
+ private $debugLogStack = array();
+ public $debugLog = array();
+
+
+ // Constant conversion from text name/value to actual (datatyped) value
+ private $_ExcelConstants = array('TRUE' => True,
+ 'FALSE' => False,
+ 'NULL' => Null
+ );
+
+ // PHPExcel functions
+ private $_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_Functions::ACCRINT',
+ 'argumentCount' => '4-7'
+ ),
+ 'ACCRINTM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::CELL_ADDRESS',
+ 'argumentCount' => '2-5'
+ ),
+ 'AMORDEGRC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AMORDEGRC',
+ 'argumentCount' => '6,7'
+ ),
+ 'AMORLINC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AMORLINC',
+ 'argumentCount' => '6,7'
+ ),
+ 'AND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::REVERSE_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_Functions::AVEDEV',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AVERAGE',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGEA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::AVERAGEA',
+ 'argumentCount' => '1+'
+ ),
+ 'AVERAGEIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ '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_Functions::BESSELI',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELJ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELJ',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELK',
+ 'argumentCount' => '2'
+ ),
+ 'BESSELY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BESSELY',
+ 'argumentCount' => '2'
+ ),
+ 'BETADIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BETADIST',
+ 'argumentCount' => '3-5'
+ ),
+ 'BETAINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BETAINV',
+ 'argumentCount' => '3-5'
+ ),
+ 'BIN2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'BIN2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'BIN2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'BINOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::BINOMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'CEILING' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::CHARACTER',
+ 'argumentCount' => '1'
+ ),
+ 'CHIDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CHIDIST',
+ 'argumentCount' => '2'
+ ),
+ 'CHIINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::CHOOSE',
+ 'argumentCount' => '2+'
+ ),
+ 'CLEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRIMNONPRINTABLE',
+ 'argumentCount' => '1'
+ ),
+ 'CODE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ASCIICODE',
+ 'argumentCount' => '1'
+ ),
+ 'COLUMN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COLUMN',
+ 'argumentCount' => '-1',
+ 'passByReference' => array(true)
+ ),
+ 'COLUMNS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COLUMNS',
+ 'argumentCount' => '1'
+ ),
+ 'COMBIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COMBIN',
+ 'argumentCount' => '2'
+ ),
+ 'COMPLEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COMPLEX',
+ 'argumentCount' => '2,3'
+ ),
+ 'CONCATENATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CONCATENATE',
+ 'argumentCount' => '1+'
+ ),
+ 'CONFIDENCE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CONFIDENCE',
+ 'argumentCount' => '3'
+ ),
+ 'CONVERT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CONVERTUOM',
+ 'argumentCount' => '3'
+ ),
+ 'CORREL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::COUNT',
+ 'argumentCount' => '1+'
+ ),
+ 'COUNTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUNTA',
+ 'argumentCount' => '1+'
+ ),
+ 'COUNTBLANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUNTBLANK',
+ 'argumentCount' => '1'
+ ),
+ 'COUNTIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPDAYS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPDAYSNC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPNCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPNUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COUPNUM',
+ 'argumentCount' => '3,4'
+ ),
+ 'COUPPCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'COVAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::COVAR',
+ 'argumentCount' => '2'
+ ),
+ 'CRITBINOM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::CUMIPMT',
+ 'argumentCount' => '6'
+ ),
+ 'CUMPRINC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CUMPRINC',
+ 'argumentCount' => '6'
+ ),
+ 'DATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATE',
+ 'argumentCount' => '3'
+ ),
+ 'DATEDIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATEDIF',
+ 'argumentCount' => '2,3'
+ ),
+ 'DATEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATEVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'DAVERAGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DAYOFMONTH',
+ 'argumentCount' => '1'
+ ),
+ 'DAYS360' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DAYS360',
+ 'argumentCount' => '2,3'
+ ),
+ 'DB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DB',
+ 'argumentCount' => '4,5'
+ ),
+ 'DCOUNT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DCOUNTA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DDB',
+ 'argumentCount' => '4,5'
+ ),
+ 'DEC2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DECTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEC2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DECTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEC2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::DELTA',
+ 'argumentCount' => '1,2'
+ ),
+ 'DEVSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DEVSQ',
+ 'argumentCount' => '1+'
+ ),
+ 'DGET' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'DMAX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DMIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DOLLAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DOLLAR',
+ 'argumentCount' => '1,2'
+ ),
+ 'DOLLARDE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DOLLARDE',
+ 'argumentCount' => '2'
+ ),
+ 'DOLLARFR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DOLLARFR',
+ 'argumentCount' => '2'
+ ),
+ 'DPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DSTDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DSTDEVP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ '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_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'DVARP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '?'
+ ),
+ 'EDATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EDATE',
+ 'argumentCount' => '2'
+ ),
+ 'EFFECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EFFECT',
+ 'argumentCount' => '2'
+ ),
+ 'EOMONTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::EOMONTH',
+ 'argumentCount' => '2'
+ ),
+ 'ERF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ERF',
+ 'argumentCount' => '1,2'
+ ),
+ 'ERFC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::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_Functions::EXPONDIST',
+ 'argumentCount' => '3'
+ ),
+ 'FACT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FACT',
+ 'argumentCount' => '1'
+ ),
+ 'FACTDOUBLE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FACTDOUBLE',
+ 'argumentCount' => '1'
+ ),
+ 'FALSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::SEARCHSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'FINDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::FISHER',
+ 'argumentCount' => '1'
+ ),
+ 'FISHERINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FISHERINV',
+ 'argumentCount' => '1'
+ ),
+ 'FIXED' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FIXEDFORMAT',
+ 'argumentCount' => '1-3'
+ ),
+ 'FLOOR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FLOOR',
+ 'argumentCount' => '2'
+ ),
+ 'FORECAST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::FV',
+ 'argumentCount' => '3-5'
+ ),
+ 'FVSCHEDULE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::FVSCHEDULE',
+ 'argumentCount' => '2'
+ ),
+ 'GAMMADIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GAMMADIST',
+ 'argumentCount' => '4'
+ ),
+ 'GAMMAINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GAMMAINV',
+ 'argumentCount' => '3'
+ ),
+ 'GAMMALN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GAMMALN',
+ 'argumentCount' => '1'
+ ),
+ 'GCD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GCD',
+ 'argumentCount' => '1+'
+ ),
+ 'GEOMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::GEOMEAN',
+ 'argumentCount' => '1+'
+ ),
+ 'GESTEP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::GROWTH',
+ 'argumentCount' => '1-4'
+ ),
+ 'HARMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HARMEAN',
+ 'argumentCount' => '1+'
+ ),
+ 'HEX2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HEXTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'HEX2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HEXTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'HEX2OCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HEXTOOCT',
+ 'argumentCount' => '1,2'
+ ),
+ 'HLOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '3,4'
+ ),
+ 'HOUR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HOUROFDAY',
+ 'argumentCount' => '1'
+ ),
+ 'HYPERLINK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '1,2'
+ ),
+ 'HYPGEOMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::HYPGEOMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'IF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STATEMENT_IF',
+ 'argumentCount' => '1-3'
+ ),
+ 'IFERROR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STATEMENT_IFERROR',
+ 'argumentCount' => '2'
+ ),
+ 'IMABS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMABS',
+ 'argumentCount' => '1'
+ ),
+ 'IMAGINARY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMAGINARY',
+ 'argumentCount' => '1'
+ ),
+ 'IMARGUMENT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMARGUMENT',
+ 'argumentCount' => '1'
+ ),
+ 'IMCONJUGATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMCONJUGATE',
+ 'argumentCount' => '1'
+ ),
+ 'IMCOS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMCOS',
+ 'argumentCount' => '1'
+ ),
+ 'IMDIV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMDIV',
+ 'argumentCount' => '2'
+ ),
+ 'IMEXP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMEXP',
+ 'argumentCount' => '1'
+ ),
+ 'IMLN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMLN',
+ 'argumentCount' => '1'
+ ),
+ 'IMLOG10' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMLOG10',
+ 'argumentCount' => '1'
+ ),
+ 'IMLOG2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMLOG2',
+ 'argumentCount' => '1'
+ ),
+ 'IMPOWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMPOWER',
+ 'argumentCount' => '2'
+ ),
+ 'IMPRODUCT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMPRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'IMREAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMREAL',
+ 'argumentCount' => '1'
+ ),
+ 'IMSIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSIN',
+ 'argumentCount' => '1'
+ ),
+ 'IMSQRT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSQRT',
+ 'argumentCount' => '1'
+ ),
+ 'IMSUB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSUB',
+ 'argumentCount' => '2'
+ ),
+ 'IMSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IMSUM',
+ 'argumentCount' => '1+'
+ ),
+ 'INDEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INDEX',
+ 'argumentCount' => '1-4'
+ ),
+ 'INDIRECT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::INTVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'INTERCEPT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INTERCEPT',
+ 'argumentCount' => '2'
+ ),
+ 'INTRATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::INTRATE',
+ 'argumentCount' => '4,5'
+ ),
+ 'IPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::IPMT',
+ 'argumentCount' => '4-6'
+ ),
+ 'IRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::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_Functions::KURT',
+ 'argumentCount' => '1+'
+ ),
+ 'LARGE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LARGE',
+ 'argumentCount' => '2'
+ ),
+ 'LCM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LCM',
+ 'argumentCount' => '1+'
+ ),
+ 'LEFT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LEFT',
+ 'argumentCount' => '1,2'
+ ),
+ 'LEFTB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LEFT',
+ 'argumentCount' => '1,2'
+ ),
+ 'LEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STRINGLENGTH',
+ 'argumentCount' => '1'
+ ),
+ 'LENB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STRINGLENGTH',
+ 'argumentCount' => '1'
+ ),
+ 'LINEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::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_Functions::LOGEST',
+ 'argumentCount' => '1-4'
+ ),
+ 'LOGINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGINV',
+ 'argumentCount' => '3'
+ ),
+ 'LOGNORMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGNORMDIST',
+ 'argumentCount' => '3'
+ ),
+ 'LOOKUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOOKUP',
+ 'argumentCount' => '2,3'
+ ),
+ 'LOWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOWERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'MATCH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MATCH',
+ 'argumentCount' => '2,3'
+ ),
+ 'MAX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MAX',
+ 'argumentCount' => '1+'
+ ),
+ 'MAXA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MAXA',
+ 'argumentCount' => '1+'
+ ),
+ 'MAXIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'MDETERM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::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_Functions::MID',
+ 'argumentCount' => '3'
+ ),
+ 'MIDB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MID',
+ 'argumentCount' => '3'
+ ),
+ 'MIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MIN',
+ 'argumentCount' => '1+'
+ ),
+ 'MINA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MINA',
+ 'argumentCount' => '1+'
+ ),
+ 'MINIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '2+'
+ ),
+ 'MINUTE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MINUTEOFHOUR',
+ 'argumentCount' => '1'
+ ),
+ 'MINVERSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MINVERSE',
+ 'argumentCount' => '1'
+ ),
+ 'MIRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MIRR',
+ 'argumentCount' => '3'
+ ),
+ 'MMULT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MMULT',
+ 'argumentCount' => '2'
+ ),
+ 'MOD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MOD',
+ 'argumentCount' => '2'
+ ),
+ 'MODE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MODE',
+ 'argumentCount' => '1+'
+ ),
+ 'MONTH' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MONTHOFYEAR',
+ 'argumentCount' => '1'
+ ),
+ 'MROUND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MROUND',
+ 'argumentCount' => '2'
+ ),
+ 'MULTINOMIAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::MULTINOMIAL',
+ 'argumentCount' => '1+'
+ ),
+ 'N' => array('category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ '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_Functions::NEGBINOMDIST',
+ 'argumentCount' => '3'
+ ),
+ 'NETWORKDAYS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NETWORKDAYS',
+ 'argumentCount' => '2+'
+ ),
+ 'NOMINAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NOMINAL',
+ 'argumentCount' => '2'
+ ),
+ 'NORMDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMDIST',
+ 'argumentCount' => '4'
+ ),
+ 'NORMINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMINV',
+ 'argumentCount' => '3'
+ ),
+ 'NORMSDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMSDIST',
+ 'argumentCount' => '1'
+ ),
+ 'NORMSINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NORMSINV',
+ 'argumentCount' => '1'
+ ),
+ 'NOT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_NOT',
+ 'argumentCount' => '1'
+ ),
+ 'NOW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATETIMENOW',
+ 'argumentCount' => '0'
+ ),
+ 'NPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NPER',
+ 'argumentCount' => '3-5'
+ ),
+ 'NPV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::NPV',
+ 'argumentCount' => '2+'
+ ),
+ 'OCT2BIN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OCTTOBIN',
+ 'argumentCount' => '1,2'
+ ),
+ 'OCT2DEC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OCTTODEC',
+ 'argumentCount' => '1'
+ ),
+ 'OCT2HEX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::OCTTOHEX',
+ 'argumentCount' => '1,2'
+ ),
+ 'ODD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::OFFSET',
+ 'argumentCount' => '3,5',
+ 'passCellReference'=> true,
+ 'passByReference' => array(true)
+ ),
+ 'OR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_OR',
+ 'argumentCount' => '1+'
+ ),
+ 'PEARSON' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::CORREL',
+ 'argumentCount' => '2'
+ ),
+ 'PERCENTILE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PERCENTILE',
+ 'argumentCount' => '2'
+ ),
+ 'PERCENTRANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PERCENTRANK',
+ 'argumentCount' => '2,3'
+ ),
+ 'PERMUT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::PMT',
+ 'argumentCount' => '3-5'
+ ),
+ 'POISSON' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::POISSON',
+ 'argumentCount' => '3'
+ ),
+ 'POWER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::POWER',
+ 'argumentCount' => '2'
+ ),
+ 'PPMT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PPMT',
+ 'argumentCount' => '4-6'
+ ),
+ 'PRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
+ 'argumentCount' => '6,7'
+ ),
+ 'PRICEDISC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PRICEDISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'PRICEMAT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::PRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'PROPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PROPERCASE',
+ 'argumentCount' => '1'
+ ),
+ 'PV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::PV',
+ 'argumentCount' => '3-5'
+ ),
+ 'QUARTILE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::QUARTILE',
+ 'argumentCount' => '2'
+ ),
+ 'QUOTIENT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::RAND',
+ 'argumentCount' => '0'
+ ),
+ 'RANDBETWEEN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RAND',
+ 'argumentCount' => '2'
+ ),
+ 'RANK' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RANK',
+ 'argumentCount' => '2,3'
+ ),
+ 'RATE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RATE',
+ 'argumentCount' => '3-6'
+ ),
+ 'RECEIVED' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RECEIVED',
+ 'argumentCount' => '4-5'
+ ),
+ 'REPLACE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::REPLACE',
+ 'argumentCount' => '4'
+ ),
+ 'REPLACEB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::RIGHT',
+ 'argumentCount' => '1,2'
+ ),
+ 'RIGHTB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::RIGHT',
+ 'argumentCount' => '1,2'
+ ),
+ 'ROMAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::ROUNDDOWN',
+ 'argumentCount' => '2'
+ ),
+ 'ROUNDUP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROUNDUP',
+ 'argumentCount' => '2'
+ ),
+ 'ROW' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROW',
+ 'argumentCount' => '-1',
+ 'passByReference' => array(true)
+ ),
+ 'ROWS' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ROWS',
+ 'argumentCount' => '1'
+ ),
+ 'RSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::SEARCHINSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'SEARCHB' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SEARCHINSENSITIVE',
+ 'argumentCount' => '2,3'
+ ),
+ 'SECOND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SECONDOFMINUTE',
+ 'argumentCount' => '1'
+ ),
+ 'SERIESSUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SERIESSUM',
+ 'argumentCount' => '4'
+ ),
+ 'SIGN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::SKEW',
+ 'argumentCount' => '1+'
+ ),
+ 'SLN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SLN',
+ 'argumentCount' => '3'
+ ),
+ 'SLOPE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SLOPE',
+ 'argumentCount' => '2'
+ ),
+ 'SMALL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::SQRTPI',
+ 'argumentCount' => '1'
+ ),
+ 'STANDARDIZE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STANDARDIZE',
+ 'argumentCount' => '3'
+ ),
+ 'STDEV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEV',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEVA',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEVP',
+ 'argumentCount' => '1+'
+ ),
+ 'STDEVPA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STDEVPA',
+ 'argumentCount' => '1+'
+ ),
+ 'STEYX' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::STEYX',
+ 'argumentCount' => '2'
+ ),
+ 'SUBSTITUTE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUBSTITUTE',
+ 'argumentCount' => '3,4'
+ ),
+ 'SUBTOTAL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUBTOTAL',
+ 'argumentCount' => '2+'
+ ),
+ 'SUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUM',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMIF' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::SUMPRODUCT',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMSQ' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMSQ',
+ 'argumentCount' => '1+'
+ ),
+ 'SUMX2MY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMX2MY2',
+ 'argumentCount' => '2'
+ ),
+ 'SUMX2PY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMX2PY2',
+ 'argumentCount' => '2'
+ ),
+ 'SUMXMY2' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SUMXMY2',
+ 'argumentCount' => '2'
+ ),
+ 'SYD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::SYD',
+ 'argumentCount' => '4'
+ ),
+ 'T' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::TBILLEQ',
+ 'argumentCount' => '3'
+ ),
+ 'TBILLPRICE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TBILLPRICE',
+ 'argumentCount' => '3'
+ ),
+ 'TBILLYIELD' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TBILLYIELD',
+ 'argumentCount' => '3'
+ ),
+ 'TDIST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TDIST',
+ 'argumentCount' => '3'
+ ),
+ 'TEXT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TEXTFORMAT',
+ 'argumentCount' => '2'
+ ),
+ 'TIME' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TIME',
+ 'argumentCount' => '3'
+ ),
+ 'TIMEVALUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TIMEVALUE',
+ 'argumentCount' => '1'
+ ),
+ 'TINV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TINV',
+ 'argumentCount' => '2'
+ ),
+ 'TODAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DATENOW',
+ 'argumentCount' => '0'
+ ),
+ 'TRANSPOSE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRANSPOSE',
+ 'argumentCount' => '1'
+ ),
+ 'TREND' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TREND',
+ 'argumentCount' => '1-4'
+ ),
+ 'TRIM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRIMSPACES',
+ 'argumentCount' => '1'
+ ),
+ 'TRIMMEAN' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::TRIMMEAN',
+ 'argumentCount' => '2'
+ ),
+ 'TRUE' => array('category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::LOGICAL_TRUE',
+ 'argumentCount' => '0'
+ ),
+ 'TRUNC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'UPPER' => array('category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::DUMMY',
+ 'argumentCount' => '1'
+ ),
+ 'VAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARFunc',
+ 'argumentCount' => '1+'
+ ),
+ 'VARA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARA',
+ 'argumentCount' => '1+'
+ ),
+ 'VARP' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::VARP',
+ 'argumentCount' => '1+'
+ ),
+ 'VARPA' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::VLOOKUP',
+ 'argumentCount' => '3,4'
+ ),
+ 'WEEKDAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::DAYOFWEEK',
+ 'argumentCount' => '1,2'
+ ),
+ 'WEEKNUM' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::WEEKOFYEAR',
+ 'argumentCount' => '1,2'
+ ),
+ 'WEIBULL' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::WEIBULL',
+ 'argumentCount' => '4'
+ ),
+ 'WORKDAY' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::WORKDAY',
+ 'argumentCount' => '2+'
+ ),
+ 'XIRR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::XIRR',
+ 'argumentCount' => '2,3'
+ ),
+ 'XNPV' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::XNPV',
+ 'argumentCount' => '3'
+ ),
+ 'YEAR' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::YEAR',
+ 'argumentCount' => '1'
+ ),
+ 'YEARFRAC' => array('category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::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_Functions::YIELDDISC',
+ 'argumentCount' => '4,5'
+ ),
+ 'YIELDMAT' => array('category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::YIELDMAT',
+ 'argumentCount' => '5,6'
+ ),
+ 'ZTEST' => array('category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
+ 'functionCall' => 'PHPExcel_Calculation_Functions::ZTEST',
+ 'argumentCount' => '2-3'
+ )
+ );
+
+
+ // Internal functions used for special control purposes
+ private $_controlFunctions = array(
+ 'MKMATRIX' => array('argumentCount' => '*',
+ 'functionCall' => 'self::_mkMatrix'
+ )
+ );
+
+
+
+
+ /**
+ * Get an instance of this class
+ *
+ * @access public
+ * @return PHPExcel_Calculation
+ */
+ public static function getInstance() {
+ if (!isset(self::$_instance) || is_null(self::$_instance)) {
+ self::$_instance = new PHPExcel_Calculation();
+ }
+
+ return self::$_instance;
+ } // function getInstance()
+
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ *
+ * @access public
+ * @throws Exception
+ */
+ public final function __clone() {
+ throw new Exception ('Cloning a Singleton is not allowed!');
+ } // function __clone()
+
+
+ /**
+ * 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 setExcelCalendar()
+
+
+ /**
+ * 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 getExcelCalendar()
+
+
+ /**
+ * 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()
+
+
+ /**
+ * Get calculation cache expiration time
+ *
+ * @return float
+ */
+ public function getCalculationCacheExpirationTime() {
+ return $this->_calculationCacheExpirationTime;
+ } // getCalculationCacheExpirationTime()
+
+
+ /**
+ * Set calculation cache expiration time
+ *
+ * @param float $pValue
+ */
+ public function setCalculationCacheExpirationTime($pValue = 2.5) {
+ $this->_calculationCacheExpirationTime = $pValue;
+ } // function setCalculationCacheExpirationTime()
+
+
+
+
+ /**
+ * 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 ((strlen($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 Exception
+ */
+ public function calculate(PHPExcel_Cell $pCell = null) {
+ return $this->calculateCellValue($pCell);
+ } // 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 Exception
+ */
+ public function calculateCellValue(PHPExcel_Cell $pCell = null, $resetLog = true) {
+ if ($resetLog) {
+ // Initialise the logging settings if requested
+ $this->formulaError = null;
+ $this->debugLog = $this->debugLogStack = array();
+
+ $returnArrayAsType = self::$returnArrayAsType;
+ self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
+ }
+
+ // Read the formula from the cell
+ if (is_null($pCell)) {
+ return null;
+ }
+
+ if ($resetLog) {
+ self::$returnArrayAsType = $returnArrayAsType;
+ }
+ // Execute the calculation for the cell formula
+ $result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
+
+ if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+ $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_shift(array_keys($result));
+ if (!is_numeric($r)) { return PHPExcel_Calculation_Functions::VALUE(); }
+ if (is_array($result[$r])) {
+ $c = array_shift(array_keys($result[$r]));
+ if (!is_numeric($c)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ }
+ $result = array_shift($testResult);
+ }
+
+ if (is_null($result)) {
+ 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 Exception
+ */
+ public function parseFormula($formula) {
+ // Basic validation that this is indeed a formula
+ // We return an empty array if not
+ $formula = trim($formula);
+ if ($formula{0} != '=') return array();
+ $formula = trim(substr($formula,1));
+ $formulaLength = strlen($formula);
+ if ($formulaLength < 1) 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
+ * @return mixed
+ * @throws Exception
+ */
+ public function calculateFormula($formula, $cellID=null, PHPExcel_Cell $pCell = null) {
+ // Initialise the logging settings
+ $this->formulaError = null;
+ $this->debugLog = $this->debugLogStack = array();
+
+ // 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
+ $result = self::_unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
+ // Reset calculation cacheing to its previous state
+ $this->_calculationCacheEnabled = $resetCache;
+
+ return $result;
+ } // function calculateFormula()
+
+
+ /**
+ * 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 Exception
+ */
+ public function _calculateFormulaValue($formula, $cellID=null, PHPExcel_Cell $pCell = null) {
+// echo ''.$cellID.' ';
+ $cellValue = '';
+
+ // Basic validation that this is indeed a formula
+ // We simply return the "cell value" (formula) if not
+ $formula = trim($formula);
+ if ($formula{0} != '=') return self::_wrapResult($formula);
+ $formula = trim(substr($formula,1));
+ $formulaLength = strlen($formula);
+ if ($formulaLength < 1) return self::_wrapResult($formula);
+
+ $wsTitle = 'Wrk';
+ if (!is_null($pCell)) {
+ $wsTitle = urlencode($pCell->getParent()->getTitle());
+ }
+ // Is calculation cacheing enabled?
+ if (!is_null($cellID)) {
+ if ($this->_calculationCacheEnabled) {
+ // Is the value present in calculation cache?
+// echo 'Testing cache value ';
+ if (isset($this->_calculationCache[$wsTitle][$cellID])) {
+// echo 'Value is in cache ';
+ $this->_writeDebug('Testing cache value for cell '.$cellID);
+ // Is cache still valid?
+ if ((time() + microtime(true)) - $this->_calculationCache[$wsTitle][$cellID]['time'] < $this->_calculationCacheExpirationTime) {
+// echo 'Cache time is still valid ';
+ $this->_writeDebug('Retrieving value for '.$cellID.' from cache');
+ // Return the cached result
+ $returnValue = $this->_calculationCache[$wsTitle][$cellID]['data'];
+// echo 'Retrieving data value of '.$returnValue.' for '.$cellID.' from cache ';
+ if (is_array($returnValue)) {
+ return array_shift(PHPExcel_Calculation_Functions::flattenArray($returnValue));
+ }
+ return $returnValue;
+ } else {
+// echo 'Cache has expired ';
+ $this->_writeDebug('Cache value for '.$cellID.' has expired');
+ // Clear the cache if it's no longer valid
+ unset($this->_calculationCache[$wsTitle][$cellID]);
+ }
+ }
+ }
+ }
+
+ $this->debugLogStack[] = $cellID;
+ // Parse the formula onto the token stack and calculate the value
+ $cellValue = $this->_processTokenStack($this->_parseFormula($formula), $cellID, $pCell);
+ array_pop($this->debugLogStack);
+
+ // Save to calculation cache
+ if (!is_null($cellID)) {
+ if ($this->_calculationCacheEnabled) {
+ $this->_calculationCache[$wsTitle][$cellID]['time'] = (time() + microtime(true));
+ $this->_calculationCache[$wsTitle][$cellID]['data'] = $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);
+ } elseif ($resize == 1) {
+ // Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
+ self::_resizeMatricesShrink($operand1,$operand2);
+ }
+ } // 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) {
+ $colCount = count($rowValue);
+ if ($colCount > $matrixColumns) {
+ $matrixColumns = $colCount;
+ }
+ $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
+ */
+ private static function _resizeMatricesShrink(&$matrix1,&$matrix2) {
+ list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($matrix1);
+ list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($matrix2);
+
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ unset($matrix1[$i][$j]);
+ }
+ }
+ }
+ if ($matrix2Rows < $matrix1Rows) {
+ for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
+ unset($matrix1[$i]);
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ unset($matrix2[$i][$j]);
+ }
+ }
+ }
+ if ($matrix1Rows < $matrix2Rows) {
+ for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
+ unset($matrix2[$i]);
+ }
+ }
+ }
+ } // 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
+ */
+ private static function _resizeMatricesExtend(&$matrix1,&$matrix2) {
+ list($matrix1Rows,$matrix1Columns) = self::_getMatrixDimensions($matrix1);
+ list($matrix2Rows,$matrix2Columns) = self::_getMatrixDimensions($matrix2);
+
+ 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 static function _showValue($value) {
+ if (is_array($value)) {
+ $returnMatrix = array();
+ $pad = $rpad = ', ';
+ foreach($value as $row) {
+ if (is_array($row)) {
+ $returnMatrix[] = implode($pad,$row);
+ $rpad = '; ';
+ } else {
+ $returnMatrix[] = $row;
+ }
+ }
+ return '{ '.implode($rpad,$returnMatrix).' }';
+ } elseif(is_bool($value)) {
+ return ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ return $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 static function _showTypeDetails($value) {
+ switch (gettype($value)) {
+ case 'double' :
+ case 'float' :
+ $typeString = 'a floating point number';
+ break;
+ case 'integer' :
+ $typeString = 'an integer number';
+ break;
+ case 'boolean' :
+ $typeString = 'a boolean';
+ break;
+ case 'array' :
+ $typeString = 'a matrix';
+ break;
+ case 'string' :
+ if ($value == '') {
+ return 'an empty string';
+ } elseif ($value{0} == '#') {
+ return 'a '.$value.' error';
+ } else {
+ $typeString = 'a string';
+ }
+ break;
+ case 'NULL' :
+ return 'a null value';
+ }
+ return $typeString.' with a value of '.self::_showValue($value);
+ } // function _showTypeDetails()
+
+
+ private static 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;
+ foreach($temp as $i => &$value) {
+ // Only count/replace in alternate array entries
+ if (($i % 2) == 0) {
+ $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()
+
+
+ // Convert infix to postfix notation
+ private function _parseFormula($formula) {
+ if (($formula = self::_convertMatrixReferences(trim($formula))) === false) {
+ return false;
+ }
+
+ // 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
+ $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
+ $comparisonOperators = array('>', '<', '=', '>=', '<=', '<>');
+
+ // 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
+ $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
+ );
+
+ $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
+ '|'.self::CALCULATION_REGEXP_NUMBER.
+ '|'.self::CALCULATION_REGEXP_STRING.
+ '|'.self::CALCULATION_REGEXP_OPENBRACE.
+ '|'.self::CALCULATION_REGEXP_CELLREF.
+ '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
+ '|'.self::CALCULATION_REGEXP_ERROR.
+ ')/si';
+
+ // Start with initialisation
+ $index = 0;
+ $stack = new PHPExcel_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).' ';
+ $opCharacter = $formula{$index}; // Get the first character of the value at the current index position
+// echo 'Initial character of expression block is '.$opCharacter.' ';
+ if ((in_array($opCharacter, $comparisonOperators)) && (strlen($formula) > $index) && (in_array($formula{$index+1}, $comparisonOperators))) {
+ $opCharacter .= $formula{++$index};
+// echo 'Initial character of expression block is comparison operator '.$opCharacter.' ';
+ }
+
+ // 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').' ';
+
+ if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
+// echo 'Element is a Negation operator ';
+ $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 ';
+ $stack->push('Unary Operator','%'); // Put a percentage on the stack
+ ++$index;
+ } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (rather than plus) can be discarded?
+// echo 'Element is a Positive number, not Plus operator ';
+ ++$index; // Drop the redundant plus symbol
+ } elseif (($opCharacter == '~') && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde, because it's legal
+ return $this->_raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression
+
+ } elseif ((in_array($opCharacter, $this->_operators) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
+// echo 'Element with value '.$opCharacter.' is an Operator ';
+ while($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ in_array($o2['value'], $this->_operators) &&
+ @($operatorAssociativity[$opCharacter] ? $operatorPrecedence[$opCharacter] < $operatorPrecedence[$o2['value']] : $operatorPrecedence[$opCharacter] <= $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 ';
+ $expectingOperand = false;
+ while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
+ if (is_null($o2)) 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.' ';
+ $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 ';
+// } elseif ($argumentCount == 1) {
+// echo 'With 1 argument ';
+// } else {
+// echo 'With '.$argumentCount.' arguments ';
+// }
+ $output[] = $d; // Dump the argument count on the output
+ $output[] = $stack->pop(); // Pop the function and push onto the output
+ if (array_key_exists($functionName, $this->_controlFunctions)) {
+// echo 'Built-in function '.$functionName.' ';
+ $expectedArgumentCount = $this->_controlFunctions[$functionName]['argumentCount'];
+ $functionCall = $this->_controlFunctions[$functionName]['functionCall'];
+ } elseif (array_key_exists($functionName, $this->_PHPExcelFunctions)) {
+// echo 'PHPExcel function '.$functionName.' ';
+ $expectedArgumentCount = $this->_PHPExcelFunctions[$functionName]['argumentCount'];
+ $functionCall = $this->_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).' ';
+ if ($argumentCount > abs($expectedArgumentCount)) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
+ }
+ } else {
+// echo '$expectedArgumentCount is numeric '.$expectedArgumentCount.' ';
+ if ($argumentCount != $expectedArgumentCount) {
+ $argumentCountError = True;
+ $expectedArgumentCountString = $expectedArgumentCount;
+ }
+ }
+ } elseif ($expectedArgumentCount != '*') {
+ $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/',$expectedArgumentCount,$argMatch);
+// print_r($argMatch);
+// echo ' ';
+ 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 comma separator for function arguments?
+// echo 'Element is a Function argument separator ';
+ while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
+ if (is_null($o2)) 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' => $this->_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/','',$val);
+// echo 'Element '.$val.' is a Function ';
+ if (array_key_exists(strtoupper($matches[1]), $this->_PHPExcelFunctions) || array_key_exists(strtoupper($matches[1]), $this->_controlFunctions)) { // it's a func
+ $stack->push('Function', strtoupper($val));
+ $ax = preg_match('/^\s*(\s*\))/i', 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
+ $cellRef = strtoupper($val);
+// $output[] = $cellRef;
+ $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $cellRef);
+// $expectingOperator = false;
+ } else { // it's a variable, constant, string, number or boolean
+// echo 'Element is a Variable, Constant, String, Number or Boolean ';
+ if ($opCharacter == '"') {
+// echo 'Element is a String ';
+ $val = str_replace('""','"',$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 (array_key_exists(trim(strtoupper($val)), $this->_ExcelConstants)) {
+ $excelConstant = trim(strtoupper($val));
+// echo 'Element '.$excelConstant.' is an Excel Constant ';
+ $val = $this->_ExcelConstants[$excelConstant];
+ }
+ $output[] = array('type' => 'Value', 'value' => $val, 'reference' => NULL);
+ }
+ $index += $length;
+
+ } elseif ($opCharacter == ')') { // miscellaneous error checking
+ if ($expectingOperand) {
+ $output[] = array('type' => 'Null Value', 'value' => $this->_ExcelConstants['NULL'], 'reference' => NULL);
+ $expectingOperand = false;
+ $expectingOperator = True;
+ } else {
+ return $this->_raiseFormulaError("Formula Error: Unexpected ')'");
+ }
+ } elseif (in_array($opCharacter, $this->_operators) && !$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 ((in_array($opCharacter, $this->_operators)) && ($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.'.*/i', substr($formula, $index), $match)) &&
+ ($output[count($output)-1]['type'] == 'Cell Reference')) {
+// echo 'Element is an Intersect Operator ';
+ while($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ in_array($o2['value'], $this->_operators) &&
+ @($operatorAssociativity[$opCharacter] ? $operatorPrecedence[$opCharacter] < $operatorPrecedence[$o2['value']] : $operatorPrecedence[$opCharacter] <= $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 (!is_null($op = $stack->pop())) { // pop everything off the stack and push onto output
+ if ($opCharacter['value'] == '(') 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()
+
+
+ // evaluate postfix notation
+ private function _processTokenStack($tokens, $cellID=null, PHPExcel_Cell $pCell = null) {
+ if ($tokens == false) return false;
+
+ $stack = new PHPExcel_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 (in_array($token, $this->_binaryOperators, true)) {
+// echo 'Token is a binary operator ';
+ // We must have two operands, error if we don't
+ if (is_null($operand2Data = $stack->pop())) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ if (is_null($operand1Data = $stack->pop())) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ // Log what we're doing
+ $operand1 = $operand1Data['value'];
+ $operand2 = $operand2Data['value'];
+ if ($token == ':') {
+ $this->_writeDebug('Evaluating Range '.self::_showValue($operand1Data['reference']).$token.self::_showValue($operand2Data['reference']));
+ } else {
+ $this->_writeDebug('Evaluating '.self::_showValue($operand1).' '.$token.' '.self::_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 = $pCell->getParent()->getTitle();
+ }
+ if (strpos($operand2Data['reference'],'!') !== false) {
+ list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']);
+ } else {
+ $sheet2 = $sheet1;
+ }
+ if ($sheet1 == $sheet2) {
+ if (is_null($operand1Data['reference'])) {
+ if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
+ $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
+ } elseif (trim($operand1Data['reference']) == '') {
+ $operand1Data['reference'] = $pCell->getColumn().$pCell->getRow();
+ } else {
+ $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
+ }
+ }
+ if (is_null($operand2Data['reference'])) {
+ if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
+ $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
+ } elseif (trim($operand2Data['reference']) == '') {
+ $operand2Data['reference'] = $pCell->getColumn().$pCell->getRow();
+ } 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);
+ $cellValue = $this->extractCellRange($cellRef, $pCell->getParent()->getParent()->getSheetByName($sheet1), false);
+ $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) ? 'TRUE' : 'FALSE';
+ }
+ if (is_bool($operand2)) {
+ $operand2 = ($operand2) ? 'TRUE' : '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 Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->concat($operand2);
+ $result = $matrixResult->getArray();
+ } catch (Exception $ex) {
+ $this->_writeDebug('JAMA Matrix Exception: '.$ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ $result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"';
+ }
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ $stack->push('Value',$result);
+ break;
+ case '|' : // Intersect
+ $rowIntersect = array_intersect_key($operand1,$operand2);
+ $cellIntersect = $oCol = $oRow = array();
+ foreach(array_keys($rowIntersect) as $col) {
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
+ $cellIntersect[$col] = array_intersect_key($operand1[$col],$operand2[$col]);
+ foreach($cellIntersect[$col] as $row => $data) {
+ $oRow[] = $row;
+ }
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ $this->_writeDebug('Evaluation Result is '.self::_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 (is_null($arg = $stack->pop())) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
+ $arg = $arg['value'];
+ if ($token === '~') {
+// echo 'Token is a negation operator ';
+ $this->_writeDebug('Evaluating Negation of '.self::_showValue($arg));
+ $multiplier = -1;
+ } else {
+// echo 'Token is a percentile operator ';
+ $this->_writeDebug('Evaluating Percentile of '.self::_showValue($arg));
+ $multiplier = 0.01;
+ }
+ if (is_array($arg)) {
+ self::_checkMatrixOperands($arg,$multiplier,2);
+ try {
+ $matrix1 = new Matrix($arg);
+ $matrixResult = $matrix1->arrayTimesEquals($multiplier);
+ $result = $matrixResult->getArray();
+ } catch (Exception $ex) {
+ $this->_writeDebug('JAMA Matrix Exception: '.$ex->getMessage());
+ $result = '#VALUE!';
+ }
+ $this->_writeDebug('Evaluation Result is '.self::_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 (is_null($pCell)) {
+// 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],"\"'");
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].' ';
+ $this->_writeDebug('Evaluating Cell Range '.$cellRef.' in worksheet '.$matches[2]);
+ $cellValue = $this->extractCellRange($cellRef, $pCell->getParent()->getParent()->getSheetByName($matches[2]), false);
+ $this->_writeDebug('Evaluation Result for cells '.$cellRef.' in worksheet '.$matches[2].' is '.self::_showTypeDetails($cellValue));
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet ';
+ $this->_writeDebug('Evaluating Cell Range '.$cellRef.' in current worksheet');
+ $cellValue = $this->extractCellRange($cellRef, $pCell->getParent(), false);
+ $this->_writeDebug('Evaluation Result for cells '.$cellRef.' is '.self::_showTypeDetails($cellValue));
+ }
+ }
+ } else {
+// echo 'Reference is a single Cell ';
+ if (is_null($pCell)) {
+// 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],"\"'");
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].' ';
+ $this->_writeDebug('Evaluating Cell '.$cellRef.' in worksheet '.$matches[2]);
+ if ($pCell->getParent()->getParent()->getSheetByName($matches[2])->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCell->getParent()->getParent()->getSheetByName($matches[2]), false);
+ } else {
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ }
+ $this->_writeDebug('Evaluation Result for cell '.$cellRef.' in worksheet '.$matches[2].' is '.self::_showTypeDetails($cellValue));
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet ';
+ $this->_writeDebug('Evaluating Cell '.$cellRef.' in current worksheet');
+ if ($pCell->getParent()->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCell->getParent(), false);
+ } else {
+ $cellValue = NULL;
+ }
+ $this->_writeDebug('Evaluation Result for cell '.$cellRef.' is '.self::_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->_writeDebug('Evaluating Function '.$functionName.'() with '.(($argCount == 0) ? 'no' : $argCount).' argument'.(($argCount == 1) ? '' : 's'));
+ }
+ if ((array_key_exists($functionName, $this->_PHPExcelFunctions)) || (array_key_exists($functionName, $this->_controlFunctions))) { // function
+ if (array_key_exists($functionName, $this->_PHPExcelFunctions)) {
+ $functionCall = $this->_PHPExcelFunctions[$functionName]['functionCall'];
+ $passByReference = isset($this->_PHPExcelFunctions[$functionName]['passByReference']);
+ $passCellReference = isset($this->_PHPExcelFunctions[$functionName]['passCellReference']);
+ } elseif (array_key_exists($functionName, $this->_controlFunctions)) {
+ $functionCall = $this->_controlFunctions[$functionName]['functionCall'];
+ $passByReference = isset($this->_controlFunctions[$functionName]['passByReference']);
+ $passCellReference = isset($this->_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($this->_PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
+ ($this->_PHPExcelFunctions[$functionName]['passByReference'][$a])) {
+ if (is_null($arg['reference'])) {
+ $args[] = $cellID;
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($cellID); }
+ } else {
+ $args[] = $arg['reference'];
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($arg['reference']); }
+ }
+ } else {
+ $args[] = self::_unwrapResult($arg['value']);
+ if ($functionName != 'MKMATRIX') { $argArrayVals[] = self::_showValue($arg['value']); }
+ }
+ }
+ // Reverse the order of the arguments
+ krsort($args);
+ if (($passByReference) && ($argCount == 0)) {
+ $args[] = $cellID;
+ $argArrayVals[] = self::_showValue($cellID);
+ }
+// echo 'Arguments are: ';
+// print_r($args);
+// echo ' ';
+ if ($functionName != 'MKMATRIX') {
+ krsort($argArrayVals);
+ $this->_writeDebug('Evaluating '. $functionName.'( '.implode(', ',$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->_writeDebug('Argument is a matrix: '.self::_showValue($operand1));
+// $result = array();
+// $row = 0;
+// foreach($operand1 as $args) {
+// if (is_array($args)) {
+// foreach($args as $arg) {
+// $this->_writeDebug('Evaluating '. $functionName.'( '.self::_showValue($arg).' )');
+// $r = call_user_func_array($functionCall,$arg);
+// $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($r));
+// $result[$row][] = $r;
+// }
+// ++$row;
+// } else {
+// $this->_writeDebug('Evaluating '. $functionName.'( '.self::_showValue($args).' )');
+// $r = call_user_func_array($functionCall,$args);
+// $this->_writeDebug('Evaluation Result is '.self::_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->_writeDebug('Evaluation Result is '.self::_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 (array_key_exists(strtoupper($token), $this->_ExcelConstants)) {
+ $excelConstant = strtoupper($token);
+// echo 'Token is a PHPExcel constant: '.$excelConstant.' ';
+ $stack->push('Constant Value',$this->_ExcelConstants[$excelConstant]);
+ $this->_writeDebug('Evaluating Constant '.$excelConstant.' as '.self::_showTypeDetails($this->_ExcelConstants[$excelConstant]));
+ } elseif ((is_numeric($token)) || (is_bool($token)) || (is_null($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->_writeDebug('Evaluating Named Range '.$namedRange);
+ $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCell->getParent() : null), false);
+ $this->_writeDebug('Evaluation Result for named range '.$namedRange.' is '.self::_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) {
+ // 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->_writeDebug('Evaluation Result is '.self::_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->_writeDebug('Evaluation Result is a '.self::_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->_writeDebug('Evaluating '.self::_showValue($operandData).' '.$operation.' '.self::_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->_writeDebug('Evaluating '.self::_showValue($operand1).' '.$operation.' '.self::_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->_writeDebug('Evaluating '.self::_showValue($operandData).' '.$operation.' '.self::_showValue($operand2[$x]));
+ $this->_executeBinaryComparisonOperation($cellID,$operandData,$operand2[$x],$operation,$stack,True);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->_writeDebug('Evaluation Result is '.self::_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); }
+
+ // execute the necessary operation
+ switch ($operation) {
+ // Greater than
+ case '>':
+ $result = ($operand1 > $operand2);
+ break;
+ // Less than
+ case '<':
+ $result = ($operand1 < $operand2);
+ break;
+ // Equality
+ case '=':
+ $result = ($operand1 == $operand2);
+ break;
+ // Greater than or equal
+ case '>=':
+ $result = ($operand1 >= $operand2);
+ break;
+ // Less than or equal
+ case '<=':
+ $result = ($operand1 <= $operand2);
+ break;
+ // Inequality
+ case '<>':
+ $result = ($operand1 != $operand2);
+ break;
+ }
+
+ // Log the result details
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value',$result);
+ return true;
+ } // function _executeBinaryComparisonOperation()
+
+
+ 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
+ self::_checkMatrixOperands($operand1,$operand2,2);
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->$matrixFunction($operand2);
+ $result = $matrixResult->getArray();
+ } catch (Exception $ex) {
+ $this->_writeDebug('JAMA Matrix Exception: '.$ex->getMessage());
+ $result = '#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->_writeDebug('Evaluation Result is '.self::_showTypeDetails('#DIV/0!'));
+ return false;
+ } else {
+ $result = $operand1/$operand2;
+ }
+ break;
+ // Power
+ case '^':
+ $result = pow($operand1,$operand2);
+ break;
+ }
+ }
+
+ // Log the result details
+ $this->_writeDebug('Evaluation Result is '.self::_showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value',$result);
+ return true;
+ } // function _executeNumericBinaryOperation()
+
+
+ private function _writeDebug($message) {
+ // Only write the debug log if logging is enabled
+ if ($this->writeDebugLog) {
+ $this->debugLog[] = implode(' -> ',$this->debugLogStack).' -> '.$message;
+ }
+ } // function _writeDebug()
+
+
+ // trigger an error, but nicely, if need be
+ private function _raiseFormulaError($errorMessage) {
+ $this->formulaError = $errorMessage;
+ echo '_raiseFormulaError message is '.$errorMessage.' ';
+ if (!$this->suppressFormulaErrors) throw new 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
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @throws Exception
+ */
+ public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog=true) {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractCellRange('.$pRange.') ';
+ if (!is_null($pSheet)) {
+// echo 'Passed sheet name is '.$pSheet->getTitle().' ';
+// echo 'Range reference is '.$pRange.' ';
+ if (strpos ($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference ';
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+ $pSheet = $pSheet->getParent()->getSheetByName($worksheetReference[0]);
+// echo 'New sheet name is '.$pSheet->getTitle().' ';
+ $pRange = $worksheetReference[1];
+// echo 'Adjusted Range reference is '.$pRange.' ';
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+ $pRange = $pSheet->getTitle().'!'.$pRange;
+ if (count($aReferences) == 1) {
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ } else {
+ // Extract cell data
+ foreach ($aReferences as $reference) {
+ // Extract range
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference);
+
+ 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.
+ * @throws Exception
+ */
+ public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog=true) {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractNamedRange('.$pRange.') ';
+ if (!is_null($pSheet)) {
+// echo 'Current sheet name is '.$pSheet->getTitle().' ';
+// echo 'Range reference is '.$pRange.' ';
+ if (strpos ($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference ';
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+ $pSheet = $pSheet->getParent()->getSheetByName($worksheetReference[0]);
+// echo 'New sheet name is '.$pSheet->getTitle().' ';
+ $pRange = $worksheetReference[1];
+// echo 'Adjusted Range reference is '.$pRange.' ';
+ }
+
+ // Named range?
+ $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet);
+ if (!is_null($namedRange)) {
+// echo 'Named Range '.$pRange.' (';
+ $pRange = $namedRange->getRange();
+// 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);
+ if (count($aReferences) == 1) {
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = NULL;
+ }
+ } else {
+ // Extract cell data
+ foreach ($aReferences as $reference) {
+ // Extract range
+ list($currentCol,$currentRow) = PHPExcel_Cell::coordinateFromString($reference);
+// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.' ';
+ 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($this->_PHPExcelFunctions[$pFunction])) {
+ return ($this->_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($this->_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 implemented Excel function names
+ *
+ * @return array
+ */
+ public function listFunctionNames() {
+ return array_keys($this->_PHPExcelFunctions);
+ } // function listFunctionNames()
+
+} // class PHPExcel_Calculation
+
+
+
+
+// for internal use
+class PHPExcel_Token_Stack {
+
+ private $_stack = array();
+ private $_count = 0;
+
+
+ public function count() {
+ return $this->_count;
+ } // function count()
+
+
+ public function push($type,$value,$reference=null) {
+ $this->_stack[$this->_count++] = array('type' => $type,
+ 'value' => $value,
+ 'reference' => $reference
+ );
+ } // function push()
+
+
+ public function pop() {
+ if ($this->_count > 0) {
+ return $this->_stack[--$this->_count];
+ }
+ return null;
+ } // function pop()
+
+
+ public function last($n=1) {
+ if ($this->_count-$n < 0) {
+ return null;
+ }
+ return $this->_stack[$this->_count-$n];
+ } // function last()
+
+
+ function __construct() {
+ }
+
+} // class PHPExcel_Token_Stack
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Exception.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Exception.php
new file mode 100644
index 0000000..84cf09d
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Exception.php
@@ -0,0 +1,52 @@
+line = $line;
+ $e->file = $file;
+ throw $e;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/ExceptionHandler.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/ExceptionHandler.php
new file mode 100644
index 0000000..48475df
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/ExceptionHandler.php
@@ -0,0 +1,60 @@
+<";
+ const OPERATORS_POSTFIX = "%";
+
+ /**
+ * Formula
+ *
+ * @var string
+ */
+ private $_formula;
+
+ /**
+ * Tokens
+ *
+ * @var PHPExcel_Calculation_FormulaToken[]
+ */
+ private $_tokens = array();
+
+ /**
+ * Create a new PHPExcel_Calculation_FormulaParser
+ *
+ * @param string $pFormula Formula to parse
+ * @throws Exception
+ */
+ public function __construct($pFormula = '')
+ {
+ // Check parameters
+ if (is_null($pFormula)) {
+ throw new Exception("Invalid parameter passed: formula");
+ }
+
+ // Initialise values
+ $this->_formula = trim($pFormula);
+ // Parse!
+ $this->_parseToTokens();
+ }
+
+ /**
+ * Get Formula
+ *
+ * @return string
+ */
+ public function getFormula() {
+ return $this->_formula;
+ }
+
+ /**
+ * Get Token
+ *
+ * @param int $pId Token id
+ * @return string
+ * @throws Exception
+ */
+ public function getToken($pId = 0) {
+ if (isset($this->_tokens[$pId])) {
+ return $this->_tokens[$pId];
+ } else {
+ throw new Exception("Token with id $pId does not exist.");
+ }
+ }
+
+ /**
+ * Get Token count
+ *
+ * @return string
+ */
+ public function getTokenCount() {
+ return count($this->_tokens);
+ }
+
+ /**
+ * Get Tokens
+ *
+ * @return PHPExcel_Calculation_FormulaToken[]
+ */
+ public function getTokens() {
+ return $this->_tokens;
+ }
+
+ /**
+ * Parse to tokens
+ */
+ private function _parseToTokens() {
+ // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
+ // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
+
+ // Check if the formula has a valid starting =
+ $formulaLength = strlen($this->_formula);
+ if ($formulaLength < 2 || $this->_formula{0} != '=') return;
+
+ // Helper variables
+ $tokens1 = $tokens2 = $stack = array();
+ $inString = $inPath = $inRange = $inError = false;
+ $token = $previousToken = $nextToken = null;
+
+ $index = 1;
+ $value = '';
+
+ $ERRORS = array("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A");
+ $COMPARATORS_MULTI = array(">=", "<=", "<>");
+
+ while ($index < $formulaLength) {
+ // state-dependent character evaluation (order is important)
+
+ // double-quoted strings
+ // embeds are doubled
+ // end marks token
+ if ($inString) {
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->_formula{$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE)) {
+ $value .= PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE;
+ ++$index;
+ } else {
+ $inString = false;
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_TEXT);
+ $value = "";
+ }
+ } else {
+ $value .= $this->_formula{$index};
+ }
+ ++$index;
+ continue;
+ }
+
+ // single-quoted strings (links)
+ // embeds are double
+ // end does not mark a token
+ if ($inPath) {
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->_formula{$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE)) {
+ $value .= PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE;
+ ++$index;
+ } else {
+ $inPath = false;
+ }
+ } else {
+ $value .= $this->_formula{$index};
+ }
+ ++$index;
+ continue;
+ }
+
+ // bracked strings (R1C1 range index or linked workbook name)
+ // no embeds (changed to "()" by Excel)
+ // end does not mark a token
+ if ($inRange) {
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACKET_CLOSE) {
+ $inRange = false;
+ }
+ $value .= $this->_formula{$index};
+ ++$index;
+ continue;
+ }
+
+ // error values
+ // end marks a token, determined from absolute list of values
+ if ($inError) {
+ $value .= $this->_formula{$index};
+ ++$index;
+ if (in_array($value, $ERRORS)) {
+ $inError = false;
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_ERROR);
+ $value = "";
+ }
+ continue;
+ }
+
+ // scientific notation check
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_SN, $this->_formula{$index}) !== false) {
+ if (strlen($value) > 1) {
+ if (preg_match("/^[1-9]{1}(\.[0-9]+)?E{1}$/", $this->_formula{$index}) != 0) {
+ $value .= $this->_formula{$index};
+ ++$index;
+ continue;
+ }
+ }
+ }
+
+ // independent character evaluation (order not important)
+
+ // establish state-dependent character evaluations
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE) {
+ if (strlen($value > 0)) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inString = true;
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE) {
+ if (strlen($value) > 0) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inPath = true;
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACKET_OPEN) {
+ $inRange = true;
+ $value .= PHPExcel_Calculation_FormulaParser::BRACKET_OPEN;
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::ERROR_START) {
+ if (strlen($value) > 0) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inError = true;
+ $value .= PHPExcel_Calculation_FormulaParser::ERROR_START;
+ ++$index;
+ continue;
+ }
+
+ // mark start and end of arrays and array rows
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACE_OPEN) {
+ if (strlen($value) > 0) { // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAY", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::SEMICOLON) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT);
+ $tokens1[] = $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::BRACE_CLOSE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ // trim white-space
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken("", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE);
+ ++$index;
+ while (($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) && ($index < $formulaLength)) {
+ ++$index;
+ }
+ continue;
+ }
+
+ // multi-character comparators
+ if (($index + 2) <= $formulaLength) {
+ if (in_array(substr($this->_formula, $index, 2), $COMPARATORS_MULTI)) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(substr($this->_formula, $index, 2), PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ $index += 2;
+ continue;
+ }
+ }
+
+ // standard infix operators
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_INFIX, $this->_formula{$index}) !== false) {
+ if (strlen($value) > 0) {
+ $tokens1[] =new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($this->_formula{$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX);
+ ++$index;
+ continue;
+ }
+
+ // standard postfix operators (only one)
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_POSTFIX, $this->_formula{$index}) !== false) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($this->_formula{$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
+ ++$index;
+ continue;
+ }
+
+ // start subexpression or function
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::PAREN_OPEN) {
+ if (strlen($value) > 0) {
+ $tmp = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ $value = "";
+ } else {
+ $tmp = new PHPExcel_Calculation_FormulaToken("", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ }
+ ++$index;
+ continue;
+ }
+
+ // function, subexpression, or array parameters, or operand unions
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::COMMA) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $stack[] = $tmp;
+
+ if ($tmp->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_UNION);
+ } else {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT);
+ }
+ ++$index;
+ continue;
+ }
+
+ // stop subexpression
+ if ($this->_formula{$index} == PHPExcel_Calculation_FormulaParser::PAREN_CLOSE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ // token accumulation
+ $value .= $this->_formula{$index};
+ ++$index;
+ }
+
+ // dump remaining accumulation
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ }
+
+ // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
+ $tokenCount = count($tokens1);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens1[$i];
+ if (isset($tokens1[$i - 1])) {
+ $previousToken = $tokens1[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens1[$i + 1])) {
+ $nextToken = $tokens1[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if (is_null($token)) {
+ continue;
+ }
+
+ if ($token->getTokenType() != PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE) {
+ $tokens2[] = $token;
+ continue;
+ }
+
+ if (is_null($previousToken)) {
+ continue;
+ }
+
+ if (! (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) ) {
+ continue;
+ }
+
+ if (is_null($nextToken)) {
+ continue;
+ }
+
+ if (! (
+ (($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) ||
+ (($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) ||
+ ($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) ) {
+ continue;
+ }
+
+ $tokens2[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
+ }
+
+ // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
+ // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
+ $this->_tokens = array();
+
+ $tokenCount = count($tokens2);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens2[$i];
+ if (isset($tokens2[$i - 1])) {
+ $previousToken = $tokens2[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens2[$i + 1])) {
+ $nextToken = $tokens2[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if (is_null($token)) {
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "-") {
+ if ($i == 0) {
+ $token->setTokenType(PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ } else if (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ $token->setTokenType(PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "+") {
+ if ($i == 0) {
+ continue;
+ } else if (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ continue;
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (strpos("<>=", substr($token->getValue(), 0, 1)) !== false) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } else if ($token->getValue() == "&") {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND && $token->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (!is_numeric($token->getValue())) {
+ if (strtoupper($token->getValue()) == "TRUE" || strtoupper($token->getValue() == "FALSE")) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_RANGE);
+ }
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NUMBER);
+ }
+
+ $this->_tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) {
+ if (strlen($token->getValue() > 0)) {
+ if (substr($token->getValue(), 0, 1) == "@") {
+ $token->setValue(substr($token->getValue(), 1));
+ }
+ }
+ }
+
+ $this->_tokens[] = $token;
+ }
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/FormulaToken.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/FormulaToken.php
new file mode 100644
index 0000000..091e69d
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/FormulaToken.php
@@ -0,0 +1,176 @@
+_value = $pValue;
+ $this->_tokenType = $pTokenType;
+ $this->_tokenSubType = $pTokenSubType;
+ }
+
+ /**
+ * Get Value
+ *
+ * @return string
+ */
+ public function getValue() {
+ return $this->_value;
+ }
+
+ /**
+ * Set Value
+ *
+ * @param string $value
+ */
+ public function setValue($value) {
+ $this->_value = $value;
+ }
+
+ /**
+ * Get Token Type (represented by TOKEN_TYPE_*)
+ *
+ * @return string
+ */
+ public function getTokenType() {
+ return $this->_tokenType;
+ }
+
+ /**
+ * Set Token Type
+ *
+ * @param string $value
+ */
+ public function setTokenType($value = PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN) {
+ $this->_tokenType = $value;
+ }
+
+ /**
+ * Get Token SubType (represented by TOKEN_SUBTYPE_*)
+ *
+ * @return string
+ */
+ public function getTokenSubType() {
+ return $this->_tokenSubType;
+ }
+
+ /**
+ * Set Token SubType
+ *
+ * @param string $value
+ */
+ public function setTokenSubType($value = PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ $this->_tokenSubType = $value;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Function.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Function.php
new file mode 100644
index 0000000..6ea8aea
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Function.php
@@ -0,0 +1,149 @@
+_category = $pCategory;
+ $this->_excelName = $pExcelName;
+ $this->_phpExcelName = $pPHPExcelName;
+ } else {
+ throw new Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Get Category (represented by CATEGORY_*)
+ *
+ * @return string
+ */
+ public function getCategory() {
+ return $this->_category;
+ }
+
+ /**
+ * Set Category (represented by CATEGORY_*)
+ *
+ * @param string $value
+ * @throws Exception
+ */
+ public function setCategory($value = null) {
+ if (!is_null($value)) {
+ $this->_category = $value;
+ } else {
+ throw new Exception("Invalid parameter passed.");
+ }
+ }
+
+ /**
+ * Get Excel name
+ *
+ * @return string
+ */
+ public function getExcelName() {
+ return $this->_excelName;
+ }
+
+ /**
+ * Set Excel name
+ *
+ * @param string $value
+ */
+ public function setExcelName($value) {
+ $this->_excelName = $value;
+ }
+
+ /**
+ * Get PHPExcel name
+ *
+ * @return string
+ */
+ public function getPHPExcelName() {
+ return $this->_phpExcelName;
+ }
+
+ /**
+ * Set PHPExcel name
+ *
+ * @param string $value
+ */
+ public function setPHPExcelName($value) {
+ $this->_phpExcelName = $value;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Functions.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Functions.php
new file mode 100644
index 0000000..2ba67fd
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/Functions.php
@@ -0,0 +1,11480 @@
+ '#NULL!',
+ 'divisionbyzero' => '#DIV/0!',
+ 'value' => '#VALUE!',
+ 'reference' => '#REF!',
+ 'name' => '#NAME?',
+ 'num' => '#NUM!',
+ 'na' => '#N/A',
+ 'gettingdata' => '#GETTING_DATA'
+ );
+
+
+ /**
+ * Set the Compatibility Mode
+ *
+ * @access public
+ * @category Function Configuration
+ * @param string $compatibilityMode Compatibility Mode
+ * Permitted values are:
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ * @return boolean (Success or Failure)
+ */
+ public static function setCompatibilityMode($compatibilityMode) {
+ if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
+ ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
+ ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
+ self::$compatibilityMode = $compatibilityMode;
+ return True;
+ }
+ return False;
+ } // function setCompatibilityMode()
+
+
+ /**
+ * Return the current Compatibility Mode
+ *
+ * @access public
+ * @category Function Configuration
+ * @return string Compatibility Mode
+ * Possible Return values are:
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ */
+ public static function getCompatibilityMode() {
+ return self::$compatibilityMode;
+ } // function getCompatibilityMode()
+
+
+ /**
+ * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
+ *
+ * @access public
+ * @category Function Configuration
+ * @param string $returnDateType Return Date Format
+ * Permitted values are:
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
+ * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
+ * @return boolean Success or failure
+ */
+ public static function setReturnDateType($returnDateType) {
+ if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
+ ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
+ ($returnDateType == self::RETURNDATE_EXCEL)) {
+ self::$ReturnDateType = $returnDateType;
+ return True;
+ }
+ return False;
+ } // function setReturnDateType()
+
+
+ /**
+ * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
+ *
+ * @access public
+ * @category Function Configuration
+ * @return string Return Date Format
+ * Possible Return values are:
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
+ * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
+ */
+ public static function getReturnDateType() {
+ return self::$ReturnDateType;
+ } // function getReturnDateType()
+
+
+ /**
+ * DUMMY
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #Not Yet Implemented
+ */
+ public static function DUMMY() {
+ return '#Not Yet Implemented';
+ } // function DUMMY()
+
+
+ /**
+ * NA
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #N/A!
+ */
+ public static function NA() {
+ return self::$_errorCodes['na'];
+ } // function NA()
+
+
+ /**
+ * NAN
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NUM!
+ */
+ public static function NaN() {
+ return self::$_errorCodes['num'];
+ } // function NAN()
+
+
+ /**
+ * NAME
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NAME!
+ */
+ public static function NAME() {
+ return self::$_errorCodes['name'];
+ } // function NAME()
+
+
+ /**
+ * REF
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #REF!
+ */
+ public static function REF() {
+ return self::$_errorCodes['reference'];
+ } // function REF()
+
+
+ /**
+ * VALUE
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #VALUE!
+ */
+ public static function VALUE() {
+ return self::$_errorCodes['value'];
+ } // function VALUE()
+
+
+ private static function isMatrixValue($idx) {
+ return ((substr_count($idx,'.') <= 1) || (preg_match('/\.[A-Z]/',$idx) > 0));
+ }
+
+
+ private static function isValue($idx) {
+ return (substr_count($idx,'.') == 0);
+ }
+
+
+ private static function isCellValue($idx) {
+ return (substr_count($idx,'.') > 1);
+ }
+
+
+ /**
+ * LOGICAL_AND
+ *
+ * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
+ *
+ * Excel Function:
+ * =AND(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $arg,... Data values
+ * @return boolean The logical AND of the arguments.
+ */
+ public static function LOGICAL_AND() {
+ // Return value
+ $returnValue = True;
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $argCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $returnValue = $returnValue && $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue = $returnValue && ($arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = strtoupper($arg);
+ if ($arg == 'TRUE') {
+ $arg = 1;
+ } elseif ($arg == 'FALSE') {
+ $arg = 0;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $returnValue = $returnValue && ($arg != 0);
+ }
+ ++$argCount;
+ }
+
+ // Return
+ if ($argCount == 0) {
+ return self::$_errorCodes['value'];
+ }
+ return $returnValue;
+ } // function LOGICAL_AND()
+
+
+ /**
+ * LOGICAL_OR
+ *
+ * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
+ *
+ * Excel Function:
+ * =OR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $arg,... Data values
+ * @return boolean The logical OR of the arguments.
+ */
+ public static function LOGICAL_OR() {
+ // Return value
+ $returnValue = False;
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $argCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $returnValue = $returnValue || $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue = $returnValue || ($arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = strtoupper($arg);
+ if ($arg == 'TRUE') {
+ $arg = 1;
+ } elseif ($arg == 'FALSE') {
+ $arg = 0;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $returnValue = $returnValue || ($arg != 0);
+ }
+ ++$argCount;
+ }
+
+ // Return
+ if ($argCount == 0) {
+ return self::$_errorCodes['value'];
+ }
+ return $returnValue;
+ } // function LOGICAL_OR()
+
+
+ /**
+ * LOGICAL_FALSE
+ *
+ * Returns the boolean FALSE.
+ *
+ * Excel Function:
+ * =FALSE()
+ *
+ * @access public
+ * @category Logical Functions
+ * @return boolean False
+ */
+ public static function LOGICAL_FALSE() {
+ return False;
+ } // function LOGICAL_FALSE()
+
+
+ /**
+ * LOGICAL_TRUE
+ *
+ * Returns the boolean TRUE.
+ *
+ * Excel Function:
+ * =TRUE()
+ *
+ * @access public
+ * @category Logical Functions
+ * @return boolean True
+ */
+ public static function LOGICAL_TRUE() {
+ return True;
+ } // function LOGICAL_TRUE()
+
+
+ /**
+ * LOGICAL_NOT
+ *
+ * Returns the boolean inverse of the argument.
+ *
+ * Excel Function:
+ * =NOT(logical)
+ *
+ * The argument must evaluate to a logical value such as TRUE or FALSE
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+ * @return boolean The boolean inverse of the argument.
+ */
+ public static function LOGICAL_NOT($logical) {
+ $logical = self::flattenSingleValue($logical);
+ if (is_string($logical)) {
+ $logical = strtoupper($logical);
+ if ($logical == 'TRUE') {
+ return False;
+ } elseif ($logical == 'FALSE') {
+ return True;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+
+ return !$logical;
+ } // function LOGICAL_NOT()
+
+
+ /**
+ * STATEMENT_IF
+ *
+ * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
+ *
+ * Excel Function:
+ * =IF(condition[,returnIfTrue[,returnIfFalse]])
+ *
+ * Condition is any value or expression that can be evaluated to TRUE or FALSE.
+ * For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
+ * the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
+ * This argument can use any comparison calculation operator.
+ * ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
+ * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
+ * then the IF function returns the text "Within budget"
+ * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
+ * the logical value TRUE for this argument.
+ * ReturnIfTrue can be another formula.
+ * ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
+ * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
+ * then the IF function returns the text "Over budget".
+ * If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
+ * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
+ * ReturnIfFalse can be another formula.
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $condition Condition to evaluate
+ * @param mixed $returnIfTrue Value to return when condition is true
+ * @param mixed $returnIfFalse Optional value to return when condition is false
+ * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
+ */
+ public static function STATEMENT_IF($condition = true, $returnIfTrue = 0, $returnIfFalse = False) {
+ $condition = (is_null($condition)) ? True : (boolean) self::flattenSingleValue($condition);
+ $returnIfTrue = (is_null($returnIfTrue)) ? 0 : self::flattenSingleValue($returnIfTrue);
+ $returnIfFalse = (is_null($returnIfFalse)) ? False : self::flattenSingleValue($returnIfFalse);
+
+ return ($condition ? $returnIfTrue : $returnIfFalse);
+ } // function STATEMENT_IF()
+
+
+ /**
+ * STATEMENT_IFERROR
+ *
+ * Excel Function:
+ * =IFERROR(testValue,errorpart)
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $testValue Value to check, is also the value returned when no error
+ * @param mixed $errorpart Value to return when testValue is an error condition
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function STATEMENT_IFERROR($testValue = '', $errorpart = '') {
+ $testValue = (is_null($testValue)) ? '' : self::flattenSingleValue($testValue);
+ $errorpart = (is_null($errorpart)) ? '' : self::flattenSingleValue($errorpart);
+
+ return self::STATEMENT_IF(self::IS_ERROR($testValue), $errorpart, $testValue);
+ } // function STATEMENT_IFERROR()
+
+
+ /**
+ * ATAN2
+ *
+ * This function calculates the arc tangent of the two variables x and y. It is similar to
+ * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
+ * to determine the quadrant of the result.
+ * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
+ * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
+ * -pi and pi, excluding -pi.
+ *
+ * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
+ * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
+ *
+ * Excel Function:
+ * ATAN2(xCoordinate,yCoordinate)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $xCoordinate The x-coordinate of the point.
+ * @param float $yCoordinate The y-coordinate of the point.
+ * @return float The inverse tangent of the specified x- and y-coordinates.
+ */
+ public static function REVERSE_ATAN2($xCoordinate, $yCoordinate) {
+ $xCoordinate = (float) self::flattenSingleValue($xCoordinate);
+ $yCoordinate = (float) self::flattenSingleValue($yCoordinate);
+
+ if (($xCoordinate == 0) && ($yCoordinate == 0)) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ return atan2($yCoordinate, $xCoordinate);
+ } // function REVERSE_ATAN2()
+
+
+ /**
+ * LOG_BASE
+ *
+ * Returns the logarithm of a number to a specified base. The default base is 10.
+ *
+ * Excel Function:
+ * LOG(number[,base])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $value The positive real number for which you want the logarithm
+ * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
+ * @return float
+ */
+ public static function LOG_BASE($number, $base=10) {
+ $number = self::flattenSingleValue($number);
+ $base = (is_null($base)) ? 10 : (float) self::flattenSingleValue($base);
+
+ return log($number, $base);
+ } // function LOG_BASE()
+
+
+ /**
+ * SUM
+ *
+ * SUM computes the sum of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * SUM(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUM() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += $arg;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function SUM()
+
+
+ /**
+ * SUMSQ
+ *
+ * SUMSQ returns the sum of the squares of the arguments
+ *
+ * Excel Function:
+ * SUMSQ(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUMSQ() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += ($arg * $arg);
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function SUMSQ()
+
+
+ /**
+ * PRODUCT
+ *
+ * PRODUCT returns the product of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * PRODUCT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function PRODUCT() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue *= $arg;
+ }
+ }
+ }
+
+ // Return
+ if (is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function PRODUCT()
+
+
+ /**
+ * QUOTIENT
+ *
+ * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
+ * and denominator is the divisor.
+ *
+ * Excel Function:
+ * QUOTIENT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function QUOTIENT() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = ($arg == 0) ? 0 : $arg;
+ } else {
+ if (($returnValue == 0) || ($arg == 0)) {
+ $returnValue = 0;
+ } else {
+ $returnValue /= $arg;
+ }
+ }
+ }
+ }
+
+ // Return
+ return intval($returnValue);
+ } // function QUOTIENT()
+
+
+ /**
+ * MIN
+ *
+ * MIN returns the value of the element of the values passed that has the smallest value,
+ * with negative numbers considered smaller than positive numbers.
+ *
+ * Excel Function:
+ * MIN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MIN() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ((is_null($returnValue)) || ($arg < $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MIN()
+
+
+ /**
+ * MINA
+ *
+ * Returns the smallest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MINA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MINA() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if ((is_null($returnValue)) || ($arg < $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MINA()
+
+
+ /**
+ * SMALL
+ *
+ * Returns the nth smallest value in a data set. You can use this function to
+ * select a value based on its relative standing.
+ *
+ * Excel Function:
+ * SMALL(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param int $entry Position (ordered from the smallest) in the array or range of data to return
+ * @return float
+ */
+ public static function SMALL() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $count = self::COUNT($mArgs);
+ $entry = floor(--$entry);
+ if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
+ return self::$_errorCodes['num'];
+ }
+ sort($mArgs);
+ return $mArgs[$entry];
+ }
+ return self::$_errorCodes['value'];
+ } // function SMALL()
+
+
+ /**
+ * MAX
+ *
+ * MAX returns the value of the element of the values passed that has the highest value,
+ * with negative numbers considered smaller than positive numbers.
+ *
+ * Excel Function:
+ * MAX(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MAX() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ((is_null($returnValue)) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MAX()
+
+
+ /**
+ * MAXA
+ *
+ * Returns the greatest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MAXA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MAXA() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if ((is_null($returnValue)) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ // Return
+ if(is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ } // function MAXA()
+
+
+ /**
+ * LARGE
+ *
+ * Returns the nth largest value in a data set. You can use this function to
+ * select a value based on its relative standing.
+ *
+ * Excel Function:
+ * LARGE(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param int $entry Position (ordered from the largest) in the array or range of data to return
+ * @return float
+ *
+ */
+ public static function LARGE() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = floor(array_pop($aArgs));
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $count = self::COUNT($mArgs);
+ $entry = floor(--$entry);
+ if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
+ return self::$_errorCodes['num'];
+ }
+ rsort($mArgs);
+ return $mArgs[$entry];
+ }
+ return self::$_errorCodes['value'];
+ } // function LARGE()
+
+
+ /**
+ * PERCENTILE
+ *
+ * Returns the nth percentile of values in a range..
+ *
+ * Excel Function:
+ * PERCENTILE(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param float $entry Percentile value in the range 0..1, inclusive.
+ * @return float
+ */
+ public static function PERCENTILE() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ if (($entry < 0) || ($entry > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs);
+ $count = self::COUNT($mArgs);
+ $index = $entry * ($count-1);
+ $iBase = floor($index);
+ if ($index == $iBase) {
+ return $mArgs[$index];
+ } else {
+ $iNext = $iBase + 1;
+ $iProportion = $index - $iBase;
+ return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function PERCENTILE()
+
+
+ /**
+ * QUARTILE
+ *
+ * Returns the quartile of a data set.
+ *
+ * Excel Function:
+ * QUARTILE(value1[,value2[, ...]],entry)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param int $entry Quartile value in the range 1..3, inclusive.
+ * @return float
+ */
+ public static function QUARTILE() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $entry = floor(array_pop($aArgs));
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $entry /= 4;
+ if (($entry < 0) || ($entry > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::PERCENTILE($aArgs,$entry);
+ }
+ return self::$_errorCodes['value'];
+ } // function QUARTILE()
+
+
+ /**
+ * COUNT
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * COUNT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return int
+ */
+ public static function COUNT() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNT()
+
+
+ /**
+ * COUNTBLANK
+ *
+ * Counts the number of empty cells within the list of arguments
+ *
+ * Excel Function:
+ * COUNTBLANK(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return int
+ */
+ public static function COUNTBLANK() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a blank cell?
+ if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNTBLANK()
+
+
+ /**
+ * COUNTA
+ *
+ * Counts the number of cells that are not empty within the list of arguments
+ *
+ * Excel Function:
+ * COUNTA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return int
+ */
+ public static function COUNTA() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric, boolean or string value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNTA()
+
+
+ /**
+ * COUNTIF
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * COUNTIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be counted.
+ * @return int
+ */
+ public static function COUNTIF($aArgs,$condition) {
+ // Return value
+ $returnValue = 0;
+
+ $aArgs = self::flattenArray($aArgs);
+ $condition = self::flattenSingleValue($condition);
+ if (!in_array($condition{0},array('>', '<', '='))) {
+ if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
+ $condition = '='.$condition;
+ } else {
+ preg_match('/([<>=]+)(.*)/',$condition,$matches);
+ list(,$operator,$operand) = $matches;
+ if (!is_numeric($operand)) { $operand = PHPExcel_Calculation::_wrapResult(strtoupper($operand)); }
+ $condition = $operator.$operand;
+ }
+ // Loop through arguments
+ foreach ($aArgs as $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is it a value within our criteria
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function COUNTIF()
+
+
+ /**
+ * SUMIF
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * SUMIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be summed.
+ * @return float
+ */
+ public static function SUMIF($aArgs,$condition,$sumArgs = array()) {
+ // Return value
+ $returnValue = 0;
+
+ $aArgs = self::flattenArray($aArgs);
+ $sumArgs = self::flattenArray($sumArgs);
+ if (count($sumArgs) == 0) {
+ $sumArgs = $aArgs;
+ }
+ if (!in_array($condition{0},array('>', '<', '='))) {
+ if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
+ $condition = '='.$condition;
+ } else {
+ preg_match('/([<>=]+)(.*)/',$condition,$matches);
+ list(,$operator,$operand) = $matches;
+ if (!is_numeric($operand)) { $operand = PHPExcel_Calculation::_wrapResult(strtoupper($operand)); }
+ $condition = $operator.$operand;
+ }
+ // Loop through arguments
+ foreach ($aArgs as $key => $arg) {
+ if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is it a value within our criteria
+ $returnValue += $sumArgs[$key];
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function SUMIF()
+
+
+ /**
+ * AVERAGE
+ *
+ * Returns the average (arithmetic mean) of the arguments
+ *
+ * Excel Function:
+ * AVERAGE(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function AVERAGE() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ $returnValue = $aCount = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue += $arg;
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ } else {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ } // function AVERAGE()
+
+
+ /**
+ * AVERAGEA
+ *
+ * Returns the average of its arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * AVERAGEA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function AVERAGEA() {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue += $arg;
+ }
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ } else {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ } // function AVERAGEA()
+
+
+ /**
+ * MEDIAN
+ *
+ * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
+ *
+ * Excel Function:
+ * MEDIAN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MEDIAN() {
+ // Return value
+ $returnValue = self::$_errorCodes['num'];
+
+ $mArgs = array();
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs,SORT_NUMERIC);
+ $mValueCount = $mValueCount / 2;
+ if ($mValueCount == floor($mValueCount)) {
+ $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
+ } else {
+ $mValueCount == floor($mValueCount);
+ $returnValue = $mArgs[$mValueCount];
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function MEDIAN()
+
+
+ //
+ // Special variant of array_count_values that isn't limited to strings and integers,
+ // but can work with floating point numbers as values
+ //
+ private static function _modeCalc($data) {
+ $frequencyArray = array();
+ foreach($data as $datum) {
+ $found = False;
+ foreach($frequencyArray as $key => $value) {
+ if ((string) $value['value'] == (string) $datum) {
+ ++$frequencyArray[$key]['frequency'];
+ $found = True;
+ break;
+ }
+ }
+ if (!$found) {
+ $frequencyArray[] = array('value' => $datum,
+ 'frequency' => 1 );
+ }
+ }
+
+ foreach($frequencyArray as $key => $value) {
+ $frequencyList[$key] = $value['frequency'];
+ $valueList[$key] = $value['value'];
+ }
+ array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
+
+ if ($frequencyArray[0]['frequency'] == 1) {
+ return self::NA();
+ }
+ return $frequencyArray[0]['value'];
+ } // function _modeCalc()
+
+
+ /**
+ * MODE
+ *
+ * Returns the most frequently occurring, or repetitive, value in an array or range of data
+ *
+ * Excel Function:
+ * MODE(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function MODE() {
+ // Return value
+ $returnValue = self::NA();
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ if (count($mArgs) > 0) {
+ return self::_modeCalc($mArgs);
+ }
+
+ // Return
+ return $returnValue;
+ } // function MODE()
+
+
+ /**
+ * DEVSQ
+ *
+ * Returns the sum of squares of deviations of data points from their sample mean.
+ *
+ * Excel Function:
+ * DEVSQ(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function DEVSQ() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if ($aMean != self::$_errorCodes['divisionbyzero']) {
+ $aCount = -1;
+ foreach ($aArgs as $k => $arg) {
+ // Is it a numeric value?
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if (is_null($returnValue)) {
+ return self::$_errorCodes['num'];
+ } else {
+ return $returnValue;
+ }
+ }
+ return self::NA();
+ } // function DEVSQ()
+
+
+ /**
+ * AVEDEV
+ *
+ * Returns the average of the absolute deviations of data points from their mean.
+ * AVEDEV is a measure of the variability in a data set.
+ *
+ * Excel Function:
+ * AVEDEV(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function AVEDEV() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if ($aMean != self::$_errorCodes['divisionbyzero']) {
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = abs($arg - $aMean);
+ } else {
+ $returnValue += abs($arg - $aMean);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount == 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ return $returnValue / $aCount;
+ }
+ return self::$_errorCodes['num'];
+ } // function AVEDEV()
+
+
+ /**
+ * GEOMEAN
+ *
+ * Returns the geometric mean of an array or range of positive data. For example, you
+ * can use GEOMEAN to calculate average growth rate given compound interest with
+ * variable rates.
+ *
+ * Excel Function:
+ * GEOMEAN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function GEOMEAN() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ $aMean = self::PRODUCT($aArgs);
+ if (is_numeric($aMean) && ($aMean > 0)) {
+ $aCount = self::COUNT($aArgs) ;
+ if (self::MIN($aArgs) > 0) {
+ return pow($aMean, (1 / $aCount));
+ }
+ }
+ return self::$_errorCodes['num'];
+ } // GEOMEAN()
+
+
+ /**
+ * HARMEAN
+ *
+ * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
+ * arithmetic mean of reciprocals.
+ *
+ * Excel Function:
+ * HARMEAN(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function HARMEAN() {
+ // Return value
+ $returnValue = self::NA();
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ if (self::MIN($aArgs) < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ($arg <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ if (is_null($returnValue)) {
+ $returnValue = (1 / $arg);
+ } else {
+ $returnValue += (1 / $arg);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return 1 / ($returnValue / $aCount);
+ } else {
+ return $returnValue;
+ }
+ } // function HARMEAN()
+
+
+ /**
+ * TRIMMEAN
+ *
+ * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
+ * taken by excluding a percentage of data points from the top and bottom tails
+ * of a data set.
+ *
+ * Excel Function:
+ * TRIMEAN(value1[,value2[, ...]],$discard)
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @param float $discard Percentage to discard
+ * @return float
+ */
+ public static function TRIMMEAN() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $percent = array_pop($aArgs);
+
+ if ((is_numeric($percent)) && (!is_string($percent))) {
+ if (($percent < 0) || ($percent > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ $mArgs = array();
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+ $discard = floor(self::COUNT($mArgs) * $percent / 2);
+ sort($mArgs);
+ for ($i=0; $i < $discard; ++$i) {
+ array_pop($mArgs);
+ array_shift($mArgs);
+ }
+ return self::AVERAGE($mArgs);
+ }
+ return self::$_errorCodes['value'];
+ } // function TRIMMEAN()
+
+
+ /**
+ * STDEV
+ *
+ * Estimates standard deviation based on a sample. The standard deviation is a measure of how
+ * widely values are dispersed from the average value (the mean).
+ *
+ * Excel Function:
+ * STDEV(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEV() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = -1;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue > 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEV()
+
+
+ /**
+ * STDEVA
+ *
+ * Estimates standard deviation based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * STDEVA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEVA() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGEA($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = -1;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue > 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEVA()
+
+
+ /**
+ * STDEVP
+ *
+ * Calculates standard deviation based on the entire population
+ *
+ * Excel Function:
+ * STDEVP(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEVP() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGE($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ ((!self::isCellValue($k)) || (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (integer) $arg;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue > 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEVP()
+
+
+ /**
+ * STDEVPA
+ *
+ * Calculates standard deviation based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * STDEVPA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function STDEVPA() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+
+ // Return value
+ $returnValue = null;
+
+ $aMean = self::AVERAGEA($aArgs);
+ if (!is_null($aMean)) {
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ if (is_null($returnValue)) {
+ $returnValue = pow(($arg - $aMean),2);
+ } else {
+ $returnValue += pow(($arg - $aMean),2);
+ }
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if (($aCount > 0) && ($returnValue > 0)) {
+ return sqrt($returnValue / $aCount);
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function STDEVPA()
+
+
+ /**
+ * VARFunc
+ *
+ * Estimates variance based on a sample.
+ *
+ * Excel Function:
+ * VAR(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARFunc() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ if (is_bool($arg)) { $arg = (integer) $arg; }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+ return $returnValue;
+ } // function VARFunc()
+
+
+ /**
+ * VARA
+ *
+ * Estimates variance based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARA() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) &&
+ (self::isValue($k))) {
+ return self::$_errorCodes['value'];
+ } elseif ((is_string($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+ return $returnValue;
+ } // function VARA()
+
+
+ /**
+ * VARP
+ *
+ * Calculates variance based on the entire population
+ *
+ * Excel Function:
+ * VARP(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARP() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ if (is_bool($arg)) { $arg = (integer) $arg; }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
+ }
+ return $returnValue;
+ } // function VARP()
+
+
+ /**
+ * VARPA
+ *
+ * Calculates variance based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARPA(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Statistical Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function VARPA() {
+ // Return value
+ $returnValue = self::$_errorCodes['divisionbyzero'];
+
+ $summerA = $summerB = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) &&
+ (self::isValue($k))) {
+ return self::$_errorCodes['value'];
+ } elseif ((is_string($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
+ if (is_bool($arg)) {
+ $arg = (integer) $arg;
+ } elseif (is_string($arg)) {
+ $arg = 0;
+ }
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+ $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
+ }
+ return $returnValue;
+ } // function VARPA()
+
+
+ /**
+ * RANK
+ *
+ * Returns the rank of a number in a list of numbers.
+ *
+ * @param number The number whose rank you want to find.
+ * @param array of number An array of, or a reference to, a list of numbers.
+ * @param mixed Order to sort the values in the value set
+ * @return float
+ */
+ public static function RANK($value,$valueSet,$order=0) {
+ $value = self::flattenSingleValue($value);
+ $valueSet = self::flattenArray($valueSet);
+ $order = (is_null($order)) ? 0 : (integer) self::flattenSingleValue($order);
+
+ foreach($valueSet as $key => $valueEntry) {
+ if (!is_numeric($valueEntry)) {
+ unset($valueSet[$key]);
+ }
+ }
+
+ if ($order == 0) {
+ rsort($valueSet,SORT_NUMERIC);
+ } else {
+ sort($valueSet,SORT_NUMERIC);
+ }
+ $pos = array_search($value,$valueSet);
+ if ($pos === False) {
+ return self::$_errorCodes['na'];
+ }
+
+ return ++$pos;
+ } // function RANK()
+
+
+ /**
+ * PERCENTRANK
+ *
+ * Returns the rank of a value in a data set as a percentage of the data set.
+ *
+ * @param array of number An array of, or a reference to, a list of numbers.
+ * @param number The number whose rank you want to find.
+ * @param number The number of significant digits for the returned percentage value.
+ * @return float
+ */
+ public static function PERCENTRANK($valueSet,$value,$significance=3) {
+ $valueSet = self::flattenArray($valueSet);
+ $value = self::flattenSingleValue($value);
+ $significance = (is_null($significance)) ? 3 : (integer) self::flattenSingleValue($significance);
+
+ foreach($valueSet as $key => $valueEntry) {
+ if (!is_numeric($valueEntry)) {
+ unset($valueSet[$key]);
+ }
+ }
+ sort($valueSet,SORT_NUMERIC);
+ $valueCount = count($valueSet);
+ if ($valueCount == 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ $valueAdjustor = $valueCount - 1;
+ if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
+ return self::$_errorCodes['na'];
+ }
+
+ $pos = array_search($value,$valueSet);
+ if ($pos === False) {
+ $pos = 0;
+ $testValue = $valueSet[0];
+ while ($testValue < $value) {
+ $testValue = $valueSet[++$pos];
+ }
+ --$pos;
+ $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
+ }
+
+ return round($pos / $valueAdjustor,$significance);
+ } // function PERCENTRANK()
+
+
+ private static function _checkTrendArrays(&$array1,&$array2) {
+ if (!is_array($array1)) { $array1 = array($array1); }
+ if (!is_array($array2)) { $array2 = array($array2); }
+
+ $array1 = self::flattenArray($array1);
+ $array2 = self::flattenArray($array2);
+ foreach($array1 as $key => $value) {
+ if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
+ unset($array1[$key]);
+ unset($array2[$key]);
+ }
+ }
+ foreach($array2 as $key => $value) {
+ if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
+ unset($array1[$key]);
+ unset($array2[$key]);
+ }
+ }
+ $array1 = array_merge($array1);
+ $array2 = array_merge($array2);
+
+ return True;
+ } // function _checkTrendArrays()
+
+
+ /**
+ * INTERCEPT
+ *
+ * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function INTERCEPT($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getIntersect();
+ } // function INTERCEPT()
+
+
+ /**
+ * RSQ
+ *
+ * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function RSQ($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getGoodnessOfFit();
+ } // function RSQ()
+
+
+ /**
+ * SLOPE
+ *
+ * Returns the slope of the linear regression line through data points in known_y's and known_x's.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function SLOPE($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getSlope();
+ } // function SLOPE()
+
+
+ /**
+ * STEYX
+ *
+ * Returns the standard error of the predicted y-value for each x in the regression.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function STEYX($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getStdevOfResiduals();
+ } // function STEYX()
+
+
+ /**
+ * COVAR
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function COVAR($yValues,$xValues) {
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getCovariance();
+ } // function COVAR()
+
+
+ /**
+ * CORREL
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function CORREL($yValues,$xValues=null) {
+ if ((is_null($xValues)) || (!is_array($yValues)) || (!is_array($xValues))) {
+ return self::$_errorCodes['value'];
+ }
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getCorrelation();
+ } // function CORREL()
+
+
+ /**
+ * LINEST
+ *
+ * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
+ * and then returns an array that describes the line.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @param boolean A logical value specifying whether to return additional regression statistics.
+ * @return array
+ */
+ public static function LINEST($yValues,$xValues=null,$const=True,$stats=False) {
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+ $stats = (is_null($stats)) ? False : (boolean) self::flattenSingleValue($stats);
+ if (is_null($xValues)) $xValues = range(1,count(self::flattenArray($yValues)));
+
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return 0;
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
+ if ($stats) {
+ return array( array( $bestFitLinear->getSlope(),
+ $bestFitLinear->getSlopeSE(),
+ $bestFitLinear->getGoodnessOfFit(),
+ $bestFitLinear->getF(),
+ $bestFitLinear->getSSRegression(),
+ ),
+ array( $bestFitLinear->getIntersect(),
+ $bestFitLinear->getIntersectSE(),
+ $bestFitLinear->getStdevOfResiduals(),
+ $bestFitLinear->getDFResiduals(),
+ $bestFitLinear->getSSResiduals()
+ )
+ );
+ } else {
+ return array( $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect()
+ );
+ }
+ } // function LINEST()
+
+
+ /**
+ * LOGEST
+ *
+ * Calculates an exponential curve that best fits the X and Y data series,
+ * and then returns an array that describes the line.
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @param boolean A logical value specifying whether to return additional regression statistics.
+ * @return array
+ */
+ public static function LOGEST($yValues,$xValues=null,$const=True,$stats=False) {
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+ $stats = (is_null($stats)) ? False : (boolean) self::flattenSingleValue($stats);
+ if (is_null($xValues)) $xValues = range(1,count(self::flattenArray($yValues)));
+
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ foreach($yValues as $value) {
+ if ($value <= 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ }
+
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return 1;
+ }
+
+ $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
+ if ($stats) {
+ return array( array( $bestFitExponential->getSlope(),
+ $bestFitExponential->getSlopeSE(),
+ $bestFitExponential->getGoodnessOfFit(),
+ $bestFitExponential->getF(),
+ $bestFitExponential->getSSRegression(),
+ ),
+ array( $bestFitExponential->getIntersect(),
+ $bestFitExponential->getIntersectSE(),
+ $bestFitExponential->getStdevOfResiduals(),
+ $bestFitExponential->getDFResiduals(),
+ $bestFitExponential->getSSResiduals()
+ )
+ );
+ } else {
+ return array( $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect()
+ );
+ }
+ } // function LOGEST()
+
+
+ /**
+ * FORECAST
+ *
+ * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
+ *
+ * @param float Value of X for which we want to find Y
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @return float
+ */
+ public static function FORECAST($xValue,$yValues,$xValues) {
+ $xValue = self::flattenSingleValue($xValue);
+ if (!is_numeric($xValue)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (!self::_checkTrendArrays($yValues,$xValues)) {
+ return self::$_errorCodes['value'];
+ }
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
+ return self::$_errorCodes['na'];
+ } elseif ($yValueCount == 1) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
+ return $bestFitLinear->getValueOfYForX($xValue);
+ } // function FORECAST()
+
+
+ /**
+ * TREND
+ *
+ * Returns values along a linear trend
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param array of mixed Values of X for which we want to find Y
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @return array of float
+ */
+ public static function TREND($yValues,$xValues=array(),$newValues=array(),$const=True) {
+ $yValues = self::flattenArray($yValues);
+ $xValues = self::flattenArray($xValues);
+ $newValues = self::flattenArray($newValues);
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+
+ $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
+ if (count($newValues) == 0) {
+ $newValues = $bestFitLinear->getXValues();
+ }
+
+ $returnArray = array();
+ foreach($newValues as $xValue) {
+ $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
+ }
+
+ return $returnArray;
+ } // function TREND()
+
+
+ /**
+ * GROWTH
+ *
+ * Returns values along a predicted emponential trend
+ *
+ * @param array of mixed Data Series Y
+ * @param array of mixed Data Series X
+ * @param array of mixed Values of X for which we want to find Y
+ * @param boolean A logical value specifying whether to force the intersect to equal 0.
+ * @return array of float
+ */
+ public static function GROWTH($yValues,$xValues=array(),$newValues=array(),$const=True) {
+ $yValues = self::flattenArray($yValues);
+ $xValues = self::flattenArray($xValues);
+ $newValues = self::flattenArray($newValues);
+ $const = (is_null($const)) ? True : (boolean) self::flattenSingleValue($const);
+
+ $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
+ if (count($newValues) == 0) {
+ $newValues = $bestFitExponential->getXValues();
+ }
+
+ $returnArray = array();
+ foreach($newValues as $xValue) {
+ $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
+ }
+
+ return $returnArray;
+ } // function GROWTH()
+
+
+ private static function _romanCut($num, $n) {
+ return ($num - ($num % $n ) ) / $n;
+ } // function _romanCut()
+
+
+ public static function ROMAN($aValue, $style=0) {
+ $aValue = (integer) self::flattenSingleValue($aValue);
+ $style = (is_null($style)) ? 0 : (integer) self::flattenSingleValue($style);
+ if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
+ return self::$_errorCodes['value'];
+ }
+ if ($aValue == 0) {
+ return '';
+ }
+
+ $mill = Array('', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM');
+ $cent = Array('', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');
+ $tens = Array('', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');
+ $ones = Array('', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
+
+ $roman = '';
+ while ($aValue > 5999) {
+ $roman .= 'M';
+ $aValue -= 1000;
+ }
+ $m = self::_romanCut($aValue, 1000); $aValue %= 1000;
+ $c = self::_romanCut($aValue, 100); $aValue %= 100;
+ $t = self::_romanCut($aValue, 10); $aValue %= 10;
+
+ return $roman.$mill[$m].$cent[$c].$tens[$t].$ones[$aValue];
+ } // function ROMAN()
+
+
+ /**
+ * SUBTOTAL
+ *
+ * Returns a subtotal in a list or database.
+ *
+ * @param int the number 1 to 11 that specifies which function to
+ * use in calculating subtotals within a list.
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function SUBTOTAL() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $subtotal = array_shift($aArgs);
+
+ if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
+ switch($subtotal) {
+ case 1 :
+ return self::AVERAGE($aArgs);
+ break;
+ case 2 :
+ return self::COUNT($aArgs);
+ break;
+ case 3 :
+ return self::COUNTA($aArgs);
+ break;
+ case 4 :
+ return self::MAX($aArgs);
+ break;
+ case 5 :
+ return self::MIN($aArgs);
+ break;
+ case 6 :
+ return self::PRODUCT($aArgs);
+ break;
+ case 7 :
+ return self::STDEV($aArgs);
+ break;
+ case 8 :
+ return self::STDEVP($aArgs);
+ break;
+ case 9 :
+ return self::SUM($aArgs);
+ break;
+ case 10 :
+ return self::VARFunc($aArgs);
+ break;
+ case 11 :
+ return self::VARP($aArgs);
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function SUBTOTAL()
+
+
+ /**
+ * SQRTPI
+ *
+ * Returns the square root of (number * pi).
+ *
+ * @param float $number Number
+ * @return float Square Root of Number * Pi
+ */
+ public static function SQRTPI($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ if ($number < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return sqrt($number * M_PI) ;
+ }
+ return self::$_errorCodes['value'];
+ } // function SQRTPI()
+
+
+ /**
+ * FACT
+ *
+ * Returns the factorial of a number.
+ *
+ * @param float $factVal Factorial Value
+ * @return int Factorial
+ */
+ public static function FACT($factVal) {
+ $factVal = self::flattenSingleValue($factVal);
+
+ if (is_numeric($factVal)) {
+ if ($factVal < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $factLoop = floor($factVal);
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ if ($factVal > $factLoop) {
+ return self::$_errorCodes['num'];
+ }
+ }
+
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ }
+ return $factorial ;
+ }
+ return self::$_errorCodes['value'];
+ } // function FACT()
+
+
+ /**
+ * FACTDOUBLE
+ *
+ * Returns the double factorial of a number.
+ *
+ * @param float $factVal Factorial Value
+ * @return int Double Factorial
+ */
+ public static function FACTDOUBLE($factVal) {
+ $factLoop = floor(self::flattenSingleValue($factVal));
+
+ if (is_numeric($factLoop)) {
+ if ($factVal < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ --$factLoop;
+ }
+ return $factorial ;
+ }
+ return self::$_errorCodes['value'];
+ } // function FACTDOUBLE()
+
+
+ /**
+ * MULTINOMIAL
+ *
+ * Returns the ratio of the factorial of a sum of values to the product of factorials.
+ *
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function MULTINOMIAL() {
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ $summer = 0;
+ $divisor = 1;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ if ($arg < 1) {
+ return self::$_errorCodes['num'];
+ }
+ $summer += floor($arg);
+ $divisor *= self::FACT($arg);
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+
+ // Return
+ if ($summer > 0) {
+ $summer = self::FACT($summer);
+ return $summer / $divisor;
+ }
+ return 0;
+ } // function MULTINOMIAL()
+
+
+ /**
+ * CEILING
+ *
+ * Returns number rounded up, away from zero, to the nearest multiple of significance.
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @return float Rounded Number
+ */
+ public static function CEILING($number,$significance=null) {
+ $number = self::flattenSingleValue($number);
+ $significance = self::flattenSingleValue($significance);
+
+ if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
+ $significance = $number/abs($number);
+ }
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if (self::SIGN($number) == self::SIGN($significance)) {
+ if ($significance == 0.0) {
+ return 0;
+ }
+ return ceil($number / $significance) * $significance;
+ } else {
+ return self::$_errorCodes['num'];
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function CEILING()
+
+
+ /**
+ * EVEN
+ *
+ * Returns number rounded up to the nearest even integer.
+ *
+ * @param float $number Number to round
+ * @return int Rounded Number
+ */
+ public static function EVEN($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ $significance = 2 * self::SIGN($number);
+ return self::CEILING($number,$significance);
+ }
+ return self::$_errorCodes['value'];
+ } // function EVEN()
+
+
+ /**
+ * ODD
+ *
+ * Returns number rounded up to the nearest odd integer.
+ *
+ * @param float $number Number to round
+ * @return int Rounded Number
+ */
+ public static function ODD($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ $significance = self::SIGN($number);
+ if ($significance == 0) {
+ return 1;
+ }
+ $result = self::CEILING($number,$significance);
+ if (self::IS_EVEN($result)) {
+ $result += $significance;
+ }
+ return $result;
+ }
+ return self::$_errorCodes['value'];
+ } // function ODD()
+
+
+ /**
+ * INTVALUE
+ *
+ * Casts a floating point value to an integer
+ *
+ * @param float $number Number to cast to an integer
+ * @return integer Integer value
+ */
+ public static function INTVALUE($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ return (int) floor($number);
+ }
+ return self::$_errorCodes['value'];
+ } // function INTVALUE()
+
+
+ /**
+ * ROUNDUP
+ *
+ * Rounds a number up to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function ROUNDUP($number,$digits) {
+ $number = self::flattenSingleValue($number);
+ $digits = self::flattenSingleValue($digits);
+
+ if ((is_numeric($number)) && (is_numeric($digits))) {
+ $significance = pow(10,$digits);
+ if ($number < 0.0) {
+ return floor($number * $significance) / $significance;
+ } else {
+ return ceil($number * $significance) / $significance;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function ROUNDUP()
+
+
+ /**
+ * ROUNDDOWN
+ *
+ * Rounds a number down to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function ROUNDDOWN($number,$digits) {
+ $number = self::flattenSingleValue($number);
+ $digits = self::flattenSingleValue($digits);
+
+ if ((is_numeric($number)) && (is_numeric($digits))) {
+ $significance = pow(10,$digits);
+ if ($number < 0.0) {
+ return ceil($number * $significance) / $significance;
+ } else {
+ return floor($number * $significance) / $significance;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function ROUNDDOWN()
+
+
+ /**
+ * MROUND
+ *
+ * Rounds a number to the nearest multiple of a specified value
+ *
+ * @param float $number Number to round
+ * @param int $multiple Multiple to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function MROUND($number,$multiple) {
+ $number = self::flattenSingleValue($number);
+ $multiple = self::flattenSingleValue($multiple);
+
+ if ((is_numeric($number)) && (is_numeric($multiple))) {
+ if ($multiple == 0) {
+ return 0;
+ }
+ if ((self::SIGN($number)) == (self::SIGN($multiple))) {
+ $multiplier = 1 / $multiple;
+ return round($number * $multiplier) / $multiplier;
+ }
+ return self::$_errorCodes['num'];
+ }
+ return self::$_errorCodes['value'];
+ } // function MROUND()
+
+
+ /**
+ * SIGN
+ *
+ * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
+ * if the number is 0, and -1 if the number is negative.
+ *
+ * @param float $number Number to round
+ * @return int sign value
+ */
+ public static function SIGN($number) {
+ $number = self::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ if ($number == 0.0) {
+ return 0;
+ }
+ return $number / abs($number);
+ }
+ return self::$_errorCodes['value'];
+ } // function SIGN()
+
+
+ /**
+ * FLOOR
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @return float Rounded Number
+ */
+ public static function FLOOR($number,$significance=null) {
+ $number = self::flattenSingleValue($number);
+ $significance = self::flattenSingleValue($significance);
+
+ if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
+ $significance = $number/abs($number);
+ }
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if ((float) $significance == 0.0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+ if (self::SIGN($number) == self::SIGN($significance)) {
+ return floor($number / $significance) * $significance;
+ } else {
+ return self::$_errorCodes['num'];
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function FLOOR()
+
+
+ /**
+ * PERMUT
+ *
+ * Returns the number of permutations for a given number of objects that can be
+ * selected from number objects. A permutation is any set or subset of objects or
+ * events where internal order is significant. Permutations are different from
+ * combinations, for which the internal order is not significant. Use this function
+ * for lottery-style probability calculations.
+ *
+ * @param int $numObjs Number of different objects
+ * @param int $numInSet Number of objects in each permutation
+ * @return int Number of permutations
+ */
+ public static function PERMUT($numObjs,$numInSet) {
+ $numObjs = self::flattenSingleValue($numObjs);
+ $numInSet = self::flattenSingleValue($numInSet);
+
+ if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
+ $numInSet = floor($numInSet);
+ if ($numObjs < $numInSet) {
+ return self::$_errorCodes['num'];
+ }
+ return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet));
+ }
+ return self::$_errorCodes['value'];
+ } // function PERMUT()
+
+
+ /**
+ * COMBIN
+ *
+ * Returns the number of combinations for a given number of items. Use COMBIN to
+ * determine the total possible number of groups for a given number of items.
+ *
+ * @param int $numObjs Number of different objects
+ * @param int $numInSet Number of objects in each combination
+ * @return int Number of combinations
+ */
+ public static function COMBIN($numObjs,$numInSet) {
+ $numObjs = self::flattenSingleValue($numObjs);
+ $numInSet = self::flattenSingleValue($numInSet);
+
+ if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
+ if ($numObjs < $numInSet) {
+ return self::$_errorCodes['num'];
+ } elseif ($numInSet < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
+ }
+ return self::$_errorCodes['value'];
+ } // function COMBIN()
+
+
+ /**
+ * SERIESSUM
+ *
+ * Returns the sum of a power series
+ *
+ * @param float $x Input value to the power series
+ * @param float $n Initial power to which you want to raise $x
+ * @param float $m Step by which to increase $n for each term in the series
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function SERIESSUM() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+
+ $x = array_shift($aArgs);
+ $n = array_shift($aArgs);
+ $m = array_shift($aArgs);
+
+ if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
+ // Calculate
+ $i = 0;
+ foreach($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += $arg * pow($x,$n + ($m * $i++));
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Return
+ return $returnValue;
+ }
+ return self::$_errorCodes['value'];
+ } // function SERIESSUM()
+
+
+ /**
+ * STANDARDIZE
+ *
+ * Returns a normalized value from a distribution characterized by mean and standard_dev.
+ *
+ * @param float $value Value to normalize
+ * @param float $mean Mean Value
+ * @param float $stdDev Standard Deviation
+ * @return float Standardized value
+ */
+ public static function STANDARDIZE($value,$mean,$stdDev) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if ($stdDev <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ return ($value - $mean) / $stdDev ;
+ }
+ return self::$_errorCodes['value'];
+ } // function STANDARDIZE()
+
+
+ //
+ // Private method to return an array of the factors of the input value
+ //
+ private static function _factors($value) {
+ $startVal = floor(sqrt($value));
+
+ $factorArray = array();
+ for ($i = $startVal; $i > 1; --$i) {
+ if (($value % $i) == 0) {
+ $factorArray = array_merge($factorArray,self::_factors($value / $i));
+ $factorArray = array_merge($factorArray,self::_factors($i));
+ if ($i <= sqrt($value)) {
+ break;
+ }
+ }
+ }
+ if (count($factorArray) > 0) {
+ rsort($factorArray);
+ return $factorArray;
+ } else {
+ return array((integer) $value);
+ }
+ } // function _factors()
+
+
+ /**
+ * LCM
+ *
+ * Returns the lowest common multiplier of a series of numbers
+ *
+ * @param $array Values to calculate the Lowest Common Multiplier
+ * @return int Lowest Common Multiplier
+ */
+ public static function LCM() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ $returnValue = 1;
+ $allPoweredFactors = array();
+ foreach($aArgs as $value) {
+ if (!is_numeric($value)) {
+ return self::$_errorCodes['value'];
+ }
+ if ($value == 0) {
+ return 0;
+ } elseif ($value < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $myFactors = self::_factors(floor($value));
+ $myCountedFactors = array_count_values($myFactors);
+ $myPoweredFactors = array();
+ foreach($myCountedFactors as $myCountedFactor => $myCountedPower) {
+ $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor,$myCountedPower);
+ }
+ foreach($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
+ if (array_key_exists($myPoweredValue,$allPoweredFactors)) {
+ if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ } else {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ }
+ }
+ foreach($allPoweredFactors as $allPoweredFactor) {
+ $returnValue *= (integer) $allPoweredFactor;
+ }
+ return $returnValue;
+ } // function LCM()
+
+
+ /**
+ * GCD
+ *
+ * Returns the greatest common divisor of a series of numbers
+ *
+ * @param $array Values to calculate the Greatest Common Divisor
+ * @return int Greatest Common Divisor
+ */
+ public static function GCD() {
+ $aArgs = self::flattenArray(func_get_args());
+
+ $returnValue = 1;
+ $allPoweredFactors = array();
+ foreach($aArgs as $value) {
+ if ($value == 0) {
+ break;
+ }
+ $myFactors = self::_factors($value);
+ $myCountedFactors = array_count_values($myFactors);
+ $allValuesFactors[] = $myCountedFactors;
+ }
+ $allValuesCount = count($allValuesFactors);
+ $mergedArray = $allValuesFactors[0];
+ for ($i=1;$i < $allValuesCount; ++$i) {
+ $mergedArray = array_intersect_key($mergedArray,$allValuesFactors[$i]);
+ }
+ $mergedArrayValues = count($mergedArray);
+ if ($mergedArrayValues == 0) {
+ return $returnValue;
+ } elseif ($mergedArrayValues > 1) {
+ foreach($mergedArray as $mergedKey => $mergedValue) {
+ foreach($allValuesFactors as $highestPowerTest) {
+ foreach($highestPowerTest as $testKey => $testValue) {
+ if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
+ $mergedArray[$mergedKey] = $testValue;
+ $mergedValue = $testValue;
+ }
+ }
+ }
+ }
+
+ $returnValue = 1;
+ foreach($mergedArray as $key => $value) {
+ $returnValue *= pow($key,$value);
+ }
+ return $returnValue;
+ } else {
+ $keys = array_keys($mergedArray);
+ $key = $keys[0];
+ $value = $mergedArray[$key];
+ foreach($allValuesFactors as $testValue) {
+ foreach($testValue as $mergedKey => $mergedValue) {
+ if (($mergedKey == $key) && ($mergedValue < $value)) {
+ $value = $mergedValue;
+ }
+ }
+ }
+ return pow($key,$value);
+ }
+ } // function GCD()
+
+
+ /**
+ * BINOMDIST
+ *
+ * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
+ * a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
+ * when trials are independent, and when the probability of success is constant throughout the
+ * experiment. For example, BINOMDIST can calculate the probability that two of the next three
+ * babies born are male.
+ *
+ * @param float $value Number of successes in trials
+ * @param float $trials Number of trials
+ * @param float $probability Probability of success on each trial
+ * @param boolean $cumulative
+ * @return float
+ *
+ * @todo Cumulative distribution function
+ *
+ */
+ public static function BINOMDIST($value, $trials, $probability, $cumulative) {
+ $value = floor(self::flattenSingleValue($value));
+ $trials = floor(self::flattenSingleValue($trials));
+ $probability = self::flattenSingleValue($probability);
+
+ if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
+ if (($value < 0) || ($value > $trials)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ $summer = 0;
+ for ($i = 0; $i <= $value; ++$i) {
+ $summer += self::COMBIN($trials,$i) * pow($probability,$i) * pow(1 - $probability,$trials - $i);
+ }
+ return $summer;
+ } else {
+ return self::COMBIN($trials,$value) * pow($probability,$value) * pow(1 - $probability,$trials - $value) ;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function BINOMDIST()
+
+
+ /**
+ * NEGBINOMDIST
+ *
+ * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
+ * there will be number_f failures before the number_s-th success, when the constant
+ * probability of a success is probability_s. This function is similar to the binomial
+ * distribution, except that the number of successes is fixed, and the number of trials is
+ * variable. Like the binomial, trials are assumed to be independent.
+ *
+ * @param float $failures Number of Failures
+ * @param float $successes Threshold number of Successes
+ * @param float $probability Probability of success on each trial
+ * @return float
+ *
+ */
+ public static function NEGBINOMDIST($failures, $successes, $probability) {
+ $failures = floor(self::flattenSingleValue($failures));
+ $successes = floor(self::flattenSingleValue($successes));
+ $probability = self::flattenSingleValue($probability);
+
+ if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
+ if (($failures < 0) || ($successes < 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ if (($failures + $successes - 1) <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ }
+ return (self::COMBIN($failures + $successes - 1,$successes - 1)) * (pow($probability,$successes)) * (pow(1 - $probability,$failures)) ;
+ }
+ return self::$_errorCodes['value'];
+ } // function NEGBINOMDIST()
+
+
+ /**
+ * CRITBINOM
+ *
+ * Returns the smallest value for which the cumulative binomial distribution is greater
+ * than or equal to a criterion value
+ *
+ * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
+ *
+ * @param float $trials number of Bernoulli trials
+ * @param float $probability probability of a success on each trial
+ * @param float $alpha criterion value
+ * @return int
+ *
+ * @todo Warning. This implementation differs from the algorithm detailed on the MS
+ * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
+ * This eliminates a potential endless loop error, but may have an adverse affect on the
+ * accuracy of the function (although all my tests have so far returned correct results).
+ *
+ */
+ public static function CRITBINOM($trials, $probability, $alpha) {
+ $trials = floor(self::flattenSingleValue($trials));
+ $probability = self::flattenSingleValue($probability);
+ $alpha = self::flattenSingleValue($alpha);
+
+ if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
+ if ($trials < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($alpha < 0) || ($alpha > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($alpha <= 0.5) {
+ $t = sqrt(log(1 / ($alpha * $alpha)));
+ $trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t));
+ } else {
+ $t = sqrt(log(1 / pow(1 - $alpha,2)));
+ $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
+ }
+ $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
+ if ($Guess < 0) {
+ $Guess = 0;
+ } elseif ($Guess > $trials) {
+ $Guess = $trials;
+ }
+
+ $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
+ $EssentiallyZero = 10e-12;
+
+ $m = floor($trials * $probability);
+ ++$TotalUnscaledProbability;
+ if ($m == $Guess) { ++$UnscaledPGuess; }
+ if ($m <= $Guess) { ++$UnscaledCumPGuess; }
+
+ $PreviousValue = 1;
+ $Done = False;
+ $k = $m + 1;
+ while ((!$Done) && ($k <= $trials)) {
+ $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
+ $TotalUnscaledProbability += $CurrentValue;
+ if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
+ if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
+ if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
+ $PreviousValue = $CurrentValue;
+ ++$k;
+ }
+
+ $PreviousValue = 1;
+ $Done = False;
+ $k = $m - 1;
+ while ((!$Done) && ($k >= 0)) {
+ $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
+ $TotalUnscaledProbability += $CurrentValue;
+ if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
+ if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
+ if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
+ $PreviousValue = $CurrentValue;
+ --$k;
+ }
+
+ $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
+ $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
+
+// $CumPGuessMinus1 = $CumPGuess - $PGuess;
+ $CumPGuessMinus1 = $CumPGuess - 1;
+
+ while (True) {
+ if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
+ return $Guess;
+ } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
+ $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
+ $CumPGuessMinus1 = $CumPGuess;
+ $CumPGuess = $CumPGuess + $PGuessPlus1;
+ $PGuess = $PGuessPlus1;
+ ++$Guess;
+ } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
+ $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
+ $CumPGuess = $CumPGuessMinus1;
+ $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
+ $PGuess = $PGuessMinus1;
+ --$Guess;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function CRITBINOM()
+
+
+ /**
+ * CHIDIST
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param float $value Value for the function
+ * @param float $degrees degrees of freedom
+ * @return float
+ */
+ public static function CHIDIST($value, $degrees) {
+ $value = self::flattenSingleValue($value);
+ $degrees = floor(self::flattenSingleValue($degrees));
+
+ if ((is_numeric($value)) && (is_numeric($degrees))) {
+ if ($degrees < 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($value < 0) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+ return self::$_errorCodes['num'];
+ }
+ return 1 - (self::_incompleteGamma($degrees/2,$value/2) / self::_gamma($degrees/2));
+ }
+ return self::$_errorCodes['value'];
+ } // function CHIDIST()
+
+
+ /**
+ * CHIINV
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param float $probability Probability for the function
+ * @param float $degrees degrees of freedom
+ * @return float
+ */
+ public static function CHIINV($probability, $degrees) {
+ $probability = self::flattenSingleValue($probability);
+ $degrees = floor(self::flattenSingleValue($degrees));
+
+ if ((is_numeric($probability)) && (is_numeric($degrees))) {
+
+ $xLo = 100;
+ $xHi = 0;
+
+ $x = $xNew = 1;
+ $dx = 1;
+ $i = 0;
+
+ while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::CHIDIST($x, $degrees);
+ $error = $result - $probability;
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+ // Avoid division by zero
+ if ($result != 0.0) {
+ $dx = $error / $result;
+ $xNew = $x - $dx;
+ }
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return round($x,12);
+ }
+ return self::$_errorCodes['value'];
+ } // function CHIINV()
+
+
+ /**
+ * EXPONDIST
+ *
+ * Returns the exponential distribution. Use EXPONDIST to model the time between events,
+ * such as how long an automated bank teller takes to deliver cash. For example, you can
+ * use EXPONDIST to determine the probability that the process takes at most 1 minute.
+ *
+ * @param float $value Value of the function
+ * @param float $lambda The parameter value
+ * @param boolean $cumulative
+ * @return float
+ */
+ public static function EXPONDIST($value, $lambda, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $lambda = self::flattenSingleValue($lambda);
+ $cumulative = self::flattenSingleValue($cumulative);
+
+ if ((is_numeric($value)) && (is_numeric($lambda))) {
+ if (($value < 0) || ($lambda < 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return 1 - exp(0-$value*$lambda);
+ } else {
+ return $lambda * exp(0-$value*$lambda);
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function EXPONDIST()
+
+
+ /**
+ * FISHER
+ *
+ * Returns the Fisher transformation at x. This transformation produces a function that
+ * is normally distributed rather than skewed. Use this function to perform hypothesis
+ * testing on the correlation coefficient.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function FISHER($value) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_numeric($value)) {
+ if (($value <= -1) || ($value >= 1)) {
+ return self::$_errorCodes['num'];
+ }
+ return 0.5 * log((1+$value)/(1-$value));
+ }
+ return self::$_errorCodes['value'];
+ } // function FISHER()
+
+
+ /**
+ * FISHERINV
+ *
+ * Returns the inverse of the Fisher transformation. Use this transformation when
+ * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
+ * FISHERINV(y) = x.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function FISHERINV($value) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_numeric($value)) {
+ return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
+ }
+ return self::$_errorCodes['value'];
+ } // function FISHERINV()
+
+
+ // Function cache for _logBeta function
+ private static $_logBetaCache_p = 0.0;
+ private static $_logBetaCache_q = 0.0;
+ private static $_logBetaCache_result = 0.0;
+
+ /**
+ * The natural logarithm of the beta function.
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ * @author Jaco van Kooten
+ */
+ private static function _logBeta($p, $q) {
+ if ($p != self::$_logBetaCache_p || $q != self::$_logBetaCache_q) {
+ self::$_logBetaCache_p = $p;
+ self::$_logBetaCache_q = $q;
+ if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
+ self::$_logBetaCache_result = 0.0;
+ } else {
+ self::$_logBetaCache_result = self::_logGamma($p) + self::_logGamma($q) - self::_logGamma($p + $q);
+ }
+ }
+ return self::$_logBetaCache_result;
+ } // function _logBeta()
+
+
+ /**
+ * Evaluates of continued fraction part of incomplete beta function.
+ * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
+ * @author Jaco van Kooten
+ */
+ private static function _betaFraction($x, $p, $q) {
+ $c = 1.0;
+ $sum_pq = $p + $q;
+ $p_plus = $p + 1.0;
+ $p_minus = $p - 1.0;
+ $h = 1.0 - $sum_pq * $x / $p_plus;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $frac = $h;
+ $m = 1;
+ $delta = 0.0;
+ while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION ) {
+ $m2 = 2 * $m;
+ // even index for d
+ $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < XMININ) {
+ $c = XMININ;
+ }
+ $frac *= $h * $c;
+ // odd index for d
+ $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < XMININ) {
+ $c = XMININ;
+ }
+ $delta = $h * $c;
+ $frac *= $delta;
+ ++$m;
+ }
+ return $frac;
+ } // function _betaFraction()
+
+
+ /**
+ * logGamma function
+ *
+ * @version 1.1
+ * @author Jaco van Kooten
+ *
+ * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
+ *
+ * The natural logarithm of the gamma function.
+ * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
+ * Applied Mathematics Division
+ * Argonne National Laboratory
+ * Argonne, IL 60439
+ *
+ * References:
+ *
+ *
W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
+ * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
+ *
K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
+ *
Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
+ *
+ *
+ *
+ * From the original documentation:
+ *
+ *
+ * This routine calculates the LOG(GAMMA) function for a positive real argument X.
+ * Computation is based on an algorithm outlined in references 1 and 2.
+ * The program uses rational functions that theoretically approximate LOG(GAMMA)
+ * to at least 18 significant decimal digits. The approximation for X > 12 is from
+ * reference 3, while approximations for X < 12.0 are similar to those in reference
+ * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
+ * the compiler, the intrinsic functions, and proper selection of the
+ * machine-dependent constants.
+ *
+ *
+ * Error returns:
+ * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
+ * The computation is believed to be free of underflow and overflow.
+ *
+ * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
+ */
+
+ // Function cache for logGamma
+ private static $_logGammaCache_result = 0.0;
+ private static $_logGammaCache_x = 0.0;
+
+ private static function _logGamma($x) {
+ // Log Gamma related constants
+ static $lg_d1 = -0.5772156649015328605195174;
+ static $lg_d2 = 0.4227843350984671393993777;
+ static $lg_d4 = 1.791759469228055000094023;
+
+ static $lg_p1 = array( 4.945235359296727046734888,
+ 201.8112620856775083915565,
+ 2290.838373831346393026739,
+ 11319.67205903380828685045,
+ 28557.24635671635335736389,
+ 38484.96228443793359990269,
+ 26377.48787624195437963534,
+ 7225.813979700288197698961 );
+ static $lg_p2 = array( 4.974607845568932035012064,
+ 542.4138599891070494101986,
+ 15506.93864978364947665077,
+ 184793.2904445632425417223,
+ 1088204.76946882876749847,
+ 3338152.967987029735917223,
+ 5106661.678927352456275255,
+ 3074109.054850539556250927 );
+ static $lg_p4 = array( 14745.02166059939948905062,
+ 2426813.369486704502836312,
+ 121475557.4045093227939592,
+ 2663432449.630976949898078,
+ 29403789566.34553899906876,
+ 170266573776.5398868392998,
+ 492612579337.743088758812,
+ 560625185622.3951465078242 );
+
+ static $lg_q1 = array( 67.48212550303777196073036,
+ 1113.332393857199323513008,
+ 7738.757056935398733233834,
+ 27639.87074403340708898585,
+ 54993.10206226157329794414,
+ 61611.22180066002127833352,
+ 36351.27591501940507276287,
+ 8785.536302431013170870835 );
+ static $lg_q2 = array( 183.0328399370592604055942,
+ 7765.049321445005871323047,
+ 133190.3827966074194402448,
+ 1136705.821321969608938755,
+ 5267964.117437946917577538,
+ 13467014.54311101692290052,
+ 17827365.30353274213975932,
+ 9533095.591844353613395747 );
+ static $lg_q4 = array( 2690.530175870899333379843,
+ 639388.5654300092398984238,
+ 41355999.30241388052042842,
+ 1120872109.61614794137657,
+ 14886137286.78813811542398,
+ 101680358627.2438228077304,
+ 341747634550.7377132798597,
+ 446315818741.9713286462081 );
+
+ static $lg_c = array( -0.001910444077728,
+ 8.4171387781295e-4,
+ -5.952379913043012e-4,
+ 7.93650793500350248e-4,
+ -0.002777777777777681622553,
+ 0.08333333333333333331554247,
+ 0.0057083835261 );
+
+ // Rough estimate of the fourth root of logGamma_xBig
+ static $lg_frtbig = 2.25e76;
+ static $pnt68 = 0.6796875;
+
+
+ if ($x == self::$_logGammaCache_x) {
+ return self::$_logGammaCache_result;
+ }
+ $y = $x;
+ if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
+ if ($y <= EPS) {
+ $res = -log(y);
+ } elseif ($y <= 1.5) {
+ // ---------------------
+ // EPS .LT. X .LE. 1.5
+ // ---------------------
+ if ($y < $pnt68) {
+ $corr = -log($y);
+ $xm1 = $y;
+ } else {
+ $corr = 0.0;
+ $xm1 = $y - 1.0;
+ }
+ if ($y <= 0.5 || $y >= $pnt68) {
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm1 + $lg_p1[$i];
+ $xden = $xden * $xm1 + $lg_q1[$i];
+ }
+ $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
+ } else {
+ $xm2 = $y - 1.0;
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + $lg_p2[$i];
+ $xden = $xden * $xm2 + $lg_q2[$i];
+ }
+ $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
+ }
+ } elseif ($y <= 4.0) {
+ // ---------------------
+ // 1.5 .LT. X .LE. 4.0
+ // ---------------------
+ $xm2 = $y - 2.0;
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + $lg_p2[$i];
+ $xden = $xden * $xm2 + $lg_q2[$i];
+ }
+ $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
+ } elseif ($y <= 12.0) {
+ // ----------------------
+ // 4.0 .LT. X .LE. 12.0
+ // ----------------------
+ $xm4 = $y - 4.0;
+ $xden = -1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm4 + $lg_p4[$i];
+ $xden = $xden * $xm4 + $lg_q4[$i];
+ }
+ $res = $lg_d4 + $xm4 * ($xnum / $xden);
+ } else {
+ // ---------------------------------
+ // Evaluate for argument .GE. 12.0
+ // ---------------------------------
+ $res = 0.0;
+ if ($y <= $lg_frtbig) {
+ $res = $lg_c[6];
+ $ysq = $y * $y;
+ for ($i = 0; $i < 6; ++$i)
+ $res = $res / $ysq + $lg_c[$i];
+ }
+ $res /= $y;
+ $corr = log($y);
+ $res = $res + log(SQRT2PI) - 0.5 * $corr;
+ $res += $y * ($corr - 1.0);
+ }
+ } else {
+ // --------------------------
+ // Return for bad arguments
+ // --------------------------
+ $res = MAX_VALUE;
+ }
+ // ------------------------------
+ // Final adjustments and return
+ // ------------------------------
+ self::$_logGammaCache_x = $x;
+ self::$_logGammaCache_result = $res;
+ return $res;
+ } // function _logGamma()
+
+
+ /**
+ * Beta function.
+ *
+ * @author Jaco van Kooten
+ *
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ */
+ private static function _beta($p, $q) {
+ if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
+ return 0.0;
+ } else {
+ return exp(self::_logBeta($p, $q));
+ }
+ } // function _beta()
+
+
+ /**
+ * Incomplete beta function
+ *
+ * @author Jaco van Kooten
+ * @author Paul Meagher
+ *
+ * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
+ * @param x require 0<=x<=1
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
+ */
+ private static function _incompleteBeta($x, $p, $q) {
+ if ($x <= 0.0) {
+ return 0.0;
+ } elseif ($x >= 1.0) {
+ return 1.0;
+ } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
+ return 0.0;
+ }
+ $beta_gam = exp((0 - self::_logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
+ if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
+ return $beta_gam * self::_betaFraction($x, $p, $q) / $p;
+ } else {
+ return 1.0 - ($beta_gam * self::_betaFraction(1 - $x, $q, $p) / $q);
+ }
+ } // function _incompleteBeta()
+
+
+ /**
+ * BETADIST
+ *
+ * Returns the beta distribution.
+ *
+ * @param float $value Value at which you want to evaluate the distribution
+ * @param float $alpha Parameter to the distribution
+ * @param float $beta Parameter to the distribution
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function BETADIST($value,$alpha,$beta,$rMin=0,$rMax=1) {
+ $value = self::flattenSingleValue($value);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+ $rMin = self::flattenSingleValue($rMin);
+ $rMax = self::flattenSingleValue($rMax);
+
+ if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
+ if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ $value -= $rMin;
+ $value /= ($rMax - $rMin);
+ return self::_incompleteBeta($value,$alpha,$beta);
+ }
+ return self::$_errorCodes['value'];
+ } // function BETADIST()
+
+
+ /**
+ * BETAINV
+ *
+ * Returns the inverse of the beta distribution.
+ *
+ * @param float $probability Probability at which you want to evaluate the distribution
+ * @param float $alpha Parameter to the distribution
+ * @param float $beta Parameter to the distribution
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function BETAINV($probability,$alpha,$beta,$rMin=0,$rMax=1) {
+ $probability = self::flattenSingleValue($probability);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+ $rMin = self::flattenSingleValue($rMin);
+ $rMax = self::flattenSingleValue($rMax);
+
+ if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
+ if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ $a = 0;
+ $b = 2;
+
+ $i = 0;
+ while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ $guess = ($a + $b) / 2;
+ $result = self::BETADIST($guess, $alpha, $beta);
+ if (($result == $probability) || ($result == 0)) {
+ $b = $a;
+ } elseif ($result > $probability) {
+ $b = $guess;
+ } else {
+ $a = $guess;
+ }
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return round($rMin + $guess * ($rMax - $rMin),12);
+ }
+ return self::$_errorCodes['value'];
+ } // function BETAINV()
+
+
+ //
+ // Private implementation of the incomplete Gamma function
+ //
+ private static function _incompleteGamma($a,$x) {
+ static $max = 32;
+ $summer = 0;
+ for ($n=0; $n<=$max; ++$n) {
+ $divisor = $a;
+ for ($i=1; $i<=$n; ++$i) {
+ $divisor *= ($a + $i);
+ }
+ $summer += (pow($x,$n) / $divisor);
+ }
+ return pow($x,$a) * exp(0-$x) * $summer;
+ } // function _incompleteGamma()
+
+
+ //
+ // Private implementation of the Gamma function
+ //
+ private static function _gamma($data) {
+ if ($data == 0.0) return 0;
+
+ static $p0 = 1.000000000190015;
+ static $p = array ( 1 => 76.18009172947146,
+ 2 => -86.50532032941677,
+ 3 => 24.01409824083091,
+ 4 => -1.231739572450155,
+ 5 => 1.208650973866179e-3,
+ 6 => -5.395239384953e-6
+ );
+
+ $y = $x = $data;
+ $tmp = $x + 5.5;
+ $tmp -= ($x + 0.5) * log($tmp);
+
+ $summer = $p0;
+ for ($j=1;$j<=6;++$j) {
+ $summer += ($p[$j] / ++$y);
+ }
+ return exp(0 - $tmp + log(SQRT2PI * $summer / $x));
+ } // function _gamma()
+
+
+ /**
+ * GAMMADIST
+ *
+ * Returns the gamma distribution.
+ *
+ * @param float $value Value at which you want to evaluate the distribution
+ * @param float $a Parameter to the distribution
+ * @param float $b Parameter to the distribution
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function GAMMADIST($value,$a,$b,$cumulative) {
+ $value = self::flattenSingleValue($value);
+ $a = self::flattenSingleValue($a);
+ $b = self::flattenSingleValue($b);
+
+ if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
+ if (($value < 0) || ($a <= 0) || ($b <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return self::_incompleteGamma($a,$value / $b) / self::_gamma($a);
+ } else {
+ return (1 / (pow($b,$a) * self::_gamma($a))) * pow($value,$a-1) * exp(0-($value / $b));
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function GAMMADIST()
+
+
+ /**
+ * GAMMAINV
+ *
+ * Returns the inverse of the beta distribution.
+ *
+ * @param float $probability Probability at which you want to evaluate the distribution
+ * @param float $alpha Parameter to the distribution
+ * @param float $beta Parameter to the distribution
+ * @return float
+ *
+ */
+ public static function GAMMAINV($probability,$alpha,$beta) {
+ $probability = self::flattenSingleValue($probability);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+
+ if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
+ if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+
+ $xLo = 0;
+ $xHi = $alpha * $beta * 5;
+
+ $x = $xNew = 1;
+ $error = $pdf = 0;
+ $dx = 1024;
+ $i = 0;
+
+ while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $error = self::GAMMADIST($x, $alpha, $beta, True) - $probability;
+ if ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+ $pdf = self::GAMMADIST($x, $alpha, $beta, False);
+ // Avoid division by zero
+ if ($pdf != 0.0) {
+ $dx = $error / $pdf;
+ $xNew = $x - $dx;
+ }
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return $x;
+ }
+ return self::$_errorCodes['value'];
+ } // function GAMMAINV()
+
+
+ /**
+ * GAMMALN
+ *
+ * Returns the natural logarithm of the gamma function.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function GAMMALN($value) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_numeric($value)) {
+ if ($value <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ return log(self::_gamma($value));
+ }
+ return self::$_errorCodes['value'];
+ } // function GAMMALN()
+
+
+ /**
+ * NORMDIST
+ *
+ * Returns the normal distribution for the specified mean and standard deviation. This
+ * function has a very wide range of applications in statistics, including hypothesis
+ * testing.
+ *
+ * @param float $value
+ * @param float $mean Mean Value
+ * @param float $stdDev Standard Deviation
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function NORMDIST($value, $mean, $stdDev, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if ($stdDev < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return 0.5 * (1 + self::_erfVal(($value - $mean) / ($stdDev * sqrt(2))));
+ } else {
+ return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean,2) / (2 * ($stdDev * $stdDev))));
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function NORMDIST()
+
+
+ /**
+ * NORMSDIST
+ *
+ * Returns the standard normal cumulative distribution function. The distribution has
+ * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
+ * table of standard normal curve areas.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function NORMSDIST($value) {
+ $value = self::flattenSingleValue($value);
+
+ return self::NORMDIST($value, 0, 1, True);
+ } // function NORMSDIST()
+
+
+ /**
+ * LOGNORMDIST
+ *
+ * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
+ * with parameters mean and standard_dev.
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function LOGNORMDIST($value, $mean, $stdDev) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::NORMSDIST((log($value) - $mean) / $stdDev);
+ }
+ return self::$_errorCodes['value'];
+ } // function LOGNORMDIST()
+
+
+ /***************************************************************************
+ * inverse_ncdf.php
+ * -------------------
+ * begin : Friday, January 16, 2004
+ * copyright : (C) 2004 Michael Nickerson
+ * email : nickersonm@yahoo.com
+ *
+ ***************************************************************************/
+ private static function _inverse_ncdf($p) {
+ // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
+ // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
+ // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
+ // I have not checked the accuracy of this implementation. Be aware that PHP
+ // will truncate the coeficcients to 14 digits.
+
+ // You have permission to use and distribute this function freely for
+ // whatever purpose you want, but please show common courtesy and give credit
+ // where credit is due.
+
+ // Input paramater is $p - probability - where 0 < p < 1.
+
+ // Coefficients in rational approximations
+ static $a = array( 1 => -3.969683028665376e+01,
+ 2 => 2.209460984245205e+02,
+ 3 => -2.759285104469687e+02,
+ 4 => 1.383577518672690e+02,
+ 5 => -3.066479806614716e+01,
+ 6 => 2.506628277459239e+00
+ );
+
+ static $b = array( 1 => -5.447609879822406e+01,
+ 2 => 1.615858368580409e+02,
+ 3 => -1.556989798598866e+02,
+ 4 => 6.680131188771972e+01,
+ 5 => -1.328068155288572e+01
+ );
+
+ static $c = array( 1 => -7.784894002430293e-03,
+ 2 => -3.223964580411365e-01,
+ 3 => -2.400758277161838e+00,
+ 4 => -2.549732539343734e+00,
+ 5 => 4.374664141464968e+00,
+ 6 => 2.938163982698783e+00
+ );
+
+ static $d = array( 1 => 7.784695709041462e-03,
+ 2 => 3.224671290700398e-01,
+ 3 => 2.445134137142996e+00,
+ 4 => 3.754408661907416e+00
+ );
+
+ // Define lower and upper region break-points.
+ $p_low = 0.02425; //Use lower region approx. below this
+ $p_high = 1 - $p_low; //Use upper region approx. above this
+
+ if (0 < $p && $p < $p_low) {
+ // Rational approximation for lower region.
+ $q = sqrt(-2 * log($p));
+ return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
+ (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ } elseif ($p_low <= $p && $p <= $p_high) {
+ // Rational approximation for central region.
+ $q = $p - 0.5;
+ $r = $q * $q;
+ return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
+ ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
+ } elseif ($p_high < $p && $p < 1) {
+ // Rational approximation for upper region.
+ $q = sqrt(-2 * log(1 - $p));
+ return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
+ (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ }
+ // If 0 < p < 1, return a null value
+ return self::$_errorCodes['null'];
+ } // function _inverse_ncdf()
+
+
+ private static function _inverse_ncdf2($prob) {
+ // Approximation of inverse standard normal CDF developed by
+ // B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
+
+ $a1 = 2.50662823884;
+ $a2 = -18.61500062529;
+ $a3 = 41.39119773534;
+ $a4 = -25.44106049637;
+
+ $b1 = -8.4735109309;
+ $b2 = 23.08336743743;
+ $b3 = -21.06224101826;
+ $b4 = 3.13082909833;
+
+ $c1 = 0.337475482272615;
+ $c2 = 0.976169019091719;
+ $c3 = 0.160797971491821;
+ $c4 = 2.76438810333863E-02;
+ $c5 = 3.8405729373609E-03;
+ $c6 = 3.951896511919E-04;
+ $c7 = 3.21767881768E-05;
+ $c8 = 2.888167364E-07;
+ $c9 = 3.960315187E-07;
+
+ $y = $prob - 0.5;
+ if (abs($y) < 0.42) {
+ $z = ($y * $y);
+ $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
+ } else {
+ if ($y > 0) {
+ $z = log(-log(1 - $prob));
+ } else {
+ $z = log(-log($prob));
+ }
+ $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
+ if ($y < 0) {
+ $z = -$z;
+ }
+ }
+ return $z;
+ } // function _inverse_ncdf2()
+
+
+ private static function _inverse_ncdf3($p) {
+ // ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
+ // Produces the normal deviate Z corresponding to a given lower
+ // tail area of P; Z is accurate to about 1 part in 10**16.
+ //
+ // This is a PHP version of the original FORTRAN code that can
+ // be found at http://lib.stat.cmu.edu/apstat/
+ $split1 = 0.425;
+ $split2 = 5;
+ $const1 = 0.180625;
+ $const2 = 1.6;
+
+ // coefficients for p close to 0.5
+ $a0 = 3.3871328727963666080;
+ $a1 = 1.3314166789178437745E+2;
+ $a2 = 1.9715909503065514427E+3;
+ $a3 = 1.3731693765509461125E+4;
+ $a4 = 4.5921953931549871457E+4;
+ $a5 = 6.7265770927008700853E+4;
+ $a6 = 3.3430575583588128105E+4;
+ $a7 = 2.5090809287301226727E+3;
+
+ $b1 = 4.2313330701600911252E+1;
+ $b2 = 6.8718700749205790830E+2;
+ $b3 = 5.3941960214247511077E+3;
+ $b4 = 2.1213794301586595867E+4;
+ $b5 = 3.9307895800092710610E+4;
+ $b6 = 2.8729085735721942674E+4;
+ $b7 = 5.2264952788528545610E+3;
+
+ // coefficients for p not close to 0, 0.5 or 1.
+ $c0 = 1.42343711074968357734;
+ $c1 = 4.63033784615654529590;
+ $c2 = 5.76949722146069140550;
+ $c3 = 3.64784832476320460504;
+ $c4 = 1.27045825245236838258;
+ $c5 = 2.41780725177450611770E-1;
+ $c6 = 2.27238449892691845833E-2;
+ $c7 = 7.74545014278341407640E-4;
+
+ $d1 = 2.05319162663775882187;
+ $d2 = 1.67638483018380384940;
+ $d3 = 6.89767334985100004550E-1;
+ $d4 = 1.48103976427480074590E-1;
+ $d5 = 1.51986665636164571966E-2;
+ $d6 = 5.47593808499534494600E-4;
+ $d7 = 1.05075007164441684324E-9;
+
+ // coefficients for p near 0 or 1.
+ $e0 = 6.65790464350110377720;
+ $e1 = 5.46378491116411436990;
+ $e2 = 1.78482653991729133580;
+ $e3 = 2.96560571828504891230E-1;
+ $e4 = 2.65321895265761230930E-2;
+ $e5 = 1.24266094738807843860E-3;
+ $e6 = 2.71155556874348757815E-5;
+ $e7 = 2.01033439929228813265E-7;
+
+ $f1 = 5.99832206555887937690E-1;
+ $f2 = 1.36929880922735805310E-1;
+ $f3 = 1.48753612908506148525E-2;
+ $f4 = 7.86869131145613259100E-4;
+ $f5 = 1.84631831751005468180E-5;
+ $f6 = 1.42151175831644588870E-7;
+ $f7 = 2.04426310338993978564E-15;
+
+ $q = $p - 0.5;
+
+ // computation for p close to 0.5
+ if (abs($q) <= split1) {
+ $R = $const1 - $q * $q;
+ $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
+ ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
+ } else {
+ if ($q < 0) {
+ $R = $p;
+ } else {
+ $R = 1 - $p;
+ }
+ $R = pow(-log($R),2);
+
+ // computation for p not close to 0, 0.5 or 1.
+ If ($R <= $split2) {
+ $R = $R - $const2;
+ $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
+ ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
+ } else {
+ // computation for p near 0 or 1.
+ $R = $R - $split2;
+ $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
+ ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
+ }
+ if ($q < 0) {
+ $z = -$z;
+ }
+ }
+ return $z;
+ } // function _inverse_ncdf3()
+
+
+ /**
+ * NORMINV
+ *
+ * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
+ *
+ * @param float $value
+ * @param float $mean Mean Value
+ * @param float $stdDev Standard Deviation
+ * @return float
+ *
+ */
+ public static function NORMINV($probability,$mean,$stdDev) {
+ $probability = self::flattenSingleValue($probability);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if (($probability < 0) || ($probability > 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if ($stdDev < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return (self::_inverse_ncdf($probability) * $stdDev) + $mean;
+ }
+ return self::$_errorCodes['value'];
+ } // function NORMINV()
+
+
+ /**
+ * NORMSINV
+ *
+ * Returns the inverse of the standard normal cumulative distribution
+ *
+ * @param float $value
+ * @return float
+ */
+ public static function NORMSINV($value) {
+ return self::NORMINV($value, 0, 1);
+ } // function NORMSINV()
+
+
+ /**
+ * LOGINV
+ *
+ * Returns the inverse of the normal cumulative distribution
+ *
+ * @param float $value
+ * @return float
+ *
+ * @todo Try implementing P J Acklam's refinement algorithm for greater
+ * accuracy if I can get my head round the mathematics
+ * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
+ */
+ public static function LOGINV($probability, $mean, $stdDev) {
+ $probability = self::flattenSingleValue($probability);
+ $mean = self::flattenSingleValue($mean);
+ $stdDev = self::flattenSingleValue($stdDev);
+
+ if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
+ if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ return exp($mean + $stdDev * self::NORMSINV($probability));
+ }
+ return self::$_errorCodes['value'];
+ } // function LOGINV()
+
+
+ /**
+ * HYPGEOMDIST
+ *
+ * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
+ * sample successes, given the sample size, population successes, and population size.
+ *
+ * @param float $sampleSuccesses Number of successes in the sample
+ * @param float $sampleNumber Size of the sample
+ * @param float $populationSuccesses Number of successes in the population
+ * @param float $populationNumber Population size
+ * @return float
+ *
+ */
+ public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {
+ $sampleSuccesses = floor(self::flattenSingleValue($sampleSuccesses));
+ $sampleNumber = floor(self::flattenSingleValue($sampleNumber));
+ $populationSuccesses = floor(self::flattenSingleValue($populationSuccesses));
+ $populationNumber = floor(self::flattenSingleValue($populationNumber));
+
+ if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
+ if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::COMBIN($populationSuccesses,$sampleSuccesses) *
+ self::COMBIN($populationNumber - $populationSuccesses,$sampleNumber - $sampleSuccesses) /
+ self::COMBIN($populationNumber,$sampleNumber);
+ }
+ return self::$_errorCodes['value'];
+ } // function HYPGEOMDIST()
+
+
+ /**
+ * TDIST
+ *
+ * Returns the probability of Student's T distribution.
+ *
+ * @param float $value Value for the function
+ * @param float $degrees degrees of freedom
+ * @param float $tails number of tails (1 or 2)
+ * @return float
+ */
+ public static function TDIST($value, $degrees, $tails) {
+ $value = self::flattenSingleValue($value);
+ $degrees = floor(self::flattenSingleValue($degrees));
+ $tails = floor(self::flattenSingleValue($tails));
+
+ if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
+ if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
+ return self::$_errorCodes['num'];
+ }
+ // tdist, which finds the probability that corresponds to a given value
+ // of t with k degrees of freedom. This algorithm is translated from a
+ // pascal function on p81 of "Statistical Computing in Pascal" by D
+ // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
+ // London). The above Pascal algorithm is itself a translation of the
+ // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
+ // Laboratory as reported in (among other places) "Applied Statistics
+ // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
+ // Horwood Ltd.; W. Sussex, England).
+ $tterm = $degrees;
+ $ttheta = atan2($value,sqrt($tterm));
+ $tc = cos($ttheta);
+ $ts = sin($ttheta);
+ $tsum = 0;
+
+ if (($degrees % 2) == 1) {
+ $ti = 3;
+ $tterm = $tc;
+ } else {
+ $ti = 2;
+ $tterm = 1;
+ }
+
+ $tsum = $tterm;
+ while ($ti < $degrees) {
+ $tterm *= $tc * $tc * ($ti - 1) / $ti;
+ $tsum += $tterm;
+ $ti += 2;
+ }
+ $tsum *= $ts;
+ if (($degrees % 2) == 1) { $tsum = M_2DIVPI * ($tsum + $ttheta); }
+ $tValue = 0.5 * (1 + $tsum);
+ if ($tails == 1) {
+ return 1 - abs($tValue);
+ } else {
+ return 1 - abs((1 - $tValue) - $tValue);
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function TDIST()
+
+
+ /**
+ * TINV
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param float $probability Probability for the function
+ * @param float $degrees degrees of freedom
+ * @return float
+ */
+ public static function TINV($probability, $degrees) {
+ $probability = self::flattenSingleValue($probability);
+ $degrees = floor(self::flattenSingleValue($degrees));
+
+ if ((is_numeric($probability)) && (is_numeric($degrees))) {
+ $xLo = 100;
+ $xHi = 0;
+
+ $x = $xNew = 1;
+ $dx = 1;
+ $i = 0;
+
+ while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::TDIST($x, $degrees, 2);
+ $error = $result - $probability;
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+ // Avoid division by zero
+ if ($result != 0.0) {
+ $dx = $error / $result;
+ $xNew = $x - $dx;
+ }
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+ if ($i == MAX_ITERATIONS) {
+ return self::$_errorCodes['na'];
+ }
+ return round($x,12);
+ }
+ return self::$_errorCodes['value'];
+ } // function TINV()
+
+
+ /**
+ * CONFIDENCE
+ *
+ * Returns the confidence interval for a population mean
+ *
+ * @param float $alpha
+ * @param float $stdDev Standard Deviation
+ * @param float $size
+ * @return float
+ *
+ */
+ public static function CONFIDENCE($alpha,$stdDev,$size) {
+ $alpha = self::flattenSingleValue($alpha);
+ $stdDev = self::flattenSingleValue($stdDev);
+ $size = floor(self::flattenSingleValue($size));
+
+ if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
+ if (($alpha <= 0) || ($alpha >= 1)) {
+ return self::$_errorCodes['num'];
+ }
+ if (($stdDev <= 0) || ($size < 1)) {
+ return self::$_errorCodes['num'];
+ }
+ return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
+ }
+ return self::$_errorCodes['value'];
+ } // function CONFIDENCE()
+
+
+ /**
+ * POISSON
+ *
+ * Returns the Poisson distribution. A common application of the Poisson distribution
+ * is predicting the number of events over a specific time, such as the number of
+ * cars arriving at a toll plaza in 1 minute.
+ *
+ * @param float $value
+ * @param float $mean Mean Value
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function POISSON($value, $mean, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $mean = self::flattenSingleValue($mean);
+
+ if ((is_numeric($value)) && (is_numeric($mean))) {
+ if (($value <= 0) || ($mean <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ $summer = 0;
+ for ($i = 0; $i <= floor($value); ++$i) {
+ $summer += pow($mean,$i) / self::FACT($i);
+ }
+ return exp(0-$mean) * $summer;
+ } else {
+ return (exp(0-$mean) * pow($mean,$value)) / self::FACT($value);
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function POISSON()
+
+
+ /**
+ * WEIBULL
+ *
+ * Returns the Weibull distribution. Use this distribution in reliability
+ * analysis, such as calculating a device's mean time to failure.
+ *
+ * @param float $value
+ * @param float $alpha Alpha Parameter
+ * @param float $beta Beta Parameter
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function WEIBULL($value, $alpha, $beta, $cumulative) {
+ $value = self::flattenSingleValue($value);
+ $alpha = self::flattenSingleValue($alpha);
+ $beta = self::flattenSingleValue($beta);
+
+ if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
+ if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
+ if ($cumulative) {
+ return 1 - exp(0 - pow($value / $beta,$alpha));
+ } else {
+ return ($alpha / pow($beta,$alpha)) * pow($value,$alpha - 1) * exp(0 - pow($value / $beta,$alpha));
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function WEIBULL()
+
+
+ /**
+ * ZTEST
+ *
+ * Returns the Weibull distribution. Use this distribution in reliability
+ * analysis, such as calculating a device's mean time to failure.
+ *
+ * @param float $value
+ * @param float $alpha Alpha Parameter
+ * @param float $beta Beta Parameter
+ * @param boolean $cumulative
+ * @return float
+ *
+ */
+ public static function ZTEST($dataSet, $m0, $sigma=null) {
+ $dataSet = self::flattenArrayIndexed($dataSet);
+ $m0 = self::flattenSingleValue($m0);
+ $sigma = self::flattenSingleValue($sigma);
+
+ if (is_null($sigma)) {
+ $sigma = self::STDEV($dataSet);
+ }
+ $n = count($dataSet);
+
+ return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0)/($sigma/SQRT($n)));
+ } // function ZTEST()
+
+
+ /**
+ * SKEW
+ *
+ * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
+ * of a distribution around its mean. Positive skewness indicates a distribution with an
+ * asymmetric tail extending toward more positive values. Negative skewness indicates a
+ * distribution with an asymmetric tail extending toward more negative values.
+ *
+ * @param array Data Series
+ * @return float
+ */
+ public static function SKEW() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $mean = self::AVERAGE($aArgs);
+ $stdDev = self::STDEV($aArgs);
+
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += pow((($arg - $mean) / $stdDev),3) ;
+ ++$count;
+ }
+ }
+ }
+
+ // Return
+ if ($count > 2) {
+ return $summer * ($count / (($count-1) * ($count-2)));
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function SKEW()
+
+
+ /**
+ * KURT
+ *
+ * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
+ * or flatness of a distribution compared with the normal distribution. Positive
+ * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
+ * relatively flat distribution.
+ *
+ * @param array Data Series
+ * @return float
+ */
+ public static function KURT() {
+ $aArgs = self::flattenArrayIndexed(func_get_args());
+ $mean = self::AVERAGE($aArgs);
+ $stdDev = self::STDEV($aArgs);
+
+ if ($stdDev > 0) {
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) &&
+ (!self::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += pow((($arg - $mean) / $stdDev),4) ;
+ ++$count;
+ }
+ }
+ }
+
+ // Return
+ if ($count > 3) {
+ return $summer * ($count * ($count+1) / (($count-1) * ($count-2) * ($count-3))) - (3 * pow($count-1,2) / (($count-2) * ($count-3)));
+ }
+ }
+ return self::$_errorCodes['divisionbyzero'];
+ } // function KURT()
+
+
+ /**
+ * RAND
+ *
+ * @param int $min Minimal value
+ * @param int $max Maximal value
+ * @return int Random number
+ */
+ public static function RAND($min = 0, $max = 0) {
+ $min = self::flattenSingleValue($min);
+ $max = self::flattenSingleValue($max);
+
+ if ($min == 0 && $max == 0) {
+ return (rand(0,10000000)) / 10000000;
+ } else {
+ return rand($min, $max);
+ }
+ } // function RAND()
+
+
+ /**
+ * MOD
+ *
+ * @param int $a Dividend
+ * @param int $b Divisor
+ * @return int Remainder
+ */
+ public static function MOD($a = 1, $b = 1) {
+ $a = self::flattenSingleValue($a);
+ $b = self::flattenSingleValue($b);
+
+ if ($b == 0.0) {
+ return self::$_errorCodes['divisionbyzero'];
+ } elseif (($a < 0.0) && ($b > 0.0)) {
+ return $b - fmod(abs($a),$b);
+ } elseif (($a > 0.0) && ($b < 0.0)) {
+ return $b + fmod($a,abs($b));
+ }
+
+ return fmod($a,$b);
+ } // function MOD()
+
+
+ /**
+ * CHARACTER
+ *
+ * @param string $character Value
+ * @return int
+ */
+ public static function CHARACTER($character) {
+ $character = self::flattenSingleValue($character);
+
+ if ((!is_numeric($character)) || ($character < 0)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding(''.intval($character).';', 'UTF-8', 'HTML-ENTITIES');
+ } else {
+ return chr(intval($character));
+ }
+ }
+
+
+ private static function _uniord($c) {
+ if (ord($c{0}) >=0 && ord($c{0}) <= 127)
+ return ord($c{0});
+ if (ord($c{0}) >= 192 && ord($c{0}) <= 223)
+ return (ord($c{0})-192)*64 + (ord($c{1})-128);
+ if (ord($c{0}) >= 224 && ord($c{0}) <= 239)
+ return (ord($c{0})-224)*4096 + (ord($c{1})-128)*64 + (ord($c{2})-128);
+ if (ord($c{0}) >= 240 && ord($c{0}) <= 247)
+ return (ord($c{0})-240)*262144 + (ord($c{1})-128)*4096 + (ord($c{2})-128)*64 + (ord($c{3})-128);
+ if (ord($c{0}) >= 248 && ord($c{0}) <= 251)
+ return (ord($c{0})-248)*16777216 + (ord($c{1})-128)*262144 + (ord($c{2})-128)*4096 + (ord($c{3})-128)*64 + (ord($c{4})-128);
+ if (ord($c{0}) >= 252 && ord($c{0}) <= 253)
+ return (ord($c{0})-252)*1073741824 + (ord($c{1})-128)*16777216 + (ord($c{2})-128)*262144 + (ord($c{3})-128)*4096 + (ord($c{4})-128)*64 + (ord($c{5})-128);
+ if (ord($c{0}) >= 254 && ord($c{0}) <= 255) //error
+ return self::$_errorCodes['value'];
+ return 0;
+ } // function _uniord()
+
+ /**
+ * ASCIICODE
+ *
+ * @param string $character Value
+ * @return int
+ */
+ public static function ASCIICODE($characters) {
+ $characters = self::flattenSingleValue($characters);
+ if (is_bool($characters)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $characters = (int) $characters;
+ } else {
+ if ($characters) {
+ $characters = 'True';
+ } else {
+ $characters = 'False';
+ }
+ }
+ }
+
+ $character = $characters;
+ if ((function_exists('mb_strlen')) && (function_exists('mb_substr'))) {
+ if (mb_strlen($characters, 'UTF-8') > 1) { $character = mb_substr($characters, 0, 1, 'UTF-8'); }
+ return self::_uniord($character);
+ } else {
+ if (strlen($characters) > 0) { $character = substr($characters, 0, 1); }
+ return ord($character);
+ }
+ } // function ASCIICODE()
+
+
+ /**
+ * CONCATENATE
+ *
+ * @return string
+ */
+ public static function CONCATENATE() {
+ // Return value
+ $returnValue = '';
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ if (is_bool($arg)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $arg = (int) $arg;
+ } else {
+ if ($arg) {
+ $arg = 'TRUE';
+ } else {
+ $arg = 'FALSE';
+ }
+ }
+ }
+ $returnValue .= $arg;
+ }
+
+ // Return
+ return $returnValue;
+ } // function CONCATENATE()
+
+
+ /**
+ * STRINGLENGTH
+ *
+ * @param string $value Value
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function STRINGLENGTH($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($value, 'UTF-8');
+ } else {
+ return strlen($value);
+ }
+ } // function STRINGLENGTH()
+
+
+ /**
+ * SEARCHSENSITIVE
+ *
+ * @param string $needle The string to look for
+ * @param string $haystack The string in which to look
+ * @param int $offset Offset within $haystack
+ * @return string
+ */
+ public static function SEARCHSENSITIVE($needle,$haystack,$offset=1) {
+ $needle = self::flattenSingleValue($needle);
+ $haystack = self::flattenSingleValue($haystack);
+ $offset = self::flattenSingleValue($offset);
+
+ if (!is_bool($needle)) {
+ if (is_bool($haystack)) {
+ $haystack = ($haystack) ? 'TRUE' : 'FALSE';
+ }
+
+ if (($offset > 0) && (strlen($haystack) > $offset)) {
+ if (function_exists('mb_strpos')) {
+ $pos = mb_strpos($haystack, $needle, --$offset,'UTF-8');
+ } else {
+ $pos = strpos($haystack, $needle, --$offset);
+ }
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function SEARCHSENSITIVE()
+
+
+ /**
+ * SEARCHINSENSITIVE
+ *
+ * @param string $needle The string to look for
+ * @param string $haystack The string in which to look
+ * @param int $offset Offset within $haystack
+ * @return string
+ */
+ public static function SEARCHINSENSITIVE($needle,$haystack,$offset=1) {
+ $needle = self::flattenSingleValue($needle);
+ $haystack = self::flattenSingleValue($haystack);
+ $offset = self::flattenSingleValue($offset);
+
+ if (!is_bool($needle)) {
+ if (is_bool($haystack)) {
+ $haystack = ($haystack) ? 'TRUE' : 'FALSE';
+ }
+
+ if (($offset > 0) && (strlen($haystack) > $offset)) {
+ if (function_exists('mb_stripos')) {
+ $pos = mb_stripos($haystack, $needle, --$offset,'UTF-8');
+ } else {
+ $pos = stripos($haystack, $needle, --$offset);
+ }
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function SEARCHINSENSITIVE()
+
+
+ /**
+ * LEFT
+ *
+ * @param string $value Value
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function LEFT($value = '', $chars = 1) {
+ $value = self::flattenSingleValue($value);
+ $chars = self::flattenSingleValue($chars);
+
+ if ($chars < 0) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_substr')) {
+ return mb_substr($value, 0, $chars, 'UTF-8');
+ } else {
+ return substr($value, 0, $chars);
+ }
+ } // function LEFT()
+
+
+ /**
+ * RIGHT
+ *
+ * @param string $value Value
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function RIGHT($value = '', $chars = 1) {
+ $value = self::flattenSingleValue($value);
+ $chars = self::flattenSingleValue($chars);
+
+ if ($chars < 0) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if ((function_exists('mb_substr')) && (function_exists('mb_strlen'))) {
+ return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
+ } else {
+ return substr($value, strlen($value) - $chars);
+ }
+ } // function RIGHT()
+
+
+ /**
+ * MID
+ *
+ * @param string $value Value
+ * @param int $start Start character
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function MID($value = '', $start = 1, $chars = null) {
+ $value = self::flattenSingleValue($value);
+ $start = self::flattenSingleValue($start);
+ $chars = self::flattenSingleValue($chars);
+
+ if (($start < 1) || ($chars < 0)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_bool($value)) {
+ $value = ($value) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_substr')) {
+ return mb_substr($value, --$start, $chars, 'UTF-8');
+ } else {
+ return substr($value, --$start, $chars);
+ }
+ } // function MID()
+
+
+ /**
+ * REPLACE
+ *
+ * @param string $value Value
+ * @param int $start Start character
+ * @param int $chars Number of characters
+ * @return string
+ */
+ public static function REPLACE($oldText = '', $start = 1, $chars = null, $newText) {
+ $oldText = self::flattenSingleValue($oldText);
+ $start = self::flattenSingleValue($start);
+ $chars = self::flattenSingleValue($chars);
+ $newText = self::flattenSingleValue($newText);
+
+ $left = self::LEFT($oldText,$start-1);
+ $right = self::RIGHT($oldText,self::STRINGLENGTH($oldText)-($start+$chars)+1);
+
+ return $left.$newText.$right;
+ } // function REPLACE()
+
+
+ /**
+ * SUBSTITUTE
+ *
+ * @param string $text Value
+ * @param string $fromText From Value
+ * @param string $toText To Value
+ * @param integer $instance Instance Number
+ * @return string
+ */
+ public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0) {
+ $text = self::flattenSingleValue($text);
+ $fromText = self::flattenSingleValue($fromText);
+ $toText = self::flattenSingleValue($toText);
+ $instance = floor(self::flattenSingleValue($instance));
+
+ if ($instance == 0) {
+ if(function_exists('mb_str_replace')) {
+ return mb_str_replace($fromText,$toText,$text);
+ } else {
+ return str_replace($fromText,$toText,$text);
+ }
+ } else {
+ $pos = -1;
+ while($instance > 0) {
+ if (function_exists('mb_strpos')) {
+ $pos = mb_strpos($text, $fromText, $pos+1, 'UTF-8');
+ } else {
+ $pos = strpos($text, $fromText, $pos+1);
+ }
+ if ($pos === false) {
+ break;
+ }
+ --$instance;
+ }
+ if ($pos !== false) {
+ if (function_exists('mb_strlen')) {
+ return self::REPLACE($text,++$pos,mb_strlen($fromText, 'UTF-8'),$toText);
+ } else {
+ return self::REPLACE($text,++$pos,strlen($fromText),$toText);
+ }
+ }
+ }
+
+ return $left.$newText.$right;
+ } // function SUBSTITUTE()
+
+
+ /**
+ * RETURNSTRING
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function RETURNSTRING($testValue = '') {
+ $testValue = self::flattenSingleValue($testValue);
+
+ if (is_string($testValue)) {
+ return $testValue;
+ }
+ return Null;
+ } // function RETURNSTRING()
+
+
+ /**
+ * FIXEDFORMAT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function FIXEDFORMAT($value,$decimals=2,$no_commas=false) {
+ $value = self::flattenSingleValue($value);
+ $decimals = self::flattenSingleValue($decimals);
+ $no_commas = self::flattenSingleValue($no_commas);
+
+ $valueResult = round($value,$decimals);
+ if ($decimals < 0) { $decimals = 0; }
+ if (!$no_commas) {
+ $valueResult = number_format($valueResult,$decimals);
+ }
+
+ return (string) $valueResult;
+ } // function FIXEDFORMAT()
+
+
+ /**
+ * TEXTFORMAT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function TEXTFORMAT($value,$format) {
+ $value = self::flattenSingleValue($value);
+ $format = self::flattenSingleValue($format);
+
+ if ((is_string($value)) && (!is_numeric($value)) && PHPExcel_Shared_Date::isDateTimeFormatCode($format)) {
+ $value = self::DATEVALUE($value);
+ }
+
+ return (string) PHPExcel_Style_NumberFormat::toFormattedString($value,$format);
+ } // function TEXTFORMAT()
+
+
+ /**
+ * TRIMSPACES
+ *
+ * @param mixed $value Value to check
+ * @return string
+ */
+ public static function TRIMSPACES($stringValue = '') {
+ $stringValue = self::flattenSingleValue($stringValue);
+
+ if (is_string($stringValue) || is_numeric($stringValue)) {
+ return trim(preg_replace('/ +/',' ',$stringValue));
+ }
+ return Null;
+ } // function TRIMSPACES()
+
+
+ private static $_invalidChars = Null;
+
+ /**
+ * TRIMNONPRINTABLE
+ *
+ * @param mixed $value Value to check
+ * @return string
+ */
+ public static function TRIMNONPRINTABLE($stringValue = '') {
+ $stringValue = self::flattenSingleValue($stringValue);
+
+ if (is_bool($stringValue)) {
+ $stringValue = ($stringValue) ? 'TRUE' : 'FALSE';
+ }
+
+ if (self::$_invalidChars == Null) {
+ self::$_invalidChars = range(chr(0),chr(31));
+ }
+
+ if (is_string($stringValue) || is_numeric($stringValue)) {
+ return str_replace(self::$_invalidChars,'',trim($stringValue,"\x00..\x1F"));
+ }
+ return Null;
+ } // function TRIMNONPRINTABLE()
+
+
+ /**
+ * ERROR_TYPE
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function ERROR_TYPE($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ $i = 1;
+ foreach(self::$_errorCodes as $errorCode) {
+ if ($value == $errorCode) {
+ return $i;
+ }
+ ++$i;
+ }
+ return self::$_errorCodes['na'];
+ } // function ERROR_TYPE()
+
+
+ /**
+ * IS_BLANK
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_BLANK($value=null) {
+ if (!is_null($value)) {
+ $value = self::flattenSingleValue($value);
+ }
+
+ return is_null($value);
+ } // function IS_BLANK()
+
+
+ /**
+ * IS_ERR
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ERR($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return self::IS_ERROR($value) && (!self::IS_NA($value));
+ } // function IS_ERR()
+
+
+ /**
+ * IS_ERROR
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ERROR($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return in_array($value, array_values(self::$_errorCodes));
+ } // function IS_ERROR()
+
+
+ /**
+ * IS_NA
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NA($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return ($value === self::$_errorCodes['na']);
+ } // function IS_NA()
+
+
+ /**
+ * IS_EVEN
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_EVEN($value = 0) {
+ $value = self::flattenSingleValue($value);
+
+ if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return self::$_errorCodes['value'];
+ }
+ return ($value % 2 == 0);
+ } // function IS_EVEN()
+
+
+ /**
+ * IS_ODD
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ODD($value = null) {
+ $value = self::flattenSingleValue($value);
+
+ if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return self::$_errorCodes['value'];
+ }
+ return (abs($value) % 2 == 1);
+ } // function IS_ODD()
+
+
+ /**
+ * IS_NUMBER
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NUMBER($value = 0) {
+ $value = self::flattenSingleValue($value);
+
+ if (is_string($value)) {
+ return False;
+ }
+ return is_numeric($value);
+ } // function IS_NUMBER()
+
+
+ /**
+ * IS_LOGICAL
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_LOGICAL($value = true) {
+ $value = self::flattenSingleValue($value);
+
+ return is_bool($value);
+ } // function IS_LOGICAL()
+
+
+ /**
+ * IS_TEXT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_TEXT($value = '') {
+ $value = self::flattenSingleValue($value);
+
+ return is_string($value);
+ } // function IS_TEXT()
+
+
+ /**
+ * IS_NONTEXT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NONTEXT($value = '') {
+ return !self::IS_TEXT($value);
+ } // function IS_NONTEXT()
+
+
+ /**
+ * VERSION
+ *
+ * @return string Version information
+ */
+ public static function VERSION() {
+ return 'PHPExcel 1.7.2, 2010-01-11';
+ } // function VERSION()
+
+
+ /**
+ * DATE
+ *
+ * @param long $year
+ * @param long $month
+ * @param long $day
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATE($year = 0, $month = 1, $day = 1) {
+ $year = (integer) self::flattenSingleValue($year);
+ $month = (integer) self::flattenSingleValue($month);
+ $day = (integer) self::flattenSingleValue($day);
+
+ $baseYear = PHPExcel_Shared_Date::getExcelCalendar();
+ // Validate parameters
+ if ($year < ($baseYear-1900)) {
+ return self::$_errorCodes['num'];
+ }
+ if ((($baseYear-1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (($year < $baseYear) && ($year >= ($baseYear-1900))) {
+ $year += 1900;
+ }
+
+ if ($month < 1) {
+ // Handle year/month adjustment if month < 1
+ --$month;
+ $year += ceil($month / 12) - 1;
+ $month = 13 - abs($month % 12);
+ } elseif ($month > 12) {
+ // Handle year/month adjustment if month > 12
+ $year += floor($month / 12);
+ $month = ($month % 12);
+ }
+
+ // Re-validate the year parameter after adjustments
+ if (($year < $baseYear) || ($year >= 10000)) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
+ break;
+ }
+ } // function DATE()
+
+
+ /**
+ * TIME
+ *
+ * @param long $hour
+ * @param long $minute
+ * @param long $second
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIME($hour = 0, $minute = 0, $second = 0) {
+ $hour = self::flattenSingleValue($hour);
+ $minute = self::flattenSingleValue($minute);
+ $second = self::flattenSingleValue($second);
+
+ if ($hour == '') { $hour = 0; }
+ if ($minute == '') { $minute = 0; }
+ if ($second == '') { $second = 0; }
+
+ if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
+ return self::$_errorCodes['value'];
+ }
+ $hour = (integer) $hour;
+ $minute = (integer) $minute;
+ $second = (integer) $second;
+
+ if ($second < 0) {
+ $minute += floor($second / 60);
+ $second = 60 - abs($second % 60);
+ if ($second == 60) { $second = 0; }
+ } elseif ($second >= 60) {
+ $minute += floor($second / 60);
+ $second = $second % 60;
+ }
+ if ($minute < 0) {
+ $hour += floor($minute / 60);
+ $minute = 60 - abs($minute % 60);
+ if ($minute == 60) { $minute = 0; }
+ } elseif ($minute >= 60) {
+ $hour += floor($minute / 60);
+ $minute = $minute % 60;
+ }
+
+ if ($hour > 23) {
+ $hour = $hour % 24;
+ } elseif ($hour < 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : $date = 0;
+ $calendar = PHPExcel_Shared_Date::getExcelCalendar();
+ if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900) {
+ $date = 1;
+ }
+ return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::FormattedPHPToExcel(1970, 1, 1, $hour-1, $minute, $second)); // -2147468400; // -2147472000 + 3600
+ break;
+ case self::RETURNDATE_PHP_OBJECT : $dayAdjust = 0;
+ if ($hour < 0) {
+ $dayAdjust = floor($hour / 24);
+ $hour = 24 - abs($hour % 24);
+ if ($hour == 24) { $hour = 0; }
+ } elseif ($hour >= 24) {
+ $dayAdjust = floor($hour / 24);
+ $hour = $hour % 24;
+ }
+ $phpDateObject = new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
+ if ($dayAdjust != 0) {
+ $phpDateObject->modify($dayAdjust.' days');
+ }
+ return $phpDateObject;
+ break;
+ }
+ } // function TIME()
+
+
+ /**
+ * DATEVALUE
+ *
+ * @param string $dateValue
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATEVALUE($dateValue = 1) {
+ $dateValue = str_replace(array('/','.',' '),array('-','-','-'),trim(self::flattenSingleValue($dateValue),'"'));
+
+ $yearFound = false;
+ $t1 = explode('-',$dateValue);
+ foreach($t1 as &$t) {
+ if ((is_numeric($t)) && (($t > 31) && ($t < 100))) {
+ if ($yearFound) {
+ return self::$_errorCodes['value'];
+ } else {
+ $t += 1900;
+ $yearFound = true;
+ }
+ }
+ }
+ unset($t);
+ $dateValue = implode('-',$t1);
+
+ $PHPDateArray = date_parse($dateValue);
+ if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
+ $testVal1 = strtok($dateValue,'- ');
+ if ($testVal1 !== False) {
+ $testVal2 = strtok('- ');
+ if ($testVal2 !== False) {
+ $testVal3 = strtok('- ');
+ if ($testVal3 === False) {
+ $testVal3 = strftime('%Y');
+ }
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $PHPDateArray = date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
+ if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
+ $PHPDateArray = date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
+ if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ }
+
+ if (($PHPDateArray !== False) && ($PHPDateArray['error_count'] == 0)) {
+ // Execute function
+ if ($PHPDateArray['year'] == '') { $PHPDateArray['year'] = strftime('%Y'); }
+ if ($PHPDateArray['month'] == '') { $PHPDateArray['month'] = strftime('%m'); }
+ if ($PHPDateArray['day'] == '') { $PHPDateArray['day'] = strftime('%d'); }
+ $excelDateValue = floor(PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']));
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function DATEVALUE()
+
+
+ /**
+ * _getDateValue
+ *
+ * @param string $dateValue
+ * @return mixed Excel date/time serial value, or string if error
+ */
+ private static function _getDateValue($dateValue) {
+ if (!is_numeric($dateValue)) {
+ if ((is_string($dateValue)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
+ return self::$_errorCodes['value'];
+ }
+ if ((is_object($dateValue)) && ($dateValue instanceof PHPExcel_Shared_Date::$dateTimeObjectType)) {
+ $dateValue = PHPExcel_Shared_Date::PHPToExcel($dateValue);
+ } else {
+ $saveReturnDateType = self::getReturnDateType();
+ self::setReturnDateType(self::RETURNDATE_EXCEL);
+ $dateValue = self::DATEVALUE($dateValue);
+ self::setReturnDateType($saveReturnDateType);
+ }
+ }
+ return $dateValue;
+ } // function _getDateValue()
+
+
+ /**
+ * TIMEVALUE
+ *
+ * @param string $timeValue
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIMEVALUE($timeValue) {
+ $timeValue = self::flattenSingleValue($timeValue);
+
+ if ((($PHPDateArray = date_parse($timeValue)) !== False) && ($PHPDateArray['error_count'] == 0)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);
+ } else {
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(1900,1,1,$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']) - 1;
+ }
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) $phpDateValue = PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+25569) - 3600;;
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function TIMEVALUE()
+
+
+ /**
+ * _getTimeValue
+ *
+ * @param string $timeValue
+ * @return mixed Excel date/time serial value, or string if error
+ */
+ private static function _getTimeValue($timeValue) {
+ $saveReturnDateType = self::getReturnDateType();
+ self::setReturnDateType(self::RETURNDATE_EXCEL);
+ $timeValue = self::TIMEVALUE($timeValue);
+ self::setReturnDateType($saveReturnDateType);
+ return $timeValue;
+ } // function _getTimeValue()
+
+
+ /**
+ * DATETIMENOW
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATETIMENOW() {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = False;
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) time();
+ break;
+ case self::RETURNDATE_PHP_OBJECT : $retValue = new DateTime();
+ break;
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ } // function DATETIMENOW()
+
+
+ /**
+ * DATENOW
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATENOW() {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = False;
+ $excelDateTime = floor(PHPExcel_Shared_Date::PHPToExcel(time()));
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : $retValue = (float) $excelDateTime;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime) - 3600;
+ break;
+ case self::RETURNDATE_PHP_OBJECT : $retValue = PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
+ break;
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ } // function DATENOW()
+
+
+ private static function _isLeapYear($year) {
+ return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
+ } // function _isLeapYear()
+
+
+ private static function _dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS) {
+ if ($startDay == 31) {
+ --$startDay;
+ } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::_isLeapYear($startYear))))) {
+ $startDay = 30;
+ }
+ if ($endDay == 31) {
+ if ($methodUS && $startDay != 30) {
+ $endDay = 1;
+ if ($endMonth == 12) {
+ ++$endYear;
+ $endMonth = 1;
+ } else {
+ ++$endMonth;
+ }
+ } else {
+ $endDay = 30;
+ }
+ }
+
+ return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
+ } // function _dateDiff360()
+
+
+ /**
+ * DAYS360
+ *
+ * @param long $startDate Excel date serial value or a standard date string
+ * @param long $endDate Excel date serial value or a standard date string
+ * @param boolean $method US or European Method
+ * @return long PHP date/time serial
+ */
+ public static function DAYS360($startDate = 0, $endDate = 0, $method = false) {
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Execute function
+ $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
+ $startDay = $PHPStartDateObject->format('j');
+ $startMonth = $PHPStartDateObject->format('n');
+ $startYear = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ $endDay = $PHPEndDateObject->format('j');
+ $endMonth = $PHPEndDateObject->format('n');
+ $endYear = $PHPEndDateObject->format('Y');
+
+ return self::_dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
+ } // function DAYS360()
+
+
+ /**
+ * DATEDIF
+ *
+ * @param long $startDate Excel date serial value or a standard date string
+ * @param long $endDate Excel date serial value or a standard date string
+ * @param string $unit
+ * @return long Interval between the dates
+ */
+ public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') {
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+ $unit = strtoupper(self::flattenSingleValue($unit));
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Validate parameters
+ if ($startDate >= $endDate) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $difference = $endDate - $startDate;
+
+ $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
+ $startDays = $PHPStartDateObject->format('j');
+ $startMonths = $PHPStartDateObject->format('n');
+ $startYears = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ $endDays = $PHPEndDateObject->format('j');
+ $endMonths = $PHPEndDateObject->format('n');
+ $endYears = $PHPEndDateObject->format('Y');
+
+ $retVal = self::$_errorCodes['num'];
+ switch ($unit) {
+ case 'D':
+ $retVal = intval($difference);
+ break;
+ case 'M':
+ $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
+ // We're only interested in full months
+ if ($endDays < $startDays) {
+ --$retVal;
+ }
+ break;
+ case 'Y':
+ $retVal = intval($endYears - $startYears);
+ // We're only interested in full months
+ if ($endMonths < $startMonths) {
+ --$retVal;
+ } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
+ --$retVal;
+ }
+ break;
+ case 'MD':
+ if ($endDays < $startDays) {
+ $retVal = $endDays;
+ $PHPEndDateObject->modify('-'.$endDays.' days');
+ $adjustDays = $PHPEndDateObject->format('j');
+ if ($adjustDays > $startDays) {
+ $retVal += ($adjustDays - $startDays);
+ }
+ } else {
+ $retVal = $endDays - $startDays;
+ }
+ break;
+ case 'YM':
+ $retVal = intval($endMonths - $startMonths);
+ if ($retVal < 0) $retVal = 12 + $retVal;
+ // We're only interested in full months
+ if ($endDays < $startDays) {
+ --$retVal;
+ }
+ break;
+ case 'YD':
+ $retVal = intval($difference);
+ if ($endYears > $startYears) {
+ while ($endYears > $startYears) {
+ $PHPEndDateObject->modify('-1 year');
+ $endYears = $PHPEndDateObject->format('Y');
+ }
+ $retVal = $PHPEndDateObject->format('z') - $PHPStartDateObject->format('z');
+ if ($retVal < 0) { $retVal += 365; }
+ }
+ break;
+ }
+ return $retVal;
+ } // function DATEDIF()
+
+
+ /**
+ * YEARFRAC
+ *
+ * Calculates the fraction of the year represented by the number of whole days between two dates (the start_date and the
+ * end_date). Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or obligations
+ * to assign to a specific term.
+ *
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
+ * @param integer $method Method used for the calculation
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float fraction of the year
+ */
+ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) {
+ $startDate = self::flattenSingleValue($startDate);
+ $endDate = self::flattenSingleValue($endDate);
+ $method = self::flattenSingleValue($method);
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if ((is_numeric($method)) && (!is_string($method))) {
+ switch($method) {
+ case 0 :
+ return self::DAYS360($startDate,$endDate) / 360;
+ break;
+ case 1 :
+ $startYear = self::YEAR($startDate);
+ $endYear = self::YEAR($endDate);
+ $leapDay = 0;
+ if (self::_isLeapYear($startYear) || self::_isLeapYear($endYear)) {
+ $leapDay = 1;
+ }
+ return self::DATEDIF($startDate,$endDate) / (365 + $leapDay);
+ break;
+ case 2 :
+ return self::DATEDIF($startDate,$endDate) / 360;
+ break;
+ case 3 :
+ return self::DATEDIF($startDate,$endDate) / 365;
+ break;
+ case 4 :
+ return self::DAYS360($startDate,$endDate,True) / 360;
+ break;
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function YEARFRAC()
+
+
+ /**
+ * NETWORKDAYS
+ *
+ * @param mixed Start date
+ * @param mixed End date
+ * @param array of mixed Optional Date Series
+ * @return long Interval between the dates
+ */
+ public static function NETWORKDAYS($startDate,$endDate) {
+ // Flush the mandatory start and end date that are referenced in the function definition
+ $dateArgs = self::flattenArray(func_get_args());
+ array_shift($dateArgs);
+ array_shift($dateArgs);
+
+ // Validate the start and end dates
+ if (is_string($startDate = $sDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (is_string($endDate = $eDate = self::_getDateValue($endDate))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if ($sDate > $eDate) {
+ $startDate = $eDate;
+ $endDate = $sDate;
+ }
+
+ // Execute function
+ $startDoW = 6 - self::DAYOFWEEK($startDate,2);
+ if ($startDoW < 0) { $startDoW = 0; }
+ $endDoW = self::DAYOFWEEK($endDate,2);
+ if ($endDoW >= 6) { $endDoW = 0; }
+
+ $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
+ $partWeekDays = $endDoW + $startDoW;
+ if ($partWeekDays > 5) {
+ $partWeekDays -= 5;
+ }
+
+ // Test any extra holiday parameters
+ $holidayCountedArray = array();
+ foreach ($dateArgs as $holidayDate) {
+ if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
+ --$partWeekDays;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+
+ if ($sDate > $eDate) {
+ return 0 - ($wholeWeekDays + $partWeekDays);
+ }
+ return $wholeWeekDays + $partWeekDays;
+ } // function NETWORKDAYS()
+
+
+ /**
+ * WORKDAY
+ *
+ * @param mixed Start date
+ * @param mixed number of days for adjustment
+ * @param array of mixed Optional Date Series
+ * @return long Interval between the dates
+ */
+ public static function WORKDAY($startDate,$endDays) {
+ $dateArgs = self::flattenArray(func_get_args());
+
+ array_shift($dateArgs);
+ array_shift($dateArgs);
+
+ if (is_string($startDate = self::_getDateValue($startDate))) {
+ return self::$_errorCodes['value'];
+ }
+ if (!is_numeric($endDays)) {
+ return self::$_errorCodes['value'];
+ }
+ $endDate = (float) $startDate + (floor($endDays / 5) * 7) + ($endDays % 5);
+ if ($endDays < 0) {
+ $endDate += 7;
+ }
+
+ $endDoW = self::DAYOFWEEK($endDate,3);
+ if ($endDoW >= 5) {
+ if ($endDays >= 0) {
+ $endDate += (7 - $endDoW);
+ } else {
+ $endDate -= ($endDoW - 5);
+ }
+ }
+
+ // Test any extra holiday parameters
+ if (count($dateArgs) > 0) {
+ $holidayCountedArray = $holidayDates = array();
+ foreach ($dateArgs as $holidayDate) {
+ if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
+ return self::$_errorCodes['value'];
+ }
+ $holidayDates[] = $holidayDate;
+ }
+ if ($endDays >= 0) {
+ sort($holidayDates, SORT_NUMERIC);
+ } else {
+ rsort($holidayDates, SORT_NUMERIC);
+ }
+ foreach ($holidayDates as $holidayDate) {
+ if ($endDays >= 0) {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
+ ++$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ } else {
+ if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
+ if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
+ --$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+ $endDoW = self::DAYOFWEEK($endDate,3);
+ if ($endDoW >= 5) {
+ if ($endDays >= 0) {
+ $endDate += (7 - $endDoW);
+ } else {
+ $endDate -= ($endDoW - 5);
+ }
+ }
+ }
+ }
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) $endDate;
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ break;
+ }
+ } // function WORKDAY()
+
+
+ /**
+ * DAYOFMONTH
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Day
+ */
+ public static function DAYOFMONTH($dateValue = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue == 0.0) {
+ return 0;
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('j');
+ } // function DAYOFMONTH()
+
+
+ /**
+ * DAYOFWEEK
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Day
+ */
+ public static function DAYOFWEEK($dateValue = 1, $style = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $style = floor(self::flattenSingleValue($style));
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $DoW = $PHPDateObject->format('w');
+
+ $firstDay = 1;
+ switch ($style) {
+ case 1: ++$DoW;
+ break;
+ case 2: if ($DoW == 0) { $DoW = 7; }
+ break;
+ case 3: if ($DoW == 0) { $DoW = 7; }
+ $firstDay = 0;
+ --$DoW;
+ break;
+ default:
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_EXCEL) {
+ // Test for Excel's 1900 leap year, and introduce the error as required
+ if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
+ --$DoW;
+ if ($DoW < $firstDay) {
+ $DoW += 7;
+ }
+ }
+ }
+
+ return (int) $DoW;
+ } // function DAYOFWEEK()
+
+
+ /**
+ * WEEKOFYEAR
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @param boolean $method Week begins on Sunday or Monday
+ * @return int Week Number
+ */
+ public static function WEEKOFYEAR($dateValue = 1, $method = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $method = floor(self::flattenSingleValue($method));
+
+ if (!is_numeric($method)) {
+ return self::$_errorCodes['value'];
+ } elseif (($method < 1) || ($method > 2)) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $dayOfYear = $PHPDateObject->format('z');
+ $dow = $PHPDateObject->format('w');
+ $PHPDateObject->modify('-'.$dayOfYear.' days');
+ $dow = $PHPDateObject->format('w');
+ $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
+ $dayOfYear -= $daysInFirstWeek;
+ $weekOfYear = ceil($dayOfYear / 7) + 1;
+
+ return (int) $weekOfYear;
+ } // function WEEKOFYEAR()
+
+
+ /**
+ * MONTHOFYEAR
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Month
+ */
+ public static function MONTHOFYEAR($dateValue = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('n');
+ } // function MONTHOFYEAR()
+
+
+ /**
+ * YEAR
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @return int Year
+ */
+ public static function YEAR($dateValue = 1) {
+ $dateValue = self::flattenSingleValue($dateValue);
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ } elseif ($dateValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('Y');
+ } // function YEAR()
+
+
+ /**
+ * HOUROFDAY
+ *
+ * @param mixed $timeValue Excel time serial value or a standard time string
+ * @return int Hour
+ */
+ public static function HOUROFDAY($timeValue = 0) {
+ $timeValue = self::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue,'/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $timeValue = self::_getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue,1);
+ } elseif ($timeValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('G',$timeValue);
+ } // function HOUROFDAY()
+
+
+ /**
+ * MINUTEOFHOUR
+ *
+ * @param long $timeValue Excel time serial value or a standard time string
+ * @return int Minute
+ */
+ public static function MINUTEOFHOUR($timeValue = 0) {
+ $timeValue = $timeTester = self::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue,'/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $timeValue = self::_getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue,1);
+ } elseif ($timeValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('i',$timeValue);
+ } // function MINUTEOFHOUR()
+
+
+ /**
+ * SECONDOFMINUTE
+ *
+ * @param long $timeValue Excel time serial value or a standard time string
+ * @return int Second
+ */
+ public static function SECONDOFMINUTE($timeValue = 0) {
+ $timeValue = self::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue,'/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $timeValue = self::_getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return self::$_errorCodes['value'];
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue,1);
+ } elseif ($timeValue < 0.0) {
+ return self::$_errorCodes['num'];
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('s',$timeValue);
+ } // function SECONDOFMINUTE()
+
+
+ private static function _adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0) {
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $oMonth = (int) $PHPDateObject->format('m');
+ $oYear = (int) $PHPDateObject->format('Y');
+
+ $adjustmentMonthsString = (string) $adjustmentMonths;
+ if ($adjustmentMonths > 0) {
+ $adjustmentMonthsString = '+'.$adjustmentMonths;
+ }
+ if ($adjustmentMonths != 0) {
+ $PHPDateObject->modify($adjustmentMonthsString.' months');
+ }
+ $nMonth = (int) $PHPDateObject->format('m');
+ $nYear = (int) $PHPDateObject->format('Y');
+
+ $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
+ if ($monthDiff != $adjustmentMonths) {
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-'.$adjustDays.' days';
+ $PHPDateObject->modify($adjustDaysString);
+ }
+ return $PHPDateObject;
+ } // function _adjustDateByMonths()
+
+
+ /**
+ * EDATE
+ *
+ * Returns the serial number that represents the date that is the indicated number of months before or after a specified date
+ * (the start_date). Use EDATE to calculate maturity dates or due dates that fall on the same day of the month as the date of issue.
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @param int $adjustmentMonths Number of months to adjust by
+ * @return long Excel date serial value
+ */
+ public static function EDATE($dateValue = 1, $adjustmentMonths = 0) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
+
+ if (!is_numeric($adjustmentMonths)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Execute function
+ $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths);
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
+ break;
+ }
+ } // function EDATE()
+
+
+ /**
+ * EOMONTH
+ *
+ * Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.
+ * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
+ *
+ * @param long $dateValue Excel date serial value or a standard date string
+ * @param int $adjustmentMonths Number of months to adjust by
+ * @return long Excel date serial value
+ */
+ public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) {
+ $dateValue = self::flattenSingleValue($dateValue);
+ $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
+
+ if (!is_numeric($adjustmentMonths)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_string($dateValue = self::_getDateValue($dateValue))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Execute function
+ $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths+1);
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-'.$adjustDays.' days';
+ $PHPDateObject->modify($adjustDaysString);
+
+ switch (self::getReturnDateType()) {
+ case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
+ break;
+ case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
+ break;
+ case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
+ break;
+ }
+ } // function EOMONTH()
+
+
+ /**
+ * TRUNC
+ *
+ * Truncates value to the number of fractional digits by number_digits.
+ *
+ * @param float $value
+ * @param int $number_digits
+ * @return float Truncated value
+ */
+ public static function TRUNC($value = 0, $number_digits = 0) {
+ $value = self::flattenSingleValue($value);
+ $number_digits = self::flattenSingleValue($number_digits);
+
+ // Validate parameters
+ if ($number_digits < 0) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Truncate
+ if ($number_digits > 0) {
+ $value = $value * pow(10, $number_digits);
+ }
+ $value = intval($value);
+ if ($number_digits > 0) {
+ $value = $value / pow(10, $number_digits);
+ }
+
+ // Return
+ return $value;
+ } // function TRUNC()
+
+ /**
+ * POWER
+ *
+ * Computes x raised to the power y.
+ *
+ * @param float $x
+ * @param float $y
+ * @return float
+ */
+ public static function POWER($x = 0, $y = 2) {
+ $x = self::flattenSingleValue($x);
+ $y = self::flattenSingleValue($y);
+
+ // Validate parameters
+ if ($x == 0 && $y <= 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ // Return
+ return pow($x, $y);
+ } // function POWER()
+
+
+ private static function _nbrConversionFormat($xVal,$places) {
+ if (!is_null($places)) {
+ if (strlen($xVal) <= $places) {
+ return substr(str_pad($xVal,$places,'0',STR_PAD_LEFT),-10);
+ } else {
+ return self::$_errorCodes['num'];
+ }
+ }
+
+ return substr($xVal,-10);
+ } // function _nbrConversionFormat()
+
+
+ /**
+ * BINTODEC
+ *
+ * Return a binary value as Decimal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function BINTODEC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
+ return self::$_errorCodes['num'];
+ }
+ if (strlen($x) > 10) {
+ return self::$_errorCodes['num'];
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ $x = substr($x,-9);
+ return '-'.(512-bindec($x));
+ }
+ return bindec($x);
+ } // function BINTODEC()
+
+
+ /**
+ * BINTOHEX
+ *
+ * Return a binary value as Hex.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function BINTOHEX($x, $places=null) {
+ $x = floor(self::flattenSingleValue($x));
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
+ return self::$_errorCodes['num'];
+ }
+ if (strlen($x) > 10) {
+ return self::$_errorCodes['num'];
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ return str_repeat('F',8).substr(strtoupper(dechex(bindec(substr($x,-9)))),-2);
+ }
+ $hexVal = (string) strtoupper(dechex(bindec($x)));
+
+ return self::_nbrConversionFormat($hexVal,$places);
+ } // function BINTOHEX()
+
+
+ /**
+ * BINTOOCT
+ *
+ * Return a binary value as Octal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function BINTOOCT($x, $places=null) {
+ $x = floor(self::flattenSingleValue($x));
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
+ return self::$_errorCodes['num'];
+ }
+ if (strlen($x) > 10) {
+ return self::$_errorCodes['num'];
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ return str_repeat('7',7).substr(strtoupper(decoct(bindec(substr($x,-9)))),-3);
+ }
+ $octVal = (string) decoct(bindec($x));
+
+ return self::_nbrConversionFormat($octVal,$places);
+ } // function BINTOOCT()
+
+
+ /**
+ * DECTOBIN
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function DECTOBIN($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) floor($x);
+ $r = decbin($x);
+ if (strlen($r) == 32) {
+ // Two's Complement
+ $r = substr($r,-10);
+ } elseif (strlen($r) > 11) {
+ return self::$_errorCodes['num'];
+ }
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function DECTOBIN()
+
+
+ /**
+ * DECTOOCT
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function DECTOOCT($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) floor($x);
+ $r = decoct($x);
+ if (strlen($r) == 11) {
+ // Two's Complement
+ $r = substr($r,-10);
+ }
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function DECTOOCT()
+
+
+ /**
+ * DECTOHEX
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function DECTOHEX($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) floor($x);
+ $r = strtoupper(dechex($x));
+ if (strlen($r) == 8) {
+ // Two's Complement
+ $r = 'FF'.$r;
+ }
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function DECTOHEX()
+
+
+ /**
+ * HEXTOBIN
+ *
+ * Return a hex value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function HEXTOBIN($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
+ return self::$_errorCodes['num'];
+ }
+ $binVal = decbin(hexdec($x));
+
+ return substr(self::_nbrConversionFormat($binVal,$places),-10);
+ } // function HEXTOBIN()
+
+
+ /**
+ * HEXTOOCT
+ *
+ * Return a hex value as octal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function HEXTOOCT($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
+ return self::$_errorCodes['num'];
+ }
+ $octVal = decoct(hexdec($x));
+
+ return self::_nbrConversionFormat($octVal,$places);
+ } // function HEXTOOCT()
+
+
+ /**
+ * HEXTODEC
+ *
+ * Return a hex value as octal.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function HEXTODEC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
+ return self::$_errorCodes['num'];
+ }
+ return hexdec($x);
+ } // function HEXTODEC()
+
+
+ /**
+ * OCTTOBIN
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function OCTTOBIN($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
+ return self::$_errorCodes['num'];
+ }
+ $r = decbin(octdec($x));
+
+ return self::_nbrConversionFormat($r,$places);
+ } // function OCTTOBIN()
+
+
+ /**
+ * OCTTODEC
+ *
+ * Return an octal value as binary.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function OCTTODEC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
+ return self::$_errorCodes['num'];
+ }
+ return octdec($x);
+ } // function OCTTODEC()
+
+
+ /**
+ * OCTTOHEX
+ *
+ * Return an octal value as hex.
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function OCTTOHEX($x, $places=null) {
+ $x = self::flattenSingleValue($x);
+ $places = self::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return self::$_errorCodes['value'];
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
+ return self::$_errorCodes['num'];
+ }
+ $hexVal = strtoupper(dechex(octdec($x)));
+
+ return self::_nbrConversionFormat($hexVal,$places);
+ } // function OCTTOHEX()
+
+
+ public static function _parseComplex($complexNumber) {
+ $workString = (string) $complexNumber;
+
+ $realNumber = $imaginary = 0;
+ // Extract the suffix, if there is one
+ $suffix = substr($workString,-1);
+ if (!is_numeric($suffix)) {
+ $workString = substr($workString,0,-1);
+ } else {
+ $suffix = '';
+ }
+
+ // Split the input into its Real and Imaginary components
+ $leadingSign = 0;
+ if (strlen($workString) > 0) {
+ $leadingSign = (($workString{0} == '+') || ($workString{0} == '-')) ? 1 : 0;
+ }
+ $power = '';
+ $realNumber = strtok($workString, '+-');
+ if (strtoupper(substr($realNumber,-1)) == 'E') {
+ $power = strtok('+-');
+ ++$leadingSign;
+ }
+
+ $realNumber = substr($workString,0,strlen($realNumber)+strlen($power)+$leadingSign);
+
+ if ($suffix != '') {
+ $imaginary = substr($workString,strlen($realNumber));
+
+ if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
+ $imaginary = $realNumber.'1';
+ $realNumber = '0';
+ } else if ($imaginary == '') {
+ $imaginary = $realNumber;
+ $realNumber = '0';
+ } elseif (($imaginary == '+') || ($imaginary == '-')) {
+ $imaginary .= '1';
+ }
+ }
+
+ $complexArray = array( 'real' => $realNumber,
+ 'imaginary' => $imaginary,
+ 'suffix' => $suffix
+ );
+
+ return $complexArray;
+ } // function _parseComplex()
+
+
+ private static function _cleanComplex($complexNumber) {
+ if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
+ if ($complexNumber{0} == '0') $complexNumber = substr($complexNumber,1);
+ if ($complexNumber{0} == '.') $complexNumber = '0'.$complexNumber;
+ if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
+ return $complexNumber;
+ }
+
+
+ /**
+ * COMPLEX
+ *
+ * returns a complex number of the form x + yi or x + yj.
+ *
+ * @param float $realNumber
+ * @param float $imaginary
+ * @param string $suffix
+ * @return string
+ */
+ public static function COMPLEX($realNumber=0.0, $imaginary=0.0, $suffix='i') {
+ $realNumber = self::flattenSingleValue($realNumber);
+ $imaginary = self::flattenSingleValue($imaginary);
+ $suffix = self::flattenSingleValue($suffix);
+
+ if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
+ (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))) {
+ if ($realNumber == 0.0) {
+ if ($imaginary == 0.0) {
+ return (string) '0';
+ } elseif ($imaginary == 1.0) {
+ return (string) $suffix;
+ } elseif ($imaginary == -1.0) {
+ return (string) '-'.$suffix;
+ }
+ return (string) $imaginary.$suffix;
+ } elseif ($imaginary == 0.0) {
+ return (string) $realNumber;
+ } elseif ($imaginary == 1.0) {
+ return (string) $realNumber.'+'.$suffix;
+ } elseif ($imaginary == -1.0) {
+ return (string) $realNumber.'-'.$suffix;
+ }
+ if ($imaginary > 0) { $imaginary = (string) '+'.$imaginary; }
+ return (string) $realNumber.$imaginary.$suffix;
+ }
+ return self::$_errorCodes['value'];
+ } // function COMPLEX()
+
+
+ /**
+ * IMAGINARY
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return real
+ */
+ public static function IMAGINARY($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ return $parsedComplex['imaginary'];
+ } // function IMAGINARY()
+
+
+ /**
+ * IMREAL
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return real
+ */
+ public static function IMREAL($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ return $parsedComplex['real'];
+ } // function IMREAL()
+
+
+ /**
+ * IMABS
+ *
+ * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return real
+ */
+ public static function IMABS($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ return sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
+ } // function IMABS()
+
+
+ /**
+ * IMARGUMENT
+ *
+ * Returns the argument theta of a complex number, i.e. the angle in radians from the real axis to the representation of the number in polar coordinates.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMARGUMENT($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['real'] == 0.0) {
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return 0.0;
+ } elseif($parsedComplex['imaginary'] < 0.0) {
+ return M_PI / -2;
+ } else {
+ return M_PI / 2;
+ }
+ } elseif ($parsedComplex['real'] > 0.0) {
+ return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
+ } elseif ($parsedComplex['imaginary'] < 0.0) {
+ return 0 - (M_PI - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
+ } else {
+ return M_PI - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
+ }
+ } // function IMARGUMENT()
+
+
+ /**
+ * IMCONJUGATE
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMCONJUGATE($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return $parsedComplex['real'];
+ } else {
+ return self::_cleanComplex(self::COMPLEX($parsedComplex['real'], 0 - $parsedComplex['imaginary'], $parsedComplex['suffix']));
+ }
+ } // function IMCONJUGATE()
+
+
+ /**
+ * IMCOS
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMCOS($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return cos($parsedComplex['real']);
+ } else {
+ return self::IMCONJUGATE(self::COMPLEX(cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']));
+ }
+ } // function IMCOS()
+
+
+ /**
+ * IMSIN
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMSIN($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return sin($parsedComplex['real']);
+ } else {
+ return self::COMPLEX(sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']);
+ }
+ } // function IMSIN()
+
+
+ /**
+ * IMSQRT
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMSQRT($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ $theta = self::IMARGUMENT($complexNumber);
+ $d1 = cos($theta / 2);
+ $d2 = sin($theta / 2);
+ $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($d1 * $r,$d2 * $r);
+ } else {
+ return self::COMPLEX($d1 * $r,$d2 * $r,$parsedComplex['suffix']);
+ }
+ } // function IMSQRT()
+
+
+ /**
+ * IMLN
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMLN($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return self::$_errorCodes['num'];
+ }
+
+ $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
+ $t = self::IMARGUMENT($complexNumber);
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($logR,$t);
+ } else {
+ return self::COMPLEX($logR,$t,$parsedComplex['suffix']);
+ }
+ } // function IMLN()
+
+
+ /**
+ * IMLOG10
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMLOG10($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return self::$_errorCodes['num'];
+ } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return log10($parsedComplex['real']);
+ }
+
+ return self::IMPRODUCT(log10(EULER),self::IMLN($complexNumber));
+ } // function IMLOG10()
+
+
+ /**
+ * IMLOG2
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMLOG2($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return self::$_errorCodes['num'];
+ } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return log($parsedComplex['real'],2);
+ }
+
+ return self::IMPRODUCT(log(EULER,2),self::IMLN($complexNumber));
+ } // function IMLOG2()
+
+
+ /**
+ * IMEXP
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMEXP($complexNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return '1';
+ }
+
+ $e = exp($parsedComplex['real']);
+ $eX = $e * cos($parsedComplex['imaginary']);
+ $eY = $e * sin($parsedComplex['imaginary']);
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($eX,$eY);
+ } else {
+ return self::COMPLEX($eX,$eY,$parsedComplex['suffix']);
+ }
+ } // function IMEXP()
+
+
+ /**
+ * IMPOWER
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * @param string $complexNumber
+ * @return string
+ */
+ public static function IMPOWER($complexNumber,$realNumber) {
+ $complexNumber = self::flattenSingleValue($complexNumber);
+ $realNumber = self::flattenSingleValue($realNumber);
+
+ if (!is_numeric($realNumber)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $parsedComplex = self::_parseComplex($complexNumber);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
+ $rPower = pow($r,$realNumber);
+ $theta = self::IMARGUMENT($complexNumber) * $realNumber;
+ if ($theta == 0) {
+ return 1;
+ } elseif ($parsedComplex['imaginary'] == 0.0) {
+ return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
+ } else {
+ return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
+ }
+ } // function IMPOWER()
+
+
+ /**
+ * IMDIV
+ *
+ * Returns the quotient of two complex numbers in x + yi or x + yj text format.
+ *
+ * @param string $complexDividend
+ * @param string $complexDivisor
+ * @return real
+ */
+ public static function IMDIV($complexDividend,$complexDivisor) {
+ $complexDividend = self::flattenSingleValue($complexDividend);
+ $complexDivisor = self::flattenSingleValue($complexDivisor);
+
+ $parsedComplexDividend = self::_parseComplex($complexDividend);
+ if (!is_array($parsedComplexDividend)) {
+ return $parsedComplexDividend;
+ }
+
+ $parsedComplexDivisor = self::_parseComplex($complexDivisor);
+ if (!is_array($parsedComplexDivisor)) {
+ return $parsedComplexDividend;
+ }
+
+ if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
+ ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])) {
+ return self::$_errorCodes['num'];
+ }
+ if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
+ $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
+ }
+
+ $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
+ $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
+ $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
+
+ $r = $d1/$d3;
+ $i = $d2/$d3;
+
+ if ($i > 0.0) {
+ return self::_cleanComplex($r.'+'.$i.$parsedComplexDivisor['suffix']);
+ } elseif ($i < 0.0) {
+ return self::_cleanComplex($r.$i.$parsedComplexDivisor['suffix']);
+ } else {
+ return $r;
+ }
+ } // function IMDIV()
+
+
+ /**
+ * IMSUB
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * @param string $complexNumber1
+ * @param string $complexNumber2
+ * @return real
+ */
+ public static function IMSUB($complexNumber1,$complexNumber2) {
+ $complexNumber1 = self::flattenSingleValue($complexNumber1);
+ $complexNumber2 = self::flattenSingleValue($complexNumber2);
+
+ $parsedComplex1 = self::_parseComplex($complexNumber1);
+ if (!is_array($parsedComplex1)) {
+ return $parsedComplex1;
+ }
+
+ $parsedComplex2 = self::_parseComplex($complexNumber2);
+ if (!is_array($parsedComplex2)) {
+ return $parsedComplex2;
+ }
+
+ if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
+ ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])) {
+ return self::$_errorCodes['num'];
+ } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
+ $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
+ }
+
+ $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
+ $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
+
+ return self::COMPLEX($d1,$d2,$parsedComplex1['suffix']);
+ } // function IMSUB()
+
+
+ /**
+ * IMSUM
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * @param array of mixed Data Series
+ * @return real
+ */
+ public static function IMSUM() {
+ // Return value
+ $returnValue = self::_parseComplex('0');
+ $activeSuffix = '';
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ $parsedComplex = self::_parseComplex($arg);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+
+ if ($activeSuffix == '') {
+ $activeSuffix = $parsedComplex['suffix'];
+ } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
+ return self::$_errorCodes['value'];
+ }
+
+ $returnValue['real'] += $parsedComplex['real'];
+ $returnValue['imaginary'] += $parsedComplex['imaginary'];
+ }
+
+ if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
+ return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
+ } // function IMSUM()
+
+
+ /**
+ * IMPRODUCT
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * @param array of mixed Data Series
+ * @return real
+ */
+ public static function IMPRODUCT() {
+ // Return value
+ $returnValue = self::_parseComplex('1');
+ $activeSuffix = '';
+
+ // Loop through the arguments
+ $aArgs = self::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ $parsedComplex = self::_parseComplex($arg);
+ if (!is_array($parsedComplex)) {
+ return $parsedComplex;
+ }
+ $workValue = $returnValue;
+ if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
+ $activeSuffix = $parsedComplex['suffix'];
+ } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
+ return self::$_errorCodes['num'];
+ }
+ $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
+ $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
+ }
+
+ if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
+ return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
+ } // function IMPRODUCT()
+
+
+ private static $_conversionUnits = array( 'g' => array( 'Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => True ),
+ 'sg' => array( 'Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => False ),
+ 'lbm' => array( 'Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => False ),
+ 'u' => array( 'Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => True ),
+ 'ozm' => array( 'Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => False ),
+ 'm' => array( 'Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => True ),
+ 'mi' => array( 'Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => False ),
+ 'Nmi' => array( 'Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => False ),
+ 'in' => array( 'Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => False ),
+ 'ft' => array( 'Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => False ),
+ 'yd' => array( 'Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => False ),
+ 'ang' => array( 'Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => True ),
+ 'Pica' => array( 'Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => False ),
+ 'yr' => array( 'Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => False ),
+ 'day' => array( 'Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => False ),
+ 'hr' => array( 'Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => False ),
+ 'mn' => array( 'Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => False ),
+ 'sec' => array( 'Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => True ),
+ 'Pa' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
+ 'p' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
+ 'atm' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
+ 'at' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
+ 'mmHg' => array( 'Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => True ),
+ 'N' => array( 'Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => True ),
+ 'dyn' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
+ 'dy' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
+ 'lbf' => array( 'Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => False ),
+ 'J' => array( 'Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => True ),
+ 'e' => array( 'Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => True ),
+ 'c' => array( 'Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => True ),
+ 'cal' => array( 'Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => True ),
+ 'eV' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
+ 'ev' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
+ 'HPh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
+ 'hh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
+ 'Wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
+ 'wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
+ 'flb' => array( 'Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => False ),
+ 'BTU' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
+ 'btu' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
+ 'HP' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
+ 'h' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
+ 'W' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
+ 'w' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
+ 'T' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => True ),
+ 'ga' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => True ),
+ 'C' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
+ 'cel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
+ 'F' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
+ 'fah' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
+ 'K' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
+ 'kel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
+ 'tsp' => array( 'Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => False ),
+ 'tbs' => array( 'Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => False ),
+ 'oz' => array( 'Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => False ),
+ 'cup' => array( 'Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => False ),
+ 'pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
+ 'us_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
+ 'uk_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => False ),
+ 'qt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => False ),
+ 'gal' => array( 'Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => False ),
+ 'l' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True ),
+ 'lt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True )
+ );
+
+ private static $_conversionMultipliers = array( 'Y' => array( 'multiplier' => 1E24, 'name' => 'yotta' ),
+ 'Z' => array( 'multiplier' => 1E21, 'name' => 'zetta' ),
+ 'E' => array( 'multiplier' => 1E18, 'name' => 'exa' ),
+ 'P' => array( 'multiplier' => 1E15, 'name' => 'peta' ),
+ 'T' => array( 'multiplier' => 1E12, 'name' => 'tera' ),
+ 'G' => array( 'multiplier' => 1E9, 'name' => 'giga' ),
+ 'M' => array( 'multiplier' => 1E6, 'name' => 'mega' ),
+ 'k' => array( 'multiplier' => 1E3, 'name' => 'kilo' ),
+ 'h' => array( 'multiplier' => 1E2, 'name' => 'hecto' ),
+ 'e' => array( 'multiplier' => 1E1, 'name' => 'deka' ),
+ 'd' => array( 'multiplier' => 1E-1, 'name' => 'deci' ),
+ 'c' => array( 'multiplier' => 1E-2, 'name' => 'centi' ),
+ 'm' => array( 'multiplier' => 1E-3, 'name' => 'milli' ),
+ 'u' => array( 'multiplier' => 1E-6, 'name' => 'micro' ),
+ 'n' => array( 'multiplier' => 1E-9, 'name' => 'nano' ),
+ 'p' => array( 'multiplier' => 1E-12, 'name' => 'pico' ),
+ 'f' => array( 'multiplier' => 1E-15, 'name' => 'femto' ),
+ 'a' => array( 'multiplier' => 1E-18, 'name' => 'atto' ),
+ 'z' => array( 'multiplier' => 1E-21, 'name' => 'zepto' ),
+ 'y' => array( 'multiplier' => 1E-24, 'name' => 'yocto' )
+ );
+
+ private static $_unitConversions = array( 'Mass' => array( 'g' => array( 'g' => 1.0,
+ 'sg' => 6.85220500053478E-05,
+ 'lbm' => 2.20462291469134E-03,
+ 'u' => 6.02217000000000E+23,
+ 'ozm' => 3.52739718003627E-02
+ ),
+ 'sg' => array( 'g' => 1.45938424189287E+04,
+ 'sg' => 1.0,
+ 'lbm' => 3.21739194101647E+01,
+ 'u' => 8.78866000000000E+27,
+ 'ozm' => 5.14782785944229E+02
+ ),
+ 'lbm' => array( 'g' => 4.5359230974881148E+02,
+ 'sg' => 3.10810749306493E-02,
+ 'lbm' => 1.0,
+ 'u' => 2.73161000000000E+26,
+ 'ozm' => 1.60000023429410E+01
+ ),
+ 'u' => array( 'g' => 1.66053100460465E-24,
+ 'sg' => 1.13782988532950E-28,
+ 'lbm' => 3.66084470330684E-27,
+ 'u' => 1.0,
+ 'ozm' => 5.85735238300524E-26
+ ),
+ 'ozm' => array( 'g' => 2.83495152079732E+01,
+ 'sg' => 1.94256689870811E-03,
+ 'lbm' => 6.24999908478882E-02,
+ 'u' => 1.70725600000000E+25,
+ 'ozm' => 1.0
+ )
+ ),
+ 'Distance' => array( 'm' => array( 'm' => 1.0,
+ 'mi' => 6.21371192237334E-04,
+ 'Nmi' => 5.39956803455724E-04,
+ 'in' => 3.93700787401575E+01,
+ 'ft' => 3.28083989501312E+00,
+ 'yd' => 1.09361329797891E+00,
+ 'ang' => 1.00000000000000E+10,
+ 'Pica' => 2.83464566929116E+03
+ ),
+ 'mi' => array( 'm' => 1.60934400000000E+03,
+ 'mi' => 1.0,
+ 'Nmi' => 8.68976241900648E-01,
+ 'in' => 6.33600000000000E+04,
+ 'ft' => 5.28000000000000E+03,
+ 'yd' => 1.76000000000000E+03,
+ 'ang' => 1.60934400000000E+13,
+ 'Pica' => 4.56191999999971E+06
+ ),
+ 'Nmi' => array( 'm' => 1.85200000000000E+03,
+ 'mi' => 1.15077944802354E+00,
+ 'Nmi' => 1.0,
+ 'in' => 7.29133858267717E+04,
+ 'ft' => 6.07611548556430E+03,
+ 'yd' => 2.02537182785694E+03,
+ 'ang' => 1.85200000000000E+13,
+ 'Pica' => 5.24976377952723E+06
+ ),
+ 'in' => array( 'm' => 2.54000000000000E-02,
+ 'mi' => 1.57828282828283E-05,
+ 'Nmi' => 1.37149028077754E-05,
+ 'in' => 1.0,
+ 'ft' => 8.33333333333333E-02,
+ 'yd' => 2.77777777686643E-02,
+ 'ang' => 2.54000000000000E+08,
+ 'Pica' => 7.19999999999955E+01
+ ),
+ 'ft' => array( 'm' => 3.04800000000000E-01,
+ 'mi' => 1.89393939393939E-04,
+ 'Nmi' => 1.64578833693305E-04,
+ 'in' => 1.20000000000000E+01,
+ 'ft' => 1.0,
+ 'yd' => 3.33333333223972E-01,
+ 'ang' => 3.04800000000000E+09,
+ 'Pica' => 8.63999999999946E+02
+ ),
+ 'yd' => array( 'm' => 9.14400000300000E-01,
+ 'mi' => 5.68181818368230E-04,
+ 'Nmi' => 4.93736501241901E-04,
+ 'in' => 3.60000000118110E+01,
+ 'ft' => 3.00000000000000E+00,
+ 'yd' => 1.0,
+ 'ang' => 9.14400000300000E+09,
+ 'Pica' => 2.59200000085023E+03
+ ),
+ 'ang' => array( 'm' => 1.00000000000000E-10,
+ 'mi' => 6.21371192237334E-14,
+ 'Nmi' => 5.39956803455724E-14,
+ 'in' => 3.93700787401575E-09,
+ 'ft' => 3.28083989501312E-10,
+ 'yd' => 1.09361329797891E-10,
+ 'ang' => 1.0,
+ 'Pica' => 2.83464566929116E-07
+ ),
+ 'Pica' => array( 'm' => 3.52777777777800E-04,
+ 'mi' => 2.19205948372629E-07,
+ 'Nmi' => 1.90484761219114E-07,
+ 'in' => 1.38888888888898E-02,
+ 'ft' => 1.15740740740748E-03,
+ 'yd' => 3.85802469009251E-04,
+ 'ang' => 3.52777777777800E+06,
+ 'Pica' => 1.0
+ )
+ ),
+ 'Time' => array( 'yr' => array( 'yr' => 1.0,
+ 'day' => 365.25,
+ 'hr' => 8766.0,
+ 'mn' => 525960.0,
+ 'sec' => 31557600.0
+ ),
+ 'day' => array( 'yr' => 2.73785078713210E-03,
+ 'day' => 1.0,
+ 'hr' => 24.0,
+ 'mn' => 1440.0,
+ 'sec' => 86400.0
+ ),
+ 'hr' => array( 'yr' => 1.14077116130504E-04,
+ 'day' => 4.16666666666667E-02,
+ 'hr' => 1.0,
+ 'mn' => 60.0,
+ 'sec' => 3600.0
+ ),
+ 'mn' => array( 'yr' => 1.90128526884174E-06,
+ 'day' => 6.94444444444444E-04,
+ 'hr' => 1.66666666666667E-02,
+ 'mn' => 1.0,
+ 'sec' => 60.0
+ ),
+ 'sec' => array( 'yr' => 3.16880878140289E-08,
+ 'day' => 1.15740740740741E-05,
+ 'hr' => 2.77777777777778E-04,
+ 'mn' => 1.66666666666667E-02,
+ 'sec' => 1.0
+ )
+ ),
+ 'Pressure' => array( 'Pa' => array( 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923299998193E-06,
+ 'at' => 9.86923299998193E-06,
+ 'mmHg' => 7.50061707998627E-03
+ ),
+ 'p' => array( 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923299998193E-06,
+ 'at' => 9.86923299998193E-06,
+ 'mmHg' => 7.50061707998627E-03
+ ),
+ 'atm' => array( 'Pa' => 1.01324996583000E+05,
+ 'p' => 1.01324996583000E+05,
+ 'atm' => 1.0,
+ 'at' => 1.0,
+ 'mmHg' => 760.0
+ ),
+ 'at' => array( 'Pa' => 1.01324996583000E+05,
+ 'p' => 1.01324996583000E+05,
+ 'atm' => 1.0,
+ 'at' => 1.0,
+ 'mmHg' => 760.0
+ ),
+ 'mmHg' => array( 'Pa' => 1.33322363925000E+02,
+ 'p' => 1.33322363925000E+02,
+ 'atm' => 1.31578947368421E-03,
+ 'at' => 1.31578947368421E-03,
+ 'mmHg' => 1.0
+ )
+ ),
+ 'Force' => array( 'N' => array( 'N' => 1.0,
+ 'dyn' => 1.0E+5,
+ 'dy' => 1.0E+5,
+ 'lbf' => 2.24808923655339E-01
+ ),
+ 'dyn' => array( 'N' => 1.0E-5,
+ 'dyn' => 1.0,
+ 'dy' => 1.0,
+ 'lbf' => 2.24808923655339E-06
+ ),
+ 'dy' => array( 'N' => 1.0E-5,
+ 'dyn' => 1.0,
+ 'dy' => 1.0,
+ 'lbf' => 2.24808923655339E-06
+ ),
+ 'lbf' => array( 'N' => 4.448222,
+ 'dyn' => 4.448222E+5,
+ 'dy' => 4.448222E+5,
+ 'lbf' => 1.0
+ )
+ ),
+ 'Energy' => array( 'J' => array( 'J' => 1.0,
+ 'e' => 9.99999519343231E+06,
+ 'c' => 2.39006249473467E-01,
+ 'cal' => 2.38846190642017E-01,
+ 'eV' => 6.24145700000000E+18,
+ 'ev' => 6.24145700000000E+18,
+ 'HPh' => 3.72506430801000E-07,
+ 'hh' => 3.72506430801000E-07,
+ 'Wh' => 2.77777916238711E-04,
+ 'wh' => 2.77777916238711E-04,
+ 'flb' => 2.37304222192651E+01,
+ 'BTU' => 9.47815067349015E-04,
+ 'btu' => 9.47815067349015E-04
+ ),
+ 'e' => array( 'J' => 1.00000048065700E-07,
+ 'e' => 1.0,
+ 'c' => 2.39006364353494E-08,
+ 'cal' => 2.38846305445111E-08,
+ 'eV' => 6.24146000000000E+11,
+ 'ev' => 6.24146000000000E+11,
+ 'HPh' => 3.72506609848824E-14,
+ 'hh' => 3.72506609848824E-14,
+ 'Wh' => 2.77778049754611E-11,
+ 'wh' => 2.77778049754611E-11,
+ 'flb' => 2.37304336254586E-06,
+ 'BTU' => 9.47815522922962E-11,
+ 'btu' => 9.47815522922962E-11
+ ),
+ 'c' => array( 'J' => 4.18399101363672E+00,
+ 'e' => 4.18398900257312E+07,
+ 'c' => 1.0,
+ 'cal' => 9.99330315287563E-01,
+ 'eV' => 2.61142000000000E+19,
+ 'ev' => 2.61142000000000E+19,
+ 'HPh' => 1.55856355899327E-06,
+ 'hh' => 1.55856355899327E-06,
+ 'Wh' => 1.16222030532950E-03,
+ 'wh' => 1.16222030532950E-03,
+ 'flb' => 9.92878733152102E+01,
+ 'BTU' => 3.96564972437776E-03,
+ 'btu' => 3.96564972437776E-03
+ ),
+ 'cal' => array( 'J' => 4.18679484613929E+00,
+ 'e' => 4.18679283372801E+07,
+ 'c' => 1.00067013349059E+00,
+ 'cal' => 1.0,
+ 'eV' => 2.61317000000000E+19,
+ 'ev' => 2.61317000000000E+19,
+ 'HPh' => 1.55960800463137E-06,
+ 'hh' => 1.55960800463137E-06,
+ 'Wh' => 1.16299914807955E-03,
+ 'wh' => 1.16299914807955E-03,
+ 'flb' => 9.93544094443283E+01,
+ 'BTU' => 3.96830723907002E-03,
+ 'btu' => 3.96830723907002E-03
+ ),
+ 'eV' => array( 'J' => 1.60219000146921E-19,
+ 'e' => 1.60218923136574E-12,
+ 'c' => 3.82933423195043E-20,
+ 'cal' => 3.82676978535648E-20,
+ 'eV' => 1.0,
+ 'ev' => 1.0,
+ 'HPh' => 5.96826078912344E-26,
+ 'hh' => 5.96826078912344E-26,
+ 'Wh' => 4.45053000026614E-23,
+ 'wh' => 4.45053000026614E-23,
+ 'flb' => 3.80206452103492E-18,
+ 'BTU' => 1.51857982414846E-22,
+ 'btu' => 1.51857982414846E-22
+ ),
+ 'ev' => array( 'J' => 1.60219000146921E-19,
+ 'e' => 1.60218923136574E-12,
+ 'c' => 3.82933423195043E-20,
+ 'cal' => 3.82676978535648E-20,
+ 'eV' => 1.0,
+ 'ev' => 1.0,
+ 'HPh' => 5.96826078912344E-26,
+ 'hh' => 5.96826078912344E-26,
+ 'Wh' => 4.45053000026614E-23,
+ 'wh' => 4.45053000026614E-23,
+ 'flb' => 3.80206452103492E-18,
+ 'BTU' => 1.51857982414846E-22,
+ 'btu' => 1.51857982414846E-22
+ ),
+ 'HPh' => array( 'J' => 2.68451741316170E+06,
+ 'e' => 2.68451612283024E+13,
+ 'c' => 6.41616438565991E+05,
+ 'cal' => 6.41186757845835E+05,
+ 'eV' => 1.67553000000000E+25,
+ 'ev' => 1.67553000000000E+25,
+ 'HPh' => 1.0,
+ 'hh' => 1.0,
+ 'Wh' => 7.45699653134593E+02,
+ 'wh' => 7.45699653134593E+02,
+ 'flb' => 6.37047316692964E+07,
+ 'BTU' => 2.54442605275546E+03,
+ 'btu' => 2.54442605275546E+03
+ ),
+ 'hh' => array( 'J' => 2.68451741316170E+06,
+ 'e' => 2.68451612283024E+13,
+ 'c' => 6.41616438565991E+05,
+ 'cal' => 6.41186757845835E+05,
+ 'eV' => 1.67553000000000E+25,
+ 'ev' => 1.67553000000000E+25,
+ 'HPh' => 1.0,
+ 'hh' => 1.0,
+ 'Wh' => 7.45699653134593E+02,
+ 'wh' => 7.45699653134593E+02,
+ 'flb' => 6.37047316692964E+07,
+ 'BTU' => 2.54442605275546E+03,
+ 'btu' => 2.54442605275546E+03
+ ),
+ 'Wh' => array( 'J' => 3.59999820554720E+03,
+ 'e' => 3.59999647518369E+10,
+ 'c' => 8.60422069219046E+02,
+ 'cal' => 8.59845857713046E+02,
+ 'eV' => 2.24692340000000E+22,
+ 'ev' => 2.24692340000000E+22,
+ 'HPh' => 1.34102248243839E-03,
+ 'hh' => 1.34102248243839E-03,
+ 'Wh' => 1.0,
+ 'wh' => 1.0,
+ 'flb' => 8.54294774062316E+04,
+ 'BTU' => 3.41213254164705E+00,
+ 'btu' => 3.41213254164705E+00
+ ),
+ 'wh' => array( 'J' => 3.59999820554720E+03,
+ 'e' => 3.59999647518369E+10,
+ 'c' => 8.60422069219046E+02,
+ 'cal' => 8.59845857713046E+02,
+ 'eV' => 2.24692340000000E+22,
+ 'ev' => 2.24692340000000E+22,
+ 'HPh' => 1.34102248243839E-03,
+ 'hh' => 1.34102248243839E-03,
+ 'Wh' => 1.0,
+ 'wh' => 1.0,
+ 'flb' => 8.54294774062316E+04,
+ 'BTU' => 3.41213254164705E+00,
+ 'btu' => 3.41213254164705E+00
+ ),
+ 'flb' => array( 'J' => 4.21400003236424E-02,
+ 'e' => 4.21399800687660E+05,
+ 'c' => 1.00717234301644E-02,
+ 'cal' => 1.00649785509554E-02,
+ 'eV' => 2.63015000000000E+17,
+ 'ev' => 2.63015000000000E+17,
+ 'HPh' => 1.56974211145130E-08,
+ 'hh' => 1.56974211145130E-08,
+ 'Wh' => 1.17055614802000E-05,
+ 'wh' => 1.17055614802000E-05,
+ 'flb' => 1.0,
+ 'BTU' => 3.99409272448406E-05,
+ 'btu' => 3.99409272448406E-05
+ ),
+ 'BTU' => array( 'J' => 1.05505813786749E+03,
+ 'e' => 1.05505763074665E+10,
+ 'c' => 2.52165488508168E+02,
+ 'cal' => 2.51996617135510E+02,
+ 'eV' => 6.58510000000000E+21,
+ 'ev' => 6.58510000000000E+21,
+ 'HPh' => 3.93015941224568E-04,
+ 'hh' => 3.93015941224568E-04,
+ 'Wh' => 2.93071851047526E-01,
+ 'wh' => 2.93071851047526E-01,
+ 'flb' => 2.50369750774671E+04,
+ 'BTU' => 1.0,
+ 'btu' => 1.0,
+ ),
+ 'btu' => array( 'J' => 1.05505813786749E+03,
+ 'e' => 1.05505763074665E+10,
+ 'c' => 2.52165488508168E+02,
+ 'cal' => 2.51996617135510E+02,
+ 'eV' => 6.58510000000000E+21,
+ 'ev' => 6.58510000000000E+21,
+ 'HPh' => 3.93015941224568E-04,
+ 'hh' => 3.93015941224568E-04,
+ 'Wh' => 2.93071851047526E-01,
+ 'wh' => 2.93071851047526E-01,
+ 'flb' => 2.50369750774671E+04,
+ 'BTU' => 1.0,
+ 'btu' => 1.0,
+ )
+ ),
+ 'Power' => array( 'HP' => array( 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45701000000000E+02,
+ 'w' => 7.45701000000000E+02
+ ),
+ 'h' => array( 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45701000000000E+02,
+ 'w' => 7.45701000000000E+02
+ ),
+ 'W' => array( 'HP' => 1.34102006031908E-03,
+ 'h' => 1.34102006031908E-03,
+ 'W' => 1.0,
+ 'w' => 1.0
+ ),
+ 'w' => array( 'HP' => 1.34102006031908E-03,
+ 'h' => 1.34102006031908E-03,
+ 'W' => 1.0,
+ 'w' => 1.0
+ )
+ ),
+ 'Magnetism' => array( 'T' => array( 'T' => 1.0,
+ 'ga' => 10000.0
+ ),
+ 'ga' => array( 'T' => 0.0001,
+ 'ga' => 1.0
+ )
+ ),
+ 'Liquid' => array( 'tsp' => array( 'tsp' => 1.0,
+ 'tbs' => 3.33333333333333E-01,
+ 'oz' => 1.66666666666667E-01,
+ 'cup' => 2.08333333333333E-02,
+ 'pt' => 1.04166666666667E-02,
+ 'us_pt' => 1.04166666666667E-02,
+ 'uk_pt' => 8.67558516821960E-03,
+ 'qt' => 5.20833333333333E-03,
+ 'gal' => 1.30208333333333E-03,
+ 'l' => 4.92999408400710E-03,
+ 'lt' => 4.92999408400710E-03
+ ),
+ 'tbs' => array( 'tsp' => 3.00000000000000E+00,
+ 'tbs' => 1.0,
+ 'oz' => 5.00000000000000E-01,
+ 'cup' => 6.25000000000000E-02,
+ 'pt' => 3.12500000000000E-02,
+ 'us_pt' => 3.12500000000000E-02,
+ 'uk_pt' => 2.60267555046588E-02,
+ 'qt' => 1.56250000000000E-02,
+ 'gal' => 3.90625000000000E-03,
+ 'l' => 1.47899822520213E-02,
+ 'lt' => 1.47899822520213E-02
+ ),
+ 'oz' => array( 'tsp' => 6.00000000000000E+00,
+ 'tbs' => 2.00000000000000E+00,
+ 'oz' => 1.0,
+ 'cup' => 1.25000000000000E-01,
+ 'pt' => 6.25000000000000E-02,
+ 'us_pt' => 6.25000000000000E-02,
+ 'uk_pt' => 5.20535110093176E-02,
+ 'qt' => 3.12500000000000E-02,
+ 'gal' => 7.81250000000000E-03,
+ 'l' => 2.95799645040426E-02,
+ 'lt' => 2.95799645040426E-02
+ ),
+ 'cup' => array( 'tsp' => 4.80000000000000E+01,
+ 'tbs' => 1.60000000000000E+01,
+ 'oz' => 8.00000000000000E+00,
+ 'cup' => 1.0,
+ 'pt' => 5.00000000000000E-01,
+ 'us_pt' => 5.00000000000000E-01,
+ 'uk_pt' => 4.16428088074541E-01,
+ 'qt' => 2.50000000000000E-01,
+ 'gal' => 6.25000000000000E-02,
+ 'l' => 2.36639716032341E-01,
+ 'lt' => 2.36639716032341E-01
+ ),
+ 'pt' => array( 'tsp' => 9.60000000000000E+01,
+ 'tbs' => 3.20000000000000E+01,
+ 'oz' => 1.60000000000000E+01,
+ 'cup' => 2.00000000000000E+00,
+ 'pt' => 1.0,
+ 'us_pt' => 1.0,
+ 'uk_pt' => 8.32856176149081E-01,
+ 'qt' => 5.00000000000000E-01,
+ 'gal' => 1.25000000000000E-01,
+ 'l' => 4.73279432064682E-01,
+ 'lt' => 4.73279432064682E-01
+ ),
+ 'us_pt' => array( 'tsp' => 9.60000000000000E+01,
+ 'tbs' => 3.20000000000000E+01,
+ 'oz' => 1.60000000000000E+01,
+ 'cup' => 2.00000000000000E+00,
+ 'pt' => 1.0,
+ 'us_pt' => 1.0,
+ 'uk_pt' => 8.32856176149081E-01,
+ 'qt' => 5.00000000000000E-01,
+ 'gal' => 1.25000000000000E-01,
+ 'l' => 4.73279432064682E-01,
+ 'lt' => 4.73279432064682E-01
+ ),
+ 'uk_pt' => array( 'tsp' => 1.15266000000000E+02,
+ 'tbs' => 3.84220000000000E+01,
+ 'oz' => 1.92110000000000E+01,
+ 'cup' => 2.40137500000000E+00,
+ 'pt' => 1.20068750000000E+00,
+ 'us_pt' => 1.20068750000000E+00,
+ 'uk_pt' => 1.0,
+ 'qt' => 6.00343750000000E-01,
+ 'gal' => 1.50085937500000E-01,
+ 'l' => 5.68260698087162E-01,
+ 'lt' => 5.68260698087162E-01
+ ),
+ 'qt' => array( 'tsp' => 1.92000000000000E+02,
+ 'tbs' => 6.40000000000000E+01,
+ 'oz' => 3.20000000000000E+01,
+ 'cup' => 4.00000000000000E+00,
+ 'pt' => 2.00000000000000E+00,
+ 'us_pt' => 2.00000000000000E+00,
+ 'uk_pt' => 1.66571235229816E+00,
+ 'qt' => 1.0,
+ 'gal' => 2.50000000000000E-01,
+ 'l' => 9.46558864129363E-01,
+ 'lt' => 9.46558864129363E-01
+ ),
+ 'gal' => array( 'tsp' => 7.68000000000000E+02,
+ 'tbs' => 2.56000000000000E+02,
+ 'oz' => 1.28000000000000E+02,
+ 'cup' => 1.60000000000000E+01,
+ 'pt' => 8.00000000000000E+00,
+ 'us_pt' => 8.00000000000000E+00,
+ 'uk_pt' => 6.66284940919265E+00,
+ 'qt' => 4.00000000000000E+00,
+ 'gal' => 1.0,
+ 'l' => 3.78623545651745E+00,
+ 'lt' => 3.78623545651745E+00
+ ),
+ 'l' => array( 'tsp' => 2.02840000000000E+02,
+ 'tbs' => 6.76133333333333E+01,
+ 'oz' => 3.38066666666667E+01,
+ 'cup' => 4.22583333333333E+00,
+ 'pt' => 2.11291666666667E+00,
+ 'us_pt' => 2.11291666666667E+00,
+ 'uk_pt' => 1.75975569552166E+00,
+ 'qt' => 1.05645833333333E+00,
+ 'gal' => 2.64114583333333E-01,
+ 'l' => 1.0,
+ 'lt' => 1.0
+ ),
+ 'lt' => array( 'tsp' => 2.02840000000000E+02,
+ 'tbs' => 6.76133333333333E+01,
+ 'oz' => 3.38066666666667E+01,
+ 'cup' => 4.22583333333333E+00,
+ 'pt' => 2.11291666666667E+00,
+ 'us_pt' => 2.11291666666667E+00,
+ 'uk_pt' => 1.75975569552166E+00,
+ 'qt' => 1.05645833333333E+00,
+ 'gal' => 2.64114583333333E-01,
+ 'l' => 1.0,
+ 'lt' => 1.0
+ )
+ )
+ );
+
+
+ /**
+ * getConversionGroups
+ *
+ * @return array
+ */
+ public static function getConversionGroups() {
+ $conversionGroups = array();
+ foreach(self::$_conversionUnits as $conversionUnit) {
+ $conversionGroups[] = $conversionUnit['Group'];
+ }
+ return array_merge(array_unique($conversionGroups));
+ } // function getConversionGroups()
+
+
+ /**
+ * getConversionGroupUnits
+ *
+ * @return array
+ */
+ public static function getConversionGroupUnits($group = NULL) {
+ $conversionGroups = array();
+ foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
+ if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
+ $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
+ }
+ }
+ return $conversionGroups;
+ } // function getConversionGroupUnits()
+
+
+ /**
+ * getConversionGroupUnitDetails
+ *
+ * @return array
+ */
+ public static function getConversionGroupUnitDetails($group = NULL) {
+ $conversionGroups = array();
+ foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
+ if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
+ $conversionGroups[$conversionGroup['Group']][] = array( 'unit' => $conversionUnit,
+ 'description' => $conversionGroup['Unit Name']
+ );
+ }
+ }
+ return $conversionGroups;
+ } // function getConversionGroupUnitDetails()
+
+
+ /**
+ * getConversionGroups
+ *
+ * @return array
+ */
+ public static function getConversionMultipliers() {
+ return self::$_conversionMultipliers;
+ } // function getConversionGroups()
+
+
+ /**
+ * CONVERTUOM
+ *
+ * @param float $value
+ * @param string $fromUOM
+ * @param string $toUOM
+ * @return float
+ */
+ public static function CONVERTUOM($value, $fromUOM, $toUOM) {
+ $value = self::flattenSingleValue($value);
+ $fromUOM = self::flattenSingleValue($fromUOM);
+ $toUOM = self::flattenSingleValue($toUOM);
+
+ if (!is_numeric($value)) {
+ return self::$_errorCodes['value'];
+ }
+ $fromMultiplier = 1;
+ if (isset(self::$_conversionUnits[$fromUOM])) {
+ $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
+ } else {
+ $fromMultiplier = substr($fromUOM,0,1);
+ $fromUOM = substr($fromUOM,1);
+ if (isset(self::$_conversionMultipliers[$fromMultiplier])) {
+ $fromMultiplier = self::$_conversionMultipliers[$fromMultiplier]['multiplier'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ if ((isset(self::$_conversionUnits[$fromUOM])) && (self::$_conversionUnits[$fromUOM]['AllowPrefix'])) {
+ $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ }
+ $value *= $fromMultiplier;
+
+ $toMultiplier = 1;
+ if (isset(self::$_conversionUnits[$toUOM])) {
+ $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
+ } else {
+ $toMultiplier = substr($toUOM,0,1);
+ $toUOM = substr($toUOM,1);
+ if (isset(self::$_conversionMultipliers[$toMultiplier])) {
+ $toMultiplier = self::$_conversionMultipliers[$toMultiplier]['multiplier'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ if ((isset(self::$_conversionUnits[$toUOM])) && (self::$_conversionUnits[$toUOM]['AllowPrefix'])) {
+ $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
+ } else {
+ return self::$_errorCodes['na'];
+ }
+ }
+ if ($unitGroup1 != $unitGroup2) {
+ return self::$_errorCodes['na'];
+ }
+
+ if ($fromUOM == $toUOM) {
+ return 1.0;
+ } elseif ($unitGroup1 == 'Temperature') {
+ if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
+ if (($toUOM == 'F') || ($toUOM == 'fah')) {
+ return 1.0;
+ } else {
+ $value = (($value - 32) / 1.8);
+ if (($toUOM == 'K') || ($toUOM == 'kel')) {
+ $value += 273.15;
+ }
+ return $value;
+ }
+ } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
+ (($toUOM == 'K') || ($toUOM == 'kel'))) {
+ return 1.0;
+ } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
+ (($toUOM == 'C') || ($toUOM == 'cel'))) {
+ return 1.0;
+ }
+ if (($toUOM == 'F') || ($toUOM == 'fah')) {
+ if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
+ $value -= 273.15;
+ }
+ return ($value * 1.8) + 32;
+ }
+ if (($toUOM == 'C') || ($toUOM == 'cel')) {
+ return $value - 273.15;
+ }
+ return $value + 273.15;
+ }
+ return ($value * self::$_unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
+ } // function CONVERTUOM()
+
+
+ /**
+ * BESSELI
+ *
+ * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments
+ *
+ * @param float $x
+ * @param float $n
+ * @return int
+ */
+ public static function BESSELI($x, $n) {
+ $x = self::flattenSingleValue($x);
+ $n = floor(self::flattenSingleValue($n));
+
+ if ((is_numeric($x)) && (is_numeric($n))) {
+ if ($n < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $f_2_PI = 2 * pi();
+
+ if (abs($x) <= 30) {
+ $fTerm = pow($x / 2, $n) / self::FACT($n);
+ $nK = 1;
+ $fResult = $fTerm;
+ $fSqrX = ($x * $x) / 4;
+ do {
+ $fTerm *= $fSqrX;
+ $fTerm /= ($nK * ($nK + $n));
+ $fResult += $fTerm;
+ } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
+ } else {
+ $fXAbs = abs($x);
+ $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
+ if (($n && 1) && ($x < 0)) {
+ $fResult = -$fResult;
+ }
+ }
+ return $fResult;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELI()
+
+
+ /**
+ * BESSELJ
+ *
+ * Returns the Bessel function
+ *
+ * @param float $x
+ * @param float $n
+ * @return int
+ */
+ public static function BESSELJ($x, $n) {
+ $x = self::flattenSingleValue($x);
+ $n = floor(self::flattenSingleValue($n));
+
+ if ((is_numeric($x)) && (is_numeric($n))) {
+ if ($n < 0) {
+ return self::$_errorCodes['num'];
+ }
+ $f_PI_DIV_2 = M_PI / 2;
+ $f_PI_DIV_4 = M_PI / 4;
+
+ $fResult = 0;
+ if (abs($x) <= 30) {
+ $fTerm = pow($x / 2, $n) / self::FACT($n);
+ $nK = 1;
+ $fResult = $fTerm;
+ $fSqrX = ($x * $x) / -4;
+ do {
+ $fTerm *= $fSqrX;
+ $fTerm /= ($nK * ($nK + $n));
+ $fResult += $fTerm;
+ } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
+ } else {
+ $fXAbs = abs($x);
+ $fResult = sqrt(M_2DIVPI / $fXAbs) * cos($fXAbs - $n * $f_PI_DIV_2 - $f_PI_DIV_4);
+ if (($n && 1) && ($x < 0)) {
+ $fResult = -$fResult;
+ }
+ }
+ return $fResult;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELJ()
+
+
+ private static function _Besselk0($fNum) {
+ if ($fNum <= 2) {
+ $fNum2 = $fNum * 0.5;
+ $y = ($fNum2 * $fNum2);
+ $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
+ (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
+ (0.10750e-3 + $y * 0.74e-5))))));
+ } else {
+ $y = 2 / $fNum;
+ $fRet = exp(-$fNum) / sqrt($fNum) *
+ (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
+ (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
+ }
+ return $fRet;
+ } // function _Besselk0()
+
+
+ private static function _Besselk1($fNum) {
+ if ($fNum <= 2) {
+ $fNum2 = $fNum * 0.5;
+ $y = ($fNum2 * $fNum2);
+ $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
+ (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
+ } else {
+ $y = 2 / $fNum;
+ $fRet = exp(-$fNum) / sqrt($fNum) *
+ (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
+ (0.325614e-2 + $y * (-0.68245e-3)))))));
+ }
+ return $fRet;
+ } // function _Besselk1()
+
+
+ /**
+ * BESSELK
+ *
+ * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.
+ *
+ * @param float $x
+ * @param float $ord
+ * @return float
+ */
+ public static function BESSELK($x, $ord) {
+ $x = self::flattenSingleValue($x);
+ $ord = floor(self::flattenSingleValue($ord));
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ if ($ord < 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ switch($ord) {
+ case 0 : return self::_Besselk0($x);
+ break;
+ case 1 : return self::_Besselk1($x);
+ break;
+ default : $fTox = 2 / $x;
+ $fBkm = self::_Besselk0($x);
+ $fBk = self::_Besselk1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fBkp = $fBkm + $n * $fTox * $fBk;
+ $fBkm = $fBk;
+ $fBk = $fBkp;
+ }
+ }
+ return $fBk;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELK()
+
+
+ private static function _Bessely0($fNum) {
+ if ($fNum < 8.0) {
+ $y = ($fNum * $fNum);
+ $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
+ $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
+ $fRet = $f1 / $f2 + M_2DIVPI * self::BESSELJ($fNum, 0) * log($fNum);
+ } else {
+ $z = 8.0 / $fNum;
+ $y = ($z * $z);
+ $xx = $fNum - 0.785398164;
+ $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
+ $fRet = sqrt(M_2DIVPI / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
+ }
+ return $fRet;
+ } // function _Bessely0()
+
+
+ private static function _Bessely1($fNum) {
+ if ($fNum < 8.0) {
+ $y = ($fNum * $fNum);
+ $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
+ (-0.4237922726e7 + $y * 0.8511937935e4)))));
+ $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
+ (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+ $fRet = $f1 / $f2 + M_2DIVPI * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
+ } else {
+ $z = 8.0 / $fNum;
+ $y = ($z * $z);
+ $xx = $fNum - 2.356194491;
+ $f1 = 1 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e6))));
+ $f2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y * (-0.88228987e-6 + $y * 0.105787412e-6)));
+ $fRet = sqrt(M_2DIVPI / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
+ #i12430# ...but this seems to work much better.
+// $fRet = sqrt(M_2DIVPI / $fNum) * sin($fNum - 2.356194491);
+ }
+ return $fRet;
+ } // function _Bessely1()
+
+
+ /**
+ * BESSELY
+ *
+ * Returns the Bessel function, which is also called the Weber function or the Neumann function.
+ *
+ * @param float $x
+ * @param float $n
+ * @return int
+ */
+ public static function BESSELY($x, $ord) {
+ $x = self::flattenSingleValue($x);
+ $ord = floor(self::flattenSingleValue($ord));
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ if ($ord < 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ switch($ord) {
+ case 0 : return self::_Bessely0($x);
+ break;
+ case 1 : return self::_Bessely1($x);
+ break;
+ default: $fTox = 2 / $x;
+ $fBym = self::_Bessely0($x);
+ $fBy = self::_Bessely1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fByp = $n * $fTox * $fBy - $fBym;
+ $fBym = $fBy;
+ $fBy = $fByp;
+ }
+ }
+ return $fBy;
+ }
+ return self::$_errorCodes['value'];
+ } // function BESSELY()
+
+
+ /**
+ * DELTA
+ *
+ * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
+ *
+ * @param float $a
+ * @param float $b
+ * @return int
+ */
+ public static function DELTA($a, $b=0) {
+ $a = self::flattenSingleValue($a);
+ $b = self::flattenSingleValue($b);
+
+ return (int) ($a == $b);
+ } // function DELTA()
+
+
+ /**
+ * GESTEP
+ *
+ * Returns 1 if number = step; returns 0 (zero) otherwise
+ *
+ * @param float $number
+ * @param float $step
+ * @return int
+ */
+ public static function GESTEP($number, $step=0) {
+ $number = self::flattenSingleValue($number);
+ $step = self::flattenSingleValue($step);
+
+ return (int) ($number >= $step);
+ } // function GESTEP()
+
+
+ //
+ // Private method to calculate the erf value
+ //
+ private static $_two_sqrtpi = 1.128379167095512574;
+
+ private static function _erfVal($x) {
+ if (abs($x) > 2.2) {
+ return 1 - self::_erfcVal($x);
+ }
+ $sum = $term = $x;
+ $xsqr = ($x * $x);
+ $j = 1;
+ do {
+ $term *= $xsqr / $j;
+ $sum -= $term / (2 * $j + 1);
+ ++$j;
+ $term *= $xsqr / $j;
+ $sum += $term / (2 * $j + 1);
+ ++$j;
+ if ($sum == 0.0) {
+ break;
+ }
+ } while (abs($term / $sum) > PRECISION);
+ return self::$_two_sqrtpi * $sum;
+ } // function _erfVal()
+
+
+ /**
+ * ERF
+ *
+ * Returns the error function integrated between lower_limit and upper_limit
+ *
+ * @param float $lower lower bound for integrating ERF
+ * @param float $upper upper bound for integrating ERF.
+ * If omitted, ERF integrates between zero and lower_limit
+ * @return int
+ */
+ public static function ERF($lower, $upper = null) {
+ $lower = self::flattenSingleValue($lower);
+ $upper = self::flattenSingleValue($upper);
+
+ if (is_numeric($lower)) {
+ if ($lower < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if (is_null($upper)) {
+ return self::_erfVal($lower);
+ }
+ if (is_numeric($upper)) {
+ if ($upper < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return self::_erfVal($upper) - self::_erfVal($lower);
+ }
+ }
+ return self::$_errorCodes['value'];
+ } // function ERF()
+
+
+ //
+ // Private method to calculate the erfc value
+ //
+ private static $_one_sqrtpi = 0.564189583547756287;
+
+ private static function _erfcVal($x) {
+ if (abs($x) < 2.2) {
+ return 1 - self::_erfVal($x);
+ }
+ if ($x < 0) {
+ return 2 - self::erfc(-$x);
+ }
+ $a = $n = 1;
+ $b = $c = $x;
+ $d = ($x * $x) + 0.5;
+ $q1 = $q2 = $b / $d;
+ $t = 0;
+ do {
+ $t = $a * $n + $b * $x;
+ $a = $b;
+ $b = $t;
+ $t = $c * $n + $d * $x;
+ $c = $d;
+ $d = $t;
+ $n += 0.5;
+ $q1 = $q2;
+ $q2 = $b / $d;
+ } while ((abs($q1 - $q2) / $q2) > PRECISION);
+ return self::$_one_sqrtpi * exp(-$x * $x) * $q2;
+ } // function _erfcVal()
+
+
+ /**
+ * ERFC
+ *
+ * Returns the complementary ERF function integrated between x and infinity
+ *
+ * @param float $x The lower bound for integrating ERF
+ * @return int
+ */
+ public static function ERFC($x) {
+ $x = self::flattenSingleValue($x);
+
+ if (is_numeric($x)) {
+ if ($x < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return self::_erfcVal($x);
+ }
+ return self::$_errorCodes['value'];
+ } // function ERFC()
+
+
+ /**
+ * LOWERCASE
+ *
+ * Converts a string value to upper case.
+ *
+ * @param string $mixedCaseString
+ * @return string
+ */
+ public static function LOWERCASE($mixedCaseString) {
+ $mixedCaseString = self::flattenSingleValue($mixedCaseString);
+
+ if (is_bool($mixedCaseString)) {
+ $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_convert_case')) {
+ return mb_convert_case($mixedCaseString, MB_CASE_LOWER, 'UTF-8');
+ } else {
+ return strtoupper($mixedCaseString);
+ }
+ } // function LOWERCASE()
+
+
+ /**
+ * UPPERCASE
+ *
+ * Converts a string value to upper case.
+ *
+ * @param string $mixedCaseString
+ * @return string
+ */
+ public static function UPPERCASE($mixedCaseString) {
+ $mixedCaseString = self::flattenSingleValue($mixedCaseString);
+
+ if (is_bool($mixedCaseString)) {
+ $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_convert_case')) {
+ return mb_convert_case($mixedCaseString, MB_CASE_UPPER, 'UTF-8');
+ } else {
+ return strtoupper($mixedCaseString);
+ }
+ } // function UPPERCASE()
+
+
+ /**
+ * PROPERCASE
+ *
+ * Converts a string value to upper case.
+ *
+ * @param string $mixedCaseString
+ * @return string
+ */
+ public static function PROPERCASE($mixedCaseString) {
+ $mixedCaseString = self::flattenSingleValue($mixedCaseString);
+
+ if (is_bool($mixedCaseString)) {
+ $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
+ }
+
+ if (function_exists('mb_convert_case')) {
+ return mb_convert_case($mixedCaseString, MB_CASE_TITLE, 'UTF-8');
+ } else {
+ return ucwords($mixedCaseString);
+ }
+ } // function PROPERCASE()
+
+
+ /**
+ * DOLLAR
+ *
+ * This function converts a number to text using currency format, with the decimals rounded to the specified place.
+ * The format used is $#,##0.00_);($#,##0.00)..
+ *
+ * @param float $value The value to format
+ * @param int $decimals The number of digits to display to the right of the decimal point.
+ * If decimals is negative, number is rounded to the left of the decimal point.
+ * If you omit decimals, it is assumed to be 2
+ * @return string
+ */
+ public static function DOLLAR($value = 0, $decimals = 2) {
+ $value = self::flattenSingleValue($value);
+ $decimals = is_null($decimals) ? 0 : self::flattenSingleValue($decimals);
+
+ // Validate parameters
+ if (!is_numeric($value) || !is_numeric($decimals)) {
+ return self::$_errorCodes['num'];
+ }
+ $decimals = floor($decimals);
+
+ if ($decimals > 0) {
+ return money_format('%.'.$decimals.'n',$value);
+ } else {
+ $round = pow(10,abs($decimals));
+ if ($value < 0) { $round = 0-$round; }
+ $value = self::MROUND($value,$round);
+ // The implementation of money_format used if the standard PHP function is not available can't handle decimal places of 0,
+ // so we display to 1 dp and chop off that character and the decimal separator using substr
+ return substr(money_format('%.1n',$value),0,-2);
+ }
+ } // function DOLLAR()
+
+
+ /**
+ * DOLLARDE
+ *
+ * Converts a dollar price expressed as an integer part and a fraction part into a dollar price expressed as a decimal number.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * @param float $fractional_dollar Fractional Dollar
+ * @param int $fraction Fraction
+ * @return float
+ */
+ public static function DOLLARDE($fractional_dollar = Null, $fraction = 0) {
+ $fractional_dollar = self::flattenSingleValue($fractional_dollar);
+ $fraction = (int)self::flattenSingleValue($fraction);
+
+ // Validate parameters
+ if (is_null($fractional_dollar) || $fraction < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if ($fraction == 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $dollars = floor($fractional_dollar);
+ $cents = fmod($fractional_dollar,1);
+ $cents /= $fraction;
+ $cents *= pow(10,ceil(log10($fraction)));
+ return $dollars + $cents;
+ } // function DOLLARDE()
+
+
+ /**
+ * DOLLARFR
+ *
+ * Converts a dollar price expressed as a decimal number into a dollar price expressed as a fraction.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * @param float $decimal_dollar Decimal Dollar
+ * @param int $fraction Fraction
+ * @return float
+ */
+ public static function DOLLARFR($decimal_dollar = Null, $fraction = 0) {
+ $decimal_dollar = self::flattenSingleValue($decimal_dollar);
+ $fraction = (int)self::flattenSingleValue($fraction);
+
+ // Validate parameters
+ if (is_null($decimal_dollar) || $fraction < 0) {
+ return self::$_errorCodes['num'];
+ }
+ if ($fraction == 0) {
+ return self::$_errorCodes['divisionbyzero'];
+ }
+
+ $dollars = floor($decimal_dollar);
+ $cents = fmod($decimal_dollar,1);
+ $cents *= $fraction;
+ $cents *= pow(10,-ceil(log10($fraction)));
+ return $dollars + $cents;
+ } // function DOLLARFR()
+
+
+ /**
+ * EFFECT
+ *
+ * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.
+ *
+ * @param float $nominal_rate Nominal interest rate
+ * @param int $npery Number of compounding payments per year
+ * @return float
+ */
+ public static function EFFECT($nominal_rate = 0, $npery = 0) {
+ $nominal_rate = self::flattenSingleValue($nominal_rate);
+ $npery = (int)self::flattenSingleValue($npery);
+
+ // Validate parameters
+ if ($nominal_rate <= 0 || $npery < 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ return pow((1 + $nominal_rate / $npery), $npery) - 1;
+ } // function EFFECT()
+
+
+ /**
+ * NOMINAL
+ *
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
+ *
+ * @param float $effect_rate Effective interest rate
+ * @param int $npery Number of compounding payments per year
+ * @return float
+ */
+ public static function NOMINAL($effect_rate = 0, $npery = 0) {
+ $effect_rate = self::flattenSingleValue($effect_rate);
+ $npery = (int)self::flattenSingleValue($npery);
+
+ // Validate parameters
+ if ($effect_rate <= 0 || $npery < 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
+ } // function NOMINAL()
+
+
+ /**
+ * PV
+ *
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pmt Periodic payment (annuity)
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = self::flattenSingleValue($nper);
+ $pmt = self::flattenSingleValue($pmt);
+ $fv = self::flattenSingleValue($fv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
+ } else {
+ return -$fv - $pmt * $nper;
+ }
+ } // function PV()
+
+
+ /**
+ * FV
+ *
+ * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pmt Periodic payment (annuity)
+ * @param float $pv Present Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = self::flattenSingleValue($nper);
+ $pmt = self::flattenSingleValue($pmt);
+ $pv = self::flattenSingleValue($pv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
+ } else {
+ return -$pv - $pmt * $nper;
+ }
+ } // function FV()
+
+
+ /**
+ * FVSCHEDULE
+ *
+ */
+ public static function FVSCHEDULE($principal, $schedule) {
+ $principal = self::flattenSingleValue($principal);
+ $schedule = self::flattenArray($schedule);
+
+ foreach($schedule as $n) {
+ $principal *= 1 + $n;
+ }
+
+ return $principal;
+ } // function FVSCHEDULE()
+
+
+ /**
+ * PMT
+ *
+ * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
+ } else {
+ return (-$pv - $fv) / $nper;
+ }
+ } // function PMT()
+
+
+ /**
+ * NPER
+ *
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $pmt Periodic payment (annuity)
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $pmt = self::flattenSingleValue($pmt);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ if ($pmt == 0 && $pv == 0) {
+ return self::$_errorCodes['num'];
+ }
+ return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+ } else {
+ if ($pmt == 0) {
+ return self::$_errorCodes['num'];
+ }
+ return (-$pv -$fv) / $pmt;
+ }
+ } // function NPER()
+
+
+
+ private static function _interestAndPrincipal($rate=0, $per=0, $nper=0, $pv=0, $fv=0, $type=0) {
+ $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
+ $capital = $pv;
+ for ($i = 1; $i<= $per; ++$i) {
+ $interest = ($type && $i == 1)? 0 : -$capital * $rate;
+ $principal = $pmt - $interest;
+ $capital += $principal;
+ }
+ return array($interest, $principal);
+ } // function _interestAndPrincipal()
+
+
+ /**
+ * IPMT
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $per Period for which we want to find the interest
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $per = (int) self::flattenSingleValue($per);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($per <= 0 || $per > $nper) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[0];
+ } // function IPMT()
+
+
+ /**
+ * CUMIPMT
+ *
+ * Returns the cumulative interest paid on a loan between start_period and end_period.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param int start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param int end The last period in the calculation.
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $start = (int) self::flattenSingleValue($start);
+ $end = (int) self::flattenSingleValue($end);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($start < 1 || $start > $end) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $interest = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
+ }
+
+ return $interest;
+ } // function CUMIPMT()
+
+
+ /**
+ * PPMT
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $per Period for which we want to find the interest
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $per = (int) self::flattenSingleValue($per);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $fv = self::flattenSingleValue($fv);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($per <= 0 || $per > $nper) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[1];
+ } // function PPMT()
+
+
+ /**
+ * CUMPRINC
+ *
+ * Returns the cumulative principal paid on a loan between start_period and end_period.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param int start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param int end The last period in the calculation.
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0) {
+ $rate = self::flattenSingleValue($rate);
+ $nper = (int) self::flattenSingleValue($nper);
+ $pv = self::flattenSingleValue($pv);
+ $start = (int) self::flattenSingleValue($start);
+ $end = (int) self::flattenSingleValue($end);
+ $type = (int) self::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return self::$_errorCodes['num'];
+ }
+ if ($start < 1 || $start > $end) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Calculate
+ $principal = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
+ }
+
+ return $principal;
+ } // function CUMPRINC()
+
+
+ /**
+ * ISPMT
+ *
+ * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
+ *
+ * Excel Function:
+ * =ISPMT(interest_rate, period, number_payments, PV)
+ *
+ * interest_rate is the interest rate for the investment
+ *
+ * period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
+ *
+ * number_payments is the number of payments for the annuity
+ *
+ * PV is the loan amount or present value of the payments
+ */
+ public static function ISPMT() {
+ // Return value
+ $returnValue = 0;
+
+ // Get the parameters
+ $aArgs = self::flattenArray(func_get_args());
+ $interestRate = array_shift($aArgs);
+ $period = array_shift($aArgs);
+ $numberPeriods = array_shift($aArgs);
+ $principleRemaining = array_shift($aArgs);
+
+ // Calculate
+ $principlePayment = ($principleRemaining * 1.0) / ($numberPeriods * 1.0);
+ for($i=0; $i <= $period; ++$i) {
+ $returnValue = $interestRate * $principleRemaining * -1;
+ $principleRemaining -= $principlePayment;
+ // principle needs to be 0 after the last payment, don't let floating point screw it up
+ if($i == $numberPeriods) {
+ $returnValue = 0;
+ }
+ }
+ return($returnValue);
+ } // function ISPMT()
+
+
+ /**
+ * NPV
+ *
+ * Returns the Net Present Value of a cash flow series given a discount rate.
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ */
+ public static function NPV() {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = self::flattenArray(func_get_args());
+
+ // Calculate
+ $rate = array_shift($aArgs);
+ for ($i = 1; $i <= count($aArgs); ++$i) {
+ // Is it a numeric value?
+ if (is_numeric($aArgs[$i - 1])) {
+ $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
+ }
+ }
+
+ // Return
+ return $returnValue;
+ } // function NPV()
+
+
+ /**
+ * XNPV
+ *
+ * Returns the net present value for a schedule of cash flows that is not necessarily periodic.
+ * To calculate the net present value for a series of cash flows that is periodic, use the NPV function.
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ */
+ public static function XNPV($rate, $values, $dates) {
+ if ((!is_array($values)) || (!is_array($dates))) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $dates = self::flattenArray($dates);
+ $valCount = count($values);
+ if ($valCount != count($dates)) return self::$_errorCodes['num'];
+
+ $xnpv = 0.0;
+ for ($i = 0; $i < $valCount; ++$i) {
+ $xnpv += $values[$i] / pow(1 + $rate, self::DATEDIF($dates[0],$dates[$i],'d') / 365);
+ }
+ return (is_finite($xnpv) ? $xnpv : self::$_errorCodes['value']);
+ } // function XNPV()
+
+
+ public static function IRR($values, $guess = 0.1) {
+ if (!is_array($values)) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $guess = self::flattenSingleValue($guess);
+
+ // create an initial range, with a root somewhere between 0 and guess
+ $x1 = 0.0;
+ $x2 = $guess;
+ $f1 = self::NPV($x1, $values);
+ $f2 = self::NPV($x2, $values);
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ if (($f1 * $f2) < 0.0) break;
+ if (abs($f1) < abs($f2)) {
+ $f1 = self::NPV($x1 += 1.6 * ($x1 - $x2), $values);
+ } else {
+ $f2 = self::NPV($x2 += 1.6 * ($x2 - $x1), $values);
+ }
+ }
+ if (($f1 * $f2) > 0.0) return self::$_errorCodes['value'];
+
+ $f = self::NPV($x1, $values);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::NPV($x_mid, $values);
+ if ($f_mid <= 0.0) $rtb = $x_mid;
+ if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) return $x_mid;
+ }
+ return self::$_errorCodes['value'];
+ } // function IRR()
+
+
+ public static function MIRR($values, $finance_rate, $reinvestment_rate) {
+ if (!is_array($values)) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $finance_rate = self::flattenSingleValue($finance_rate);
+ $reinvestment_rate = self::flattenSingleValue($reinvestment_rate);
+ $n = count($values);
+
+ $rr = 1.0 + $reinvestment_rate;
+ $fr = 1.0 + $finance_rate;
+
+ $npv_pos = $npv_neg = 0.0;
+ foreach($values as $i => $v) {
+ if ($v >= 0) {
+ $npv_pos += $v / pow($rr, $i);
+ } else {
+ $npv_neg += $v / pow($fr, $i);
+ }
+ }
+
+ if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvestment_rate <= -1)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $mirr = pow((-$npv_pos * pow($rr, $n))
+ / ($npv_neg * ($rr)), (1.0 / ($n - 1))) - 1.0;
+
+ return (is_finite($mirr) ? $mirr : self::$_errorCodes['value']);
+ } // function MIRR()
+
+
+ public static function XIRR($values, $dates, $guess = 0.1) {
+ if ((!is_array($values)) && (!is_array($dates))) return self::$_errorCodes['value'];
+ $values = self::flattenArray($values);
+ $dates = self::flattenArray($dates);
+ $guess = self::flattenSingleValue($guess);
+ if (count($values) != count($dates)) return self::$_errorCodes['num'];
+
+ // create an initial range, with a root somewhere between 0 and guess
+ $x1 = 0.0;
+ $x2 = $guess;
+ $f1 = self::XNPV($x1, $values, $dates);
+ $f2 = self::XNPV($x2, $values, $dates);
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ if (($f1 * $f2) < 0.0) break;
+ if (abs($f1) < abs($f2)) {
+ $f1 = self::XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
+ } else {
+ $f2 = self::XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
+ }
+ }
+ if (($f1 * $f2) > 0.0) return self::$_errorCodes['value'];
+
+ $f = self::XNPV($x1, $values, $dates);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::XNPV($x_mid, $values, $dates);
+ if ($f_mid <= 0.0) $rtb = $x_mid;
+ if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) return $x_mid;
+ }
+ return self::$_errorCodes['value'];
+ }
+
+
+ /**
+ * RATE
+ *
+ **/
+ public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
+ $nper = (int) self::flattenSingleValue($nper);
+ $pmt = self::flattenSingleValue($pmt);
+ $pv = self::flattenSingleValue($pv);
+ $fv = (is_null($fv)) ? 0.0 : self::flattenSingleValue($fv);
+ $type = (is_null($type)) ? 0 : (int) self::flattenSingleValue($type);
+ $guess = (is_null($guess)) ? 0.1 : self::flattenSingleValue($guess);
+
+ $rate = $guess;
+ if (abs($rate) < FINANCIAL_PRECISION) {
+ $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
+ } else {
+ $f = exp($nper * log(1 + $rate));
+ $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+ }
+ $y0 = $pv + $pmt * $nper + $fv;
+ $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+
+ // find root by secant method
+ $i = $x0 = 0.0;
+ $x1 = $rate;
+ while ((abs($y0 - $y1) > FINANCIAL_PRECISION) && ($i < FINANCIAL_MAX_ITERATIONS)) {
+ $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
+ $x0 = $x1;
+ $x1 = $rate;
+
+ if (abs($rate) < FINANCIAL_PRECISION) {
+ $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
+ } else {
+ $f = exp($nper * log(1 + $rate));
+ $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+ }
+
+ $y0 = $y1;
+ $y1 = $y;
+ ++$i;
+ }
+ return $rate;
+ } // function RATE()
+
+
+ /**
+ * DB
+ *
+ * Returns the depreciation of an asset for a specified period using the fixed-declining balance method.
+ * This form of depreciation is used if you want to get a higher depreciation value at the beginning of the depreciation
+ * (as opposed to linear depreciation). The depreciation value is reduced with every depreciation period by the
+ * depreciation already deducted from the initial cost.
+ *
+ * @param float cost Initial cost of the asset.
+ * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
+ * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
+ * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
+ * @param float month Number of months in the first year. If month is omitted, it defaults to 12.
+ * @return float
+ */
+ public static function DB($cost, $salvage, $life, $period, $month=12) {
+ $cost = (float) self::flattenSingleValue($cost);
+ $salvage = (float) self::flattenSingleValue($salvage);
+ $life = (int) self::flattenSingleValue($life);
+ $period = (int) self::flattenSingleValue($period);
+ $month = (int) self::flattenSingleValue($month);
+
+ // Validate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
+ if ($cost == 0) {
+ return 0.0;
+ } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
+ return self::$_errorCodes['num'];
+ }
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ $previousDepreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ if ($per == 1) {
+ $depreciation = $cost * $fixedDepreciationRate * $month / 12;
+ } elseif ($per == ($life + 1)) {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
+ } else {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
+ }
+ $previousDepreciation += $depreciation;
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $depreciation = round($depreciation,2);
+ }
+ return $depreciation;
+ }
+ return self::$_errorCodes['value'];
+ } // function DB()
+
+
+ /**
+ * DDB
+ *
+ * Returns the depreciation of an asset for a specified period using the double-declining balance method or some other method you specify.
+ *
+ * @param float cost Initial cost of the asset.
+ * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
+ * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
+ * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
+ * @param float factor The rate at which the balance declines.
+ * If factor is omitted, it is assumed to be 2 (the double-declining balance method).
+ * @return float
+ */
+ public static function DDB($cost, $salvage, $life, $period, $factor=2.0) {
+ $cost = (float) self::flattenSingleValue($cost);
+ $salvage = (float) self::flattenSingleValue($salvage);
+ $life = (int) self::flattenSingleValue($life);
+ $period = (int) self::flattenSingleValue($period);
+ $factor = (float) self::flattenSingleValue($factor);
+
+ // Validate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
+ if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
+ return self::$_errorCodes['num'];
+ }
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ $previousDepreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ $depreciation = min( ($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation) );
+ $previousDepreciation += $depreciation;
+ }
+ if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
+ $depreciation = round($depreciation,2);
+ }
+ return $depreciation;
+ }
+ return self::$_errorCodes['value'];
+ } // function DDB()
+
+
+ private static function _daysPerYear($year,$basis) {
+ switch ($basis) {
+ case 0 :
+ case 2 :
+ case 4 :
+ $daysPerYear = 360;
+ break;
+ case 3 :
+ $daysPerYear = 365;
+ break;
+ case 1 :
+ if (self::_isLeapYear(self::YEAR($year))) {
+ $daysPerYear = 366;
+ } else {
+ $daysPerYear = 365;
+ }
+ break;
+ default :
+ return self::$_errorCodes['num'];
+ }
+ return $daysPerYear;
+ } // function _daysPerYear()
+
+
+ /**
+ * ACCRINT
+ *
+ * Returns the discount rate for a security.
+ *
+ * @param mixed issue The security's issue date.
+ * @param mixed firstinter The security's first interest date.
+ * @param mixed settlement The security's settlement date.
+ * @param float rate The security's annual coupon rate.
+ * @param float par The security's par value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function ACCRINT($issue, $firstinter, $settlement, $rate, $par=1000, $frequency=1, $basis=0) {
+ $issue = self::flattenSingleValue($issue);
+ $firstinter = self::flattenSingleValue($firstinter);
+ $settlement = self::flattenSingleValue($settlement);
+ $rate = (float) self::flattenSingleValue($rate);
+ $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
+ $frequency = (is_null($frequency)) ? 1 : (int) self::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($rate)) && (is_numeric($par))) {
+ if (($rate <= 0) || ($par <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+
+ return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear);
+ }
+ return self::$_errorCodes['value'];
+ } // function ACCRINT()
+
+
+ /**
+ * ACCRINTM
+ *
+ * Returns the discount rate for a security.
+ *
+ * @param mixed issue The security's issue date.
+ * @param mixed settlement The security's settlement date.
+ * @param float rate The security's annual coupon rate.
+ * @param float par The security's par value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function ACCRINTM($issue, $settlement, $rate, $par=1000, $basis=0) {
+ $issue = self::flattenSingleValue($issue);
+ $settlement = self::flattenSingleValue($settlement);
+ $rate = (float) self::flattenSingleValue($rate);
+ $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
+ $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($rate)) && (is_numeric($par))) {
+ if (($rate <= 0) || ($par <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+
+ return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear);
+ }
+ return self::$_errorCodes['value'];
+ } // function ACCRINTM()
+
+
+ public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
+ $cost = self::flattenSingleValue($cost);
+ $purchased = self::flattenSingleValue($purchased);
+ $firstPeriod = self::flattenSingleValue($firstPeriod);
+ $salvage = self::flattenSingleValue($salvage);
+ $period = floor(self::flattenSingleValue($period));
+ $rate = self::flattenSingleValue($rate);
+ $basis = floor(self::flattenSingleValue($basis));
+
+ $fUsePer = 1.0 / $rate;
+
+ if ($fUsePer < 3.0) {
+ $amortiseCoeff = 1.0;
+ } elseif ($fUsePer < 5.0) {
+ $amortiseCoeff = 1.5;
+ } elseif ($fUsePer <= 6.0) {
+ $amortiseCoeff = 2.0;
+ } else {
+ $amortiseCoeff = 2.5;
+ }
+
+ $rate *= $amortiseCoeff;
+ $fNRate = floor((self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost) + 0.5);
+ $cost -= $fNRate;
+ $fRest = $cost - $salvage;
+
+ for ($n = 0; $n < $period; ++$n) {
+ $fNRate = floor(($rate * $cost) + 0.5);
+ $fRest -= $fNRate;
+
+ if ($fRest < 0.0) {
+ switch ($period - $n) {
+ case 0 :
+ case 1 : return floor(($cost * 0.5) + 0.5);
+ break;
+ default : return 0.0;
+ break;
+ }
+ }
+ $cost -= $fNRate;
+ }
+ return $fNRate;
+ } // function AMORDEGRC()
+
+
+ public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
+ $cost = self::flattenSingleValue($cost);
+ $purchased = self::flattenSingleValue($purchased);
+ $firstPeriod = self::flattenSingleValue($firstPeriod);
+ $salvage = self::flattenSingleValue($salvage);
+ $period = self::flattenSingleValue($period);
+ $rate = self::flattenSingleValue($rate);
+ $basis = self::flattenSingleValue($basis);
+
+ $fOneRate = $cost * $rate;
+ $fCostDelta = $cost - $salvage;
+ $f0Rate = self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost;
+ $nNumOfFullPeriods = intval(($cost - $salvage - $f0Rate) / $fOneRate);
+
+ if ($period == 0) {
+ return $f0Rate;
+ } elseif ($period <= $nNumOfFullPeriods) {
+ return $fOneRate;
+ } elseif ($period == ($nNumOfFullPeriods + 1)) {
+ return ($fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate);
+ } else {
+ return 0.0;
+ }
+ } // function AMORLINC()
+
+
+ public static function COUPNUM($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = self::flattenSingleValue($frequency);
+ $basis = self::flattenSingleValue($basis);
+
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis) * 365;
+
+ switch ($frequency) {
+ case 1: // annual payments
+ return ceil($daysBetweenSettlementAndMaturity / 360);
+ case 2: // half-yearly
+ return ceil($daysBetweenSettlementAndMaturity / 180);
+ case 4: // quarterly
+ return ceil($daysBetweenSettlementAndMaturity / 90);
+ }
+ return self::$_errorCodes['value'];
+ } // function COUPNUM()
+
+
+ public static function COUPDAYBS($settlement, $maturity, $frequency, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $frequency = self::flattenSingleValue($frequency);
+ $basis = self::flattenSingleValue($basis);
+
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis) * 365;
+
+ switch ($frequency) {
+ case 1: // annual payments
+ return 365 - ($daysBetweenSettlementAndMaturity % 360);
+ case 2: // half-yearly
+ return 365 - ($daysBetweenSettlementAndMaturity % 360);
+ case 4: // quarterly
+ return self::DATEDIF($maturity, $settlement);
+ }
+ return self::$_errorCodes['value'];
+ } // function COUPDAYBS()
+
+
+ /**
+ * DISC
+ *
+ * Returns the discount rate for a security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int price The security's price per $100 face value.
+ * @param int redemption the security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function DISC($settlement, $maturity, $price, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $price = (float) self::flattenSingleValue($price);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($price <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return ((1 - $price / $redemption) / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function DISC()
+
+
+ /**
+ * PRICEDISC
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int discount The security's discount rate.
+ * @param int redemption The security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $discount = (float) self::flattenSingleValue($discount);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($discount <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function PRICEDISC()
+
+
+ /**
+ * PRICEMAT
+ *
+ * Returns the price per $100 face value of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed issue The security's issue date.
+ * @param int rate The security's interest rate at date of issue.
+ * @param int yield The security's annual yield.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $issue = self::flattenSingleValue($issue);
+ $rate = self::flattenSingleValue($rate);
+ $yield = self::flattenSingleValue($yield);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($rate) && is_numeric($yield)) {
+ if (($rate <= 0) || ($yield <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
+ (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
+ (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100));
+ }
+ return self::$_errorCodes['value'];
+ } // function PRICEMAT()
+
+
+ /**
+ * RECEIVED
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int investment The amount invested in the security.
+ * @param int discount The security's discount rate.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $investment = (float) self::flattenSingleValue($investment);
+ $discount = (float) self::flattenSingleValue($discount);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
+ if (($investment <= 0) || ($discount <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $investment / ( 1 - ($discount * $daysBetweenSettlementAndMaturity));
+ }
+ return self::$_errorCodes['value'];
+ } // function RECEIVED()
+
+
+ /**
+ * INTRATE
+ *
+ * Returns the interest rate for a fully invested security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int investment The amount invested in the security.
+ * @param int redemption The amount to be received at maturity.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $investment = (float) self::flattenSingleValue($investment);
+ $redemption = (float) self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($investment <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function INTRATE()
+
+
+ /**
+ * TBILLEQ
+ *
+ * Returns the bond-equivalent yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int discount The Treasury bill's discount rate.
+ * @return float
+ */
+ public static function TBILLEQ($settlement, $maturity, $discount) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $discount = self::flattenSingleValue($discount);
+
+ // Use TBILLPRICE for validation
+ $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
+ if (is_string($testValue)) {
+ return $testValue;
+ }
+
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
+ } else {
+ $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
+ }
+
+ return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ } // function TBILLEQ()
+
+
+ /**
+ * TBILLPRICE
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int discount The Treasury bill's discount rate.
+ * @return float
+ */
+ public static function TBILLPRICE($settlement, $maturity, $discount) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $discount = self::flattenSingleValue($discount);
+
+ if (is_string($maturity = self::_getDateValue($maturity))) {
+ return self::$_errorCodes['value'];
+ }
+
+ // Validate
+ if (is_numeric($discount)) {
+ if ($discount <= 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ } else {
+ $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
+ }
+
+ if ($daysBetweenSettlementAndMaturity > 360) {
+ return self::$_errorCodes['num'];
+ }
+
+ $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
+ if ($price <= 0) {
+ return self::$_errorCodes['num'];
+ }
+ return $price;
+ }
+ return self::$_errorCodes['value'];
+ } // function TBILLPRICE()
+
+
+ /**
+ * TBILLYIELD
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int price The Treasury bill's price per $100 face value.
+ * @return float
+ */
+ public static function TBILLYIELD($settlement, $maturity, $price) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $price = self::flattenSingleValue($price);
+
+ // Validate
+ if (is_numeric($price)) {
+ if ($price <= 0) {
+ return self::$_errorCodes['num'];
+ }
+
+ if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ } else {
+ $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
+ }
+
+ if ($daysBetweenSettlementAndMaturity > 360) {
+ return self::$_errorCodes['num'];
+ }
+
+ return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function TBILLYIELD()
+
+
+ /**
+ * SLN
+ *
+ * Returns the straight-line depreciation of an asset for one period
+ *
+ * @param cost Initial cost of the asset
+ * @param salvage Value at the end of the depreciation
+ * @param life Number of periods over which the asset is depreciated
+ * @return float
+ */
+ public static function SLN($cost, $salvage, $life) {
+ $cost = self::flattenSingleValue($cost);
+ $salvage = self::flattenSingleValue($salvage);
+ $life = self::flattenSingleValue($life);
+
+ // Calculate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
+ if ($life < 0) {
+ return self::$_errorCodes['num'];
+ }
+ return ($cost - $salvage) / $life;
+ }
+ return self::$_errorCodes['value'];
+ } // function SLN()
+
+
+ /**
+ * YIELDMAT
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed issue The security's issue date.
+ * @param int rate The security's interest rate at date of issue.
+ * @param int price The security's price per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $issue = self::flattenSingleValue($issue);
+ $rate = self::flattenSingleValue($rate);
+ $price = self::flattenSingleValue($price);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($rate) && is_numeric($price)) {
+ if (($rate <= 0) || ($price <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
+ ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function YIELDMAT()
+
+
+ /**
+ * YIELDDISC
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int price The security's price per $100 face value.
+ * @param int redemption The security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis=0) {
+ $settlement = self::flattenSingleValue($settlement);
+ $maturity = self::flattenSingleValue($maturity);
+ $price = self::flattenSingleValue($price);
+ $redemption = self::flattenSingleValue($redemption);
+ $basis = (int) self::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($price) && is_numeric($redemption)) {
+ if (($price <= 0) || ($redemption <= 0)) {
+ return self::$_errorCodes['num'];
+ }
+ $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity,$basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+ return self::$_errorCodes['value'];
+ } // function YIELDDISC()
+
+
+ /**
+ * CELL_ADDRESS
+ *
+ * Creates a cell address as text, given specified row and column numbers.
+ *
+ * @param row Row number to use in the cell reference
+ * @param column Column number to use in the cell reference
+ * @param relativity Flag indicating the type of reference to return
+ * 1 or omitted Absolute
+ * 2 Absolute row; relative column
+ * 3 Relative row; absolute column
+ * 4 Relative
+ * @param referenceStyle A logical value that specifies the A1 or R1C1 reference style.
+ * TRUE or omitted CELL_ADDRESS returns an A1-style reference
+ * FALSE CELL_ADDRESS returns an R1C1-style reference
+ * @param sheetText Optional Name of worksheet to use
+ * @return string
+ */
+ public static function CELL_ADDRESS($row, $column, $relativity=1, $referenceStyle=True, $sheetText='') {
+ $row = self::flattenSingleValue($row);
+ $column = self::flattenSingleValue($column);
+ $relativity = self::flattenSingleValue($relativity);
+ $sheetText = self::flattenSingleValue($sheetText);
+
+ if (($row < 1) || ($column < 1)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if ($sheetText > '') {
+ if (strpos($sheetText,' ') !== False) { $sheetText = "'".$sheetText."'"; }
+ $sheetText .='!';
+ }
+ if ((!is_bool($referenceStyle)) || $referenceStyle) {
+ $rowRelative = $columnRelative = '$';
+ $column = PHPExcel_Cell::stringFromColumnIndex($column-1);
+ if (($relativity == 2) || ($relativity == 4)) { $columnRelative = ''; }
+ if (($relativity == 3) || ($relativity == 4)) { $rowRelative = ''; }
+ return $sheetText.$columnRelative.$column.$rowRelative.$row;
+ } else {
+ if (($relativity == 2) || ($relativity == 4)) { $column = '['.$column.']'; }
+ if (($relativity == 3) || ($relativity == 4)) { $row = '['.$row.']'; }
+ return $sheetText.'R'.$row.'C'.$column;
+ }
+ } // function CELL_ADDRESS()
+
+
+ /**
+ * COLUMN
+ *
+ * Returns the column number of the given cell reference
+ * If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.
+ * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
+ * reference of the cell in which the COLUMN function appears; otherwise this function returns 0.
+ *
+ * @param cellAddress A reference to a range of cells for which you want the column numbers
+ * @return integer or array of integer
+ */
+ public static function COLUMN($cellAddress=Null) {
+ if (is_null($cellAddress) || trim($cellAddress) === '') { return 0; }
+
+ if (is_array($cellAddress)) {
+ foreach($cellAddress as $columnKey => $value) {
+ $columnKey = preg_replace('/[^a-z]/i','',$columnKey);
+ return (integer) PHPExcel_Cell::columnIndexFromString($columnKey);
+ }
+ } else {
+ if (strpos($cellAddress,'!') !== false) {
+ list($sheet,$cellAddress) = explode('!',$cellAddress);
+ }
+ if (strpos($cellAddress,':') !== false) {
+ list($startAddress,$endAddress) = explode(':',$cellAddress);
+ $startAddress = preg_replace('/[^a-z]/i','',$startAddress);
+ $endAddress = preg_replace('/[^a-z]/i','',$endAddress);
+ $returnValue = array();
+ do {
+ $returnValue[] = (integer) PHPExcel_Cell::columnIndexFromString($startAddress);
+ } while ($startAddress++ != $endAddress);
+ return $returnValue;
+ } else {
+ $cellAddress = preg_replace('/[^a-z]/i','',$cellAddress);
+ return (integer) PHPExcel_Cell::columnIndexFromString($cellAddress);
+ }
+ }
+ } // function COLUMN()
+
+
+ /**
+ * COLUMNS
+ *
+ * Returns the number of columns in an array or reference.
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
+ * @return integer
+ */
+ public static function COLUMNS($cellAddress=Null) {
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return 1;
+ } elseif (!is_array($cellAddress)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $isMatrix = (is_numeric(array_shift(array_keys($cellAddress))));
+ list($columns,$rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $rows;
+ } else {
+ return $columns;
+ }
+ } // function COLUMNS()
+
+
+ /**
+ * ROW
+ *
+ * Returns the row number of the given cell reference
+ * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.
+ * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
+ * reference of the cell in which the ROW function appears; otherwise this function returns 0.
+ *
+ * @param cellAddress A reference to a range of cells for which you want the row numbers
+ * @return integer or array of integer
+ */
+ public static function ROW($cellAddress=Null) {
+ if (is_null($cellAddress) || trim($cellAddress) === '') { return 0; }
+
+ if (is_array($cellAddress)) {
+ foreach($cellAddress as $columnKey => $rowValue) {
+ foreach($rowValue as $rowKey => $cellValue) {
+ return (integer) preg_replace('/[^0-9]/i','',$rowKey);
+ }
+ }
+ } else {
+ if (strpos($cellAddress,'!') !== false) {
+ list($sheet,$cellAddress) = explode('!',$cellAddress);
+ }
+ if (strpos($cellAddress,':') !== false) {
+ list($startAddress,$endAddress) = explode(':',$cellAddress);
+ $startAddress = preg_replace('/[^0-9]/','',$startAddress);
+ $endAddress = preg_replace('/[^0-9]/','',$endAddress);
+ $returnValue = array();
+ do {
+ $returnValue[][] = (integer) $startAddress;
+ } while ($startAddress++ != $endAddress);
+ return $returnValue;
+ } else {
+ list($cellAddress) = explode(':',$cellAddress);
+ return (integer) preg_replace('/[^0-9]/','',$cellAddress);
+ }
+ }
+ } // function ROW()
+
+
+ /**
+ * ROWS
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @return integer
+ */
+ public static function ROWS($cellAddress=Null) {
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return 1;
+ } elseif (!is_array($cellAddress)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $isMatrix = (is_numeric(array_shift(array_keys($cellAddress))));
+ list($columns,$rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $columns;
+ } else {
+ return $rows;
+ }
+ } // function ROWS()
+
+
+ /**
+ * INDIRECT
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @return integer
+ */
+ public static function INDIRECT($cellAddress=Null, PHPExcel_Cell $pCell = null) {
+ $cellAddress = self::flattenSingleValue($cellAddress);
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return self::REF();
+ }
+
+ $cellAddress1 = $cellAddress;
+ $cellAddress2 = NULL;
+ if (strpos($cellAddress,':') !== false) {
+ list($cellAddress1,$cellAddress2) = explode(':',$cellAddress);
+ }
+
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress1, $matches)) ||
+ ((!is_null($cellAddress2)) && (!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress2, $matches)))) {
+ return self::REF();
+ }
+
+ if (strpos($cellAddress,'!') !== false) {
+ list($sheetName,$cellAddress) = explode('!',$cellAddress);
+ $pSheet = $pCell->getParent()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getParent();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, False);
+ } // function INDIRECT()
+
+
+ /**
+ * OFFSET
+ *
+ * Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
+ * The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and
+ * the number of columns to be returned.
+ *
+ * @param cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
+ * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
+ * @param rows The number of rows, up or down, that you want the upper-left cell to refer to.
+ * Using 5 as the rows argument specifies that the upper-left cell in the reference is
+ * five rows below reference. Rows can be positive (which means below the starting reference)
+ * or negative (which means above the starting reference).
+ * @param cols The number of columns, to the left or right, that you want the upper-left cell of the result
+ * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
+ * reference is five columns to the right of reference. Cols can be positive (which means
+ * to the right of the starting reference) or negative (which means to the left of the
+ * starting reference).
+ * @param height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
+ * @param width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
+ * @return string A reference to a cell or range of cells
+ */
+ public static function OFFSET($cellAddress=Null,$rows=0,$columns=0,$height=null,$width=null) {
+ if ($cellAddress == Null) {
+ return 0;
+ }
+
+ $pCell = array_pop(func_get_args());
+ if (!is_object($pCell)) {
+ return self::$_errorCodes['reference'];
+ }
+
+ $sheetName = null;
+ if (strpos($cellAddress,"!")) {
+ list($sheetName,$cellAddress) = explode("!",$cellAddress);
+ }
+ if (strpos($cellAddress,":")) {
+ list($startCell,$endCell) = explode(":",$cellAddress);
+ } else {
+ $startCell = $endCell = $cellAddress;
+ }
+ list($startCellColumn,$startCellRow) = PHPExcel_Cell::coordinateFromString($startCell);
+ list($endCellColumn,$endCellRow) = PHPExcel_Cell::coordinateFromString($endCell);
+
+ $startCellRow += $rows;
+ $startCellColumn = PHPExcel_Cell::columnIndexFromString($startCellColumn) - 1;
+ $startCellColumn += $columns;
+
+ if (($startCellRow <= 0) || ($startCellColumn < 0)) {
+ return self::$_errorCodes['reference'];
+ }
+ $endCellColumn = PHPExcel_Cell::columnIndexFromString($endCellColumn) - 1;
+ if (($width != null) && (!is_object($width))) {
+ $endCellColumn = $startCellColumn + $width - 1;
+ } else {
+ $endCellColumn += $columns;
+ }
+ $startCellColumn = PHPExcel_Cell::stringFromColumnIndex($startCellColumn);
+
+ if (($height != null) && (!is_object($height))) {
+ $endCellRow = $startCellRow + $height - 1;
+ } else {
+ $endCellRow += $rows;
+ }
+
+ if (($endCellRow <= 0) || ($endCellColumn < 0)) {
+ return self::$_errorCodes['reference'];
+ }
+ $endCellColumn = PHPExcel_Cell::stringFromColumnIndex($endCellColumn);
+
+ $cellAddress = $startCellColumn.$startCellRow;
+ if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
+ $cellAddress .= ':'.$endCellColumn.$endCellRow;
+ }
+
+ if ($sheetName !== null) {
+ $pSheet = $pCell->getParent()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getParent();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, False);
+ } // function OFFSET()
+
+
+ public static function CHOOSE() {
+ $chooseArgs = func_get_args();
+ $chosenEntry = self::flattenArray(array_shift($chooseArgs));
+ $entryCount = count($chooseArgs) - 1;
+
+ if(is_array($chosenEntry)) {
+ $chosenEntry = array_shift($chosenEntry);
+ }
+ if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
+ --$chosenEntry;
+ } else {
+ return self::$_errorCodes['value'];
+ }
+ $chosenEntry = floor($chosenEntry);
+ if (($chosenEntry <= 0) || ($chosenEntry > $entryCount)) {
+ return self::$_errorCodes['value'];
+ }
+
+ if (is_array($chooseArgs[$chosenEntry])) {
+ return self::flattenArray($chooseArgs[$chosenEntry]);
+ } else {
+ return $chooseArgs[$chosenEntry];
+ }
+ } // function CHOOSE()
+
+
+ /**
+ * MATCH
+ *
+ * The MATCH function searches for a specified item in a range of cells
+ *
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_array The range of cells being searched
+ * @param match_type The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered.
+ * @return integer The relative position of the found item
+ */
+ public static function MATCH($lookup_value, $lookup_array, $match_type=1) {
+
+ // flatten the lookup_array
+ $lookup_array = self::flattenArray($lookup_array);
+
+ // flatten lookup_value since it may be a cell reference to a value or the value itself
+ $lookup_value = self::flattenSingleValue($lookup_value);
+
+ // MATCH is not case sensitive
+ $lookup_value = strtolower($lookup_value);
+
+ /*
+ echo "-------------------- looking for $lookup_value in ";
+ print_r($lookup_array);
+ echo " ";
+ //return 1;
+ /**/
+
+ // **
+ // check inputs
+ // **
+ // lookup_value type has to be number, text, or logical values
+ if (!is_numeric($lookup_value) && !is_string($lookup_value) && !is_bool($lookup_value)){
+ // error: lookup_array should contain only number, text, or logical values
+ //echo "error: lookup_array should contain only number, text, or logical values ";
+ return self::$_errorCodes['na'];
+ }
+
+ // match_type is 0, 1 or -1
+ if ($match_type!==0 && $match_type!==-1 && $match_type!==1){
+ // error: wrong value for match_type
+ //echo "error: wrong value for match_type ";
+ return self::$_errorCodes['na'];
+ }
+
+ // lookup_array should not be empty
+ if (sizeof($lookup_array)<=0){
+ // error: empty range
+ //echo "error: empty range ".sizeof($lookup_array)." ";
+ return self::$_errorCodes['na'];
+ }
+
+ // lookup_array should contain only number, text, or logical values
+ for ($i=0;$i";
+ return self::$_errorCodes['na'];
+ }
+ // convert tpo lowercase
+ if (is_string($lookup_array[$i]))
+ $lookup_array[$i] = strtolower($lookup_array[$i]);
+ }
+
+ // if match_type is 1 or -1, the list has to be ordered
+ if($match_type==1 || $match_type==-1){
+ // **
+ // iniitialization
+ // store the last value
+ $iLastValue=$lookup_array[0];
+ // **
+ // loop on the cells
+ for ($i=0;$i$iLastValue)){
+ // error: list is not ordered correctly
+ //echo "error: list is not ordered correctly ";
+ return self::$_errorCodes['na'];
+ }
+ }
+ }
+ // **
+ // find the match
+ // **
+ // loop on the cells
+ for ($i=0; $i < sizeof($lookup_array); ++$i){
+ // if match_type is 0 <=> find the first value that is exactly equal to lookup_value
+ if ($match_type==0 && $lookup_array[$i]==$lookup_value){
+ // this is the exact match
+ return $i+1;
+ }
+ // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value
+ if ($match_type==-1 && $lookup_array[$i] < $lookup_value){
+ if ($i<1){
+ // 1st cell was allready smaller than the lookup_value
+ break;
+ }
+ else
+ // the previous cell was the match
+ return $i;
+ }
+ // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value
+ if ($match_type==1 && $lookup_array[$i] > $lookup_value){
+ if ($i<1){
+ // 1st cell was allready bigger than the lookup_value
+ break;
+ }
+ else
+ // the previous cell was the match
+ return $i;
+ }
+ }
+ // unsuccessful in finding a match, return #N/A error value
+ //echo "unsuccessful in finding a match ";
+ return self::$_errorCodes['na'];
+ } // function MATCH()
+
+
+ /**
+ * Uses an index to choose a value from a reference or array
+ * implemented: Return the value of a specified cell or array of cells Array form
+ * not implemented: Return a reference to specified cells Reference form
+ *
+ * @param range_array a range of cells or an array constant
+ * @param row_num selects the row in array from which to return a value. If row_num is omitted, column_num is required.
+ * @param column_num selects the column in array from which to return a value. If column_num is omitted, row_num is required.
+ */
+ public static function INDEX($arrayValues,$rowNum = 0,$columnNum = 0) {
+
+ if (($rowNum < 0) || ($columnNum < 0)) {
+ return self::$_errorCodes['value'];
+ }
+
+ $rowKeys = array_keys($arrayValues);
+ $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
+
+ if ($columnNum > count($columnKeys)) {
+ return self::$_errorCodes['value'];
+ } elseif ($columnNum == 0) {
+ if ($rowNum == 0) {
+ return $arrayValues;
+ }
+ $rowNum = $rowKeys[--$rowNum];
+ $returnArray = array();
+ foreach($arrayValues as $arrayColumn) {
+ if (is_array($arrayColumn)) {
+ if (isset($arrayColumn[$rowNum])) {
+ $returnArray[] = $arrayColumn[$rowNum];
+ } else {
+ return $arrayValues[$rowNum];
+ }
+ } else {
+ return $arrayValues[$rowNum];
+ }
+ }
+ return $returnArray;
+ }
+ $columnNum = $columnKeys[--$columnNum];
+ if ($rowNum > count($rowKeys)) {
+ return self::$_errorCodes['value'];
+ } elseif ($rowNum == 0) {
+ return $arrayValues[$columnNum];
+ }
+ $rowNum = $rowKeys[--$rowNum];
+
+ return $arrayValues[$rowNum][$columnNum];
+ } // function INDEX()
+
+
+ /**
+ * SYD
+ *
+ * Returns the sum-of-years' digits depreciation of an asset for a specified period.
+ *
+ * @param cost Initial cost of the asset
+ * @param salvage Value at the end of the depreciation
+ * @param life Number of periods over which the asset is depreciated
+ * @param period Period
+ * @return float
+ */
+ public static function SYD($cost, $salvage, $life, $period) {
+ $cost = self::flattenSingleValue($cost);
+ $salvage = self::flattenSingleValue($salvage);
+ $life = self::flattenSingleValue($life);
+ $period = self::flattenSingleValue($period);
+
+ // Calculate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
+ if (($life < 1) || ($period > $life)) {
+ return self::$_errorCodes['num'];
+ }
+ return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
+ }
+ return self::$_errorCodes['value'];
+ } // function SYD()
+
+
+ /**
+ * TRANSPOSE
+ *
+ * @param array $matrixData A matrix of values
+ * @return array
+ *
+ * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.
+ */
+ public static function TRANSPOSE($matrixData) {
+ $returnMatrix = array();
+ if (!is_array($matrixData)) { $matrixData = array(array($matrixData)); }
+
+ $column = 0;
+ foreach($matrixData as $matrixRow) {
+ $row = 0;
+ foreach($matrixRow as $matrixCell) {
+ $returnMatrix[$row][$column] = $matrixCell;
+ ++$row;
+ }
+ ++$column;
+ }
+ return $returnMatrix;
+ } // function TRANSPOSE()
+
+
+ /**
+ * MMULT
+ *
+ * @param array $matrixData1 A matrix of values
+ * @param array $matrixData2 A matrix of values
+ * @return array
+ */
+ public static function MMULT($matrixData1,$matrixData2) {
+ $matrixAData = $matrixBData = array();
+ if (!is_array($matrixData1)) { $matrixData1 = array(array($matrixData1)); }
+ if (!is_array($matrixData2)) { $matrixData2 = array(array($matrixData2)); }
+
+ $rowA = 0;
+ foreach($matrixData1 as $matrixRow) {
+ $columnA = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixAData[$rowA][$columnA] = $matrixCell;
+ ++$columnA;
+ }
+ ++$rowA;
+ }
+ try {
+ $matrixA = new Matrix($matrixAData);
+ $rowB = 0;
+ foreach($matrixData2 as $matrixRow) {
+ $columnB = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixBData[$rowB][$columnB] = $matrixCell;
+ ++$columnB;
+ }
+ ++$rowB;
+ }
+ $matrixB = new Matrix($matrixBData);
+
+ if (($rowA != $columnB) || ($rowB != $columnA)) {
+ return self::$_errorCodes['value'];
+ }
+
+ return $matrixA->times($matrixB)->getArray();
+ } catch (Exception $ex) {
+ return self::$_errorCodes['value'];
+ }
+ } // function MMULT()
+
+
+ /**
+ * MINVERSE
+ *
+ * @param array $matrixValues A matrix of values
+ * @return array
+ */
+ public static function MINVERSE($matrixValues) {
+ $matrixData = array();
+ if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
+
+ $row = $maxColumn = 0;
+ foreach($matrixValues as $matrixRow) {
+ $column = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixData[$column][$row] = $matrixCell;
+ ++$column;
+ }
+ if ($column > $maxColumn) { $maxColumn = $column; }
+ ++$row;
+ }
+ if ($row != $maxColumn) { return self::$_errorCodes['value']; }
+
+ try {
+ $matrix = new Matrix($matrixData);
+ return $matrix->inverse()->getArray();
+ } catch (Exception $ex) {
+ return self::$_errorCodes['value'];
+ }
+ } // function MINVERSE()
+
+
+ /**
+ * MDETERM
+ *
+ * @param array $matrixValues A matrix of values
+ * @return float
+ */
+ public static function MDETERM($matrixValues) {
+ $matrixData = array();
+ if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
+
+ $row = $maxColumn = 0;
+ foreach($matrixValues as $matrixRow) {
+ $column = 0;
+ foreach($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return self::$_errorCodes['value'];
+ }
+ $matrixData[$column][$row] = $matrixCell;
+ ++$column;
+ }
+ if ($column > $maxColumn) { $maxColumn = $column; }
+ ++$row;
+ }
+ if ($row != $maxColumn) { return self::$_errorCodes['value']; }
+
+ try {
+ $matrix = new Matrix($matrixData);
+ return $matrix->det();
+ } catch (Exception $ex) {
+ return self::$_errorCodes['value'];
+ }
+ } // function MDETERM()
+
+
+ /**
+ * SUMPRODUCT
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMPRODUCT() {
+ $arrayList = func_get_args();
+
+ $wrkArray = self::flattenArray(array_shift($arrayList));
+ $wrkCellCount = count($wrkArray);
+
+ foreach($arrayList as $matrixData) {
+ $array2 = self::flattenArray($matrixData);
+ $count = count($array2);
+ if ($wrkCellCount != $count) {
+ return self::$_errorCodes['value'];
+ }
+
+ foreach ($array2 as $i => $val) {
+ if (((is_numeric($wrkArray[$i])) && (!is_string($wrkArray[$i]))) &&
+ ((is_numeric($val)) && (!is_string($val)))) {
+ $wrkArray[$i] *= $val;
+ }
+ }
+ }
+
+ return array_sum($wrkArray);
+ } // function SUMPRODUCT()
+
+
+ /**
+ * SUMX2MY2
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMX2MY2($matrixData1,$matrixData2) {
+ $array1 = self::flattenArray($matrixData1);
+ $array2 = self::flattenArray($matrixData2);
+ $count1 = count($array1);
+ $count2 = count($array2);
+ if ($count1 < $count2) {
+ $count = $count1;
+ } else {
+ $count = $count2;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
+ }
+ }
+
+ return $result;
+ } // function SUMX2MY2()
+
+
+ /**
+ * SUMX2PY2
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMX2PY2($matrixData1,$matrixData2) {
+ $array1 = self::flattenArray($matrixData1);
+ $array2 = self::flattenArray($matrixData2);
+ $count1 = count($array1);
+ $count2 = count($array2);
+ if ($count1 < $count2) {
+ $count = $count1;
+ } else {
+ $count = $count2;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
+ }
+ }
+
+ return $result;
+ } // function SUMX2PY2()
+
+
+ /**
+ * SUMXMY2
+ *
+ * @param mixed $value Value to check
+ * @return float
+ */
+ public static function SUMXMY2($matrixData1,$matrixData2) {
+ $array1 = self::flattenArray($matrixData1);
+ $array2 = self::flattenArray($matrixData2);
+ $count1 = count($array1);
+ $count2 = count($array2);
+ if ($count1 < $count2) {
+ $count = $count1;
+ } else {
+ $count = $count2;
+ }
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
+ }
+ }
+
+ return $result;
+ } // function SUMXMY2()
+
+
+ private static function _vlookupSort($a,$b) {
+ $firstColumn = array_shift(array_keys($a));
+ if (strtolower($a[$firstColumn]) == strtolower($b[$firstColumn])) {
+ return 0;
+ }
+ return (strtolower($a[$firstColumn]) < strtolower($b[$firstColumn])) ? -1 : 1;
+ } // function _vlookupSort()
+
+
+ /**
+ * VLOOKUP
+ * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value in the same row based on the index_number.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_array The range of cells being searched
+ * @param index_number The column number in table_array from which the matching value must be returned. The first column is 1.
+ * @param not_exact_match Determines if you are looking for an exact match based on lookup_value.
+ * @return mixed The value of the found cell
+ */
+ public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match=true) {
+ // index_number must be greater than or equal to 1
+ if ($index_number < 1) {
+ return self::$_errorCodes['value'];
+ }
+
+ // index_number must be less than or equal to the number of columns in lookup_array
+ if ((!is_array($lookup_array)) || (count($lookup_array) < 1)) {
+ return self::$_errorCodes['reference'];
+ } else {
+ $firstRow = array_pop(array_keys($lookup_array));
+ if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
+ return self::$_errorCodes['reference'];
+ } else {
+ $columnKeys = array_keys($lookup_array[$firstRow]);
+ $returnColumn = $columnKeys[--$index_number];
+ $firstColumn = array_shift($columnKeys);
+ }
+ }
+
+ if (!$not_exact_match) {
+ uasort($lookup_array,array('self','_vlookupSort'));
+ }
+
+ $rowNumber = $rowValue = False;
+ foreach($lookup_array as $rowKey => $rowData) {
+ if (strtolower($rowData[$firstColumn]) > strtolower($lookup_value)) {
+ break;
+ }
+ $rowNumber = $rowKey;
+ $rowValue = $rowData[$firstColumn];
+ }
+
+ if ($rowNumber !== false) {
+ if ((!$not_exact_match) && ($rowValue != $lookup_value)) {
+ // if an exact match is required, we have what we need to return an appropriate response
+ return self::$_errorCodes['na'];
+ } else {
+ // otherwise return the appropriate value
+ return $lookup_array[$rowNumber][$returnColumn];
+ }
+ }
+
+ return self::$_errorCodes['na'];
+ } // function VLOOKUP()
+
+
+ /**
+ * LOOKUP
+ * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_vector The range of cells being searched
+ * @param result_vector The column from which the matching value must be returned
+ * @return mixed The value of the found cell
+ */
+ public static function LOOKUP($lookup_value, $lookup_vector, $result_vector=null) {
+ $lookup_value = self::flattenSingleValue($lookup_value);
+
+ if (!is_array($lookup_vector)) {
+ return self::$_errorCodes['na'];
+ }
+ $lookupRows = count($lookup_vector);
+ $lookupColumns = count($lookup_vector[array_shift(array_keys($lookup_vector))]);
+ if ((($lookupRows == 1) && ($lookupColumns > 1)) || (($lookupRows == 2) && ($lookupColumns != 2))) {
+ $lookup_vector = self::TRANSPOSE($lookup_vector);
+ $lookupRows = count($lookup_vector);
+ $lookupColumns = count($lookup_vector[array_shift(array_keys($lookup_vector))]);
+ }
+
+ if (is_null($result_vector)) {
+ $result_vector = $lookup_vector;
+ }
+ $resultRows = count($result_vector);
+ $resultColumns = count($result_vector[array_shift(array_keys($result_vector))]);
+ if ((($resultRows == 1) && ($resultColumns > 1)) || (($resultRows == 2) && ($resultColumns != 2))) {
+ $result_vector = self::TRANSPOSE($result_vector);
+ $resultRows = count($result_vector);
+ $resultColumns = count($result_vector[array_shift(array_keys($result_vector))]);
+ }
+
+ if ($lookupRows == 2) {
+ $result_vector = array_pop($lookup_vector);
+ $lookup_vector = array_shift($lookup_vector);
+ }
+ if ($lookupColumns != 2) {
+ foreach($lookup_vector as &$value) {
+ if (is_array($value)) {
+ $key1 = $key2 = array_shift(array_keys($value));
+ $key2++;
+ $dataValue1 = $value[$key1];
+ } else {
+ $key1 = 0;
+ $key2 = 1;
+ $dataValue1 = $value;
+ }
+ $dataValue2 = array_shift($result_vector);
+ if (is_array($dataValue2)) {
+ $dataValue2 = array_shift($dataValue2);
+ }
+ $value = array($key1 => $dataValue1, $key2 => $dataValue2);
+ }
+ unset($value);
+ }
+
+ return self::VLOOKUP($lookup_value,$lookup_vector,2);
+ } // function LOOKUP()
+
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array
+ *
+ * @param array $array Array to be flattened
+ * @return array Flattened array
+ */
+ public static function flattenArray($array) {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = array();
+ foreach ($array as $value) {
+ if (is_array($value)) {
+ foreach ($value as $val) {
+ if (is_array($val)) {
+ foreach ($val as $v) {
+ $arrayValues[] = $v;
+ }
+ } else {
+ $arrayValues[] = $val;
+ }
+ }
+ } else {
+ $arrayValues[] = $value;
+ }
+ }
+
+ return $arrayValues;
+ } // function flattenArray()
+
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing
+ *
+ * @param array $array Array to be flattened
+ * @return array Flattened array
+ */
+ public static function flattenArrayIndexed($array) {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = array();
+ foreach ($array as $k1 => $value) {
+ if (is_array($value)) {
+ foreach ($value as $k2 => $val) {
+ if (is_array($val)) {
+ foreach ($val as $k3 => $v) {
+ $arrayValues[$k1.'.'.$k2.'.'.$k3] = $v;
+ }
+ } else {
+ $arrayValues[$k1.'.'.$k2] = $val;
+ }
+ }
+ } else {
+ $arrayValues[$k1] = $value;
+ }
+ }
+
+ return $arrayValues;
+ } // function flattenArrayIndexed()
+
+
+ /**
+ * Convert an array to a single scalar value by extracting the first element
+ *
+ * @param mixed $value Array or scalar value
+ * @return mixed
+ */
+ public static function flattenSingleValue($value = '') {
+ if (is_array($value)) {
+ return self::flattenSingleValue(array_pop($value));
+ }
+ return $value;
+ } // function flattenSingleValue()
+
+} // class PHPExcel_Calculation_Functions
+
+
+//
+// There are a few mathematical functions that aren't available on all versions of PHP for all platforms
+// These functions aren't available in Windows implementations of PHP prior to version 5.3.0
+// So we test if they do exist for this version of PHP/operating platform; and if not we create them
+//
+if (!function_exists('acosh')) {
+ function acosh($x) {
+ return 2 * log(sqrt(($x + 1) / 2) + sqrt(($x - 1) / 2));
+ } // function acosh()
+}
+
+if (!function_exists('asinh')) {
+ function asinh($x) {
+ return log($x + sqrt(1 + $x * $x));
+ } // function asinh()
+}
+
+if (!function_exists('atanh')) {
+ function atanh($x) {
+ return (log(1 + $x) - log(1 - $x)) / 2;
+ } // function atanh()
+}
+
+if (!function_exists('money_format')) {
+ function money_format($format, $number) {
+ $regex = array( '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?(?:#([0-9]+))?',
+ '(?:\.([0-9]+))?([in%])/'
+ );
+ $regex = implode('', $regex);
+ if (setlocale(LC_MONETARY, null) == '') {
+ setlocale(LC_MONETARY, '');
+ }
+ $locale = localeconv();
+ $number = floatval($number);
+ if (!preg_match($regex, $format, $fmatch)) {
+ trigger_error("No format specified or invalid format", E_USER_WARNING);
+ return $number;
+ }
+ $flags = array( 'fillchar' => preg_match('/\=(.)/', $fmatch[1], $match) ? $match[1] : ' ',
+ 'nogroup' => preg_match('/\^/', $fmatch[1]) > 0,
+ 'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ? $match[0] : '+',
+ 'nosimbol' => preg_match('/\!/', $fmatch[1]) > 0,
+ 'isleft' => preg_match('/\-/', $fmatch[1]) > 0
+ );
+ $width = trim($fmatch[2]) ? (int)$fmatch[2] : 0;
+ $left = trim($fmatch[3]) ? (int)$fmatch[3] : 0;
+ $right = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits'];
+ $conversion = $fmatch[5];
+ $positive = true;
+ if ($number < 0) {
+ $positive = false;
+ $number *= -1;
+ }
+ $letter = $positive ? 'p' : 'n';
+ $prefix = $suffix = $cprefix = $csuffix = $signal = '';
+ if (!$positive) {
+ $signal = $locale['negative_sign'];
+ switch (true) {
+ case $locale['n_sign_posn'] == 0 || $flags['usesignal'] == '(':
+ $prefix = '(';
+ $suffix = ')';
+ break;
+ case $locale['n_sign_posn'] == 1:
+ $prefix = $signal;
+ break;
+ case $locale['n_sign_posn'] == 2:
+ $suffix = $signal;
+ break;
+ case $locale['n_sign_posn'] == 3:
+ $cprefix = $signal;
+ break;
+ case $locale['n_sign_posn'] == 4:
+ $csuffix = $signal;
+ break;
+ }
+ }
+ if (!$flags['nosimbol']) {
+ $currency = $cprefix;
+ $currency .= ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']);
+ $currency .= $csuffix;
+ $currency = iconv('ISO-8859-1','UTF-8',$currency);
+ } else {
+ $currency = '';
+ }
+ $space = $locale["{$letter}_sep_by_space"] ? ' ' : '';
+
+ $number = number_format($number, $right, $locale['mon_decimal_point'], $flags['nogroup'] ? '' : $locale['mon_thousands_sep'] );
+ $number = explode($locale['mon_decimal_point'], $number);
+
+ $n = strlen($prefix) + strlen($currency);
+ if ($left > 0 && $left > $n) {
+ if ($flags['isleft']) {
+ $number[0] .= str_repeat($flags['fillchar'], $left - $n);
+ } else {
+ $number[0] = str_repeat($flags['fillchar'], $left - $n) . $number[0];
+ }
+ }
+ $number = implode($locale['mon_decimal_point'], $number);
+ if ($locale["{$letter}_cs_precedes"]) {
+ $number = $prefix . $currency . $space . $number . $suffix;
+ } else {
+ $number = $prefix . $number . $space . $currency . $suffix;
+ }
+ if ($width > 0) {
+ $number = str_pad($number, $width, $flags['fillchar'], $flags['isleft'] ? STR_PAD_RIGHT : STR_PAD_LEFT);
+ }
+ $format = str_replace($fmatch[0], $number, $format);
+ return $format;
+ } // function money_format()
+}
+
+
+//
+// Strangely, PHP doesn't have a mb_str_replace multibyte function
+// As we'll only ever use this function with UTF-8 characters, we can simply "hard-code" the character set
+//
+if ((!function_exists('mb_str_replace')) &&
+ (function_exists('mb_substr')) && (function_exists('mb_strlen')) && (function_exists('mb_strpos'))) {
+ function mb_str_replace($search, $replace, $subject) {
+ if(is_array($subject)) {
+ $ret = array();
+ foreach($subject as $key => $val) {
+ $ret[$key] = mb_str_replace($search, $replace, $val);
+ }
+ return $ret;
+ }
+
+ foreach((array) $search as $key => $s) {
+ if($s == '') {
+ continue;
+ }
+ $r = !is_array($replace) ? $replace : (array_key_exists($key, $replace) ? $replace[$key] : '');
+ $pos = mb_strpos($subject, $s, 0, 'UTF-8');
+ while($pos !== false) {
+ $subject = mb_substr($subject, 0, $pos, 'UTF-8') . $r . mb_substr($subject, $pos + mb_strlen($s, 'UTF-8'), 65535, 'UTF-8');
+ $pos = mb_strpos($subject, $s, $pos + mb_strlen($r, 'UTF-8'), 'UTF-8');
+ }
+ }
+ return $subject;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/functionlist.txt b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/functionlist.txt
new file mode 100644
index 0000000..67dbd49
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Calculation/functionlist.txt
@@ -0,0 +1,351 @@
+ABS
+ACCRINT
+ACCRINTM
+ACOS
+ACOSH
+ADDRESS
+AMORDEGRC
+AMORLINC
+AND
+AREAS
+ASC
+ASIN
+ASINH
+ATAN
+ATAN2
+ATANH
+AVEDEV
+AVERAGE
+AVERAGEA
+AVERAGEIF
+AVERAGEIFS
+BAHTTEXT
+BESSELI
+BESSELJ
+BESSELK
+BESSELY
+BETADIST
+BETAINV
+BIN2DEC
+BIN2HEX
+BIN2OCT
+BINOMDIST
+CEILING
+CELL
+CHAR
+CHIDIST
+CHIINV
+CHITEST
+CHOOSE
+CLEAN
+CODE
+COLUMN
+COLUMNS
+COMBIN
+COMPLEX
+CONCATENATE
+CONFIDENCE
+CONVERT
+CORREL
+COS
+COSH
+COUNT
+COUNTA
+COUNTBLANK
+COUNTIF
+COUNTIFS
+COUPDAYBS
+COUPDAYBS
+COUPDAYSNC
+COUPNCD
+COUPNUM
+COUPPCD
+COVAR
+CRITBINOM
+CUBEKPIMEMBER
+CUBEMEMBER
+CUBEMEMBERPROPERTY
+CUBERANKEDMEMBER
+CUBESET
+CUBESETCOUNT
+CUBEVALUE
+CUMIPMT
+CUMPRINC
+DATE
+DATEDIF
+DATEVALUE
+DAVERAGE
+DAY
+DAYS360
+DB
+DCOUNT
+DCOUNTA
+DDB
+DEC2BIN
+DEC2HEX
+DEC2OCT
+DEGREES
+DELTA
+DEVSQ
+DGET
+DISC
+DMAX
+DMIN
+DOLLAR
+DOLLARDE
+DOLLARFR
+DPRODUCT
+DSTDEV
+DSTDEVP
+DSUM
+DURATION
+DVAR
+DVARP
+EDATE
+EFFECT
+EOMONTH
+ERF
+ERFC
+ERROR.TYPE
+EVEN
+EXACT
+EXP
+EXPONDIST
+FACT
+FACTDOUBLE
+FALSE
+FDIST
+FIND
+FINDB
+FINV
+FISHER
+FISHERINV
+FIXED
+FLOOR
+FORECAST
+FREQUENCY
+FTEST
+FV
+FVSCHEDULE
+GAMAMDIST
+GAMMAINV
+GAMMALN
+GCD
+GEOMEAN
+GESTEP
+GETPIVOTDATA
+GROWTH
+HARMEAN
+HEX2BIN
+HEX2OCT
+HLOOKUP
+HOUR
+HYPERLINK
+HYPGEOMDIST
+IF
+IFERROR
+IMABS
+IMAGINARY
+IMARGUMENT
+IMCONJUGATE
+IMCOS
+IMEXP
+IMLN
+IMLOG10
+IMLOG2
+IMPOWER
+IMPRODUCT
+IMREAL
+IMSIN
+IMSQRT
+IMSUB
+IMSUM
+INDEX
+INDIRECT
+INFO
+INT
+INTERCEPT
+INTRATE
+IPMT
+IRR
+ISBLANK
+ISERR
+ISERROR
+ISEVEN
+ISLOGICAL
+ISNA
+ISNONTEXT
+ISNUMBER
+ISODD
+ISPMT
+ISREF
+ISTEXT
+JIS
+KURT
+LARGE
+LCM
+LEFT
+LEFTB
+LEN
+LENB
+LINEST
+LN
+LOG
+LOG10
+LOGEST
+LOGINV
+LOGNORMDIST
+LOOKUP
+LOWER
+MATCH
+MAX
+MAXA
+MDETERM
+MDURATION
+MEDIAN
+MID
+MIDB
+MIN
+MINA
+MINUTE
+MINVERSE
+MIRR
+MMULT
+MOD
+MODE
+MONTH
+MROUND
+MULTINOMIAL
+N
+NA
+NEGBINOMDIST
+NETWORKDAYS
+NOMINAL
+NORMDIST
+NORMINV
+NORMSDIST
+NORMSINV
+NOT
+NOW
+NPER
+NPV
+OCT2BIN
+OCT2DEC
+OCT2HEX
+ODD
+ODDFPRICE
+ODDFYIELD
+ODDLPRICE
+ODDLYIELD
+OFFSET
+OR
+PEARSON
+PERCENTILE
+PERCENTRANK
+PERMUT
+PHONETIC
+PI
+PMT
+POISSON
+POWER
+PPMT
+PRICE
+PRICEDISC
+PRICEMAT
+PROB
+PRODUCT
+PROPER
+PV
+QUARTILE
+QUOTIENT
+RADIANS
+RAND
+RANDBETWEEN
+RANK
+RATE
+RECEIVED
+REPLACE
+REPLACEB
+REPT
+RIGHT
+RIGHTB
+ROMAN
+ROUND
+ROUNDDOWN
+ROUNDUP
+ROW
+ROWS
+RSQ
+RTD
+SEARCH
+SEARCHB
+SECOND
+SERIESSUM
+SIGN
+SIN
+SINH
+SKEW
+SLN
+SLOPE
+SMALL
+SQRT
+SQRTPI
+STANDARDIZE
+STDEV
+STDEVA
+STDEVP
+STDEVPA
+STEYX
+SUBSTITUTE
+SUBTOTAL
+SUM
+SUMIF
+SUMIFS
+SUMPRODUCT
+SUMSQ
+SUMX2MY2
+SUMX2PY2
+SUMXMY2
+SYD
+T
+TAN
+TANH
+TBILLEQ
+TBILLPRICE
+TBILLYIELD
+TDIST
+TEXT
+TIME
+TIMEVALUE
+TINV
+TODAY
+TRANSPOSE
+TREND
+TRIM
+TRIMMEAN
+TRUE
+TRUNC
+TTEST
+TYPE
+UPPER
+USDOLLAR
+VALUE
+VAR
+VARA
+VARP
+VARPA
+VDB
+VERSION
+VLOOKUP
+WEEKDAY
+WEEKNUM
+WEIBULL
+WORKDAY
+XIRR
+XNPV
+YEAR
+YEARFRAC
+YIELD
+YIELDDISC
+YIELDMAT
+ZTEST
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell.php
new file mode 100644
index 0000000..c0603ac
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell.php
@@ -0,0 +1,815 @@
+_column = strtoupper($pColumn);
+ $this->_row = $pRow;
+
+ // Initialise cell value
+ $this->_value = $pValue;
+
+ // Set worksheet
+ $this->_parent = $pSheet;
+
+ // Set datatype?
+ if (!is_null($pDataType)) {
+ $this->_dataType = $pDataType;
+ } else {
+ if (!self::getValueBinder()->bindValue($this, $pValue)) {
+ throw new Exception("Value could not be bound to cell.");
+ }
+ }
+
+ // set default index to cellXf
+ $this->_xfIndex = 0;
+ }
+
+ /**
+ * Get cell coordinate column
+ *
+ * @return string
+ */
+ public function getColumn()
+ {
+ return $this->_column;
+ }
+
+ /**
+ * Get cell coordinate row
+ *
+ * @return int
+ */
+ public function getRow()
+ {
+ return $this->_row;
+ }
+
+ /**
+ * Get cell coordinate
+ *
+ * @return string
+ */
+ public function getCoordinate()
+ {
+ return $this->_column . $this->_row;
+ }
+
+ /**
+ * Get cell value
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * Set cell value
+ *
+ * This clears the cell formula.
+ *
+ * @param mixed $pValue Value
+ * @return PHPExcel_Cell
+ */
+ public function setValue($pValue = null)
+ {
+ if (!self::getValueBinder()->bindValue($this, $pValue)) {
+ throw new Exception("Value could not be bound to cell.");
+ }
+ return $this;
+ }
+
+ /**
+ * Set cell value (with explicit data type given)
+ *
+ * @param mixed $pValue Value
+ * @param string $pDataType Explicit data type
+ * @return PHPExcel_Cell
+ * @throws 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_STRING:
+ case PHPExcel_Cell_DataType::TYPE_NULL:
+ 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 Exception('Invalid datatype: ' . $pDataType);
+ break;
+ }
+
+ // set the datatype
+ $this->_dataType = $pDataType;
+ return $this;
+ }
+
+ /**
+ * Get caluclated cell value
+ *
+ * @return mixed
+ */
+ public function getCalculatedValue($resetLog=true)
+ {
+// echo 'Cell '.$this->getCoordinate().' value is a '.$this->_dataType.' with a value of '.$this->getValue().' ';
+ if (!is_null($this->_calculatedValue) && $this->_dataType == PHPExcel_Cell_DataType::TYPE_FORMULA) {
+ try {
+// echo 'Cell value for '.$this->getCoordinate().' is a formula: Calculating value ';
+ $result = PHPExcel_Calculation::getInstance()->calculateCellValue($this,$resetLog);
+ } catch ( Exception $ex ) {
+// echo 'Calculation Exception: '.$ex->getMessage().' ';
+ $result = '#N/A';
+ }
+
+ if ((is_string($result)) && ($result == '#Not Yet Implemented')) {
+// echo 'Returning fallback value of '.$this->_calculatedValue.' for cell '.$this->getCoordinate().' ';
+ return $this->_calculatedValue; // Fallback if calculation engine does not support the formula.
+ } else {
+// echo 'Returning calculated value of '.$result.' for cell '.$this->getCoordinate().' ';
+ return $result;
+ }
+ }
+
+ if (is_null($this->_value)) {
+// echo 'Cell '.$this->getCoordinate().' has no value, formula or otherwise ';
+ return null;
+ } else if ($this->_dataType != PHPExcel_Cell_DataType::TYPE_FORMULA) {
+// echo 'Cell value for '.$this->getCoordinate().' is not a formula: Returning data value of '.$this->_value.' ';
+ return $this->_value;
+ } else {
+// echo 'Cell value is a formula: Calculating value ';
+ return PHPExcel_Calculation::getInstance()->calculateCellValue($this,$resetLog);
+ }
+ }
+
+ /**
+ * Set calculated value (used for caching)
+ *
+ * @param mixed $pValue Value
+ * @return PHPExcel_Cell
+ */
+ public function setCalculatedValue($pValue = null)
+ {
+ if (!is_null($pValue)) {
+ $this->_calculatedValue = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get old calculated value (cached)
+ *
+ * @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)
+ {
+ $this->_dataType = $pDataType;
+ return $this;
+ }
+
+ /**
+ * Has Data validation?
+ *
+ * @return boolean
+ */
+ public function hasDataValidation()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
+ }
+
+ return $this->_parent->dataValidationExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Data validation
+ *
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function getDataValidation()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
+ }
+
+ $dataValidation = $this->_parent->getDataValidation($this->getCoordinate());
+ return $dataValidation;
+ }
+
+ /**
+ * Set Data validation
+ *
+ * @param PHPExcel_Cell_DataValidation $pDataValidation
+ * @throws Exception
+ * @return PHPExcel_Cell
+ */
+ public function setDataValidation(PHPExcel_Cell_DataValidation $pDataValidation = null)
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
+ }
+
+ $this->_parent->setDataValidation($this->getCoordinate(), $pDataValidation);
+ return $this;
+ }
+
+ /**
+ * Has Hyperlink
+ *
+ * @return boolean
+ */
+ public function hasHyperlink()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
+ }
+
+ return $this->_parent->hyperlinkExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Hyperlink
+ *
+ * @throws Exception
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function getHyperlink()
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
+ }
+
+ $hyperlink = $this->_parent->getHyperlink($this->getCoordinate());
+ return $hyperlink;
+ }
+
+ /**
+ * Set Hyperlink
+ *
+ * @param PHPExcel_Cell_Hyperlink $pHyperlink
+ * @throws Exception
+ * @return PHPExcel_Cell
+ */
+ public function setHyperlink(PHPExcel_Cell_Hyperlink $pHyperlink = null)
+ {
+ if (!isset($this->_parent)) {
+ throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
+ }
+
+ $this->_parent->setHyperlink($this->getCoordinate(), $pHyperlink);
+ return $this;
+ }
+
+ /**
+ * Get parent
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getParent() {
+ return $this->_parent;
+ }
+
+ /**
+ * Re-bind parent
+ *
+ * @param PHPExcel_Worksheet $parent
+ * @return PHPExcel_Cell
+ */
+ public function rebindParent(PHPExcel_Worksheet $parent) {
+ $this->_parent = $parent;
+ return $this;
+ }
+
+ /**
+ * Is cell in a specific range?
+ *
+ * @param string $pRange Cell range (e.g. A1:A1)
+ * @return boolean
+ */
+ public function isInRange($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Extract range
+ $rangeA = '';
+ $rangeB = '';
+ 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;
+
+ // Translate properties
+ $myColumn = PHPExcel_Cell::columnIndexFromString($this->getColumn()) - 1;
+ $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 Exception
+ */
+ public static function coordinateFromString($pCoordinateString = 'A1')
+ {
+ if (strpos($pCoordinateString,':') !== false) {
+ throw new Exception('Cell coordinate string can not be a range of cells.');
+
+ } else if ($pCoordinateString == '') {
+ throw new Exception('Cell coordinate can not be zero-length string.');
+
+ } else if (preg_match("/([$]?[A-Z]+)([$]?\d+)/", $pCoordinateString, $matches)) {
+ list(, $column, $row) = $matches;
+ return array($column, $row);
+
+ } else {
+ throw new Exception('Invalid cell coordinate.');
+
+ }
+ }
+
+ /**
+ * Make string coordinate absolute
+ *
+ * @param string $pCoordinateString
+ * @return string Absolute coordinate
+ * @throws Exception
+ */
+ public static function absoluteCoordinate($pCoordinateString = 'A1')
+ {
+ if (strpos($pCoordinateString,':') === false && strpos($pCoordinateString,',') === false) {
+ // Return value
+ $returnValue = '';
+
+ // Create absolute coordinate
+ list($column, $row) = PHPExcel_Cell::coordinateFromString($pCoordinateString);
+ $returnValue = '$' . $column . '$' . $row;
+
+ // Return
+ return $returnValue;
+ } else {
+ throw new Exception("Coordinate string should not be a cell range.");
+ }
+ }
+
+ /**
+ * Split range into coordinate strings
+ *
+ * @param string $pRange
+ * @return array Array containg one or more arrays containing one or two coordinate strings
+ */
+ public static function splitRange($pRange = 'A1:A1')
+ {
+ $exploded = explode(',', $pRange);
+ for ($i = 0; $i < count($exploded); ++$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 Exception
+ */
+ public static function buildRange($pRange)
+ {
+ // Verify range
+ if (!is_array($pRange) || count($pRange) == 0 || !is_array($pRange[0])) {
+ throw new Exception('Range does not contain any information.');
+ }
+
+ // Build range
+ $imploded = array();
+ for ($i = 0; $i < count($pRange); ++$i) {
+ $pRange[$i] = implode(':', $pRange[$i]);
+ }
+ $imploded = implode(',', $pRange);
+
+ return $imploded;
+ }
+
+ /**
+ * 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')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Extract range
+ $rangeA = '';
+ $rangeB = '';
+ 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]);
+ $rangeEnd[0] = PHPExcel_Cell::columnIndexFromString($rangeEnd[0]);
+
+ return array( ($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1) );
+ }
+
+ /**
+ * Column index from string
+ *
+ * @param string $pString
+ * @return int Column index (base 1 !!!)
+ * @throws Exception
+ */
+ public static function columnIndexFromString($pString = 'A')
+ {
+ // Convert to uppercase
+ $pString = strtoupper($pString);
+
+ $strLen = strlen($pString);
+ // Convert column to integer
+ if ($strLen == 1) {
+ return (ord($pString{0}) - 64);
+ } elseif ($strLen == 2) {
+ return $result = ((1 + (ord($pString{0}) - 65)) * 26) + (ord($pString{1}) - 64);
+ } elseif ($strLen == 3) {
+ return ((1 + (ord($pString{0}) - 65)) * 676) + ((1 + (ord($pString{1}) - 65)) * 26) + (ord($pString{2}) - 64);
+ } else {
+ throw new Exception("Column string index can not be " . ($strLen != 0 ? "longer than 3 characters" : "empty") . ".");
+ }
+ }
+
+ /**
+ * String from columnindex
+ *
+ * @param int $pColumnIndex Column index (base 0 !!!)
+ * @return string
+ */
+ public static function stringFromColumnIndex($pColumnIndex = 0)
+ {
+ // Determine column string
+ if ($pColumnIndex < 26) {
+ return chr(65 + $pColumnIndex);
+ }
+ return PHPExcel_Cell::stringFromColumnIndex((int)($pColumnIndex / 26) -1).chr(65 + $pColumnIndex%26) ;
+ }
+
+ /**
+ * Extract all cell references in range
+ *
+ * @param string $pRange Range (e.g. A1 or A1:A10 or A1:A10 A100:A1000)
+ * @return array Array containing single cell references
+ */
+ public static function extractAllCellReferencesInRange($pRange = 'A1') {
+ // Returnvalue
+ $returnValue = array();
+
+ // Explode spaces
+ $aExplodeSpaces = explode(' ', str_replace('$', '', strtoupper($pRange)));
+ foreach ($aExplodeSpaces as $explodedSpaces) {
+ // Single cell?
+ if (strpos($explodedSpaces,':') === false && strpos($explodedSpaces,',') === false) {
+ $col = 'A';
+ $row = 1;
+ list($col, $row) = PHPExcel_Cell::coordinateFromString($explodedSpaces);
+
+ if (strlen($col) <= 2) {
+ $returnValue[] = $explodedSpaces;
+ }
+
+ continue;
+ }
+
+ // Range...
+ $range = PHPExcel_Cell::splitRange($explodedSpaces);
+ for ($i = 0; $i < count($range); ++$i) {
+ // Single cell?
+ if (count($range[$i]) == 1) {
+ $col = 'A';
+ $row = 1;
+ list($col, $row) = PHPExcel_Cell::coordinateFromString($range[$i]);
+
+ if (strlen($col) <= 2) {
+ $returnValue[] = $explodedSpaces;
+ }
+ }
+
+ // Range...
+ $rangeStart = $rangeEnd = '';
+ $startingCol = $startingRow = $endingCol = $endingRow = 0;
+
+ list($rangeStart, $rangeEnd) = $range[$i];
+ list($startingCol, $startingRow) = PHPExcel_Cell::coordinateFromString($rangeStart);
+ list($endingCol, $endingRow) = PHPExcel_Cell::coordinateFromString($rangeEnd);
+
+ // Conversions...
+ $startingCol = PHPExcel_Cell::columnIndexFromString($startingCol);
+ $endingCol = PHPExcel_Cell::columnIndexFromString($endingCol);
+
+ // Current data
+ $currentCol = --$startingCol;
+ $currentRow = $startingRow;
+
+ // Loop cells
+ while ($currentCol < $endingCol) {
+ $loopColumn = PHPExcel_Cell::stringFromColumnIndex($currentCol);
+ while ($currentRow <= $endingRow) {
+ $returnValue[] = $loopColumn.$currentRow;
+ ++$currentRow;
+ }
+ ++$currentCol;
+ $currentRow = $startingRow;
+ }
+ }
+ }
+
+ // Return value
+ return $returnValue;
+ }
+
+ /**
+ * Compare 2 cells
+ *
+ * @param PHPExcel_Cell $a Cell a
+ * @param PHPExcel_Cell $a 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->_row < $b->_row) {
+ return -1;
+ } elseif ($a->_row > $b->_row) {
+ return 1;
+ } elseif (PHPExcel_Cell::columnIndexFromString($a->_column) < PHPExcel_Cell::columnIndexFromString($b->_column)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Get value binder to use
+ *
+ * @return PHPExcel_Cell_IValueBinder
+ */
+ public static function getValueBinder() {
+ if (is_null(self::$_valueBinder)) {
+ self::$_valueBinder = new PHPExcel_Cell_DefaultValueBinder();
+ }
+
+ return self::$_valueBinder;
+ }
+
+ /**
+ * Set value binder to use
+ *
+ * @param PHPExcel_Cell_IValueBinder $binder
+ * @throws Exception
+ */
+ public static function setValueBinder(PHPExcel_Cell_IValueBinder $binder = null) {
+ if (is_null($binder)) {
+ throw new 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)) {
+ $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;
+ }
+
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/AdvancedValueBinder.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/AdvancedValueBinder.php
new file mode 100644
index 0000000..d8e2564
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/AdvancedValueBinder.php
@@ -0,0 +1,131 @@
+setValueExplicit( (float)str_replace('%', '', $value) / 100, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_PERCENTAGE );
+
+ return true;
+ }
+
+ // Check for time e.g. '9:45', '09:45'
+ if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
+ list($h, $m) = explode(':', $value);
+ $days = $h / 24 + $m / 1440;
+
+ // Convert value to number
+ $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME3 );
+
+ return true;
+ }
+
+ // Check for date
+ if (strtotime($value) !== false) {
+ // make sure we have UTC for the sake of strtotime
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+
+ // Convert value to Excel date
+ $cell->setValueExplicit( PHPExcel_Shared_Date::PHPToExcel(strtotime($value)), PHPExcel_Cell_DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getParent()->getStyle( $cell->getCoordinate() )->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2 );
+
+ // restore original value for timezone
+ date_default_timezone_set($saveTimeZone);
+
+ return true;
+ }
+ }
+
+ // Not bound yet? Use parent...
+ return parent::bindValue($cell, $value);
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DataType.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DataType.php
new file mode 100644
index 0000000..add1bec
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DataType.php
@@ -0,0 +1,125 @@
+ 0, '#DIV/0!' => 1, '#VALUE!' => 2, '#REF!' => 3, '#NAME?' => 4, '#NUM!' => 5, '#N/A' => 6);
+
+ /**
+ * Get list of error codes
+ *
+ * @return array
+ */
+ public static function getErrorCodes() {
+ return self::$_errorCodes;
+ }
+
+ /**
+ * DataType for value
+ *
+ * @deprecated Replaced by PHPExcel_Cell_IValueBinder infrastructure
+ * @param mixed $pValue
+ * @return int
+ */
+ public static function dataTypeForValue($pValue = null) {
+ return PHPExcel_Cell_DefaultValueBinder::dataTypeForValue($pValue);
+ }
+
+ /**
+ * Check a string that it satisfies Excel requirements
+ *
+ * @param mixed Value to sanitize to an Excel string
+ * @return mixed Sanitized value
+ */
+ public static function checkString($pValue = null)
+ {
+ if ($pValue instanceof PHPExcel_RichText) {
+ // TODO: Sanitize Rich-Text string (max. character count is 32,767)
+ return $pValue;
+ }
+
+ // string must never be longer than 32,767 characters, truncate if necessary
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 32767);
+
+ // we require that newline is represented as "\n" in core, not as "\r\n" or "\r"
+ $pValue = str_replace(array("\r\n", "\r"), "\n", $pValue);
+
+ return $pValue;
+ }
+
+ /**
+ * Check a value that it is a valid error code
+ *
+ * @param mixed Value to sanitize to an Excel error code
+ * @return string Sanitized value
+ */
+ public static function checkErrorCode($pValue = null)
+ {
+ $pValue = (string)$pValue;
+
+ if ( !array_key_exists($pValue, self::$_errorCodes) ) {
+ $pValue = '#NULL!';
+ }
+
+ return $pValue;
+ }
+
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DataValidation.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DataValidation.php
new file mode 100644
index 0000000..8d7f293
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DataValidation.php
@@ -0,0 +1,509 @@
+_formula1 = '';
+ $this->_formula2 = '';
+ $this->_type = PHPExcel_Cell_DataValidation::TYPE_NONE;
+ $this->_errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP;
+ $this->_operator = '';
+ $this->_allowBlank = false;
+ $this->_showDropDown = false;
+ $this->_showInputMessage = false;
+ $this->_showErrorMessage = false;
+ $this->_errorTitle = '';
+ $this->_error = '';
+ $this->_promptTitle = '';
+ $this->_prompt = '';
+
+ // Set cell
+ $this->_parent = $pCell;
+ }
+
+ /**
+ * Get Formula 1
+ *
+ * @return string
+ */
+ public function getFormula1() {
+ return $this->_formula1;
+ }
+
+ /**
+ * Set Formula 1
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setFormula1($value = '') {
+ $this->_formula1 = $value;
+ return $this;
+ }
+
+ /**
+ * Get Formula 2
+ *
+ * @return string
+ */
+ public function getFormula2() {
+ return $this->_formula2;
+ }
+
+ /**
+ * Set Formula 2
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setFormula2($value = '') {
+ $this->_formula2 = $value;
+ return $this;
+ }
+
+ /**
+ * Get Type
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->_type;
+ }
+
+ /**
+ * Set Type
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setType($value = PHPExcel_Cell_DataValidation::TYPE_NONE) {
+ $this->_type = $value;
+ return $this;
+ }
+
+ /**
+ * Get Error style
+ *
+ * @return string
+ */
+ public function getErrorStyle() {
+ return $this->_errorStyle;
+ }
+
+ /**
+ * Set Error style
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setErrorStyle($value = PHPExcel_Cell_DataValidation::STYLE_STOP) {
+ $this->_errorStyle = $value;
+ return $this;
+ }
+
+ /**
+ * Get Operator
+ *
+ * @return string
+ */
+ public function getOperator() {
+ return $this->_operator;
+ }
+
+ /**
+ * Set Operator
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setOperator($value = '') {
+ $this->_operator = $value;
+ return $this;
+ }
+
+ /**
+ * Get Allow Blank
+ *
+ * @return boolean
+ */
+ public function getAllowBlank() {
+ return $this->_allowBlank;
+ }
+
+ /**
+ * Set Allow Blank
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setAllowBlank($value = false) {
+ $this->_allowBlank = $value;
+ return $this;
+ }
+
+ /**
+ * Get Show DropDown
+ *
+ * @return boolean
+ */
+ public function getShowDropDown() {
+ return $this->_showDropDown;
+ }
+
+ /**
+ * Set Show DropDown
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setShowDropDown($value = false) {
+ $this->_showDropDown = $value;
+ return $this;
+ }
+
+ /**
+ * Get Show InputMessage
+ *
+ * @return boolean
+ */
+ public function getShowInputMessage() {
+ return $this->_showInputMessage;
+ }
+
+ /**
+ * Set Show InputMessage
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setShowInputMessage($value = false) {
+ $this->_showInputMessage = $value;
+ return $this;
+ }
+
+ /**
+ * Get Show ErrorMessage
+ *
+ * @return boolean
+ */
+ public function getShowErrorMessage() {
+ return $this->_showErrorMessage;
+ }
+
+ /**
+ * Set Show ErrorMessage
+ *
+ * @param boolean $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setShowErrorMessage($value = false) {
+ $this->_showErrorMessage = $value;
+ return $this;
+ }
+
+ /**
+ * Get Error title
+ *
+ * @return string
+ */
+ public function getErrorTitle() {
+ return $this->_errorTitle;
+ }
+
+ /**
+ * Set Error title
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setErrorTitle($value = '') {
+ $this->_errorTitle = $value;
+ return $this;
+ }
+
+ /**
+ * Get Error
+ *
+ * @return string
+ */
+ public function getError() {
+ return $this->_error;
+ }
+
+ /**
+ * Set Error
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setError($value = '') {
+ $this->_error = $value;
+ return $this;
+ }
+
+ /**
+ * Get Prompt title
+ *
+ * @return string
+ */
+ public function getPromptTitle() {
+ return $this->_promptTitle;
+ }
+
+ /**
+ * Set Prompt title
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setPromptTitle($value = '') {
+ $this->_promptTitle = $value;
+ return $this;
+ }
+
+ /**
+ * Get Prompt
+ *
+ * @return string
+ */
+ public function getPrompt() {
+ return $this->_prompt;
+ }
+
+ /**
+ * Set Prompt
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setPrompt($value = '') {
+ $this->_prompt = $value;
+ return $this;
+ }
+
+ /**
+ * Get parent
+ *
+ * @return PHPExcel_Cell
+ */
+ public function getParent() {
+ return $this->_parent;
+ }
+
+ /**
+ * Set Parent
+ *
+ * @param PHPExcel_Cell $value
+ * @return PHPExcel_Cell_DataValidation
+ */
+ public function setParent($value = null) {
+ $this->_parent = $value;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_formula1
+ . $this->_formula2
+ . $this->_type = PHPExcel_Cell_DataValidation::TYPE_NONE
+ . $this->_errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP
+ . $this->_operator
+ . ($this->_allowBlank ? 't' : 'f')
+ . ($this->_showDropDown ? 't' : 'f')
+ . ($this->_showInputMessage ? 't' : 'f')
+ . ($this->_showErrorMessage ? 't' : 'f')
+ . $this->_errorTitle
+ . $this->_error
+ . $this->_promptTitle
+ . $this->_prompt
+ . $this->_parent->getCoordinate()
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone() {
+ // unbind parent
+ $this->setParent(null);
+
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value) && $key != '_parent') {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DefaultValueBinder.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DefaultValueBinder.php
new file mode 100644
index 0000000..2b8bef7
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/DefaultValueBinder.php
@@ -0,0 +1,117 @@
+setValueExplicit( $value, PHPExcel_Cell_DataType::dataTypeForValue($value) );
+
+ // Done!
+ return true;
+ }
+
+ /**
+ * DataType for value
+ *
+ * @param mixed $pValue
+ * @return int
+ */
+ public static function dataTypeForValue($pValue = null) {
+ // Match the value against a few data types
+ if (is_null($pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_NULL;
+
+ } elseif ($pValue === '') {
+ return PHPExcel_Cell_DataType::TYPE_STRING;
+
+ } elseif ($pValue instanceof PHPExcel_RichText) {
+ return PHPExcel_Cell_DataType::TYPE_STRING;
+
+ } elseif ($pValue{0} === '=') {
+ return PHPExcel_Cell_DataType::TYPE_FORMULA;
+
+ } elseif (is_bool($pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_BOOL;
+
+ } elseif (is_float($pValue) || is_int($pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_NUMERIC;
+
+ } elseif (preg_match('/^\-?([0-9]+\\.?[0-9]*|[0-9]*\\.?[0-9]+)$/', $pValue)) {
+ return PHPExcel_Cell_DataType::TYPE_NUMERIC;
+
+ } elseif (is_string($pValue) && array_key_exists($pValue, PHPExcel_Cell_DataType::getErrorCodes())) {
+ return PHPExcel_Cell_DataType::TYPE_ERROR;
+
+ } else {
+ return PHPExcel_Cell_DataType::TYPE_STRING;
+
+ }
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/Hyperlink.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/Hyperlink.php
new file mode 100644
index 0000000..a4f3758
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/Hyperlink.php
@@ -0,0 +1,159 @@
+_url = $pUrl;
+ $this->_tooltip = $pTooltip;
+
+ // Set cell
+ $this->_parent = $pCell;
+ }
+
+ /**
+ * Get URL
+ *
+ * @return string
+ */
+ public function getUrl() {
+ return $this->_url;
+ }
+
+ /**
+ * Set URL
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function setUrl($value = '') {
+ $this->_url = $value;
+ return $this;
+ }
+
+ /**
+ * Get tooltip
+ *
+ * @return string
+ */
+ public function getTooltip() {
+ return $this->_tooltip;
+ }
+
+ /**
+ * Set tooltip
+ *
+ * @param string $value
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function setTooltip($value = '') {
+ $this->_tooltip = $value;
+ return $this;
+ }
+
+ /**
+ * Is this hyperlink internal? (to another sheet)
+ *
+ * @return boolean
+ */
+ public function isInternal() {
+ return strpos($this->_url, 'sheet://') !== false;
+ }
+
+ /**
+ * Get parent
+ *
+ * @return PHPExcel_Cell
+ */
+ public function getParent() {
+ return $this->_parent;
+ }
+
+ /**
+ * Set Parent
+ *
+ * @param PHPExcel_Cell $value
+ * @return PHPExcel_Cell_Hyperlink
+ */
+ public function setParent($value = null) {
+ $this->_parent = $value;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode() {
+ return md5(
+ $this->_url
+ . $this->_tooltip
+ . $this->_parent->getCoordinate()
+ . __CLASS__
+ );
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/IValueBinder.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/IValueBinder.php
new file mode 100644
index 0000000..3a4186c
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Cell/IValueBinder.php
@@ -0,0 +1,58 @@
+_author = 'Author';
+ $this->_text = new PHPExcel_RichText();
+ $this->_fillColor = new PHPExcel_Style_Color('FFFFFFE1');
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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()
+ . __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/flaxil/inc/cube/externals/PHPExcel/PHPExcel/DocumentProperties.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/DocumentProperties.php
new file mode 100644
index 0000000..41fe1fb
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/DocumentProperties.php
@@ -0,0 +1,345 @@
+_creator = 'Unknown Creator';
+ $this->_lastModifiedBy = $this->_creator;
+ $this->_created = time();
+ $this->_modified = time();
+ $this->_title = "Untitled Spreadsheet";
+ $this->_subject = '';
+ $this->_description = '';
+ $this->_keywords = '';
+ $this->_category = '';
+ $this->_company = 'Microsoft Corporation';
+ }
+
+ /**
+ * 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 (is_null($pValue)) {
+ $pValue = time();
+ }
+ $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 (is_null($pValue)) {
+ $pValue = time();
+ }
+ $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 PHPPowerPoint_DocumentProperties
+ */
+ public function setCompany($pValue = '') {
+ $this->_company = $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/flaxil/inc/cube/externals/PHPExcel/PHPExcel/DocumentSecurity.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/DocumentSecurity.php
new file mode 100644
index 0000000..6344d40
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/DocumentSecurity.php
@@ -0,0 +1,230 @@
+_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/flaxil/inc/cube/externals/PHPExcel/PHPExcel/HashTable.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/HashTable.php
new file mode 100644
index 0000000..afbced9
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/HashTable.php
@@ -0,0 +1,212 @@
+addFromSource($pSource);
+ }
+ }
+
+ /**
+ * Add HashTable items from source
+ *
+ * @param PHPExcel_IComparable[] $pSource Source array to create HashTable from
+ * @throws Exception
+ */
+ public function addFromSource($pSource = null) {
+ // Check if an array was passed
+ if ($pSource == null) {
+ return;
+ } else if (!is_array($pSource)) {
+ throw new Exception('Invalid array parameter passed.');
+ }
+
+ foreach ($pSource as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Add HashTable item
+ *
+ * @param PHPExcel_IComparable $pSource Item to add
+ * @throws Exception
+ */
+ public function add(PHPExcel_IComparable $pSource = null) {
+ if (!isset($this->_items[ $pSource->getHashCode() ])) {
+ $this->_items[ $pSource->getHashCode() ] = $pSource;
+ $this->_keyMap[ count($this->_items) - 1 ] = $pSource->getHashCode();
+ }
+ }
+
+ /**
+ * Remove HashTable item
+ *
+ * @param PHPExcel_IComparable $pSource Item to remove
+ * @throws Exception
+ */
+ public function remove(PHPExcel_IComparable $pSource = null) {
+ if (isset($this->_items[ $pSource->getHashCode() ])) {
+ unset($this->_items[ $pSource->getHashCode() ]);
+
+ $deleteKey = -1;
+ foreach ($this->_keyMap as $key => $value) {
+ if ($deleteKey >= 0) {
+ $this->_keyMap[$key - 1] = $value;
+ }
+
+ if ($value == $pSource->getHashCode()) {
+ $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/flaxil/inc/cube/externals/PHPExcel/PHPExcel/IComparable.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/IComparable.php
new file mode 100644
index 0000000..1a3a689
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/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
+ */
+ private static $_autoResolveClasses = array(
+ 'Excel2007',
+ 'Excel5',
+ 'Excel2003XML',
+ 'OOCalc',
+ 'SYLK',
+ 'Serialized',
+ 'CSV',
+ );
+
+ /**
+ * Private constructor for PHPExcel_IOFactory
+ */
+ private function __construct() { }
+
+ /**
+ * Get search locations
+ *
+ * @return array
+ */
+ public static function getSearchLocations() {
+ return self::$_searchLocations;
+ }
+
+ /**
+ * Set search locations
+ *
+ * @param array $value
+ * @throws Exception
+ */
+ public static function setSearchLocations($value) {
+ if (is_array($value)) {
+ self::$_searchLocations = $value;
+ } else {
+ throw new Exception('Invalid parameter passed.');
+ }
+ }
+
+ /**
+ * Add search location
+ *
+ * @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 );
+ }
+
+ /**
+ * Create PHPExcel_Writer_IWriter
+ *
+ * @param PHPExcel $phpExcel
+ * @param string $writerType Example: Excel2007
+ * @return PHPExcel_Writer_IWriter
+ */
+ 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']);
+ $classFile = str_replace('{0}', $writerType, $searchLocation['path']);
+
+ if (!class_exists($className)) {
+ require_once PHPEXCEL_ROOT . $classFile;
+ }
+
+ $instance = new $className($phpExcel);
+ if (!is_null($instance)) {
+ return $instance;
+ }
+ }
+ }
+
+ // Nothing found...
+ throw new Exception("No $searchType found for type $writerType");
+ }
+
+ /**
+ * Create PHPExcel_Reader_IReader
+ *
+ * @param string $readerType Example: Excel2007
+ * @return PHPExcel_Reader_IReader
+ */
+ 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']);
+ $classFile = str_replace('{0}', $readerType, $searchLocation['path']);
+
+ if (!class_exists($className)) {
+ require_once PHPEXCEL_ROOT . $classFile;
+ }
+
+ $instance = new $className();
+ if (!is_null($instance)) {
+ return $instance;
+ }
+ }
+ }
+
+ // Nothing found...
+ throw new Exception("No $searchType found for type $readerType");
+ }
+
+ /**
+ * Loads PHPExcel from file using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @param string $pFileName
+ * @return PHPExcel
+ */
+ public static function load($pFilename) {
+ $reader = self::createReaderForFile($pFilename);
+ return $reader->load($pFilename);
+ }
+
+ /**
+ * Create PHPExcel_Reader_IReader for file using automatic PHPExcel_Reader_IReader resolution
+ *
+ * @param string $pFileName
+ * @return PHPExcel_Reader_IReader
+ */
+ public static function createReaderForFile($pFilename) {
+
+ // First, lucky guess by inspecting file extension
+ $pathinfo = pathinfo($pFilename);
+
+ if (isset($pathinfo['extension'])) {
+
+ switch (strtolower($pathinfo['extension'])) {
+ case 'xlsx':
+ $reader = self::createReader('Excel2007');
+ break;
+
+ case 'xls':
+ $reader = self::createReader('Excel5');
+ break;
+
+ case 'ods':
+ $reader = self::createReader('OOCalc');
+ break;
+
+ case 'slk':
+ $reader = self::createReader('SYLK');
+ break;
+
+ case 'xml':
+ $reader = self::createReader('Excel2003XML');
+ 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;
+
+ }
+
+ // Let's see if we are lucky
+ if ($reader->canRead($pFilename)) {
+ return $reader;
+ }
+
+ }
+
+ // If we reach here then "lucky guess" didn't give any result
+
+ // Try loading using self::$_autoResolveClasses
+ foreach (self::$_autoResolveClasses as $autoResolveClass) {
+ $reader = self::createReader($autoResolveClass);
+ if ($reader->canRead($pFilename)) {
+ return $reader;
+ }
+ }
+
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/NamedRange.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/NamedRange.php
new file mode 100644
index 0000000..87054a5
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/NamedRange.php
@@ -0,0 +1,231 @@
+_worksheet)
+ *
+ * @var bool
+ */
+ private $_localOnly;
+
+ /**
+ * Create a new NamedRange
+ *
+ * @param string $pName
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @param string $pRange
+ * @param bool $pLocalOnly
+ */
+ public function __construct($pName = null, PHPExcel_Worksheet $pWorksheet, $pRange = 'A1', $pLocalOnly = false)
+ {
+ // Validate data
+ if (is_null($pName) || is_null($pWorksheet)|| is_null($pRange)) {
+ throw new Exception('Parameters can not be null.');
+ }
+
+ // Set local members
+ $this->_name = $pName;
+ $this->_worksheet = $pWorksheet;
+ $this->_range = $pRange;
+ $this->_localOnly = $pLocalOnly;
+ }
+
+ /**
+ * Get name
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->_name;
+ }
+
+ /**
+ * Set name
+ *
+ * @param string $value
+ * @return PHPExcel_NamedRange
+ */
+ public function setName($value = null) {
+ if (!is_null($value)) {
+ // Old title
+ $oldTitle = $this->_name;
+
+ // Re-attach
+ if (!is_null($this->_worksheet)) {
+ $this->_worksheet->getParent()->removeNamedRange($this->_name,$this->_worksheet);
+ }
+ $this->_name = $value;
+
+ if (!is_null($this->_worksheet)) {
+ $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 (!is_null($value)) {
+ $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 (!is_null($value)) {
+ $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;
+ return $this;
+ }
+
+ /**
+ * Resolve a named range to a regular cell range
+ *
+ * @param string $pNamedRange Named range
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @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/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/CSV.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/CSV.php
new file mode 100644
index 0000000..8758112
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/CSV.php
@@ -0,0 +1,337 @@
+_inputEncoding = 'UTF-8';
+ $this->_delimiter = ',';
+ $this->_enclosure = '"';
+ $this->_lineEnding = PHP_EOL;
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ return true;
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ }
+
+ /**
+ * Set input encoding
+ *
+ * @param string $pValue Input encoding
+ */
+ public function setInputEncoding($pValue = 'UTF-8')
+ {
+ $this->_inputEncoding = $pValue;
+ }
+
+ /**
+ * Get input encoding
+ *
+ * @return string
+ */
+ public function getInputEncoding()
+ {
+ return $this->_inputEncoding;
+ }
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Create new PHPExcel
+ while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) {
+ $objPHPExcel->createSheet();
+ }
+ $objPHPExcel->setActiveSheetIndex( $this->_sheetIndex );
+
+ // Open file
+ $fileHandle = fopen($pFilename, 'r');
+ if ($fileHandle === false) {
+ throw new Exception("Could not open file $pFilename for reading.");
+ }
+
+ // Skip BOM, if any
+ switch ($this->_inputEncoding) {
+ case 'UTF-8':
+ fgets($fileHandle, 4) == "\xEF\xBB\xBF" ?
+ fseek($fileHandle, 3) : fseek($fileHandle, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ // Loop through file
+ $currentRow = 0;
+ $rowData = array();
+ while (($rowData = fgetcsv($fileHandle, 0, $this->_delimiter, $this->_enclosure)) !== FALSE) {
+ ++$currentRow;
+ $rowDataCount = count($rowData);
+ for ($i = 0; $i < $rowDataCount; ++$i) {
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
+ if ($rowData[$i] != '' && $this->_readFilter->readCell($columnLetter, $currentRow)) {
+ // Unescape enclosures
+ $rowData[$i] = str_replace("\\" . $this->_enclosure, $this->_enclosure, $rowData[$i]);
+ $rowData[$i] = str_replace($this->_enclosure . $this->_enclosure, $this->_enclosure, $rowData[$i]);
+
+ // Convert encoding if necessary
+ if ($this->_inputEncoding !== 'UTF-8') {
+ $rowData[$i] = PHPExcel_Shared_String::ConvertEncoding($rowData[$i], 'UTF-8', $this->_inputEncoding);
+ }
+
+ // Set cell value
+ $objPHPExcel->getActiveSheet()->setCellValue(
+ $columnLetter . $currentRow, $rowData[$i]
+ );
+ }
+ }
+ }
+
+ // Close file
+ fclose($fileHandle);
+
+ // Return
+ return $objPHPExcel;
+ }
+
+ /**
+ * Get delimiter
+ *
+ * @return string
+ */
+ public function getDelimiter() {
+ return $this->_delimiter;
+ }
+
+ /**
+ * Set delimiter
+ *
+ * @param string $pValue Delimiter, defaults to ,
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setDelimiter($pValue = ',') {
+ $this->_delimiter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get enclosure
+ *
+ * @return string
+ */
+ public function getEnclosure() {
+ return $this->_enclosure;
+ }
+
+ /**
+ * Set enclosure
+ *
+ * @param string $pValue Enclosure, defaults to "
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setEnclosure($pValue = '"') {
+ if ($pValue == '') {
+ $pValue = '"';
+ }
+ $this->_enclosure = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get line ending
+ *
+ * @return string
+ */
+ public function getLineEnding() {
+ return $this->_lineEnding;
+ }
+
+ /**
+ * Set line ending
+ *
+ * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setLineEnding($pValue = PHP_EOL) {
+ $this->_lineEnding = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get sheet index
+ *
+ * @return int
+ */
+ public function getSheetIndex() {
+ return $this->_sheetIndex;
+ }
+
+ /**
+ * Set sheet index
+ *
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Reader_CSV
+ */
+ public function setSheetIndex($pValue = 0) {
+ $this->_sheetIndex = $pValue;
+ return $this;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/DefaultReadFilter.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/DefaultReadFilter.php
new file mode 100644
index 0000000..63c5ae3
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/DefaultReadFilter.php
@@ -0,0 +1,61 @@
+_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadDataOnly($pValue = false) {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_Excel2003XML
+ */
+ public function __construct() {
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+
+// Office xmlns:o="urn:schemas-microsoft-com:office:office"
+// Excel xmlns:x="urn:schemas-microsoft-com:office:excel"
+// XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
+// Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
+// XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
+// XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
+// MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset"
+// Rowset xmlns:z="#RowsetSchema"
+//
+
+ $signature = array(
+ '',
+ ''
+ );
+
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Read sample data (first 2 KB will do)
+ $fh = fopen($pFilename, 'r');
+ $data = fread($fh, 2048);
+ fclose($fh);
+
+ $headers = explode("\n",$data);
+ $valid = true;
+ foreach($signature as $key => $match) {
+ if (isset($headers[$key])) {
+ $line = trim(rtrim($headers[$key], "\r\n"));
+ if ($line != $match) {
+ $valid = false;
+ break;
+ }
+ } else {
+ $valid = false;
+ break;
+ }
+ }
+
+ return $valid;
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ }
+
+ private static function identifyFixedStyleValue($styleList,&$styleAttributeValue) {
+ $styleAttributeValue = strtolower($styleAttributeValue);
+ foreach($styleList as $style) {
+ if ($styleAttributeValue == strtolower($style)) {
+ $styleAttributeValue = $style;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * pixel units to excel width units(units of 1/256th of a character width)
+ * @param pxs
+ * @return
+ */
+ private static function _pixel2WidthUnits($pxs) {
+ $UNIT_OFFSET_MAP = array(0, 36, 73, 109, 146, 182, 219);
+
+ $widthUnits = 256 * ($pxs / 7);
+ $widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
+ return $widthUnits;
+ }
+
+ /**
+ * excel width units(units of 1/256th of a character width) to pixel units
+ * @param widthUnits
+ * @return
+ */
+ private static function _widthUnits2Pixel($widthUnits) {
+ $pixels = ($widthUnits / 256) * 7;
+ $offsetWidthUnits = $widthUnits % 256;
+ $pixels += round($offsetWidthUnits / (256 / 7));
+ return $pixels;
+ }
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ $fromFormats = array('\-', '\ ');
+ $toFormats = array('-', ' ');
+
+ $underlineStyles = array (
+ PHPExcel_Style_Font::UNDERLINE_NONE,
+ PHPExcel_Style_Font::UNDERLINE_DOUBLE,
+ PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING,
+ PHPExcel_Style_Font::UNDERLINE_SINGLE,
+ PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING
+ );
+ $verticalAlignmentStyles = array (
+ PHPExcel_Style_Alignment::VERTICAL_BOTTOM,
+ PHPExcel_Style_Alignment::VERTICAL_TOP,
+ PHPExcel_Style_Alignment::VERTICAL_CENTER,
+ PHPExcel_Style_Alignment::VERTICAL_JUSTIFY
+ );
+ $horizontalAlignmentStyles = array (
+ PHPExcel_Style_Alignment::HORIZONTAL_GENERAL,
+ PHPExcel_Style_Alignment::HORIZONTAL_LEFT,
+ PHPExcel_Style_Alignment::HORIZONTAL_RIGHT,
+ PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
+ PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+ PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY
+ );
+
+
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ $xml = simplexml_load_file($pFilename);
+ $namespaces = $xml->getNamespaces(true);
+// echo '
';
+// print_r($namespaces);
+// echo '
';
+//
+// echo '
';
+// print_r($xml);
+// echo '
';
+//
+ $docProps = $objPHPExcel->getProperties();
+ foreach($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
+ switch ($propertyName) {
+ case 'Title' :
+ $docProps->setTitle($propertyValue);
+ break;
+ case 'Subject' :
+ $docProps->setSubject($propertyValue);
+ break;
+ case 'Author' :
+ $docProps->setCreator($propertyValue);
+ break;
+ case 'Created' :
+ $creationDate = strtotime($propertyValue);
+ $docProps->setCreated($creationDate);
+ break;
+ case 'LastAuthor' :
+ $docProps->setLastModifiedBy($propertyValue);
+ break;
+ case 'Company' :
+ $docProps->setCompany($propertyValue);
+ break;
+ case 'Category' :
+ $docProps->setCategory($propertyValue);
+ break;
+ case 'Keywords' :
+ $docProps->setKeywords($propertyValue);
+ break;
+ case 'Description' :
+ $docProps->setDescription($propertyValue);
+ break;
+ }
+ }
+
+
+ foreach($xml->Styles[0] as $style) {
+ $style_ss = $style->attributes($namespaces['ss']);
+ $styleID = (string) $style_ss['ID'];
+// echo 'Style ID = '.$styleID.' ';
+ if ($styleID == 'Default') {
+ $this->_styles['Default'] = array();
+ } else {
+ $this->_styles[$styleID] = $this->_styles['Default'];
+ }
+ foreach ($style as $styleType => $styleData) {
+ $styleAttributes = $styleData->attributes($namespaces['ss']);
+// echo $styleType.' ';
+ switch ($styleType) {
+ case 'Alignment' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.' ';
+ $styleAttributeValue = (string) $styleAttributeValue;
+ switch ($styleAttributeKey) {
+ case 'Vertical' :
+ if (self::identifyFixedStyleValue($verticalAlignmentStyles,$styleAttributeValue)) {
+ $this->_styles[$styleID]['alignment']['vertical'] = $styleAttributeValue;
+ }
+ break;
+ case 'Horizontal' :
+ if (self::identifyFixedStyleValue($horizontalAlignmentStyles,$styleAttributeValue)) {
+ $this->_styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue;
+ }
+ break;
+ case 'WrapText' :
+ $this->_styles[$styleID]['alignment']['wrap'] = true;
+ break;
+ }
+ }
+ break;
+ case 'Borders' :
+ foreach($styleData->Border as $borderStyle) {
+ $borderAttributes = $borderStyle->attributes($namespaces['ss']);
+ $thisBorder = array();
+ foreach($borderAttributes as $borderStyleKey => $borderStyleValue) {
+// echo $borderStyleKey.' = '.$borderStyleValue.' ';
+ switch ($borderStyleKey) {
+ case 'LineStyle' :
+ $thisBorder['style'] = PHPExcel_Style_Border::BORDER_MEDIUM;
+// $thisBorder['style'] = $borderStyleValue;
+ break;
+ case 'Weight' :
+// $thisBorder['style'] = $borderStyleValue;
+ break;
+ case 'Position' :
+ $borderPosition = strtolower($borderStyleValue);
+ break;
+ case 'Color' :
+ $borderColour = substr($borderStyleValue,1);
+ $thisBorder['color']['rgb'] = $borderColour;
+ break;
+ }
+ }
+ if (count($thisBorder) > 0) {
+ if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) {
+ $this->_styles[$styleID]['borders'][$borderPosition] = $thisBorder;
+ }
+ }
+ }
+ break;
+ case 'Font' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.' ';
+ $styleAttributeValue = (string) $styleAttributeValue;
+ switch ($styleAttributeKey) {
+ case 'FontName' :
+ $this->_styles[$styleID]['font']['name'] = $styleAttributeValue;
+ break;
+ case 'Size' :
+ $this->_styles[$styleID]['font']['size'] = $styleAttributeValue;
+ break;
+ case 'Color' :
+ $this->_styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue,1);
+ break;
+ case 'Bold' :
+ $this->_styles[$styleID]['font']['bold'] = true;
+ break;
+ case 'Italic' :
+ $this->_styles[$styleID]['font']['italic'] = true;
+ break;
+ case 'Underline' :
+ if (self::identifyFixedStyleValue($underlineStyles,$styleAttributeValue)) {
+ $this->_styles[$styleID]['font']['underline'] = $styleAttributeValue;
+ }
+ break;
+ }
+ }
+ break;
+ case 'Interior' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.' ';
+ switch ($styleAttributeKey) {
+ case 'Color' :
+ $this->_styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue,1);
+ break;
+ }
+ }
+ break;
+ case 'NumberFormat' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.' ';
+ $styleAttributeValue = str_replace($fromFormats,$toFormats,$styleAttributeValue);
+ switch ($styleAttributeValue) {
+ case 'Short Date' :
+ $styleAttributeValue = 'dd/mm/yyyy';
+ break;
+ }
+ if ($styleAttributeValue > '') {
+ $this->_styles[$styleID]['numberformat']['code'] = $styleAttributeValue;
+ }
+ }
+ break;
+ case 'Protection' :
+ foreach($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
+// echo $styleAttributeKey.' = '.$styleAttributeValue.' ';
+ }
+ break;
+ }
+ }
+// print_r($this->_styles[$styleID]);
+// echo '';
+ }
+// echo '';
+
+ $worksheetID = 0;
+ foreach($xml->Worksheet as $worksheet) {
+ $worksheet_ss = $worksheet->attributes($namespaces['ss']);
+ if ((isset($this->_loadSheetsOnly)) && (isset($worksheet_ss['Name'])) &&
+ (!in_array($worksheet_ss['Name'], $this->_loadSheetsOnly))) {
+ continue;
+ }
+
+ // Create new Worksheet
+ $objPHPExcel->createSheet();
+ $objPHPExcel->setActiveSheetIndex($worksheetID);
+ if (isset($worksheet_ss['Name'])) {
+ $worksheetName = $worksheet_ss['Name'];
+ $objPHPExcel->getActiveSheet()->setTitle($worksheetName);
+ }
+
+ $columnID = 'A';
+ foreach($worksheet->Table->Column as $columnData) {
+ $columnData_ss = $columnData->attributes($namespaces['ss']);
+ if (isset($columnData_ss['Index'])) {
+ $columnID = PHPExcel_Cell::stringFromColumnIndex($columnData_ss['Index']-1);
+ }
+ if (isset($columnData_ss['Width'])) {
+ $columnWidth = $columnData_ss['Width'];
+// echo 'Setting column width for '.$columnID.' to '.$columnWidth.' ';
+ $objPHPExcel->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
+ }
+ ++$columnID;
+ }
+
+ $rowID = 1;
+ foreach($worksheet->Table->Row as $rowData) {
+ $row_ss = $rowData->attributes($namespaces['ss']);
+ if (isset($row_ss['Index'])) {
+ $rowID = (integer) $row_ss['Index'];
+ }
+// echo 'Row '.$rowID.' ';
+ if (isset($row_ss['StyleID'])) {
+ $rowStyle = $row_ss['StyleID'];
+ }
+ if (isset($row_ss['Height'])) {
+ $rowHeight = $row_ss['Height'];
+// echo 'Setting row height to '.$rowHeight.' ';
+ $objPHPExcel->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight);
+ }
+ $columnID = 'A';
+ foreach($rowData->Cell as $cell) {
+
+ $cell_ss = $cell->attributes($namespaces['ss']);
+ if (isset($cell_ss['Index'])) {
+ $columnID = PHPExcel_Cell::stringFromColumnIndex($cell_ss['Index']-1);
+ }
+ $cellRange = $columnID.$rowID;
+
+ if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
+ $columnTo = $columnID;
+ if (isset($cell_ss['MergeAcross'])) {
+ $columnTo = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($columnID) + $cell_ss['MergeAcross'] -1);
+ }
+ $rowTo = $rowID;
+ if (isset($cell_ss['MergeDown'])) {
+ $rowTo = $rowTo + $cell_ss['MergeDown'];
+ }
+ $cellRange .= ':'.$columnTo.$rowTo;
+ $objPHPExcel->getActiveSheet()->mergeCells($cellRange);
+ }
+
+ $hasCalculatedValue = false;
+ $cellDataFormula = '';
+ if (isset($cell_ss['Formula'])) {
+ $cellDataFormula = $cell_ss['Formula'];
+ $hasCalculatedValue = true;
+ }
+ if (isset($cell->Data)) {
+ $cellValue = $cellData = $cell->Data;
+ $type = PHPExcel_Cell_DataType::TYPE_NULL;
+ $cellData_ss = $cellData->attributes($namespaces['ss']);
+ if (isset($cellData_ss['Type'])) {
+ $cellDataType = $cellData_ss['Type'];
+ switch ($cellDataType) {
+ /*
+ const TYPE_STRING = 's';
+ const TYPE_FORMULA = 'f';
+ const TYPE_NUMERIC = 'n';
+ const TYPE_BOOL = 'b';
+ const TYPE_NULL = 's';
+ const TYPE_INLINE = 'inlineStr';
+ const TYPE_ERROR = 'e';
+ */
+ case 'String' :
+ $type = PHPExcel_Cell_DataType::TYPE_STRING;
+ break;
+ case 'Number' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $cellValue = (float) $cellValue;
+ if (floor($cellValue) == $cellValue) {
+ $cellValue = (integer) $cellValue;
+ }
+ break;
+ case 'Boolean' :
+ $type = PHPExcel_Cell_DataType::TYPE_BOOL;
+ $cellValue = ($cellValue != 0);
+ break;
+ case 'DateTime' :
+ $type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $cellValue = PHPExcel_Shared_Date::PHPToExcel(strtotime($cellValue));
+ break;
+ case 'Error' :
+ $type = PHPExcel_Cell_DataType::TYPE_ERROR;
+ break;
+ }
+ }
+ if ($hasCalculatedValue) {
+ $type = PHPExcel_Cell_DataType::TYPE_FORMULA;
+ $columnNumber = PHPExcel_Cell::columnIndexFromString($columnID);
+ // Convert R1C1 style references to A1 style references (but only when not quoted)
+ $temp = explode('"',$cellDataFormula);
+ foreach($temp as $key => &$value) {
+ // Only replace in alternate array entries (i.e. non-quoted blocks)
+ if (($key % 2) == 0) {
+ preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/',$value, $cellReferences,PREG_SET_ORDER+PREG_OFFSET_CAPTURE);
+ // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
+ // through the formula from left to right. Reversing means that we work right to left.through
+ // the formula
+ $cellReferences = array_reverse($cellReferences);
+ // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
+ // then modify the formula to use that new reference
+ foreach($cellReferences as $cellReference) {
+ $rowReference = $cellReference[2][0];
+ // Empty R reference is the current row
+ if ($rowReference == '') $rowReference = $rowID;
+ // Bracketed R references are relative to the current row
+ if ($rowReference{0} == '[') $rowReference = $rowID + trim($rowReference,'[]');
+ $columnReference = $cellReference[4][0];
+ // Empty C reference is the current column
+ if ($columnReference == '') $columnReference = $columnNumber;
+ // Bracketed C references are relative to the current column
+ if ($columnReference{0} == '[') $columnReference = $columnNumber + trim($columnReference,'[]');
+ $A1CellReference = PHPExcel_Cell::stringFromColumnIndex($columnReference-1).$rowReference;
+ $value = substr_replace($value,$A1CellReference,$cellReference[0][1],strlen($cellReference[0][0]));
+ }
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $cellDataFormula = implode('"',$temp);
+ }
+
+// echo 'Cell '.$columnID.$rowID.' is a '.$type.' with a value of '.(($hasCalculatedValue) ? $cellDataFormula : $cellValue).' ';
+//
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue),$type);
+ if ($hasCalculatedValue) {
+// echo 'Forumla result is '.$cellValue.' ';
+ $objPHPExcel->getActiveSheet()->getCell($columnID.$rowID)->setCalculatedValue($cellValue);
+ }
+ }
+ if (isset($cell_ss['StyleID'])) {
+ $style = (string) $cell_ss['StyleID'];
+// echo 'Cell style for '.$columnID.$rowID.' is '.$style.' ';
+ if ((isset($this->_styles[$style])) && (count($this->_styles[$style]) > 0)) {
+// echo 'Cell '.$columnID.$rowID.' ';
+// print_r($this->_styles[$style]);
+// echo ' ';
+ if (!$objPHPExcel->getActiveSheet()->cellExists($columnID.$rowID)) {
+ $objPHPExcel->getActiveSheet()->setCellValue($columnID.$rowID,NULL);
+ }
+ $objPHPExcel->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->_styles[$style]);
+ }
+ }
+ ++$columnID;
+ }
+ ++$rowID;
+ }
+ ++$worksheetID;
+ }
+
+ // Return
+ return $objPHPExcel;
+ }
+
+ /**
+ * Get sheet index
+ *
+ * @return int
+ */
+ public function getSheetIndex() {
+ return $this->_sheetIndex;
+ }
+
+ /**
+ * Set sheet index
+ *
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Reader_Excel2003XML
+ */
+ public function setSheetIndex($pValue = 0) {
+ $this->_sheetIndex = $pValue;
+ return $this;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel2007.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel2007.php
new file mode 100644
index 0000000..6cbc893
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel2007.php
@@ -0,0 +1,1634 @@
+_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadDataOnly($pValue = false) {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_Excel2007 instance
+ */
+ public function __construct() {
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Load file
+ $zip = new ZipArchive;
+ if ($zip->open($pFilename) === true) {
+ // check if it is an OOXML archive
+ $rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels"));
+
+ $zip->close();
+
+ return ($rels !== false);
+ }
+
+ return false;
+ }
+
+ private function _castToBool($c) {
+// echo 'Initial Cast to Boolean ';
+ $value = isset($c->v) ? (string) $c->v : null;
+ if ($value == '0') {
+ $value = false;
+ } elseif ($value == '1') {
+ $value = true;
+ } else {
+ $value = (bool)$c->v;
+ }
+ return $value;
+ } // function _castToBool()
+
+ private function _castToError($c) {
+// echo 'Initial Cast to Error ';
+ return isset($c->v) ? (string) $c->v : null;;
+ } // function _castToError()
+
+ private function _castToString($c) {
+// echo 'Initial Cast to String ';
+ return isset($c->v) ? (string) $c->v : null;;
+ } // function _castToString()
+
+ private function _castToFormula($c,$r,&$cellDataType,&$value,&$calculatedValue,&$sharedFormulas,$castBaseType) {
+// echo 'Formula ';
+// echo '$c->f is '.$c->f.' ';
+ $cellDataType = 'f';
+ $value = "={$c->f}";
+ $calculatedValue = $this->$castBaseType($c);
+
+ // Shared formula?
+ if (isset($c->f['t']) && strtolower((string)$c->f['t']) == 'shared') {
+// echo 'SHARED FORMULA ';
+ $instance = (string)$c->f['si'];
+
+// echo 'Instance ID = '.$instance.' ';
+//
+// echo 'Shared Formula Array:
';
+// print_r($sharedFormulas);
+// echo '
';
+ if (!isset($sharedFormulas[(string)$c->f['si']])) {
+// echo 'SETTING NEW SHARED FORMULA ';
+// echo 'Master is '.$r.' ';
+// echo 'Formula is '.$value.' ';
+ $sharedFormulas[$instance] = array( 'master' => $r,
+ 'formula' => $value
+ );
+// echo 'New Shared Formula Array:
';
+// print_r($sharedFormulas);
+// echo '
';
+ } else {
+// echo 'GETTING SHARED FORMULA ';
+// echo 'Master is '.$sharedFormulas[$instance]['master'].' ';
+// echo 'Formula is '.$sharedFormulas[$instance]['formula'].' ';
+ $master = PHPExcel_Cell::coordinateFromString($sharedFormulas[$instance]['master']);
+ $current = PHPExcel_Cell::coordinateFromString($r);
+
+ $difference = array(0, 0);
+ $difference[0] = PHPExcel_Cell::columnIndexFromString($current[0]) - PHPExcel_Cell::columnIndexFromString($master[0]);
+ $difference[1] = $current[1] - $master[1];
+
+ $helper = PHPExcel_ReferenceHelper::getInstance();
+ $value = $helper->updateFormulaReferences( $sharedFormulas[$instance]['formula'],
+ 'A1',
+ $difference[0],
+ $difference[1]
+ );
+// echo 'Adjusted Formula is '.$value.' ';
+ }
+ }
+ }
+
+ public function _getFromZipArchive(ZipArchive $archive, $fileName = '')
+ {
+ // Root-relative paths
+ if (strpos($fileName, '//') !== false)
+ {
+ $fileName = substr($fileName, strpos($fileName, '//') + 1);
+ }
+ $fileName = PHPExcel_Shared_File::realpath($fileName);
+
+ // Apache POI fixes
+ $contents = $archive->getFromName($fileName);
+ if ($contents === false)
+ {
+ $contents = $archive->getFromName(substr($fileName, 1));
+ }
+
+ /*
+ if (strpos($contents, 'removeSheetByIndex(0);
+ if (!$this->_readDataOnly) {
+ $excel->removeCellStyleXfByIndex(0); // remove the default style
+ $excel->removeCellXfByIndex(0); // remove the default style
+ }
+ $zip = new ZipArchive;
+ $zip->open($pFilename);
+
+ $rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ foreach ($rels->Relationship as $rel) {
+ switch ($rel["Type"]) {
+ case "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties":
+ $xmlCore = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}"));
+ if ($xmlCore) {
+ $xmlCore->registerXPathNamespace("dc", "http://purl.org/dc/elements/1.1/");
+ $xmlCore->registerXPathNamespace("dcterms", "http://purl.org/dc/terms/");
+ $xmlCore->registerXPathNamespace("cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
+ $docProps = $excel->getProperties();
+ $docProps->setCreator((string) self::array_item($xmlCore->xpath("dc:creator")));
+ $docProps->setLastModifiedBy((string) self::array_item($xmlCore->xpath("cp:lastModifiedBy")));
+ $docProps->setCreated(strtotime(self::array_item($xmlCore->xpath("dcterms:created")))); //! respect xsi:type
+ $docProps->setModified(strtotime(self::array_item($xmlCore->xpath("dcterms:modified")))); //! respect xsi:type
+ $docProps->setTitle((string) self::array_item($xmlCore->xpath("dc:title")));
+ $docProps->setDescription((string) self::array_item($xmlCore->xpath("dc:description")));
+ $docProps->setSubject((string) self::array_item($xmlCore->xpath("dc:subject")));
+ $docProps->setKeywords((string) self::array_item($xmlCore->xpath("cp:keywords")));
+ $docProps->setCategory((string) self::array_item($xmlCore->xpath("cp:category")));
+ }
+ break;
+
+ case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument":
+ $dir = dirname($rel["Target"]);
+ $relsWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/_rels/" . basename($rel["Target"]) . ".rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $relsWorkbook->registerXPathNamespace("rel", "http://schemas.openxmlformats.org/package/2006/relationships");
+
+ $sharedStrings = array();
+ $xpath = self::array_item($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
+ $xmlStrings = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/$xpath[Target]")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ if (isset($xmlStrings) && isset($xmlStrings->si)) {
+ foreach ($xmlStrings->si as $val) {
+ if (isset($val->t)) {
+ $sharedStrings[] = PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $val->t );
+ } elseif (isset($val->r)) {
+ $sharedStrings[] = $this->_parseRichText($val);
+ }
+ }
+ }
+
+ $worksheets = array();
+ foreach ($relsWorkbook->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet") {
+ $worksheets[(string) $ele["Id"]] = $ele["Target"];
+ }
+ }
+
+ $styles = array();
+ $cellStyles = array();
+ $xpath = self::array_item($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
+ $xmlStyles = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/$xpath[Target]")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ $numFmts = null;
+ if ($xmlStyles && $xmlStyles->numFmts[0]) {
+ $numFmts = $xmlStyles->numFmts[0];
+ }
+ if (isset($numFmts) && !is_null($numFmts)) {
+ $numFmts->registerXPathNamespace("sml", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ }
+ if (!$this->_readDataOnly && $xmlStyles) {
+ foreach ($xmlStyles->cellXfs->xf as $xf) {
+ $numFmt = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
+
+ if ($xf["numFmtId"]) {
+ if (isset($numFmts)) {
+ $tmpNumFmt = self::array_item($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+
+ if (isset($tmpNumFmt["formatCode"])) {
+ $numFmt = (string) $tmpNumFmt["formatCode"];
+ }
+ }
+
+ if ((int)$xf["numFmtId"] < 164) {
+ $numFmt = PHPExcel_Style_NumberFormat::builtInFormatCode((int)$xf["numFmtId"]);
+ }
+ }
+ //$numFmt = str_replace('mm', 'i', $numFmt);
+ //$numFmt = str_replace('h', 'H', $numFmt);
+
+ $style = (object) array(
+ "numFmt" => $numFmt,
+ "font" => $xmlStyles->fonts->font[intval($xf["fontId"])],
+ "fill" => $xmlStyles->fills->fill[intval($xf["fillId"])],
+ "border" => $xmlStyles->borders->border[intval($xf["borderId"])],
+ "alignment" => $xf->alignment,
+ "protection" => $xf->protection,
+ );
+ $styles[] = $style;
+
+ // add style to cellXf collection
+ $objStyle = new PHPExcel_Style;
+ $this->_readStyle($objStyle, $style);
+ $excel->addCellXf($objStyle);
+ }
+
+ foreach ($xmlStyles->cellStyleXfs->xf as $xf) {
+ $numFmt = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
+ if ($numFmts && $xf["numFmtId"]) {
+ $tmpNumFmt = self::array_item($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+ if (isset($tmpNumFmt["formatCode"])) {
+ $numFmt = (string) $tmpNumFmt["formatCode"];
+ } else if ((int)$xf["numFmtId"] < 165) {
+ $numFmt = PHPExcel_Style_NumberFormat::builtInFormatCode((int)$xf["numFmtId"]);
+ }
+ }
+
+ $cellStyle = (object) array(
+ "numFmt" => $numFmt,
+ "font" => $xmlStyles->fonts->font[intval($xf["fontId"])],
+ "fill" => $xmlStyles->fills->fill[intval($xf["fillId"])],
+ "border" => $xmlStyles->borders->border[intval($xf["borderId"])],
+ "alignment" => $xf->alignment,
+ "protection" => $xf->protection,
+ );
+ $cellStyles[] = $cellStyle;
+
+ // add style to cellStyleXf collection
+ $objStyle = new PHPExcel_Style;
+ $this->_readStyle($objStyle, $cellStyle);
+ $excel->addCellStyleXf($objStyle);
+ }
+ }
+
+ $dxfs = array();
+ if (!$this->_readDataOnly && $xmlStyles) {
+ if ($xmlStyles->dxfs) {
+ foreach ($xmlStyles->dxfs->dxf as $dxf) {
+ $style = new PHPExcel_Style;
+ $this->_readStyle($style, $dxf);
+ $dxfs[] = $style;
+ }
+ }
+
+ if ($xmlStyles->cellStyles)
+ {
+ foreach ($xmlStyles->cellStyles->cellStyle as $cellStyle) {
+ if (intval($cellStyle['builtinId']) == 0) {
+ if (isset($cellStyles[intval($cellStyle['xfId'])])) {
+ // Set default style
+ $style = new PHPExcel_Style;
+ $this->_readStyle($style, $cellStyles[intval($cellStyle['xfId'])]);
+
+ // normal style, currently not using it for anything
+ }
+ }
+ }
+ }
+ }
+
+ $xmlWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+
+ // Set base date
+ if ($xmlWorkbook->workbookPr) {
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
+ if (isset($xmlWorkbook->workbookPr['date1904'])) {
+ $date1904 = (string)$xmlWorkbook->workbookPr['date1904'];
+ if ($date1904 == "true" || $date1904 == "1") {
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
+ }
+ }
+ }
+
+ $sheetId = 0; // keep track of new sheet id in final workbook
+ $oldSheetId = -1; // keep track of old sheet id in final workbook
+ $countSkippedSheets = 0; // keep track of number of skipped sheets
+ $mapSheetId = array(); // mapping of sheet ids from old to new
+
+ if ($xmlWorkbook->sheets)
+ {
+ foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+ ++$oldSheetId;
+
+ // Check if sheet should be skipped
+ if (isset($this->_loadSheetsOnly) && !in_array((string) $eleSheet["name"], $this->_loadSheetsOnly)) {
+ ++$countSkippedSheets;
+ $mapSheetId[$oldSheetId] = null;
+ continue;
+ }
+
+ // Map old sheet id in original workbook to new sheet id.
+ // They will differ if loadSheetsOnly() is being used
+ $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
+
+ // Load sheet
+ $docSheet = $excel->createSheet();
+ $docSheet->setTitle((string) $eleSheet["name"]);
+ $fileWorksheet = $worksheets[(string) self::array_item($eleSheet->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "id")];
+ $xmlSheet = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/$fileWorksheet")); //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+
+ $sharedFormulas = array();
+
+ if (isset($eleSheet["state"]) && (string) $eleSheet["state"] != '') {
+ $docSheet->setSheetState( (string) $eleSheet["state"] );
+ }
+
+ if (isset($xmlSheet->sheetViews) && isset($xmlSheet->sheetViews->sheetView)) {
+ if (isset($xmlSheet->sheetViews->sheetView['zoomScale'])) {
+ $docSheet->getSheetView()->setZoomScale( intval($xmlSheet->sheetViews->sheetView['zoomScale']) );
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['zoomScaleNormal'])) {
+ $docSheet->getSheetView()->setZoomScaleNormal( intval($xmlSheet->sheetViews->sheetView['zoomScaleNormal']) );
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['showGridLines'])) {
+ $docSheet->setShowGridLines((string)$xmlSheet->sheetViews->sheetView['showGridLines'] ? true : false);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView['rightToLeft'])) {
+ $docSheet->setRightToLeft((string)$xmlSheet->sheetViews->sheetView['rightToLeft'] ? true : false);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView->pane)) {
+ if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
+ $docSheet->freezePane( (string)$xmlSheet->sheetViews->sheetView->pane['topLeftCell'] );
+ } else {
+ $xSplit = 0;
+ $ySplit = 0;
+
+ if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
+ $xSplit = 1 + intval($xmlSheet->sheetViews->sheetView->pane['xSplit']);
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
+ $ySplit = 1 + intval($xmlSheet->sheetViews->sheetView->pane['ySplit']);
+ }
+
+ $docSheet->freezePaneByColumnAndRow($xSplit, $ySplit);
+ }
+ }
+
+ if (isset($xmlSheet->sheetViews->sheetView->selection)) {
+ if (isset($xmlSheet->sheetViews->sheetView->selection['sqref'])) {
+ $sqref = (string)$xmlSheet->sheetViews->sheetView->selection['sqref'];
+ $sqref = explode(' ', $sqref);
+ $sqref = $sqref[0];
+ $docSheet->setSelectedCells($sqref);
+ }
+ }
+
+ }
+
+ if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->tabColor)) {
+ if (isset($xmlSheet->sheetPr->tabColor['rgb'])) {
+ $docSheet->getTabColor()->setARGB( (string)$xmlSheet->sheetPr->tabColor['rgb'] );
+ }
+ }
+
+ if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->outlinePr)) {
+ if (isset($xmlSheet->sheetPr->outlinePr['summaryRight']) && $xmlSheet->sheetPr->outlinePr['summaryRight'] == false) {
+ $docSheet->setShowSummaryRight(false);
+ } else {
+ $docSheet->setShowSummaryRight(true);
+ }
+
+ if (isset($xmlSheet->sheetPr->outlinePr['summaryBelow']) && $xmlSheet->sheetPr->outlinePr['summaryBelow'] == false) {
+ $docSheet->setShowSummaryBelow(false);
+ } else {
+ $docSheet->setShowSummaryBelow(true);
+ }
+ }
+
+ if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->pageSetUpPr)) {
+ if (isset($xmlSheet->sheetPr->pageSetUpPr['fitToPage']) && $xmlSheet->sheetPr->pageSetUpPr['fitToPage'] == false) {
+ $docSheet->getPageSetup()->setFitToPage(false);
+ } else {
+ $docSheet->getPageSetup()->setFitToPage(true);
+ }
+ }
+
+ if (isset($xmlSheet->sheetFormatPr)) {
+ if (isset($xmlSheet->sheetFormatPr['customHeight']) && ((string)$xmlSheet->sheetFormatPr['customHeight'] == '1' || strtolower((string)$xmlSheet->sheetFormatPr['customHeight']) == 'true') && isset($xmlSheet->sheetFormatPr['defaultRowHeight'])) {
+ $docSheet->getDefaultRowDimension()->setRowHeight( (float)$xmlSheet->sheetFormatPr['defaultRowHeight'] );
+ }
+ if (isset($xmlSheet->sheetFormatPr['defaultColWidth'])) {
+ $docSheet->getDefaultColumnDimension()->setWidth( (float)$xmlSheet->sheetFormatPr['defaultColWidth'] );
+ }
+ }
+
+ if (isset($xmlSheet->cols) && !$this->_readDataOnly) {
+ foreach ($xmlSheet->cols->col as $col) {
+ for ($i = intval($col["min"]) - 1; $i < intval($col["max"]); ++$i) {
+ if ($col["style"]) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setXfIndex(intval($col["style"]));
+ }
+ if ($col["bestFit"]) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setAutoSize(true);
+ }
+ if ($col["hidden"]) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setVisible(false);
+ }
+ if ($col["collapsed"]) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setCollapsed(true);
+ }
+ if ($col["outlineLevel"] > 0) {
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setOutlineLevel(intval($col["outlineLevel"]));
+ }
+ $docSheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($i))->setWidth(floatval($col["width"]));
+
+ if (intval($col["max"]) == 16384) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (isset($xmlSheet->printOptions) && !$this->_readDataOnly) {
+ if ($xmlSheet->printOptions['gridLinesSet'] == 'true' && $xmlSheet->printOptions['gridLinesSet'] == '1') {
+ $docSheet->setShowGridlines(true);
+ }
+
+ if ($xmlSheet->printOptions['gridLines'] == 'true' || $xmlSheet->printOptions['gridLines'] == '1') {
+ $docSheet->setPrintGridlines(true);
+ }
+
+ if ($xmlSheet->printOptions['horizontalCentered']) {
+ $docSheet->getPageSetup()->setHorizontalCentered(true);
+ }
+ if ($xmlSheet->printOptions['verticalCentered']) {
+ $docSheet->getPageSetup()->setVerticalCentered(true);
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
+ foreach ($xmlSheet->sheetData->row as $row) {
+ if ($row["ht"] && !$this->_readDataOnly) {
+ $docSheet->getRowDimension(intval($row["r"]))->setRowHeight(floatval($row["ht"]));
+ }
+ if ($row["hidden"] && !$this->_readDataOnly) {
+ $docSheet->getRowDimension(intval($row["r"]))->setVisible(false);
+ }
+ if ($row["collapsed"]) {
+ $docSheet->getRowDimension(intval($row["r"]))->setCollapsed(true);
+ }
+ if ($row["outlineLevel"] > 0) {
+ $docSheet->getRowDimension(intval($row["r"]))->setOutlineLevel(intval($row["outlineLevel"]));
+ }
+ if ($row["s"]) {
+ $docSheet->getRowDimension(intval($row["r"]))->setXfIndex(intval($row["s"]));
+ }
+
+ foreach ($row->c as $c) {
+ $r = (string) $c["r"];
+ $cellDataType = (string) $c["t"];
+ $value = null;
+ $calculatedValue = null;
+
+ // Read cell?
+ if (!is_null($this->getReadFilter())) {
+ $coordinates = PHPExcel_Cell::coordinateFromString($r);
+
+ if (!$this->getReadFilter()->readCell($coordinates[0], $coordinates[1], $docSheet->getTitle())) {
+ continue;
+ }
+ }
+
+ // echo 'Reading cell '.$coordinates[0].$coordinates[1].' ';
+ // print_r($c);
+ // echo ' ';
+ // echo 'Cell Data Type is '.$cellDataType.': ';
+ //
+ // Read cell!
+ switch ($cellDataType) {
+ case "s":
+ // echo 'String ';
+ if ((string)$c->v != '') {
+ $value = $sharedStrings[intval($c->v)];
+
+ if ($value instanceof PHPExcel_RichText) {
+ $value = clone $value;
+ }
+ } else {
+ $value = '';
+ }
+
+ break;
+ case "b":
+ // echo 'Boolean ';
+ if (!isset($c->f)) {
+ $value = $this->_castToBool($c);
+ } else {
+ // Formula
+ $this->_castToFormula($c,$r,$cellDataType,$value,$calculatedValue,$sharedFormulas,'_castToBool');
+ // echo '$calculatedValue = '.$calculatedValue.' ';
+ }
+ break;
+ case "inlineStr":
+ // echo 'Inline String ';
+ $value = $this->_parseRichText($c->is);
+
+ break;
+ case "e":
+ // echo 'Error ';
+ if (!isset($c->f)) {
+ $value = $this->_castToError($c);
+ } else {
+ // Formula
+ $this->_castToFormula($c,$r,$cellDataType,$value,$calculatedValue,$sharedFormulas,'_castToError');
+ // echo '$calculatedValue = '.$calculatedValue.' ';
+ }
+
+ break;
+
+ default:
+ // echo 'Default ';
+ if (!isset($c->f)) {
+ // echo 'Not a Formula ';
+ $value = $this->_castToString($c);
+ } else {
+ // echo 'Treat as Formula ';
+ // Formula
+ $this->_castToFormula($c,$r,$cellDataType,$value,$calculatedValue,$sharedFormulas,'_castToString');
+ // echo '$calculatedValue = '.$calculatedValue.' ';
+ }
+
+ break;
+ }
+ // echo 'Value is '.$value.' ';
+
+ // Check for numeric values
+ if (is_numeric($value) && $cellDataType != 's') {
+ if ($value == (int)$value) $value = (int)$value;
+ elseif ($value == (float)$value) $value = (float)$value;
+ elseif ($value == (double)$value) $value = (double)$value;
+ }
+
+ // Rich text?
+ if ($value instanceof PHPExcel_RichText && $this->_readDataOnly) {
+ $value = $value->getPlainText();
+ }
+
+ // Assign value
+ if ($cellDataType != '') {
+ $docSheet->setCellValueExplicit($r, $value, $cellDataType);
+ } else {
+ $docSheet->setCellValue($r, $value);
+ }
+ if (!is_null($calculatedValue)) {
+ $docSheet->getCell($r)->setCalculatedValue($calculatedValue);
+ }
+
+ // Style information?
+ if ($c["s"] && !$this->_readDataOnly) {
+ // no style index means 0, it seems
+ $docSheet->getCell($r)->setXfIndex(isset($styles[intval($c["s"])]) ?
+ intval($c["s"]) : 0);
+ }
+
+ // Set rich text parent
+ if ($value instanceof PHPExcel_RichText && !$this->_readDataOnly) {
+ $value->setParent($docSheet->getCell($r));
+ }
+ }
+ }
+ }
+
+ $conditionals = array();
+ if (!$this->_readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
+ foreach ($xmlSheet->conditionalFormatting as $conditional) {
+ foreach ($conditional->cfRule as $cfRule) {
+ if (
+ (
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_NONE ||
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_CELLIS ||
+ (string)$cfRule["type"] == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ ) && isset($dxfs[intval($cfRule["dxfId"])])
+ ) {
+ $conditionals[(string) $conditional["sqref"]][intval($cfRule["priority"])] = $cfRule;
+ }
+ }
+ }
+
+ foreach ($conditionals as $ref => $cfRules) {
+ ksort($cfRules);
+ $conditionalStyles = array();
+ foreach ($cfRules as $cfRule) {
+ $objConditional = new PHPExcel_Style_Conditional();
+ $objConditional->setConditionType((string)$cfRule["type"]);
+ $objConditional->setOperatorType((string)$cfRule["operator"]);
+
+ if ((string)$cfRule["text"] != '') {
+ $objConditional->setText((string)$cfRule["text"]);
+ }
+
+ if (count($cfRule->formula) > 1) {
+ foreach ($cfRule->formula as $formula) {
+ $objConditional->addCondition((string)$formula);
+ }
+ } else {
+ $objConditional->addCondition((string)$cfRule->formula);
+ }
+ $objConditional->setStyle(clone $dxfs[intval($cfRule["dxfId"])]);
+ $conditionalStyles[] = $objConditional;
+ }
+
+ // Extract all cell references in $ref
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($ref);
+ foreach ($aReferences as $reference) {
+ $docSheet->getStyle($reference)->setConditionalStyles($conditionalStyles);
+ }
+ }
+ }
+
+ $aKeys = array("sheet", "objects", "scenarios", "formatCells", "formatColumns", "formatRows", "insertColumns", "insertRows", "insertHyperlinks", "deleteColumns", "deleteRows", "selectLockedCells", "sort", "autoFilter", "pivotTables", "selectUnlockedCells");
+ if (!$this->_readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
+ foreach ($aKeys as $key) {
+ $method = "set" . ucfirst($key);
+ $docSheet->getProtection()->$method($xmlSheet->sheetProtection[$key] == "true");
+ }
+ }
+
+ if (!$this->_readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
+ $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection["password"], true);
+ if ($xmlSheet->protectedRanges->protectedRange) {
+ foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
+ $docSheet->protectCells((string) $protectedRange["sqref"], (string) $protectedRange["password"], true);
+ }
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->autoFilter && !$this->_readDataOnly) {
+ $docSheet->setAutoFilter((string) $xmlSheet->autoFilter["ref"]);
+ }
+
+ if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->_readDataOnly) {
+ foreach ($xmlSheet->mergeCells->mergeCell as $mergeCell) {
+ $docSheet->mergeCells((string) $mergeCell["ref"]);
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->pageMargins && !$this->_readDataOnly) {
+ $docPageMargins = $docSheet->getPageMargins();
+ $docPageMargins->setLeft(floatval($xmlSheet->pageMargins["left"]));
+ $docPageMargins->setRight(floatval($xmlSheet->pageMargins["right"]));
+ $docPageMargins->setTop(floatval($xmlSheet->pageMargins["top"]));
+ $docPageMargins->setBottom(floatval($xmlSheet->pageMargins["bottom"]));
+ $docPageMargins->setHeader(floatval($xmlSheet->pageMargins["header"]));
+ $docPageMargins->setFooter(floatval($xmlSheet->pageMargins["footer"]));
+ }
+
+ if ($xmlSheet && $xmlSheet->pageSetup && !$this->_readDataOnly) {
+ $docPageSetup = $docSheet->getPageSetup();
+
+ if (isset($xmlSheet->pageSetup["orientation"])) {
+ $docPageSetup->setOrientation((string) $xmlSheet->pageSetup["orientation"]);
+ }
+ if (isset($xmlSheet->pageSetup["paperSize"])) {
+ $docPageSetup->setPaperSize(intval($xmlSheet->pageSetup["paperSize"]));
+ }
+ if (isset($xmlSheet->pageSetup["scale"])) {
+ $docPageSetup->setScale(intval($xmlSheet->pageSetup["scale"]), false);
+ }
+ if (isset($xmlSheet->pageSetup["fitToHeight"]) && intval($xmlSheet->pageSetup["fitToHeight"]) >= 0) {
+ $docPageSetup->setFitToHeight(intval($xmlSheet->pageSetup["fitToHeight"]), false);
+ }
+ if (isset($xmlSheet->pageSetup["fitToWidth"]) && intval($xmlSheet->pageSetup["fitToWidth"]) >= 0) {
+ $docPageSetup->setFitToWidth(intval($xmlSheet->pageSetup["fitToWidth"]), false);
+ }
+ if (isset($xmlSheet->pageSetup["firstPageNumber"]) && isset($xmlSheet->pageSetup["useFirstPageNumber"]) &&
+ ((string)$xmlSheet->pageSetup["useFirstPageNumber"] == 'true' || (string)$xmlSheet->pageSetup["useFirstPageNumber"] == '1')) {
+ $docPageSetup->setFirstPageNumber(intval($xmlSheet->pageSetup["firstPageNumber"]));
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->headerFooter && !$this->_readDataOnly) {
+ $docHeaderFooter = $docSheet->getHeaderFooter();
+
+ if (isset($xmlSheet->headerFooter["differentOddEven"]) &&
+ ((string)$xmlSheet->headerFooter["differentOddEven"] == 'true' || (string)$xmlSheet->headerFooter["differentOddEven"] == '1')) {
+ $docHeaderFooter->setDifferentOddEven(true);
+ } else {
+ $docHeaderFooter->setDifferentOddEven(false);
+ }
+ if (isset($xmlSheet->headerFooter["differentFirst"]) &&
+ ((string)$xmlSheet->headerFooter["differentFirst"] == 'true' || (string)$xmlSheet->headerFooter["differentFirst"] == '1')) {
+ $docHeaderFooter->setDifferentFirst(true);
+ } else {
+ $docHeaderFooter->setDifferentFirst(false);
+ }
+ if (isset($xmlSheet->headerFooter["scaleWithDoc"]) &&
+ ((string)$xmlSheet->headerFooter["scaleWithDoc"] == 'false' || (string)$xmlSheet->headerFooter["scaleWithDoc"] == '0')) {
+ $docHeaderFooter->setScaleWithDocument(false);
+ } else {
+ $docHeaderFooter->setScaleWithDocument(true);
+ }
+ if (isset($xmlSheet->headerFooter["alignWithMargins"]) &&
+ ((string)$xmlSheet->headerFooter["alignWithMargins"] == 'false' || (string)$xmlSheet->headerFooter["alignWithMargins"] == '0')) {
+ $docHeaderFooter->setAlignWithMargins(false);
+ } else {
+ $docHeaderFooter->setAlignWithMargins(true);
+ }
+
+ $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
+ $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
+ $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
+ $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
+ $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
+ $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
+ }
+
+ if ($xmlSheet && $xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk && !$this->_readDataOnly) {
+ foreach ($xmlSheet->rowBreaks->brk as $brk) {
+ if ($brk["man"]) {
+ $docSheet->setBreak("A$brk[id]", PHPExcel_Worksheet::BREAK_ROW);
+ }
+ }
+ }
+ if ($xmlSheet && $xmlSheet->colBreaks && $xmlSheet->colBreaks->brk && !$this->_readDataOnly) {
+ foreach ($xmlSheet->colBreaks->brk as $brk) {
+ if ($brk["man"]) {
+ $docSheet->setBreak(PHPExcel_Cell::stringFromColumnIndex($brk["id"]) . "1", PHPExcel_Worksheet::BREAK_COLUMN);
+ }
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->dataValidations && !$this->_readDataOnly) {
+ foreach ($xmlSheet->dataValidations->dataValidation as $dataValidation) {
+ // Uppercase coordinate
+ $range = strtoupper($dataValidation["sqref"]);
+
+ // Extract all cell references in $range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($range);
+ foreach ($aReferences as $reference) {
+ // Create validation
+ $docValidation = $docSheet->getCell($reference)->getDataValidation();
+ $docValidation->setType((string) $dataValidation["type"]);
+ $docValidation->setErrorStyle((string) $dataValidation["errorStyle"]);
+ $docValidation->setOperator((string) $dataValidation["operator"]);
+ $docValidation->setAllowBlank($dataValidation["allowBlank"] != 0);
+ $docValidation->setShowDropDown($dataValidation["showDropDown"] == 0);
+ $docValidation->setShowInputMessage($dataValidation["showInputMessage"] != 0);
+ $docValidation->setShowErrorMessage($dataValidation["showErrorMessage"] != 0);
+ $docValidation->setErrorTitle((string) $dataValidation["errorTitle"]);
+ $docValidation->setError((string) $dataValidation["error"]);
+ $docValidation->setPromptTitle((string) $dataValidation["promptTitle"]);
+ $docValidation->setPrompt((string) $dataValidation["prompt"]);
+ $docValidation->setFormula1((string) $dataValidation->formula1);
+ $docValidation->setFormula2((string) $dataValidation->formula2);
+ }
+ }
+ }
+
+ // Add hyperlinks
+ $hyperlinks = array();
+ if (!$this->_readDataOnly) {
+ // Locate hyperlink relations
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") {
+ $hyperlinks[(string)$ele["Id"]] = (string)$ele["Target"];
+ }
+ }
+ }
+
+ // Loop through hyperlinks
+ if ($xmlSheet && $xmlSheet->hyperlinks) {
+ foreach ($xmlSheet->hyperlinks->hyperlink as $hyperlink) {
+ // Link url
+ $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+ foreach (PHPExcel_Cell::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
+ if (isset($linkRel['id'])) {
+ $docSheet->getCell( $cellReference )->getHyperlink()->setUrl( $hyperlinks[ (string)$linkRel['id'] ] );
+ }
+ if (isset($hyperlink['location'])) {
+ $docSheet->getCell( $cellReference )->getHyperlink()->setUrl( 'sheet://' . (string)$hyperlink['location'] );
+ }
+
+ // Tooltip
+ if (isset($hyperlink['tooltip'])) {
+ $docSheet->getCell( $cellReference )->getHyperlink()->setTooltip( (string)$hyperlink['tooltip'] );
+ }
+ }
+ }
+ }
+ }
+
+ // Add comments
+ $comments = array();
+ $vmlComments = array();
+ if (!$this->_readDataOnly) {
+ // Locate comment relations
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments") {
+ $comments[(string)$ele["Id"]] = (string)$ele["Target"];
+ }
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing") {
+ $vmlComments[(string)$ele["Id"]] = (string)$ele["Target"];
+ }
+ }
+ }
+
+ // Loop through comments
+ foreach ($comments as $relName => $relPath) {
+ // Load comments file
+ $relPath = PHPExcel_Shared_File::realpath(dirname("$dir/$fileWorksheet") . "/" . $relPath);
+ $commentsFile = simplexml_load_string($this->_getFromZipArchive($zip, $relPath) );
+
+ // Utility variables
+ $authors = array();
+
+ // Loop through authors
+ foreach ($commentsFile->authors->author as $author) {
+ $authors[] = (string)$author;
+ }
+
+ // Loop through contents
+ foreach ($commentsFile->commentList->comment as $comment) {
+ $docSheet->getComment( (string)$comment['ref'] )->setAuthor( $authors[(string)$comment['authorId']] );
+ $docSheet->getComment( (string)$comment['ref'] )->setText( $this->_parseRichText($comment->text) );
+ }
+ }
+
+ // Loop through VML comments
+ foreach ($vmlComments as $relName => $relPath) {
+ // Load VML comments file
+ $relPath = PHPExcel_Shared_File::realpath(dirname("$dir/$fileWorksheet") . "/" . $relPath);
+ $vmlCommentsFile = simplexml_load_string( $this->_getFromZipArchive($zip, $relPath) );
+ $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+ $shapes = $vmlCommentsFile->xpath('//v:shape');
+ foreach ($shapes as $shape) {
+ $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+ if (isset($shape['style'])) {
+ $style = (string)$shape['style'];
+ $fillColor = strtoupper( substr( (string)$shape['fillcolor'], 1 ) );
+ $column = null;
+ $row = null;
+
+ $clientData = $shape->xpath('.//x:ClientData');
+ if (is_array($clientData)) {
+ $clientData = $clientData[0];
+
+ if ( isset($clientData['ObjectType']) && (string)$clientData['ObjectType'] == 'Note' ) {
+ $temp = $clientData->xpath('.//x:Row');
+ if (is_array($temp)) $row = $temp[0];
+
+ $temp = $clientData->xpath('.//x:Column');
+ if (is_array($temp)) $column = $temp[0];
+ }
+ }
+
+ if (!is_null($column) && !is_null($row)) {
+ // Set comment properties
+ $comment = $docSheet->getCommentByColumnAndRow($column, $row + 1);
+ $comment->getFillColor()->setRGB( $fillColor );
+
+ // Parse style
+ $styleArray = explode(';', str_replace(' ', '', $style));
+ foreach ($styleArray as $stylePair) {
+ $stylePair = explode(':', $stylePair);
+
+ if ($stylePair[0] == 'margin-left') $comment->setMarginLeft($stylePair[1]);
+ if ($stylePair[0] == 'margin-top') $comment->setMarginTop($stylePair[1]);
+ if ($stylePair[0] == 'width') $comment->setWidth($stylePair[1]);
+ if ($stylePair[0] == 'height') $comment->setHeight($stylePair[1]);
+ if ($stylePair[0] == 'visibility') $comment->setVisible( $stylePair[1] == 'visible' );
+
+ }
+ }
+ }
+ }
+ }
+
+ // Header/footer images
+ if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->_readDataOnly) {
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $vmlRelationship = '';
+
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing") {
+ $vmlRelationship = self::dir_add("$dir/$fileWorksheet", $ele["Target"]);
+ }
+ }
+
+ if ($vmlRelationship != '') {
+ // Fetch linked images
+ $relsVML = simplexml_load_string($this->_getFromZipArchive($zip, dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels' )); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $drawings = array();
+ foreach ($relsVML->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") {
+ $drawings[(string) $ele["Id"]] = self::dir_add($vmlRelationship, $ele["Target"]);
+ }
+ }
+
+ // Fetch VML document
+ $vmlDrawing = simplexml_load_string($this->_getFromZipArchive($zip, $vmlRelationship));
+ $vmlDrawing->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+ $hfImages = array();
+
+ $shapes = $vmlDrawing->xpath('//v:shape');
+ foreach ($shapes as $shape) {
+ $shape->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+ $imageData = $shape->xpath('//v:imagedata');
+ $imageData = $imageData[0];
+
+ $imageData = $imageData->attributes('urn:schemas-microsoft-com:office:office');
+ $style = self::toCSSArray( (string)$shape['style'] );
+
+ $hfImages[ (string)$shape['id'] ] = new PHPExcel_Worksheet_HeaderFooterDrawing();
+ if (isset($imageData['title'])) {
+ $hfImages[ (string)$shape['id'] ]->setName( (string)$imageData['title'] );
+ }
+
+ $hfImages[ (string)$shape['id'] ]->setPath("zip://$pFilename#" . $drawings[(string)$imageData['relid']], false);
+ $hfImages[ (string)$shape['id'] ]->setResizeProportional(false);
+ $hfImages[ (string)$shape['id'] ]->setWidth($style['width']);
+ $hfImages[ (string)$shape['id'] ]->setHeight($style['height']);
+ $hfImages[ (string)$shape['id'] ]->setOffsetX($style['margin-left']);
+ $hfImages[ (string)$shape['id'] ]->setOffsetY($style['margin-top']);
+ $hfImages[ (string)$shape['id'] ]->setResizeProportional(true);
+ }
+
+ $docSheet->getHeaderFooter()->setImages($hfImages);
+ }
+ }
+ }
+
+ }
+
+ // TODO: Make sure drawings and graph are loaded differently!
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels")) {
+ $relsWorksheet = simplexml_load_string($this->_getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . "/_rels/" . basename($fileWorksheet) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $drawings = array();
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing") {
+ $drawings[(string) $ele["Id"]] = self::dir_add("$dir/$fileWorksheet", $ele["Target"]);
+ }
+ }
+ if ($xmlSheet->drawing && !$this->_readDataOnly) {
+ foreach ($xmlSheet->drawing as $drawing) {
+ $fileDrawing = $drawings[(string) self::array_item($drawing->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "id")];
+ $relsDrawing = simplexml_load_string($this->_getFromZipArchive($zip, dirname($fileDrawing) . "/_rels/" . basename($fileDrawing) . ".rels") ); //~ http://schemas.openxmlformats.org/package/2006/relationships");
+ $images = array();
+
+ if ($relsDrawing && $relsDrawing->Relationship) {
+ foreach ($relsDrawing->Relationship as $ele) {
+ if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") {
+ $images[(string) $ele["Id"]] = self::dir_add($fileDrawing, $ele["Target"]);
+ }
+ }
+ }
+ $xmlDrawing = simplexml_load_string($this->_getFromZipArchive($zip, $fileDrawing))->children("http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing");
+
+ if ($xmlDrawing->oneCellAnchor) {
+ foreach ($xmlDrawing->oneCellAnchor as $oneCellAnchor) {
+ if ($oneCellAnchor->pic->blipFill) {
+ $blip = $oneCellAnchor->pic->blipFill->children("http://schemas.openxmlformats.org/drawingml/2006/main")->blip;
+ $xfrm = $oneCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->xfrm;
+ $outerShdw = $oneCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->effectLst->outerShdw;
+ $objDrawing = new PHPExcel_Worksheet_Drawing;
+ $objDrawing->setName((string) self::array_item($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), "name"));
+ $objDrawing->setDescription((string) self::array_item($oneCellAnchor->pic->nvPicPr->cNvPr->attributes(), "descr"));
+ $objDrawing->setPath("zip://$pFilename#" . $images[(string) self::array_item($blip->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "embed")], false);
+ $objDrawing->setCoordinates(PHPExcel_Cell::stringFromColumnIndex($oneCellAnchor->from->col) . ($oneCellAnchor->from->row + 1));
+ $objDrawing->setOffsetX(PHPExcel_Shared_Drawing::EMUToPixels($oneCellAnchor->from->colOff));
+ $objDrawing->setOffsetY(PHPExcel_Shared_Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
+ $objDrawing->setResizeProportional(false);
+ $objDrawing->setWidth(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($oneCellAnchor->ext->attributes(), "cx")));
+ $objDrawing->setHeight(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($oneCellAnchor->ext->attributes(), "cy")));
+ if ($xfrm) {
+ $objDrawing->setRotation(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($xfrm->attributes(), "rot")));
+ }
+ if ($outerShdw) {
+ $shadow = $objDrawing->getShadow();
+ $shadow->setVisible(true);
+ $shadow->setBlurRadius(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "blurRad")));
+ $shadow->setDistance(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "dist")));
+ $shadow->setDirection(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($outerShdw->attributes(), "dir")));
+ $shadow->setAlignment((string) self::array_item($outerShdw->attributes(), "algn"));
+ $shadow->getColor()->setRGB(self::array_item($outerShdw->srgbClr->attributes(), "val"));
+ $shadow->setAlpha(self::array_item($outerShdw->srgbClr->alpha->attributes(), "val") / 1000);
+ }
+ $objDrawing->setWorksheet($docSheet);
+ }
+ }
+ }
+ if ($xmlDrawing->twoCellAnchor) {
+ foreach ($xmlDrawing->twoCellAnchor as $twoCellAnchor) {
+ if ($twoCellAnchor->pic->blipFill) {
+ $blip = $twoCellAnchor->pic->blipFill->children("http://schemas.openxmlformats.org/drawingml/2006/main")->blip;
+ $xfrm = $twoCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->xfrm;
+ $outerShdw = $twoCellAnchor->pic->spPr->children("http://schemas.openxmlformats.org/drawingml/2006/main")->effectLst->outerShdw;
+ $objDrawing = new PHPExcel_Worksheet_Drawing;
+ $objDrawing->setName((string) self::array_item($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), "name"));
+ $objDrawing->setDescription((string) self::array_item($twoCellAnchor->pic->nvPicPr->cNvPr->attributes(), "descr"));
+ $objDrawing->setPath("zip://$pFilename#" . $images[(string) self::array_item($blip->attributes("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), "embed")], false);
+ $objDrawing->setCoordinates(PHPExcel_Cell::stringFromColumnIndex($twoCellAnchor->from->col) . ($twoCellAnchor->from->row + 1));
+ $objDrawing->setOffsetX(PHPExcel_Shared_Drawing::EMUToPixels($twoCellAnchor->from->colOff));
+ $objDrawing->setOffsetY(PHPExcel_Shared_Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
+ $objDrawing->setResizeProportional(false);
+
+ $objDrawing->setWidth(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($xfrm->ext->attributes(), "cx")));
+ $objDrawing->setHeight(PHPExcel_Shared_Drawing::EMUToPixels(self::array_item($xfrm->ext->attributes(), "cy")));
+
+ if ($xfrm) {
+ $objDrawing->setRotation(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($xfrm->attributes(), "rot")));
+ }
+ if ($outerShdw) {
+ $shadow = $objDrawing->getShadow();
+ $shadow->setVisible(true);
+ $shadow->setBlurRadius(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "blurRad")));
+ $shadow->setDistance(PHPExcel_Shared_Drawing::EMUTopixels(self::array_item($outerShdw->attributes(), "dist")));
+ $shadow->setDirection(PHPExcel_Shared_Drawing::angleToDegrees(self::array_item($outerShdw->attributes(), "dir")));
+ $shadow->setAlignment((string) self::array_item($outerShdw->attributes(), "algn"));
+ $shadow->getColor()->setRGB(self::array_item($outerShdw->srgbClr->attributes(), "val"));
+ $shadow->setAlpha(self::array_item($outerShdw->srgbClr->alpha->attributes(), "val") / 1000);
+ }
+ $objDrawing->setWorksheet($docSheet);
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ // Loop through definedNames
+ if ($xmlWorkbook->definedNames) {
+ foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+ // Extract range
+ $extractedRange = (string)$definedName;
+ $extractedRange = preg_replace('/\'(\w+)\'\!/', '', $extractedRange);
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ // Valid range?
+ if (stripos((string)$definedName, '#REF!') !== false || $extractedRange == '') {
+ continue;
+ }
+
+ // Some definedNames are only applicable if we are on the same sheet...
+ if ((string)$definedName['localSheetId'] != '' && (string)$definedName['localSheetId'] == $sheetId) {
+ // Switch on type
+ switch ((string)$definedName['name']) {
+
+ case '_xlnm._FilterDatabase':
+ $docSheet->setAutoFilter($extractedRange);
+ break;
+
+ case '_xlnm.Print_Titles':
+ // Split $extractedRange
+ $extractedRange = explode(',', $extractedRange);
+
+ // Set print titles
+ if (isset($extractedRange[0])) {
+ $range = explode(':', $extractedRange[0]);
+
+ if (PHPExcel_Worksheet::extractSheetTitle($range[0]) != '')
+ $range[0] = PHPExcel_Worksheet::extractSheetTitle($range[0]);
+ $range[0] = str_replace('$', '', $range[0]);
+ if (PHPExcel_Worksheet::extractSheetTitle($range[1]) != '')
+ $range[1] = PHPExcel_Worksheet::extractSheetTitle($range[1]);
+ $range[1] = str_replace('$', '', $range[1]);
+
+ $docSheet->getPageSetup()->setColumnsToRepeatAtLeft( $range );
+ }
+ if (isset($extractedRange[1])) {
+ $range = explode(':', $extractedRange[1]);
+
+ if (PHPExcel_Worksheet::extractSheetTitle($range[0]) != '')
+ $range[0] = PHPExcel_Worksheet::extractSheetTitle($range[0]);
+ $range[0] = str_replace('$', '', $range[0]);
+ if (PHPExcel_Worksheet::extractSheetTitle($range[1]) != '')
+ $range[1] = PHPExcel_Worksheet::extractSheetTitle($range[1]);
+ $range[1] = str_replace('$', '', $range[1]);
+
+ $docSheet->getPageSetup()->setRowsToRepeatAtTop( $range );
+ }
+
+ break;
+
+ case '_xlnm.Print_Area':
+ $range = explode('!', $extractedRange);
+ $extractedRange = isset($range[1]) ? $range[1] : $range[0];
+
+ $docSheet->getPageSetup()->setPrintArea($extractedRange);
+ break;
+
+ default:
+ $range = explode('!', $extractedRange);
+ $extractedRange = isset($range[1]) ? $range[1] : $range[0];
+
+ $excel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, true) );
+ break;
+ }
+ } else {
+ // "Global" definedNames
+ $locatedSheet = null;
+ $extractedSheetName = '';
+ if (strpos( (string)$definedName, '!' ) !== false) {
+ // Extract sheet name
+ $extractedSheetName = PHPExcel_Worksheet::extractSheetTitle( (string)$definedName, true );
+ $extractedSheetName = $extractedSheetName[0];
+
+ // Locate sheet
+ $locatedSheet = $excel->getSheetByName($extractedSheetName);
+
+ // Modify range
+ $range = explode('!', $extractedRange);
+ $extractedRange = isset($range[1]) ? $range[1] : $range[0];
+ }
+
+ if (!is_null($locatedSheet)) {
+ $excel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $locatedSheet, $extractedRange, false) );
+ }
+ }
+ }
+ }
+
+ // Next sheet id
+ ++$sheetId;
+ }
+ }
+
+ if (!$this->_readDataOnly) {
+ // active sheet index
+ $activeTab = intval($xmlWorkbook->bookViews->workbookView["activeTab"]); // refers to old sheet index
+
+ // keep active sheet index if sheet is still loaded, else first sheet is set as the active
+ if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
+ $excel->setActiveSheetIndex($mapSheetId[$activeTab]);
+ } else {
+ if ($excel->getSheetCount() == 0)
+ {
+ $excel->createSheet();
+ }
+ $excel->setActiveSheetIndex(0);
+ }
+ }
+ break;
+ }
+
+ }
+
+ return $excel;
+ }
+
+ private function _readColor($color) {
+ if (isset($color["rgb"])) {
+ return (string)$color["rgb"];
+ } else if (isset($color["indexed"])) {
+ return PHPExcel_Style_Color::indexedColor($color["indexed"])->getARGB();
+ }
+ }
+
+ private function _readStyle($docStyle, $style) {
+ // format code
+ if (isset($style->numFmt)) {
+ $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+ }
+
+ // font
+ if (isset($style->font)) {
+ $docStyle->getFont()->setName((string) $style->font->name["val"]);
+ $docStyle->getFont()->setSize((string) $style->font->sz["val"]);
+ if (isset($style->font->b)) {
+ $docStyle->getFont()->setBold(!isset($style->font->b["val"]) || $style->font->b["val"] == 'true');
+ }
+ if (isset($style->font->i)) {
+ $docStyle->getFont()->setItalic(!isset($style->font->i["val"]) || $style->font->i["val"] == 'true');
+ }
+ if (isset($style->font->strike)) {
+ $docStyle->getFont()->setStrikethrough(!isset($style->font->strike["val"]) || $style->font->strike["val"] == 'true');
+ }
+ $docStyle->getFont()->getColor()->setARGB($this->_readColor($style->font->color));
+
+ if (isset($style->font->u) && !isset($style->font->u["val"])) {
+ $docStyle->getFont()->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
+ } else if (isset($style->font->u) && isset($style->font->u["val"])) {
+ $docStyle->getFont()->setUnderline((string)$style->font->u["val"]);
+ }
+
+ if (isset($style->font->vertAlign) && isset($style->font->vertAlign["val"])) {
+ $vertAlign = strtolower((string)$style->font->vertAlign["val"]);
+ if ($vertAlign == 'superscript') {
+ $docStyle->getFont()->setSuperScript(true);
+ }
+ if ($vertAlign == 'subscript') {
+ $docStyle->getFont()->setSubScript(true);
+ }
+ }
+ }
+
+ // fill
+ if (isset($style->fill)) {
+ if ($style->fill->gradientFill) {
+ $gradientFill = $style->fill->gradientFill[0];
+ $docStyle->getFill()->setFillType((string) $gradientFill["type"]);
+ $docStyle->getFill()->setRotation(floatval($gradientFill["degree"]));
+ $gradientFill->registerXPathNamespace("sml", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
+ $docStyle->getFill()->getStartColor()->setARGB($this->_readColor( self::array_item($gradientFill->xpath("sml:stop[@position=0]"))->color) );
+ $docStyle->getFill()->getEndColor()->setARGB($this->_readColor( self::array_item($gradientFill->xpath("sml:stop[@position=1]"))->color) );
+ } elseif ($style->fill->patternFill) {
+ $patternType = (string)$style->fill->patternFill["patternType"] != '' ? (string)$style->fill->patternFill["patternType"] : 'solid';
+ $docStyle->getFill()->setFillType($patternType);
+ if ($style->fill->patternFill->fgColor) {
+ $docStyle->getFill()->getStartColor()->setARGB($this->_readColor($style->fill->patternFill->fgColor));
+ } else {
+ $docStyle->getFill()->getStartColor()->setARGB('FF000000');
+ }
+ if ($style->fill->patternFill->bgColor) {
+ $docStyle->getFill()->getEndColor()->setARGB($this->_readColor($style->fill->patternFill->bgColor));
+ }
+ }
+ }
+
+ // border
+ if (isset($style->border)) {
+ $diagonalUp = false;
+ $diagonalDown = false;
+ if ($style->border["diagonalUp"] == 'true' || $style->border["diagonalUp"] == 1) {
+ $diagonalUp = true;
+ }
+ if ($style->border["diagonalDown"] == 'true' || $style->border["diagonalDown"] == 1) {
+ $diagonalDown = true;
+ }
+ if ($diagonalUp == false && $diagonalDown == false) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE);
+ } elseif ($diagonalUp == true && $diagonalDown == false) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP);
+ } elseif ($diagonalUp == false && $diagonalDown == true) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN);
+ } elseif ($diagonalUp == true && $diagonalDown == true) {
+ $docStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH);
+ }
+ $this->_readBorder($docStyle->getBorders()->getLeft(), $style->border->left);
+ $this->_readBorder($docStyle->getBorders()->getRight(), $style->border->right);
+ $this->_readBorder($docStyle->getBorders()->getTop(), $style->border->top);
+ $this->_readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom);
+ $this->_readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal);
+ }
+
+ // alignment
+ if (isset($style->alignment)) {
+ $docStyle->getAlignment()->setHorizontal((string) $style->alignment["horizontal"]);
+ $docStyle->getAlignment()->setVertical((string) $style->alignment["vertical"]);
+
+ $textRotation = 0;
+ if ((int)$style->alignment["textRotation"] <= 90) {
+ $textRotation = (int)$style->alignment["textRotation"];
+ } else if ((int)$style->alignment["textRotation"] > 90) {
+ $textRotation = 90 - (int)$style->alignment["textRotation"];
+ }
+
+ $docStyle->getAlignment()->setTextRotation(intval($textRotation));
+ $docStyle->getAlignment()->setWrapText( (string)$style->alignment["wrapText"] == "true" || (string)$style->alignment["wrapText"] == "1" );
+ $docStyle->getAlignment()->setShrinkToFit( (string)$style->alignment["shrinkToFit"] == "true" || (string)$style->alignment["shrinkToFit"] == "1" );
+ $docStyle->getAlignment()->setIndent( intval((string)$style->alignment["indent"]) > 0 ? intval((string)$style->alignment["indent"]) : 0 );
+ }
+
+ // protection
+ if (isset($style->protection)) {
+ if (isset($style->protection['locked'])) {
+ if ((string)$style->protection['locked'] == 'true') {
+ $docStyle->getProtection()->setLocked(PHPExcel_Style_Protection::PROTECTION_PROTECTED);
+ } else {
+ $docStyle->getProtection()->setLocked(PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+ }
+ }
+
+ if (isset($style->protection['hidden'])) {
+ if ((string)$style->protection['hidden'] == 'true') {
+ $docStyle->getProtection()->setHidden(PHPExcel_Style_Protection::PROTECTION_PROTECTED);
+ } else {
+ $docStyle->getProtection()->setHidden(PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+ }
+ }
+ }
+ }
+
+ private function _readBorder($docBorder, $eleBorder) {
+ if (isset($eleBorder["style"])) {
+ $docBorder->setBorderStyle((string) $eleBorder["style"]);
+ }
+ if (isset($eleBorder->color)) {
+ $docBorder->getColor()->setARGB($this->_readColor($eleBorder->color));
+ }
+ }
+
+ private function _parseRichText($is = null) {
+ $value = new PHPExcel_RichText();
+
+ if (isset($is->t)) {
+ $value->createText( PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $is->t ) );
+ } else {
+ foreach ($is->r as $run) {
+ $objText = $value->createTextRun( PHPExcel_Shared_String::ControlCharacterOOXML2PHP( (string) $run->t ) );
+
+ if (isset($run->rPr)) {
+ if (isset($run->rPr->rFont["val"])) {
+ $objText->getFont()->setName((string) $run->rPr->rFont["val"]);
+ }
+
+ if (isset($run->rPr->sz["val"])) {
+ $objText->getFont()->setSize((string) $run->rPr->sz["val"]);
+ }
+
+ if (isset($run->rPr->color)) {
+ $objText->getFont()->setColor( new PHPExcel_Style_Color( $this->_readColor($run->rPr->color) ) );
+ }
+
+ if ( (isset($run->rPr->b["val"]) && ((string) $run->rPr->b["val"] == 'true' || (string) $run->rPr->b["val"] == '1'))
+ || (isset($run->rPr->b) && !isset($run->rPr->b["val"])) ) {
+ $objText->getFont()->setBold(true);
+ }
+
+ if ( (isset($run->rPr->i["val"]) && ((string) $run->rPr->i["val"] == 'true' || (string) $run->rPr->i["val"] == '1'))
+ || (isset($run->rPr->i) && !isset($run->rPr->i["val"])) ) {
+ $objText->getFont()->setItalic(true);
+ }
+
+ if (isset($run->rPr->vertAlign) && isset($run->rPr->vertAlign["val"])) {
+ $vertAlign = strtolower((string)$run->rPr->vertAlign["val"]);
+ if ($vertAlign == 'superscript') {
+ $objText->getFont()->setSuperScript(true);
+ }
+ if ($vertAlign == 'subscript') {
+ $objText->getFont()->setSubScript(true);
+ }
+ }
+
+ if (isset($run->rPr->u) && !isset($run->rPr->u["val"])) {
+ $objText->getFont()->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
+ } else if (isset($run->rPr->u) && isset($run->rPr->u["val"])) {
+ $objText->getFont()->setUnderline((string)$run->rPr->u["val"]);
+ }
+
+ if ( (isset($run->rPr->strike["val"]) && ((string) $run->rPr->strike["val"] == 'true' || (string) $run->rPr->strike["val"] == '1'))
+ || (isset($run->rPr->strike) && !isset($run->rPr->strike["val"])) ) {
+ $objText->getFont()->setStrikethrough(true);
+ }
+ }
+ }
+ }
+
+ return $value;
+ }
+
+ private static function array_item($array, $key = 0) {
+ return (isset($array[$key]) ? $array[$key] : null);
+ }
+
+ private static function dir_add($base, $add) {
+ return preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
+ }
+
+ private static function toCSSArray($style) {
+ $style = str_replace("\r", "", $style);
+ $style = str_replace("\n", "", $style);
+
+ $temp = explode(';', $style);
+
+ $style = array();
+ foreach ($temp as $item) {
+ $item = explode(':', $item);
+
+ if (strpos($item[1], 'px') !== false) {
+ $item[1] = str_replace('px', '', $item[1]);
+ }
+ if (strpos($item[1], 'pt') !== false) {
+ $item[1] = str_replace('pt', '', $item[1]);
+ $item[1] = PHPExcel_Shared_Font::fontSizeToPixels($item[1]);
+ }
+ if (strpos($item[1], 'in') !== false) {
+ $item[1] = str_replace('in', '', $item[1]);
+ $item[1] = PHPExcel_Shared_Font::inchSizeToPixels($item[1]);
+ }
+ if (strpos($item[1], 'cm') !== false) {
+ $item[1] = str_replace('cm', '', $item[1]);
+ $item[1] = PHPExcel_Shared_Font::centimeterSizeToPixels($item[1]);
+ }
+
+ $style[$item[0]] = $item[1];
+ }
+
+ return $style;
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel5.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel5.php
new file mode 100644
index 0000000..440edab
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel5.php
@@ -0,0 +1,5909 @@
+_data
+ *
+ * @var int
+ */
+ private $_dataSize;
+
+ /**
+ * Current position in stream
+ *
+ * @var integer
+ */
+ private $_pos;
+
+ /**
+ * Workbook to be returned by the reader.
+ *
+ * @var PHPExcel
+ */
+ private $_phpExcel;
+
+ /**
+ * Worksheet that is currently being built by the reader.
+ *
+ * @var PHPExcel_Worksheet
+ */
+ private $_phpSheet;
+
+ /**
+ * BIFF version
+ *
+ * @var int
+ */
+ private $_version;
+
+ /**
+ * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
+ * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'
+ *
+ * @var string
+ */
+ private $_codepage;
+
+ /**
+ * Shared formats
+ *
+ * @var array
+ */
+ private $_formats;
+
+ /**
+ * Shared fonts
+ *
+ * @var array
+ */
+ private $_objFonts;
+
+ /**
+ * Color palette
+ *
+ * @var array
+ */
+ private $_palette;
+
+ /**
+ * Worksheets
+ *
+ * @var array
+ */
+ private $_sheets;
+
+ /**
+ * External books
+ *
+ * @var array
+ */
+ private $_externalBooks;
+
+ /**
+ * REF structures. Only applies to BIFF8.
+ *
+ * @var array
+ */
+ private $_ref;
+
+ /**
+ * Defined names
+ *
+ * @var array
+ */
+ private $_definedname;
+
+ /**
+ * Shared strings. Only applies to BIFF8.
+ *
+ * @var array
+ */
+ private $_sst;
+
+ /**
+ * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
+ *
+ * @var boolean
+ */
+ private $_frozen;
+
+ /**
+ * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
+ *
+ * @var boolean
+ */
+ private $_isFitToPages;
+
+ /**
+ * Objects. One OBJ record contributes with one entry.
+ *
+ * @var array
+ */
+ private $_objs;
+
+ /**
+ * The combined MSODRAWINGGROUP data
+ *
+ * @var string
+ */
+ private $_drawingGroupData;
+
+ /**
+ * The combined MSODRAWING data (per sheet)
+ *
+ * @var string
+ */
+ private $_drawingData;
+
+ /**
+ * Keep track of XF index
+ *
+ * @var int
+ */
+ private $_xfIndex;
+
+ /**
+ * Mapping of XF index (that is a cell XF) to final index in cellXf collection
+ *
+ * @var array
+ */
+ private $_mapCellXfIndex;
+
+ /**
+ * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection
+ *
+ * @var array
+ */
+ private $_mapCellStyleXfIndex;
+
+ /**
+ * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
+ *
+ * @var array
+ */
+ private $_sharedFormulas;
+
+ /**
+ * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
+ * refers to a shared formula.
+ *
+ * @var array
+ */
+ private $_sharedFormulaParts;
+
+ /**
+ * Read data only?
+ *
+ * @return boolean
+ */
+ public function getReadDataOnly()
+ {
+ return $this->_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setReadDataOnly($pValue = false)
+ {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel5
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_Excel5 instance
+ */
+ public function __construct() {
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ try {
+ // Use ParseXL for the hard work.
+ $this->_ole = new PHPExcel_Shared_OLERead();
+
+ // get excel data
+ $res = $this->_ole->read($pFilename);
+ return true;
+
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Initialisations
+ $this->_phpExcel = new PHPExcel;
+ $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet
+ if (!$this->_readDataOnly) {
+ $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style
+ $this->_phpExcel->removeCellXfByIndex(0); // remove the default style
+ }
+
+ // Use ParseXL for the hard work.
+ $this->_ole = new PHPExcel_Shared_OLERead();
+
+ // get excel data
+ $res = $this->_ole->read($pFilename);
+ $this->_data = $this->_ole->getWorkBook();
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->_dataSize = strlen($this->_data);
+
+ // initialize
+ $this->_pos = 0;
+ $this->_codepage = 'CP1252';
+ $this->_formats = array();
+ $this->_objFonts = array();
+ $this->_palette = array();
+ $this->_sheets = array();
+ $this->_externalBooks = array();
+ $this->_ref = array();
+ $this->_definedname = array();
+ $this->_sst = array();
+ $this->_drawingGroupData = '';
+ $this->_xfIndex = '';
+ $this->_mapCellXfIndex = array();
+ $this->_mapCellStyleXfIndex = array();
+
+ // Parse Workbook Global Substream
+ while ($this->_pos < $this->_dataSize) {
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+
+ switch ($code) {
+ case self::XLS_Type_BOF:
+ $pos = $this->_pos;
+ $length = $this->_GetInt2d($this->_data, $pos + 2);
+ $recordData = substr($this->_data, $pos + 4, $length);
+
+ // offset: 0; size: 2; BIFF version
+ $this->_version = $this->_GetInt2d($this->_data, $pos + 4);
+
+ if (($this->_version != self::XLS_BIFF8) && ($this->_version != self::XLS_BIFF7)) {
+ return false;
+ }
+
+ // offset: 2; size: 2; type of stream
+ $substreamType = $this->_GetInt2d($this->_data, $pos + 6);
+ if ($substreamType != self::XLS_WorkbookGlobals) {
+ return false;
+ }
+ $this->_pos += 4 + $length;
+ break;
+
+ case self::XLS_Type_FILEPASS: $this->_readFilepass(); break;
+ case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break;
+ case self::XLS_Type_DATEMODE: $this->_readDateMode(); break;
+ case self::XLS_Type_FONT: $this->_readFont(); break;
+ case self::XLS_Type_FORMAT: $this->_readFormat(); break;
+ case self::XLS_Type_XF: $this->_readXf(); break;
+ case self::XLS_Type_XFEXT: $this->_readXfExt(); break;
+ case self::XLS_Type_STYLE: $this->_readStyle(); break;
+ case self::XLS_Type_PALETTE: $this->_readPalette(); break;
+ case self::XLS_Type_SHEET: $this->_readSheet(); break;
+ case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break;
+ case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break;
+ case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break;
+ case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break;
+ case self::XLS_Type_SST: $this->_readSst(); break;
+ case self::XLS_Type_EOF: $this->_readDefault(); break 2;
+ default: $this->_readDefault(); break;
+ }
+ }
+
+ // Resolve indexed colors for font, fill, and border colors
+ // Cannot be resolved already in XF record, because PALETTE record comes afterwards
+ if (!$this->_readDataOnly) {
+ foreach ($this->_objFonts as $objFont) {
+ if (isset($objFont->colorIndex)) {
+ $color = $this->_readColor($objFont->colorIndex);
+ $objFont->getColor()->setRGB($color['rgb']);
+ }
+ }
+
+ foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) {
+ // fill start and end color
+ $fill = $objStyle->getFill();
+
+ if (isset($fill->startcolorIndex)) {
+ $startColor = $this->_readColor($fill->startcolorIndex);
+ $fill->getStartColor()->setRGB($startColor['rgb']);
+ }
+
+ if (isset($fill->endcolorIndex)) {
+ $endColor = $this->_readColor($fill->endcolorIndex);
+ $fill->getEndColor()->setRGB($endColor['rgb']);
+ }
+
+ // border colors
+ $top = $objStyle->getBorders()->getTop();
+ $right = $objStyle->getBorders()->getRight();
+ $bottom = $objStyle->getBorders()->getBottom();
+ $left = $objStyle->getBorders()->getLeft();
+ $diagonal = $objStyle->getBorders()->getDiagonal();
+
+ if (isset($top->colorIndex)) {
+ $borderTopColor = $this->_readColor($top->colorIndex);
+ $top->getColor()->setRGB($borderTopColor['rgb']);
+ }
+
+ if (isset($right->colorIndex)) {
+ $borderRightColor = $this->_readColor($right->colorIndex);
+ $right->getColor()->setRGB($borderRightColor['rgb']);
+ }
+
+ if (isset($bottom->colorIndex)) {
+ $borderBottomColor = $this->_readColor($bottom->colorIndex);
+ $bottom->getColor()->setRGB($borderBottomColor['rgb']);
+ }
+
+ if (isset($left->colorIndex)) {
+ $borderLeftColor = $this->_readColor($left->colorIndex);
+ $left->getColor()->setRGB($borderLeftColor['rgb']);
+ }
+
+ if (isset($diagonal->colorIndex)) {
+ $borderDiagonalColor = $this->_readColor($diagonal->colorIndex);
+ $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
+ }
+ }
+ }
+
+ // treat MSODRAWINGGROUP records, workbook-level Escher
+ if (!$this->_readDataOnly && $this->_drawingGroupData) {
+ $escherWorkbook = new PHPExcel_Shared_Escher();
+ $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook);
+ $escherWorkbook = $reader->load($this->_drawingGroupData);
+
+ // debug Escher stream
+ //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
+ //$debug->load($this->_drawingGroupData);
+ }
+
+ // Parse the individual sheets
+ foreach ($this->_sheets as $sheet) {
+
+ // check if sheet should be skipped
+ if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
+ continue;
+ }
+
+ // add sheet to PHPExcel object
+ $this->_phpSheet = $this->_phpExcel->createSheet();
+ $this->_phpSheet->setTitle($sheet['name']);
+ $this->_phpSheet->setSheetState($sheet['sheetState']);
+
+ $this->_pos = $sheet['offset'];
+
+ // Initialize isFitToPages. May change after reading SHEETPR record.
+ $this->_isFitToPages = false;
+
+ // Initialize drawingData
+ $this->_drawingData = '';
+
+ // Initialize objs
+ $this->_objs = array();
+
+ // Initialize shared formula parts
+ $this->_sharedFormulaParts = array();
+
+ // Initialize shared formulas
+ $this->_sharedFormulas = array();
+
+ while ($this->_pos < $this->_dataSize) {
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+
+ switch ($code) {
+ case self::XLS_Type_BOF:
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // do not use this version information for anything
+ // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
+
+ // offset: 2; size: 2; type of the following data
+ $substreamType = $this->_GetInt2d($recordData, 2);
+ if ($substreamType != self::XLS_Worksheet) {
+ break 2;
+ }
+ break;
+
+ case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break;
+ case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break;
+ case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break;
+ case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break;
+ case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break;
+ case self::XLS_Type_HEADER: $this->_readHeader(); break;
+ case self::XLS_Type_FOOTER: $this->_readFooter(); break;
+ case self::XLS_Type_HCENTER: $this->_readHcenter(); break;
+ case self::XLS_Type_VCENTER: $this->_readVcenter(); break;
+ case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break;
+ case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break;
+ case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break;
+ case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break;
+ case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break;
+ case self::XLS_Type_PROTECT: $this->_readProtect(); break;
+ case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break;
+ case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break;
+ case self::XLS_Type_PASSWORD: $this->_readPassword(); break;
+ case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break;
+ case self::XLS_Type_COLINFO: $this->_readColInfo(); break;
+ case self::XLS_Type_DIMENSION: $this->_readDefault(); break;
+ case self::XLS_Type_ROW: $this->_readRow(); break;
+ case self::XLS_Type_DBCELL: $this->_readDefault(); break;
+ case self::XLS_Type_RK: $this->_readRk(); break;
+ case self::XLS_Type_LABELSST: $this->_readLabelSst(); break;
+ case self::XLS_Type_MULRK: $this->_readMulRk(); break;
+ case self::XLS_Type_NUMBER: $this->_readNumber(); break;
+ case self::XLS_Type_FORMULA: $this->_readFormula(); break;
+ case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break;
+ case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break;
+ case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break;
+ case self::XLS_Type_LABEL: $this->_readLabel(); break;
+ case self::XLS_Type_BLANK: $this->_readBlank(); break;
+ case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break;
+ case self::XLS_Type_OBJ: $this->_readObj(); break;
+ case self::XLS_Type_WINDOW2: $this->_readWindow2(); break;
+ case self::XLS_Type_SCL: $this->_readScl(); break;
+ case self::XLS_Type_PANE: $this->_readPane(); break;
+ case self::XLS_Type_SELECTION: $this->_readSelection(); break;
+ case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break;
+ case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break;
+ case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break;
+ case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break;
+ case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break;
+ //case self::XLS_Type_IMDATA: $this->_readImData(); break;
+ case self::XLS_Type_CONTINUE: $this->_readContinue(); break;
+ case self::XLS_Type_EOF: $this->_readDefault(); break 2;
+ default: $this->_readDefault(); break;
+ }
+
+ }
+
+ // treat MSODRAWING records, sheet-level Escher
+ if (!$this->_readDataOnly && $this->_drawingData) {
+ $escherWorksheet = new PHPExcel_Shared_Escher();
+ $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet);
+ $escherWorksheet = $reader->load($this->_drawingData);
+
+ // debug Escher stream
+ //$debug = new Debug_Escher(new PHPExcel_Shared_Escher());
+ //$debug->load($this->_drawingData);
+
+ // get all spContainers in one long array, so they can be mapped to OBJ records
+ $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
+ }
+
+ // treat OBJ records
+ foreach ($this->_objs as $n => $obj) {
+
+ // the first shape container never has a corresponding OBJ record, hence $n + 1
+ $spContainer = $allSpContainers[$n + 1];
+
+ // we skip all spContainers that are a part of a group shape since we cannot yet handle those
+ if ($spContainer->getNestingLevel() > 1) {
+ continue;
+ }
+
+ // calculate the width and height of the shape
+ list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates());
+ list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates());
+
+ $startOffsetX = $spContainer->getStartOffsetX();
+ $startOffsetY = $spContainer->getStartOffsetY();
+ $endOffsetX = $spContainer->getEndOffsetX();
+ $endOffsetY = $spContainer->getEndOffsetY();
+
+ $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
+ $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
+
+ // calculate offsetX and offsetY of the shape
+ $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024;
+ $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256;
+
+ switch ($obj['type']) {
+
+ case 0x08:
+ // picture
+
+ // get index to BSE entry (1-based)
+ $BSEindex = $spContainer->getOPT(0x0104);
+ $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
+ $BSE = $BSECollection[$BSEindex - 1];
+ $blipType = $BSE->getBlipType();
+
+ // need check because some blip types are not supported by Escher reader such as EMF
+ if ($blip = $BSE->getBlip()) {
+ $ih = imagecreatefromstring($blip->getData());
+ $drawing = new PHPExcel_Worksheet_MemoryDrawing();
+ $drawing->setImageResource($ih);
+
+ // width, height, offsetX, offsetY
+ $drawing->setResizeProportional(false);
+ $drawing->setWidth($width);
+ $drawing->setHeight($height);
+ $drawing->setOffsetX($offsetX);
+ $drawing->setOffsetY($offsetY);
+
+ switch ($blipType) {
+
+ case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG:
+ $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG);
+ $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG);
+ break;
+
+ case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG:
+ $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG);
+ $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG);
+ break;
+ }
+
+ $drawing->setWorksheet($this->_phpSheet);
+ $drawing->setCoordinates($spContainer->getStartCoordinates());
+ }
+
+ break;
+
+ default:
+ // other object type
+ break;
+
+ }
+ }
+
+ // treat SHAREDFMLA records
+ if ($this->_version == self::XLS_BIFF8) {
+ foreach ($this->_sharedFormulaParts as $cell => $baseCell) {
+ $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell);
+ $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
+ }
+ }
+ }
+
+ // add the named ranges (defined names)
+ foreach ($this->_definedname as $definedName) {
+ if ($definedName['isBuiltInName']) {
+ switch ($definedName['name']) {
+
+ case pack('C', 0x06):
+ // print area
+ // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
+
+ $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+
+ $extractedRanges = array();
+ foreach ($ranges as $range) {
+ // $range should look like one of these
+ // Foo!$C$7:$J$66
+ // Bar!$A$1:$IV$2
+
+ $explodes = explode('!', $range);
+ $sheetName = $explodes[0];
+
+ if (count($explodes) == 2) {
+ $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
+ }
+ }
+ if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) {
+ $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
+ }
+ break;
+
+ case pack('C', 0x07):
+ // print titles (repeating rows)
+ // Assuming BIFF8, there are 3 cases
+ // 1. repeating rows
+ // formula looks like this: Sheet!$A$1:$IV$2
+ // rows 1-2 repeat
+ // 2. repeating columns
+ // formula looks like this: Sheet!$A$1:$B$65536
+ // columns A-B repeat
+ // 3. both repeating rows and repeating columns
+ // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
+
+ $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+
+ foreach ($ranges as $range) {
+ // $range should look like this one of these
+ // Sheet!$A$1:$B$65536
+ // Sheet!$A$1:$IV$2
+
+ $explodes = explode('!', $range);
+
+ if (count($explodes) == 2) {
+ if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
+
+ $extractedRange = $explodes[1];
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ $coordinateStrings = explode(':', $extractedRange);
+ if (count($coordinateStrings) == 2) {
+ list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]);
+ list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]);
+
+ if ($firstColumn == 'A' and $lastColumn == 'IV') {
+ // then we have repeating rows
+ $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow));
+ } elseif ($firstRow == 1 and $lastRow == 65536) {
+ // then we have repeating columns
+ $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn));
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ }
+ } else {
+ // Extract range
+ $explodes = explode('!', $definedName['formula']);
+
+ if (count($explodes) == 2) {
+ if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) {
+ $extractedRange = $explodes[1];
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, false) );
+ }
+ }
+ }
+ }
+
+ return $this->_phpExcel;
+ }
+
+ /**
+ * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
+ */
+ private function _readDefault()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+ }
+
+ /**
+ * FILEPASS
+ *
+ * This record is part of the File Protection Block. It
+ * contains information about the read/write password of the
+ * file. All record contents following this record will be
+ * encrypted.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readFilepass()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ throw new Exception('Cannot read encrypted file');
+ }
+
+ /**
+ * CODEPAGE
+ *
+ * This record stores the text encoding used to write byte
+ * strings, stored as MS Windows code page identifier.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readCodepage()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; code page identifier
+ $codepage = $this->_GetInt2d($recordData, 0);
+
+ switch ($codepage) {
+
+ case 367: // ASCII
+ $this->_codepage ="ASCII";
+ break;
+
+ case 437: //OEM US
+ $this->_codepage ="CP437";
+ break;
+
+ case 720: //OEM Arabic
+ // currently not supported by libiconv
+ $this->_codepage = "";
+ break;
+
+ case 737: //OEM Greek
+ $this->_codepage ="CP737";
+ break;
+
+ case 775: //OEM Baltic
+ $this->_codepage ="CP775";
+ break;
+
+ case 850: //OEM Latin I
+ $this->_codepage ="CP850";
+ break;
+
+ case 852: //OEM Latin II (Central European)
+ $this->_codepage ="CP852";
+ break;
+
+ case 855: //OEM Cyrillic
+ $this->_codepage ="CP855";
+ break;
+
+ case 857: //OEM Turkish
+ $this->_codepage ="CP857";
+ break;
+
+ case 858: //OEM Multilingual Latin I with Euro
+ $this->_codepage ="CP858";
+ break;
+
+ case 860: //OEM Portugese
+ $this->_codepage ="CP860";
+ break;
+
+ case 861: //OEM Icelandic
+ $this->_codepage ="CP861";
+ break;
+
+ case 862: //OEM Hebrew
+ $this->_codepage ="CP862";
+ break;
+
+ case 863: //OEM Canadian (French)
+ $this->_codepage ="CP863";
+ break;
+
+ case 864: //OEM Arabic
+ $this->_codepage ="CP864";
+ break;
+
+ case 865: //OEM Nordic
+ $this->_codepage ="CP865";
+ break;
+
+ case 866: //OEM Cyrillic (Russian)
+ $this->_codepage ="CP866";
+ break;
+
+ case 869: //OEM Greek (Modern)
+ $this->_codepage ="CP869";
+ break;
+
+ case 874: //ANSI Thai
+ $this->_codepage ="CP874";
+ break;
+
+ case 932: //ANSI Japanese Shift-JIS
+ $this->_codepage ="CP932";
+ break;
+
+ case 936: //ANSI Chinese Simplified GBK
+ $this->_codepage ="CP936";
+ break;
+
+ case 949: //ANSI Korean (Wansung)
+ $this->_codepage ="CP949";
+ break;
+
+ case 950: //ANSI Chinese Traditional BIG5
+ $this->_codepage ="CP950";
+ break;
+
+ case 1200: //UTF-16 (BIFF8)
+ $this->_codepage ="UTF-16LE";
+ break;
+
+ case 1250:// ANSI Latin II (Central European)
+ $this->_codepage ="CP1250";
+ break;
+
+ case 1251: //ANSI Cyrillic
+ $this->_codepage ="CP1251";
+ break;
+
+ case 1252: //ANSI Latin I (BIFF4-BIFF7)
+ $this->_codepage ="CP1252";
+ break;
+
+ case 1253: //ANSI Greek
+ $this->_codepage ="CP1253";
+ break;
+
+ case 1254: //ANSI Turkish
+ $this->_codepage ="CP1254";
+ break;
+
+ case 1255: //ANSI Hebrew
+ $this->_codepage ="CP1255";
+ break;
+
+ case 1256: //ANSI Arabic
+ $this->_codepage ="CP1256";
+ break;
+
+ case 1257: //ANSI Baltic
+ $this->_codepage ="CP1257";
+ break;
+
+ case 1258: //ANSI Vietnamese
+ $this->_codepage ="CP1258";
+ break;
+
+ case 1361: //ANSI Korean (Johab)
+ $this->_codepage ="CP1361";
+ break;
+
+ case 10000: //Apple Roman
+ $this->_codepage = 'MAC';
+ break;
+
+ case 32768: //Apple Roman
+ $this->_codepage = 'MAC';
+ break;
+
+ case 32769: //ANSI Latin I (BIFF2-BIFF3)
+ // currently not supported by libiconv
+ $this->_codepage = "";
+ break;
+
+ }
+ }
+
+ /**
+ * DATEMODE
+ *
+ * This record specifies the base date for displaying date
+ * values. All dates are stored as count of days past this
+ * base date. In BIFF2-BIFF4 this record is part of the
+ * Calculation Settings Block. In BIFF5-BIFF8 it is
+ * stored in the Workbook Globals Substream.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readDateMode()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900);
+ if (ord($recordData{0}) == 1) {
+ PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904);
+ }
+ }
+
+ /**
+ * Read a FONT record
+ */
+ private function _readFont()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ $objFont = new PHPExcel_Style_Font();
+
+ // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
+ $size = $this->_GetInt2d($recordData, 0);
+ $objFont->setSize($size / 20);
+
+ // offset: 2; size: 2; option flags
+ // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
+ // bit: 1; mask 0x0002; italic
+ $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1;
+ if ($isItalic) $objFont->setItalic(true);
+
+ // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
+ // bit: 3; mask 0x0008; strike
+ $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3;
+ if ($isStrike) $objFont->setStrikethrough(true);
+
+ // offset: 4; size: 2; colour index
+ $colorIndex = $this->_GetInt2d($recordData, 4);
+ $objFont->colorIndex = $colorIndex;
+
+ // offset: 6; size: 2; font weight
+ $weight = $this->_GetInt2d($recordData, 6);
+ switch ($weight) {
+ case 0x02BC:
+ $objFont->setBold(true);
+ break;
+ }
+
+ // offset: 8; size: 2; escapement type
+ $escapement = $this->_GetInt2d($recordData, 8);
+ switch ($escapement) {
+ case 0x0001:
+ $objFont->setSuperScript(true);
+ break;
+ case 0x0002:
+ $objFont->setSubScript(true);
+ break;
+ }
+
+ // offset: 10; size: 1; underline type
+ $underlineType = ord($recordData{10});
+ switch ($underlineType) {
+ case 0x00:
+ break; // no underline
+ case 0x01:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
+ break;
+ case 0x02:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE);
+ break;
+ case 0x21:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING);
+ break;
+ case 0x22:
+ $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING);
+ break;
+ }
+
+ // offset: 11; size: 1; font family
+ // offset: 12; size: 1; character set
+ // offset: 13; size: 1; not used
+ // offset: 14; size: var; font name
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringShort(substr($recordData, 14));
+ } else {
+ $string = $this->_readByteStringShort(substr($recordData, 14));
+ }
+ $objFont->setName($string['value']);
+
+ $this->_objFonts[] = $objFont;
+ }
+ }
+
+ /**
+ * FORMAT
+ *
+ * This record contains information about a number format.
+ * All FORMAT records occur together in a sequential list.
+ *
+ * In BIFF2-BIFF4 other records referencing a FORMAT record
+ * contain a zero-based index into this list. From BIFF5 on
+ * the FORMAT record contains the index itself that will be
+ * used by other records.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readFormat()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ $indexCode = $this->_GetInt2d($recordData, 0);
+
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong(substr($recordData, 2));
+ } else {
+ // BIFF7
+ $string = $this->_readByteStringShort(substr($recordData, 2));
+ }
+
+ $formatString = $string['value'];
+ $this->_formats[$indexCode] = $formatString;
+ }
+ }
+
+ /**
+ * XF - Extended Format
+ *
+ * This record contains formatting information for cells, rows, columns or styles.
+ * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF
+ * and 1 cell XF.
+ * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
+ * and XF record 15 is a cell XF
+ * We only read the first cell style XF and skip the remaining cell style XF records
+ * We read all cell XF records.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readXf()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ $objStyle = new PHPExcel_Style();
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; Index to FONT record
+ if ($this->_GetInt2d($recordData, 0) < 4) {
+ $fontIndex = $this->_GetInt2d($recordData, 0);
+ } else {
+ // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+ // check the OpenOffice documentation of the FONT record
+ $fontIndex = $this->_GetInt2d($recordData, 0) - 1;
+ }
+ $objStyle->setFont($this->_objFonts[$fontIndex]);
+
+ // offset: 2; size: 2; Index to FORMAT record
+ $numberFormatIndex = $this->_GetInt2d($recordData, 2);
+ if (isset($this->_formats[$numberFormatIndex])) {
+ // then we have user-defined format code
+ $numberformat = array('code' => $this->_formats[$numberFormatIndex]);
+ } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
+ // then we have built-in format code
+ $numberformat = array('code' => $code);
+ } else {
+ // we set the general format code
+ $numberformat = array('code' => 'General');
+ }
+ $objStyle->getNumberFormat()->setFormatCode($numberformat['code']);
+
+ // offset: 4; size: 2; XF type, cell protection, and parent style XF
+ // bit 2-0; mask 0x0007; XF_TYPE_PROT
+ $xfTypeProt = $this->_GetInt2d($recordData, 4);
+ // bit 0; mask 0x01; 1 = cell is locked
+ $isLocked = (0x01 & $xfTypeProt) >> 0;
+ $objStyle->getProtection()->setLocked($isLocked ?
+ PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+
+ // bit 1; mask 0x02; 1 = Formula is hidden
+ $isHidden = (0x02 & $xfTypeProt) >> 1;
+ $objStyle->getProtection()->setHidden($isHidden ?
+ PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED);
+
+ // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
+ $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
+
+ // offset: 6; size: 1; Alignment and text break
+ // bit 2-0, mask 0x07; horizontal alignment
+ $horAlign = (0x07 & ord($recordData{6})) >> 0;
+ switch ($horAlign) {
+ case 0:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
+ break;
+ case 2:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
+ break;
+ case 3:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
+ break;
+ case 5:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY);
+ break;
+ case 6:
+ $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS);
+ break;
+ }
+ // bit 3, mask 0x08; wrap text
+ $wrapText = (0x08 & ord($recordData{6})) >> 3;
+ switch ($wrapText) {
+ case 0:
+ $objStyle->getAlignment()->setWrapText(false);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setWrapText(true);
+ break;
+ }
+ // bit 6-4, mask 0x70; vertical alignment
+ $vertAlign = (0x70 & ord($recordData{6})) >> 4;
+ switch ($vertAlign) {
+ case 0:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
+ break;
+ case 2:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM);
+ break;
+ case 3:
+ $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY);
+ break;
+ }
+
+ if ($this->_version == self::XLS_BIFF8) {
+ // offset: 7; size: 1; XF_ROTATION: Text rotation angle
+ $angle = ord($recordData{7});
+ $rotation = 0;
+ if ($angle <= 90) {
+ $rotation = $angle;
+ } else if ($angle <= 180) {
+ $rotation = 90 - $angle;
+ } else if ($angle == 255) {
+ $rotation = -165;
+ }
+ $objStyle->getAlignment()->setTextRotation($rotation);
+
+ // offset: 8; size: 1; Indentation, shrink to cell size, and text direction
+ // bit: 3-0; mask: 0x0F; indent level
+ $indent = (0x0F & ord($recordData{8})) >> 0;
+ $objStyle->getAlignment()->setIndent($indent);
+
+ // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
+ $shrinkToFit = (0x10 & ord($recordData{8})) >> 4;
+ switch ($shrinkToFit) {
+ case 0:
+ $objStyle->getAlignment()->setShrinkToFit(false);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setShrinkToFit(true);
+ break;
+ }
+
+ // offset: 9; size: 1; Flags used for attribute groups
+
+ // offset: 10; size: 4; Cell border lines and background area
+ // bit: 3-0; mask: 0x0000000F; left style
+ if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) {
+ $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
+ }
+ // bit: 7-4; mask: 0x000000F0; right style
+ if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) {
+ $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
+ }
+ // bit: 11-8; mask: 0x00000F00; top style
+ if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) {
+ $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
+ }
+ // bit: 15-12; mask: 0x0000F000; bottom style
+ if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) {
+ $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
+ }
+ // bit: 22-16; mask: 0x007F0000; left color
+ $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16;
+
+ // bit: 29-23; mask: 0x3F800000; right color
+ $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23;
+
+ // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
+ $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ?
+ true : false;
+
+ // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
+ $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ?
+ true : false;
+
+ if ($diagonalUp == false && $diagonalDown == false) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE);
+ } elseif ($diagonalUp == true && $diagonalDown == false) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP);
+ } elseif ($diagonalUp == false && $diagonalDown == true) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN);
+ } elseif ($diagonalUp == true && $diagonalDown == true) {
+ $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH);
+ }
+
+ // offset: 14; size: 4;
+ // bit: 6-0; mask: 0x0000007F; top color
+ $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0;
+
+ // bit: 13-7; mask: 0x00003F80; bottom color
+ $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7;
+
+ // bit: 20-14; mask: 0x001FC000; diagonal color
+ $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14;
+
+ // bit: 24-21; mask: 0x01E00000; diagonal style
+ if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14)) >> 21)) {
+ $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
+ }
+
+ // bit: 31-26; mask: 0xFC000000 fill pattern
+ if ($fillType = $this->_mapFillPattern((0xFC000000 & $this->_GetInt4d($recordData, 14)) >> 26)) {
+ $objStyle->getFill()->setFillType($fillType);
+ }
+ // offset: 18; size: 2; pattern and background colour
+ // bit: 6-0; mask: 0x007F; color index for pattern color
+ $objStyle->getFill()->startcolorIndex = (0x007F & $this->_GetInt2d($recordData, 18)) >> 0;
+
+ // bit: 13-7; mask: 0x3F80; color index for pattern background
+ $objStyle->getFill()->endcolorIndex = (0x3F80 & $this->_GetInt2d($recordData, 18)) >> 7;
+ } else {
+ // BIFF5
+
+ // offset: 7; size: 1; Text orientation and flags
+ $orientationAndFlags = ord($recordData{7});
+
+ // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
+ $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
+ switch ($xfOrientation) {
+ case 0:
+ $objStyle->getAlignment()->setTextRotation(0);
+ break;
+ case 1:
+ $objStyle->getAlignment()->setTextRotation(-165);
+ break;
+ case 2:
+ $objStyle->getAlignment()->setTextRotation(90);
+ break;
+ case 3:
+ $objStyle->getAlignment()->setTextRotation(-90);
+ break;
+ }
+
+ // offset: 8; size: 4; cell border lines and background area
+ $borderAndBackground = $this->_GetInt4d($recordData, 8);
+
+ // bit: 6-0; mask: 0x0000007F; color index for pattern color
+ $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
+
+ // bit: 13-7; mask: 0x00003F80; color index for pattern background
+ $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
+
+ // bit: 21-16; mask: 0x003F0000; fill pattern
+ $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 & $borderAndBackground) >> 16));
+
+ // bit: 24-22; mask: 0x01C00000; bottom line style
+ $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 & $borderAndBackground) >> 22));
+
+ // bit: 31-25; mask: 0xFE000000; bottom line color
+ $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
+
+ // offset: 12; size: 4; cell border lines
+ $borderLines = $this->_GetInt4d($recordData, 12);
+
+ // bit: 2-0; mask: 0x00000007; top line style
+ $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 & $borderLines) >> 0));
+
+ // bit: 5-3; mask: 0x00000038; left line style
+ $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 & $borderLines) >> 3));
+
+ // bit: 8-6; mask: 0x000001C0; right line style
+ $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 & $borderLines) >> 6));
+
+ // bit: 15-9; mask: 0x0000FE00; top line color index
+ $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
+
+ // bit: 22-16; mask: 0x007F0000; left line color index
+ $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
+
+ // bit: 29-23; mask: 0x3F800000; right line color index
+ $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
+ }
+
+ // add cellStyleXf or cellXf and update mapping
+ if ($isCellStyleXf) {
+ // we only read one style XF record which is always the first
+ if ($this->_xfIndex == 0) {
+ $this->_phpExcel->addCellStyleXf($objStyle);
+ $this->_mapCellStyleXfIndex[$this->_xfIndex] = 0;
+ }
+ } else {
+ // we read all cell XF records
+ $this->_phpExcel->addCellXf($objStyle);
+ $this->_mapCellXfIndex[$this->_xfIndex] = count($this->_phpExcel->getCellXfCollection()) - 1;
+ }
+
+ // update XF index for when we read next record
+ ++$this->_xfIndex;
+ }
+ }
+
+ /**
+ *
+ */
+ private function _readXfExt()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 0x087D = repeated header
+
+ // offset: 2; size: 2
+
+ // offset: 4; size: 8; not used
+
+ // offset: 12; size: 2; record version
+
+ // offset: 14; size: 2; index to XF record which this record modifies
+ $ixfe = $this->_GetInt2d($recordData, 14);
+
+ // offset: 16; size: 2; not used
+
+ // offset: 18; size: 2; number of extension properties that follow
+ $cexts = $this->_GetInt2d($recordData, 18);
+
+ // start reading the actual extension data
+ $offset = 20;
+ while ($offset < $length) {
+ // extension type
+ $extType = $this->_GetInt2d($recordData, $offset);
+
+ // extension length
+ $cb = $this->_GetInt2d($recordData, $offset + 2);
+
+ // extension data
+ $extData = substr($recordData, $offset + 4, $cb);
+
+ switch ($extType) {
+ case 4: // fill start color
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill();
+ $fill->getStartColor()->setRGB($rgb);
+ unset($fill->startcolorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 5: // fill end color
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill();
+ $fill->getEndColor()->setRGB($rgb);
+ unset($fill->endcolorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 7: // border color top
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $top = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop();
+ $top->getColor()->setRGB($rgb);
+ unset($top->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 8: // border color bottom
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $bottom = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom();
+ $bottom->getColor()->setRGB($rgb);
+ unset($bottom->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 9: // border color left
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $left = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft();
+ $left->getColor()->setRGB($rgb);
+ unset($left->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 10: // border color right
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $right = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight();
+ $right->getColor()->setRGB($rgb);
+ unset($right->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 11: // border color diagonal
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $diagonal = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
+ $diagonal->getColor()->setRGB($rgb);
+ unset($diagonal->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+
+ case 13: // font color
+ $xclfType = $this->_GetInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2}));
+
+ // modify the relevant style property
+ if ( isset($this->_mapCellXfIndex[$ixfe]) ) {
+ $font = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont();
+ $font->getColor()->setRGB($rgb);
+ unset($font->colorIndex); // normal color index does not apply, discard
+ }
+ }
+ break;
+ }
+
+ $offset += $cb;
+ }
+ }
+
+ }
+
+ /**
+ * Read STYLE record
+ */
+ private function _readStyle()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; index to XF record and flag for built-in style
+ $ixfe = $this->_GetInt2d($recordData, 0);
+
+ // bit: 11-0; mask 0x0FFF; index to XF record
+ $xfIndex = (0x0FFF & $ixfe) >> 0;
+
+ // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
+ $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
+
+ if ($isBuiltIn) {
+ // offset: 2; size: 1; identifier for built-in style
+ $builtInId = ord($recordData{2});
+
+ switch ($builtInId) {
+ case 0x00:
+ // currently, we are not using this for anything
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+ // user-defined; not supported by PHPExcel
+ }
+ }
+ }
+
+ /**
+ * Read PALETTE record
+ */
+ private function _readPalette()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; number of following colors
+ $nm = $this->_GetInt2d($recordData, 0);
+
+ // list of RGB colors
+ for ($i = 0; $i < $nm; ++$i) {
+ $rgb = substr($recordData, 2 + 4 * $i, 4);
+ $this->_palette[] = $this->_readRGB($rgb);
+ }
+ }
+ }
+
+ /**
+ * SHEET
+ *
+ * This record is located in the Workbook Globals
+ * Substream and represents a sheet inside the workbook.
+ * One SHEET record is written for each sheet. It stores the
+ * sheet name and a stream offset to the BOF record of the
+ * respective Sheet Substream within the Workbook Stream.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readSheet()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
+ $rec_offset = $this->_GetInt4d($recordData, 0);
+
+ // offset: 4; size: 1; sheet state
+ $rec_typeFlag = ord($recordData{4});
+ switch (ord($recordData{4})) {
+ case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break;
+ case 0x01: $sheetState = PHPExcel_Worksheet::SHEETSTATE_HIDDEN; break;
+ case 0x02: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN; break;
+ default: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break;
+ }
+
+ // offset: 5; size: 1; sheet type
+ $rec_visibilityFlag = ord($recordData{5});
+
+ // offset: 6; size: var; sheet name
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringShort(substr($recordData, 6));
+ $rec_name = $string['value'];
+ } elseif ($this->_version == self::XLS_BIFF7) {
+ $string = $this->_readByteStringShort(substr($recordData, 6));
+ $rec_name = $string['value'];
+ }
+ $this->_sheets[] = array(
+ 'name' => $rec_name,
+ 'offset' => $rec_offset,
+ 'sheetState' => $sheetState,
+ );
+ }
+
+ /**
+ * Read EXTERNALBOOK record
+ */
+ private function _readExternalBook()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset within record data
+ $offset = 0;
+
+ // there are 4 types of records
+ if (strlen($recordData) > 4) {
+ // external reference
+ // offset: 0; size: 2; number of sheet names ($nm)
+ $nm = $this->_GetInt2d($recordData, 0);
+ $offset += 2;
+
+ // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
+ $encodedUrlString = $this->_readUnicodeStringLong(substr($recordData, 2));
+ $offset += $encodedUrlString['size'];
+
+ // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
+ $externalSheetNames = array();
+ for ($i = 0; $i < $nm; ++$i) {
+ $externalSheetNameString = $this->_readUnicodeStringLong(substr($recordData, $offset));
+ $externalSheetNames[] = $externalSheetNameString['value'];
+ $offset += $externalSheetNameString['size'];
+ }
+
+ // store the record data
+ $this->_externalBooks[] = array(
+ 'type' => 'external',
+ 'encodedUrl' => $encodedUrlString['value'],
+ 'externalSheetNames' => $externalSheetNames,
+ );
+
+ } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
+ // internal reference
+ // offset: 0; size: 2; number of sheet in this document
+ // offset: 2; size: 2; 0x01 0x04
+ $this->_externalBooks[] = array(
+ 'type' => 'internal',
+ );
+ } elseif (substr($recordData, 0, 4) == pack('VCC', 0x0001, 0x01, 0x3A)) {
+ // add-in function
+ // offset: 0; size: 2; 0x0001
+ $this->_externalBooks[] = array(
+ 'type' => 'addInFunction',
+ );
+ } elseif (substr($recordData, 0, 2) == pack('V', 0x0000)) {
+ // DDE links, OLE links
+ // offset: 0; size: 2; 0x0000
+ // offset: 2; size: var; encoded source document name
+ $this->_externalBooks[] = array(
+ 'type' => 'DDEorOLE',
+ );
+ }
+ }
+
+ /**
+ * Read EXTERNSHEET record
+ */
+ private function _readExternSheet()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // external sheet references provided for named cells
+ if ($this->_version == self::XLS_BIFF8) {
+ // offset: 0; size: 2; number of following ref structures
+ $nm = $this->_GetInt2d($recordData, 0);
+ for ($i = 0; $i < $nm; ++$i) {
+ $this->_ref[] = array(
+ // offset: 2 + 6 * $i; index to EXTERNALBOOK record
+ 'externalBookIndex' => $this->_GetInt2d($recordData, 2 + 6 * $i),
+ // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
+ 'firstSheetIndex' => $this->_GetInt2d($recordData, 4 + 6 * $i),
+ // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
+ 'lastSheetIndex' => $this->_GetInt2d($recordData, 6 + 6 * $i),
+ );
+ }
+ }
+ }
+
+ /**
+ * DEFINEDNAME
+ *
+ * This record is part of a Link Table. It contains the name
+ * and the token array of an internal defined name. Token
+ * arrays of defined names contain tokens with aberrant
+ * token classes.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readDefinedName()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8) {
+ // retrieves named cells
+
+ // offset: 0; size: 2; option flags
+ $opts = $this->_GetInt2d($recordData, 0);
+
+ // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
+ $isBuiltInName = (0x0020 & $opts) >> 5;
+
+ // offset: 2; size: 1; keyboard shortcut
+
+ // offset: 3; size: 1; length of the name (character count)
+ $nlen = ord($recordData{3});
+
+ // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
+ // note: there can also be additional data, this is not included in $flen
+ $flen = $this->_GetInt2d($recordData, 4);
+
+ // offset: 14; size: var; Name (Unicode string without length field)
+ $string = $this->_readUnicodeString(substr($recordData, 14), $nlen);
+
+ // offset: var; size: $flen; formula data
+ $offset = 14 + $string['size'];
+ $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
+
+ try {
+ $formula = $this->_getFormulaFromStructure($formulaStructure);
+ } catch (Exception $e) {
+ $formula = '';
+ }
+
+ $this->_definedname[] = array(
+ 'isBuiltInName' => $isBuiltInName,
+ 'name' => $string['value'],
+ 'formula' => $formula,
+ );
+ }
+ }
+
+ /**
+ * Read MSODRAWINGGROUP record
+ */
+ private function _readMsoDrawingGroup()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ $this->_drawingGroupData .= $recordData;
+ }
+
+ /**
+ * SST - Shared String Table
+ *
+ * This record contains a list of all strings used anywhere
+ * in the workbook. Each string occurs only once. The
+ * workbook uses indexes into the list to reference the
+ * strings.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ **/
+ private function _readSst()
+ {
+ // offset within (spliced) record data
+ $pos = 0;
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+
+ $recordData = $splicedRecordData['recordData'];
+ $spliceOffsets = $splicedRecordData['spliceOffsets'];
+
+ // offset: 0; size: 4; total number of strings in the workbook
+ $pos += 4;
+
+ // offset: 4; size: 4; number of following strings ($nm)
+ $nm = $this->_GetInt4d($recordData, 4);
+ $pos += 4;
+
+ // loop through the Unicode strings (16-bit length)
+ for ($i = 0; $i < $nm; ++$i) {
+
+ // number of characters in the Unicode string
+ $numChars = $this->_GetInt2d($recordData, $pos);
+ $pos += 2;
+
+ // option flags
+ $optionFlags = ord($recordData{$pos});
+ ++$pos;
+
+ // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
+ $isCompressed = (($optionFlags & 0x01) == 0) ;
+
+ // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
+ $hasAsian = (($optionFlags & 0x04) != 0);
+
+ // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
+ $hasRichText = (($optionFlags & 0x08) != 0);
+
+ if ($hasRichText) {
+ // number of Rich-Text formatting runs
+ $formattingRuns = $this->_GetInt2d($recordData, $pos);
+ $pos += 2;
+ }
+
+ if ($hasAsian) {
+ // size of Asian phonetic setting
+ $extendedRunLength = $this->_GetInt4d($recordData, $pos);
+ $pos += 4;
+ }
+
+ // expected byte length of character array if not split
+ $len = ($isCompressed) ? $numChars : $numChars * 2;
+
+ // look up limit position
+ foreach ($spliceOffsets as $spliceOffset) {
+ // it can happen that the string is empty, therefore we need
+ // <= and not just <
+ if ($pos <= $spliceOffset) {
+ $limitpos = $spliceOffset;
+ break;
+ }
+ }
+
+ if ($pos + $len <= $limitpos) {
+ // character array is not split between records
+
+ $retstr = substr($recordData, $pos, $len);
+ $pos += $len;
+
+ } else {
+ // character array is split between records
+
+ // first part of character array
+ $retstr = substr($recordData, $pos, $limitpos - $pos);
+
+ $bytesRead = $limitpos - $pos;
+
+ // remaining characters in Unicode string
+ $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
+
+ $pos = $limitpos;
+
+ // keep reading the characters
+ while ($charsLeft > 0) {
+
+ // look up next limit position, in case the string span more than one continue record
+ foreach ($spliceOffsets as $spliceOffset) {
+ if ($pos < $spliceOffset) {
+ $limitpos = $spliceOffset;
+ break;
+ }
+ }
+
+ // repeated option flags
+ // OpenOffice.org documentation 5.21
+ $option = ord($recordData{$pos});
+ ++$pos;
+
+ if ($isCompressed && ($option == 0)) {
+ // 1st fragment compressed
+ // this fragment compressed
+ $len = min($charsLeft, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len;
+ $isCompressed = true;
+
+ } elseif (!$isCompressed && ($option != 0)) {
+ // 1st fragment uncompressed
+ // this fragment uncompressed
+ $len = min($charsLeft * 2, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len / 2;
+ $isCompressed = false;
+
+ } elseif (!$isCompressed && ($option == 0)) {
+ // 1st fragment uncompressed
+ // this fragment compressed
+ $len = min($charsLeft, $limitpos - $pos);
+ for ($j = 0; $j < $len; ++$j) {
+ $retstr .= $recordData{$pos + $j} . chr(0);
+ }
+ $charsLeft -= $len;
+ $isCompressed = false;
+
+ } else {
+ // 1st fragment compressed
+ // this fragment uncompressed
+ $newstr = '';
+ for ($j = 0; $j < strlen($retstr); ++$j) {
+ $newstr .= $retstr[$j] . chr(0);
+ }
+ $retstr = $newstr;
+ $len = min($charsLeft * 2, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len / 2;
+ $isCompressed = false;
+ }
+
+ $pos += $len;
+ }
+ }
+
+ // convert to UTF-8
+ $retstr = $this->_encodeUTF16($retstr, $isCompressed);
+
+ // read additional Rich-Text information, if any
+ $fmtRuns = array();
+ if ($hasRichText) {
+ // list of formatting runs
+ for ($j = 0; $j < $formattingRuns; ++$j) {
+ // first formatted character; zero-based
+ $charPos = $this->_GetInt2d($recordData, $pos + $j * 4);
+
+ // index to font record
+ $fontIndex = $this->_GetInt2d($recordData, $pos + 2 + $j * 4);
+
+ $fmtRuns[] = array(
+ 'charPos' => $charPos,
+ 'fontIndex' => $fontIndex,
+ );
+ }
+ $pos += 4 * $formattingRuns;
+ }
+
+ // read additional Asian phonetics information, if any
+ if ($hasAsian) {
+ // For Asian phonetic settings, we skip the extended string data
+ $pos += $extendedRunLength;
+ }
+
+ // store the shared sting
+ $this->_sst[] = array(
+ 'value' => $retstr,
+ 'fmtRuns' => $fmtRuns,
+ );
+ }
+
+ // _getSplicedRecordData() takes care of moving current position in data stream
+ }
+
+ /**
+ * Read PRINTGRIDLINES record
+ */
+ private function _readPrintGridlines()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+ // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
+ $printGridlines = (bool) $this->_GetInt2d($recordData, 0);
+ $this->_phpSheet->setPrintGridlines($printGridlines);
+ }
+ }
+
+ /**
+ * Read DEFAULTROWHEIGHT record
+ */
+ private function _readDefaultRowHeight()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; option flags
+ // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
+ $height = $this->_GetInt2d($recordData, 2);
+ $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
+ }
+
+ /**
+ * Read SHEETPR record
+ */
+ private function _readSheetPr()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2
+
+ // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
+ $isSummaryBelow = (0x0040 & $this->_GetInt2d($recordData, 0)) >> 6;
+ $this->_phpSheet->setShowSummaryBelow($isSummaryBelow);
+
+ // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
+ $isSummaryRight = (0x0080 & $this->_GetInt2d($recordData, 0)) >> 7;
+ $this->_phpSheet->setShowSummaryRight($isSummaryRight);
+
+ // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
+ // this corresponds to radio button setting in page setup dialog in Excel
+ $this->_isFitToPages = (bool) ((0x0100 & $this->_GetInt2d($recordData, 0)) >> 8);
+ }
+
+ /**
+ * Read HORIZONTALPAGEBREAKS record
+ */
+ private function _readHorizontalPageBreaks()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+
+ // offset: 0; size: 2; number of the following row index structures
+ $nm = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 6 * $nm; list of $nm row index structures
+ for ($i = 0; $i < $nm; ++$i) {
+ $r = $this->_GetInt2d($recordData, 2 + 6 * $i);
+ $cf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2);
+ $cl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4);
+
+ // not sure why two column indexes are necessary?
+ $this->_phpSheet->setBreakByColumnAndRow($cf, $r, PHPExcel_Worksheet::BREAK_ROW);
+ }
+ }
+ }
+
+ /**
+ * Read VERTICALPAGEBREAKS record
+ */
+ private function _readVerticalPageBreaks()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+ // offset: 0; size: 2; number of the following column index structures
+ $nm = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 6 * $nm; list of $nm row index structures
+ for ($i = 0; $i < $nm; ++$i) {
+ $c = $this->_GetInt2d($recordData, 2 + 6 * $i);
+ $rf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2);
+ $rl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4);
+
+ // not sure why two row indexes are necessary?
+ $this->_phpSheet->setBreakByColumnAndRow($c, $rf, PHPExcel_Worksheet::BREAK_COLUMN);
+ }
+ }
+ }
+
+ /**
+ * Read HEADER record
+ */
+ private function _readHeader()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: var
+ // realized that $recordData can be empty even when record exists
+ if ($recordData) {
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong($recordData);
+ } else {
+ $string = $this->_readByteStringShort($recordData);
+ }
+
+ $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']);
+ $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
+ }
+ }
+ }
+
+ /**
+ * Read FOOTER record
+ */
+ private function _readFooter()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: var
+ // realized that $recordData can be empty even when record exists
+ if ($recordData) {
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong($recordData);
+ } else {
+ $string = $this->_readByteStringShort($recordData);
+ }
+ $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']);
+ $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
+ }
+ }
+ }
+
+ /**
+ * Read HCENTER record
+ */
+ private function _readHcenter()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
+ $isHorizontalCentered = (bool) $this->_GetInt2d($recordData, 0);
+
+ $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
+ }
+ }
+
+ /**
+ * Read VCENTER record
+ */
+ private function _readVcenter()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
+ $isVerticalCentered = (bool) $this->_GetInt2d($recordData, 0);
+
+ $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
+ }
+ }
+
+ /**
+ * Read LEFTMARGIN record
+ */
+ private function _readLeftMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read RIGHTMARGIN record
+ */
+ private function _readRightMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read TOPMARGIN record
+ */
+ private function _readTopMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read BOTTOMMARGIN record
+ */
+ private function _readBottomMargin()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8
+ $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read PAGESETUP record
+ */
+ private function _readPageSetup()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; paper size
+ $paperSize = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; scaling factor
+ $scale = $this->_GetInt2d($recordData, 2);
+
+ // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
+ $fitToWidth = $this->_GetInt2d($recordData, 6);
+
+ // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
+ $fitToHeight = $this->_GetInt2d($recordData, 8);
+
+ // offset: 10; size: 2; option flags
+
+ // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
+ $isPortrait = (0x0002 & $this->_GetInt2d($recordData, 10)) >> 1;
+
+ // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
+ // when this bit is set, do not use flags for those properties
+ $isNotInit = (0x0004 & $this->_GetInt2d($recordData, 10)) >> 2;
+
+ if (!$isNotInit) {
+ $this->_phpSheet->getPageSetup()->setPaperSize($paperSize);
+ switch ($isPortrait) {
+ case 0: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); break;
+ case 1: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT); break;
+ }
+
+ $this->_phpSheet->getPageSetup()->setScale($scale, false);
+ $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages);
+ $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
+ $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
+ }
+
+ // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
+ $marginHeader = $this->_extractNumber(substr($recordData, 16, 8));
+ $this->_phpSheet->getPageMargins()->setHeader($marginHeader);
+
+ // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
+ $marginFooter = $this->_extractNumber(substr($recordData, 24, 8));
+ $this->_phpSheet->getPageMargins()->setFooter($marginFooter);
+ }
+ }
+
+ /**
+ * PROTECT - Sheet protection (BIFF2 through BIFF8)
+ * if this record is omitted, then it also means no sheet protection
+ */
+ private function _readProtect()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit 0, mask 0x01; 1 = sheet is protected
+ $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0;
+ $this->_phpSheet->getProtection()->setSheet((bool)$bool);
+ }
+
+ /**
+ * SCENPROTECT
+ */
+ private function _readScenProtect()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit: 0, mask 0x01; 1 = scenarios are protected
+ $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0;
+
+ $this->_phpSheet->getProtection()->setScenarios((bool)$bool);
+ }
+
+ /**
+ * OBJECTPROTECT
+ */
+ private function _readObjectProtect()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit: 0, mask 0x01; 1 = objects are protected
+ $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0;
+
+ $this->_phpSheet->getProtection()->setObjects((bool)$bool);
+ }
+
+ /**
+ * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8)
+ */
+ private function _readPassword()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; 16-bit hash value of password
+ $password = strtoupper(dechex($this->_GetInt2d($recordData, 0))); // the hashed password
+ $this->_phpSheet->getProtection()->setPassword($password, true);
+ }
+ }
+
+ /**
+ * Read DEFCOLWIDTH record
+ */
+ private function _readDefColWidth()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; default column width
+ $width = $this->_GetInt2d($recordData, 0);
+ if ($width != 8) {
+ $this->_phpSheet->getDefaultColumnDimension()->setWidth($width);
+ }
+ }
+
+ /**
+ * Read COLINFO record
+ */
+ private function _readColInfo()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; index to first column in range
+ $fc = $this->_GetInt2d($recordData, 0); // first column index
+
+ // offset: 2; size: 2; index to last column in range
+ $lc = $this->_GetInt2d($recordData, 2); // first column index
+
+ // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
+ $width = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 2; index to XF record for default column formatting
+ $xfIndex = $this->_GetInt2d($recordData, 6);
+
+ // offset: 8; size: 2; option flags
+
+ // bit: 0; mask: 0x0001; 1= columns are hidden
+ $isHidden = (0x0001 & $this->_GetInt2d($recordData, 8)) >> 0;
+
+ // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
+ $level = (0x0700 & $this->_GetInt2d($recordData, 8)) >> 8;
+
+ // bit: 12; mask: 0x1000; 1 = collapsed
+ $isCollapsed = (0x1000 & $this->_GetInt2d($recordData, 8)) >> 12;
+
+ // offset: 10; size: 2; not used
+
+ for ($i = $fc; $i <= $lc; ++$i) {
+ if ($lc == 255 || $lc == 256) {
+ $this->_phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
+ break;
+ }
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
+ $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * ROW
+ *
+ * This record contains the properties of a single row in a
+ * sheet. Rows and cells in a sheet are divided into blocks
+ * of 32 rows.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readRow()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; index of this row
+ $r = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column of the first cell which is described by a cell record
+
+ // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
+
+ // offset: 6; size: 2;
+
+ // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
+ $height = (0x7FFF & $this->_GetInt2d($recordData, 6)) >> 0;
+
+ // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
+ $useDefaultHeight = (0x8000 & $this->_GetInt2d($recordData, 6)) >> 15;
+
+ if (!$useDefaultHeight) {
+ $this->_phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
+ }
+
+ // offset: 8; size: 2; not used
+
+ // offset: 10; size: 2; not used in BIFF5-BIFF8
+
+ // offset: 12; size: 4; option flags and default row formatting
+
+ // bit: 2-0: mask: 0x00000007; outline level of the row
+ $level = (0x00000007 & $this->_GetInt4d($recordData, 12)) >> 0;
+ $this->_phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
+
+ // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
+ $isCollapsed = (0x00000010 & $this->_GetInt4d($recordData, 12)) >> 4;
+ $this->_phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
+
+ // bit: 5; mask: 0x00000020; 1 = row is hidden
+ $isHidden = (0x00000020 & $this->_GetInt4d($recordData, 12)) >> 5;
+ $this->_phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
+
+ // bit: 7; mask: 0x00000080; 1 = row has explicit format
+ $hasExplicitFormat = (0x00000080 & $this->_GetInt4d($recordData, 12)) >> 7;
+
+ // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
+ $xfIndex = (0x0FFF0000 & $this->_GetInt4d($recordData, 12)) >> 16;
+
+ if ($hasExplicitFormat) {
+ $this->_phpSheet->getRowDimension($r + 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read RK record
+ * This record represents a cell that contains an RK value
+ * (encoded integer or floating-point value). If a
+ * floating-point value cannot be encoded to an RK value,
+ * a NUMBER record will be written. This record replaces the
+ * record INTEGER written in BIFF2.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readRk()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 4; RK value
+ $rknum = $this->_GetInt4d($recordData, 6);
+ $numValue = $this->_GetIEEE754($rknum);
+
+ // add style information
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read LABELSST record
+ * This record represents a cell that contains a string. It
+ * replaces the LABEL record and RSTRING record used in
+ * BIFF2-BIFF5.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readLabelSst()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 4; index to SST record
+ $index = $this->_GetInt4d($recordData, 6);
+
+ // add cell
+ if (($fmtRuns = $this->_sst[$index]['fmtRuns']) && !$this->_readDataOnly) {
+ // then we should treat as rich text
+ $richText = new PHPExcel_RichText($this->_phpSheet->getCell($columnString . ($row + 1)));
+ $charPos = 0;
+ for ($i = 0; $i <= count($this->_sst[$index]['fmtRuns']); ++$i) {
+ if (isset($fmtRuns[$i])) {
+ $text = mb_substr($this->_sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos, 'UTF-8');
+ $charPos = $fmtRuns[$i]['charPos'];
+ } else {
+ $text = mb_substr($this->_sst[$index]['value'], $charPos, mb_strlen($this->_sst[$index]['value']), 'UTF-8');
+ }
+
+ if (mb_strlen($text) > 0) {
+ if ($i == 0) { // first text run, no style
+ $richText->createText($text);
+ } else {
+ $textRun = $richText->createTextRun($text);
+ if (isset($fmtRuns[$i - 1])) {
+ if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
+ $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
+ } else {
+ // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+ // check the OpenOffice documentation of the FONT record
+ $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
+ }
+ $textRun->setFont(clone $this->_objFonts[$fontIndex]);
+ }
+ }
+ }
+ }
+ } else {
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $this->_sst[$index]['value'], PHPExcel_Cell_DataType::TYPE_STRING);
+ }
+
+ // add style information
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MULRK record
+ * This record represents a cell range containing RK value
+ * cells. All cells are located in the same row.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readMulRk()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first column
+ $colFirst = $this->_GetInt2d($recordData, 2);
+
+ // offset: var; size: 2; index to last column
+ $colLast = $this->_GetInt2d($recordData, $length - 2);
+ $columns = $colLast - $colFirst + 1;
+
+ // offset within record data
+ $offset = 4;
+
+ for ($i = 0; $i < $columns; ++$i) {
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($colFirst + $i);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+
+ // offset: var; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, $offset);
+
+ // offset: var; size: 4; RK value
+ $numValue = $this->_GetIEEE754($this->_GetInt4d($recordData, $offset + 2));
+ if (!$this->_readDataOnly) {
+ // add style
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell value
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+ }
+
+ $offset += 6;
+ }
+ }
+
+ /**
+ * Read NUMBER record
+ * This record represents a cell that contains a
+ * floating-point value.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readNumber()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ $numValue = $this->_extractNumber(substr($recordData, 6, 8));
+
+ // add cell style
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell value
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read FORMULA record + perhaps a following STRING record if formula result is a string
+ * This record contains the token array and the result of a
+ * formula cell.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readFormula()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; col index
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+
+ // offset: 20: size: variable; formula structure
+ $formulaStructure = substr($recordData, 20);
+
+ // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
+ $options = $this->_GetInt2d($recordData, 14);
+
+ // bit: 0; mask: 0x0001; 1 = recalculate always
+ // bit: 1; mask: 0x0002; 1 = calculate on open
+ // bit: 2; mask: 0x0008; 1 = part of a shared formula
+ $isPartOfSharedFormula = (bool) (0x0008 & $options);
+
+ // WARNING:
+ // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
+ // the formula data may be ordinary formula data, therefore we need to check
+ // explicitly for the tExp token (0x01)
+ $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01;
+
+ if ($isPartOfSharedFormula) {
+ // part of shared formula which means there will be a formula with a tExp token and nothing else
+ // get the base cell, grab tExp token
+ $baseRow = $this->_GetInt2d($formulaStructure, 3);
+ $baseCol = $this->_GetInt2d($formulaStructure, 5);
+ $this->_baseCell = PHPExcel_Cell::stringFromColumnIndex($baseCol). ($baseRow + 1);
+
+ // formula is added to this cell after the sheet has been read
+ $this->_sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell;
+ }
+
+ // offset: 16: size: 4; not used
+
+ // offset: 4; size: 2; XF index
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 8; result of the formula
+ if ( (ord($recordData{6}) == 0)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255) ) {
+
+ // String formula. Result follows in appended STRING record
+ $dataType = PHPExcel_Cell_DataType::TYPE_STRING;
+
+ // read possible SHAREDFMLA record
+ $code = $this->_GetInt2d($this->_data, $this->_pos);
+ if ($code == self::XLS_Type_SHAREDFMLA) {
+ $this->_readSharedFmla();
+ }
+
+ // read STRING record
+ $value = $this->_readString();
+
+ } elseif ((ord($recordData{6}) == 1)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255)) {
+
+ // Boolean formula. Result is in +2; 0=false, 1=true
+ $dataType = PHPExcel_Cell_DataType::TYPE_BOOL;
+ $value = (bool) ord($recordData{8});
+
+ } elseif ((ord($recordData{6}) == 2)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255)) {
+
+ // Error formula. Error code is in +2
+ $dataType = PHPExcel_Cell_DataType::TYPE_ERROR;
+ $value = $this->_mapErrorCode(ord($recordData{8}));
+
+ } elseif ((ord($recordData{6}) == 3)
+ && (ord($recordData{12}) == 255)
+ && (ord($recordData{13}) == 255)) {
+
+ // Formula result is a null string
+ $dataType = PHPExcel_Cell_DataType::TYPE_NULL;
+ $value = '';
+
+ } else {
+
+ // forumla result is a number, first 14 bytes like _NUMBER record
+ $dataType = PHPExcel_Cell_DataType::TYPE_NUMERIC;
+ $value = $this->_extractNumber(substr($recordData, 6, 8));
+
+ }
+
+ // add cell style
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+
+ // store the formula
+ if (!$isPartOfSharedFormula) {
+ // not part of shared formula
+ // add cell value. If we can read formula, populate with formula, otherwise just used cached value
+ try {
+ if ($this->_version != self::XLS_BIFF8) {
+ throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
+ }
+ $formula = $this->_getFormulaFromStructure($formulaStructure); // get formula in human language
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA);
+
+ } catch (Exception $e) {
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $value, $dataType);
+ }
+ } else {
+ if ($this->_version == self::XLS_BIFF8) {
+ // do nothing at this point, formula id added later in the code
+ } else {
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $value, $dataType);
+ }
+ }
+
+ // store the cached calculated value
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setCalculatedValue($value);
+ }
+ }
+
+ /**
+ * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
+ * which usually contains relative references.
+ * These will be used to construct the formula in each shared formula part after the sheet is read.
+ */
+ private function _readSharedFmla()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
+ $cellRange = substr($recordData, 0, 6);
+ $cellRange = $this->_readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
+
+ // offset: 6, size: 1; not used
+
+ // offset: 7, size: 1; number of existing FORMULA records for this shared formula
+ $no = ord($recordData{7});
+
+ // offset: 8, size: var; Binary token array of the shared formula
+ $formula = substr($recordData, 8);
+
+ // at this point we only store the shared formula for later use
+ $this->_sharedFormulas[$this->_baseCell] = $formula;
+
+ }
+
+ /**
+ * Read a STRING record from current stream position and advance the stream pointer to next record
+ * This record is used for storing result from FORMULA record when it is a string, and
+ * it occurs directly after the FORMULA record
+ *
+ * @return string The string contents as UTF-8
+ */
+ private function _readString()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong($recordData);
+ $value = $string['value'];
+ } else {
+ $string = $this->_readByteStringLong($recordData);
+ $value = $string['value'];
+ }
+
+ return $value;
+ }
+
+ /**
+ * Read BOOLERR record
+ * This record represents a Boolean value or error value
+ * cell.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readBoolErr()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; column index
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 1; the boolean value or error value
+ $boolErr = ord($recordData{6});
+
+ // offset: 7; size: 1; 0=boolean; 1=error
+ $isError = ord($recordData{7});
+
+ switch ($isError) {
+ case 0: // boolean
+ $value = (bool) $boolErr;
+
+ // add cell value
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_BOOL);
+ break;
+
+ case 1: // error type
+ $value = $this->_mapErrorCode($boolErr);
+
+ // add cell value
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_ERROR);
+ break;
+ }
+
+ // add cell style
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MULBLANK record
+ * This record represents a cell range of empty cells. All
+ * cells are located in the same row
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readMulBlank()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first column
+ $fc = $this->_GetInt2d($recordData, 2);
+
+ // offset: 4; size: 2 x nc; list of indexes to XF records
+ // add style information
+ if (!$this->_readDataOnly) {
+ for ($i = 0; $i < $length / 2 - 3; ++$i) {
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($fc + $i);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ $xfIndex = $this->_GetInt2d($recordData, 4 + 2 * $i);
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ // offset: 6; size 2; index to last column (not needed)
+ }
+
+ /**
+ * Read LABEL record
+ * This record represents a cell that contains a string. In
+ * BIFF8 it is usually replaced by the LABELSST record.
+ * Excel still uses this record, if it copies unformatted
+ * text cells to the clipboard.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readLabel()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($column);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; XF index
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // add cell value
+ // todo: what if string is very long? continue record
+ if ($this->_version == self::XLS_BIFF8) {
+ $string = $this->_readUnicodeStringLong(substr($recordData, 6));
+ $value = $string['value'];
+ } else {
+ $string = $this->_readByteStringLong(substr($recordData, 6));
+ $value = $string['value'];
+ }
+ $this->_phpSheet->setCellValueExplicit($columnString . ($row + 1), $value, PHPExcel_Cell_DataType::TYPE_STRING);
+
+ // add cell style
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read BLANK record
+ */
+ private function _readBlank()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; col index
+ $col = $this->_GetInt2d($recordData, 2);
+ $columnString = PHPExcel_Cell::stringFromColumnIndex($col);
+
+ // Read cell?
+ if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) {
+ // offset: 4; size: 2; XF index
+ $xfIndex = $this->_GetInt2d($recordData, 4);
+
+ // add style information
+ if (!$this->_readDataOnly) {
+ $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]);
+ }
+ }
+
+ }
+
+ /**
+ * Read MSODRAWING record
+ */
+ private function _readMsoDrawing()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ $this->_drawingData .= $recordData;
+ }
+
+ /**
+ * Read OBJ record
+ */
+ private function _readObj()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8) {
+ return;
+ }
+
+ // recordData consists of an array of subrecords looking like this:
+ // ft: 2 bytes; id number
+ // cb: 2 bytes; size in bytes of following data
+ // data: var; subrecord data
+
+ // for now, we are just interested in the second subrecord containing the object type
+ $ot = $this->_GetInt2d($recordData, 4);
+
+ $this->_objs[] = array(
+ 'type' => $ot,
+ );
+ }
+
+ /**
+ * Read WINDOW2 record
+ */
+ private function _readWindow2()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; option flags
+ $options = $this->_GetInt2d($recordData, 0);
+
+ // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
+ $showGridlines = (bool) ((0x0002 & $options) >> 1);
+ $this->_phpSheet->setShowGridlines($showGridlines);
+
+ // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
+ $this->_frozen = (bool) ((0x0008 & $options) >> 3);
+
+ // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
+ $this->_phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6));
+
+ // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
+ $isActive = (bool) ((0x0400 & $options) >> 10);
+ if ($isActive) {
+ $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet));
+ }
+ }
+
+ /**
+ * Read SCL record
+ */
+ private function _readScl()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // offset: 0; size: 2; numerator of the view magnification
+ $numerator = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; numerator of the view magnification
+ $denumerator = $this->_GetInt2d($recordData, 2);
+
+ // set the zoom scale (in percent)
+ $this->_phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
+ }
+
+ /**
+ * Read PANE record
+ */
+ private function _readPane()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; position of vertical split
+ $px = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; position of horizontal split
+ $py = $this->_GetInt2d($recordData, 2);
+
+ if ($this->_frozen) {
+ // frozen panes
+ $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px) . ($py + 1));
+ } else {
+ // unfrozen panes; split windows; not supported by PHPExcel core
+ }
+ }
+ }
+
+ /**
+ * Read SELECTION record. There is one such record for each pane in the sheet.
+ */
+ private function _readSelection()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 1; pane identifier
+ $paneId = ord($recordData{0});
+
+ // offset: 1; size: 2; index to row of the active cell
+ $r = $this->_GetInt2d($recordData, 1);
+
+ // offset: 3; size: 2; index to column of the active cell
+ $c = $this->_GetInt2d($recordData, 3);
+
+ // offset: 5; size: 2; index into the following cell range list to the
+ // entry that contains the active cell
+ $index = $this->_GetInt2d($recordData, 5);
+
+ // offset: 7; size: var; cell range address list containing all selected cell ranges
+ $data = substr($recordData, 7);
+ $cellRangeAddressList = $this->_readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
+
+ $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
+
+ // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
+ if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
+ $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
+ }
+
+ // first row '1' + last row '65536' indicates that full column is selected
+ if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
+ $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
+ }
+
+ // first column 'A' + last column 'IV' indicates that full row is selected
+ if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) {
+ $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells);
+ }
+
+ $this->_phpSheet->setSelectedCells($selectedCells);
+ }
+ }
+
+ /**
+ * MERGEDCELLS
+ *
+ * This record contains the addresses of merged cell ranges
+ * in the current sheet.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function _readMergedCells()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) {
+ $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($recordData);
+ foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
+ $this->_phpSheet->mergeCells($cellRangeAddress);
+ }
+ }
+ }
+
+ /**
+ * Read HYPERLINK record
+ */
+ private function _readHyperLink()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->_pos += 4 + $length;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 8; cell range address of all cells containing this hyperlink
+ try {
+ $cellRange = $this->_readBIFF8CellRangeAddressFixed($recordData, 0, 8);
+ } catch (Exception $e) {
+ return;
+ }
+
+ // offset: 8, size: 16; GUID of StdLink
+
+ // offset: 24, size: 4; unknown value
+
+ // offset: 28, size: 4; option flags
+
+ // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
+ $isFileLinkOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 0;
+
+ // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
+ $isAbsPathOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 1;
+
+ // bit: 2 (and 4); mask: 0x00000014; 0 = no description
+ $hasDesc = (0x00000014 & $this->_GetInt2d($recordData, 28)) >> 2;
+
+ // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
+ $hasText = (0x00000008 & $this->_GetInt2d($recordData, 28)) >> 3;
+
+ // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
+ $hasFrame = (0x00000080 & $this->_GetInt2d($recordData, 28)) >> 7;
+
+ // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
+ $isUNC = (0x00000100 & $this->_GetInt2d($recordData, 28)) >> 8;
+
+ // offset within record data
+ $offset = 32;
+
+ if ($hasDesc) {
+ // offset: 32; size: var; character count of description text
+ $dl = $this->_GetInt4d($recordData, 32);
+ // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
+ $desc = $this->_encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
+ $offset += 4 + 2 * $dl;
+ }
+ if ($hasFrame) {
+ $fl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4 + 2 * $fl;
+ }
+
+ // detect type of hyperlink (there are 4 types)
+ $hyperlinkType = null;
+
+ if ($isUNC) {
+ $hyperlinkType = 'UNC';
+ } else if (!$isFileLinkOrUrl) {
+ $hyperlinkType = 'workbook';
+ } else if (ord($recordData{$offset}) == 0x03) {
+ $hyperlinkType = 'local';
+ } else if (ord($recordData{$offset}) == 0xE0) {
+ $hyperlinkType = 'URL';
+ }
+
+ switch ($hyperlinkType) {
+ case 'URL':
+ // section 5.58.2: Hyperlink containing a URL
+ // e.g. http://example.org/index.php
+
+ // offset: var; size: 16; GUID of URL Moniker
+ $offset += 16;
+ // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
+ $us = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+ // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
+ $url = $this->_encodeUTF16(substr($recordData, $offset, $us - 2), false);
+ $url .= $hasText ? '#' : '';
+ $offset += $us;
+ break;
+
+ case 'local':
+ // section 5.58.3: Hyperlink to local file
+ // examples:
+ // mydoc.txt
+ // ../../somedoc.xls#Sheet!A1
+
+ // offset: var; size: 16; GUI of File Moniker
+ $offset += 16;
+
+ // offset: var; size: 2; directory up-level count.
+ $upLevelCount = $this->_GetInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
+ $sl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
+ $shortenedFilePath = substr($recordData, $offset, $sl);
+ $shortenedFilePath = $this->_encodeUTF16($shortenedFilePath, true);
+ $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
+
+ $offset += $sl;
+
+ // offset: var; size: 24; unknown sequence
+ $offset += 24;
+
+ // extended file path
+ // offset: var; size: 4; size of the following file link field including string lenth mark
+ $sz = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // only present if $sz > 0
+ if ($sz > 0) {
+ // offset: var; size: 4; size of the character array of the extended file path and name
+ $xl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size 2; unknown
+ $offset += 2;
+
+ // offset: var; size $xl; character array of the extended file path and name.
+ $extendedFilePath = substr($recordData, $offset, $xl);
+ $extendedFilePath = $this->_encodeUTF16($extendedFilePath, false);
+ $offset += $xl;
+ }
+
+ // construct the path
+ $url = str_repeat('..\\', $upLevelCount);
+ $url .= ($sz > 0) ?
+ $extendedFilePath : $shortenedFilePath; // use extended path if available
+ $url .= $hasText ? '#' : '';
+
+ break;
+
+
+ case 'UNC':
+ // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
+ // todo: implement
+ return;
+
+ case 'workbook':
+ // section 5.58.5: Hyperlink to the Current Workbook
+ // e.g. Sheet2!B1:C2, stored in text mark field
+ $url = 'sheet://';
+ break;
+
+ default:
+ return;
+
+ }
+
+ if ($hasText) {
+ // offset: var; size: 4; character count of text mark including trailing zero word
+ $tl = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+ // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
+ $text = $this->_encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
+ $url .= $text;
+ }
+
+ // apply the hyperlink to all the relevant cells
+ foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) {
+ $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
+ }
+ }
+ }
+
+ /**
+ * Read SHEETLAYOUT record. Stores sheet tab color information.
+ */
+ private function _readSheetLayout()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // local pointer in record data
+ $offset = 0;
+
+ if (!$this->_readDataOnly) {
+ // offset: 0; size: 2; repeated record identifier 0x0862
+
+ // offset: 2; size: 10; not used
+
+ // offset: 12; size: 4; size of record data
+ // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
+ $sz = $this->_GetInt4d($recordData, 12);
+
+ switch ($sz) {
+ case 0x14:
+ // offset: 16; size: 2; color index for sheet tab
+ $colorIndex = $this->_GetInt2d($recordData, 16);
+ $color = $this->_readColor($colorIndex);
+ $this->_phpSheet->getTabColor()->setRGB($color['rgb']);
+ break;
+
+ case 0x28:
+ // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
+ return;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Read SHEETPROTECTION record
+ */
+ private function _readSheetProtection()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ if ($this->_readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2; repeated record header
+
+ // offset: 2; size: 9; not used
+
+ // offset: 11; size: 8; unknown data
+
+ // offset: 19; size: 2; option flags
+ $options = $this->_GetInt2d($recordData, 19);
+
+ // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
+ $bool = (0x0001 & $options) >> 0;
+ $this->_phpSheet->getProtection()->setObjects(!$bool);
+
+ // bit: 1; mask 0x0002; edit scenarios
+ $bool = (0x0002 & $options) >> 1;
+ $this->_phpSheet->getProtection()->setScenarios(!$bool);
+
+ // bit: 2; mask 0x0004; format cells
+ $bool = (0x0004 & $options) >> 2;
+ $this->_phpSheet->getProtection()->setFormatCells(!$bool);
+
+ // bit: 3; mask 0x0008; format columns
+ $bool = (0x0008 & $options) >> 3;
+ $this->_phpSheet->getProtection()->setFormatColumns(!$bool);
+
+ // bit: 4; mask 0x0010; format rows
+ $bool = (0x0010 & $options) >> 4;
+ $this->_phpSheet->getProtection()->setFormatRows(!$bool);
+
+ // bit: 5; mask 0x0020; insert columns
+ $bool = (0x0020 & $options) >> 5;
+ $this->_phpSheet->getProtection()->setInsertColumns(!$bool);
+
+ // bit: 6; mask 0x0040; insert rows
+ $bool = (0x0040 & $options) >> 6;
+ $this->_phpSheet->getProtection()->setInsertRows(!$bool);
+
+ // bit: 7; mask 0x0080; insert hyperlinks
+ $bool = (0x0080 & $options) >> 7;
+ $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool);
+
+ // bit: 8; mask 0x0100; delete columns
+ $bool = (0x0100 & $options) >> 8;
+ $this->_phpSheet->getProtection()->setDeleteColumns(!$bool);
+
+ // bit: 9; mask 0x0200; delete rows
+ $bool = (0x0200 & $options) >> 9;
+ $this->_phpSheet->getProtection()->setDeleteRows(!$bool);
+
+ // bit: 10; mask 0x0400; select locked cells
+ $bool = (0x0400 & $options) >> 10;
+ $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool);
+
+ // bit: 11; mask 0x0800; sort cell range
+ $bool = (0x0800 & $options) >> 11;
+ $this->_phpSheet->getProtection()->setSort(!$bool);
+
+ // bit: 12; mask 0x1000; auto filter
+ $bool = (0x1000 & $options) >> 12;
+ $this->_phpSheet->getProtection()->setAutoFilter(!$bool);
+
+ // bit: 13; mask 0x2000; pivot tables
+ $bool = (0x2000 & $options) >> 13;
+ $this->_phpSheet->getProtection()->setPivotTables(!$bool);
+
+ // bit: 14; mask 0x4000; select unlocked cells
+ $bool = (0x4000 & $options) >> 14;
+ $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
+
+ // offset: 21; size: 2; not used
+ }
+
+ /**
+ * Read RANGEPROTECTION record
+ * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
+ * where it is referred to as FEAT record
+ */
+ private function _readRangeProtection()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // local pointer in record data
+ $offset = 0;
+
+ if (!$this->_readDataOnly) {
+ $offset += 12;
+
+ // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
+ $isf = $this->_GetInt2d($recordData, 12);
+ if ($isf != 2) {
+ // we only read FEAT records of type 2
+ return;
+ }
+ $offset += 2;
+
+ $offset += 5;
+
+ // offset: 19; size: 2; count of ref ranges this feature is on
+ $cref = $this->_GetInt2d($recordData, 19);
+ $offset += 2;
+
+ $offset += 6;
+
+ // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
+ $cellRanges = array();
+ for ($i = 0; $i < $cref; ++$i) {
+ try {
+ $cellRange = $this->_readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
+ } catch (Exception $e) {
+ return;
+ }
+ $cellRanges[] = $cellRange;
+ $offset += 8;
+ }
+
+ // offset: var; size: var; variable length of feature specific data
+ $rgbFeat = substr($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
+ $wPassword = $this->_GetInt4d($recordData, $offset);
+ $offset += 4;
+
+ // Apply range protection to sheet
+ if ($cellRanges) {
+ $this->_phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
+ }
+ }
+ }
+
+ /**
+ * Read IMDATA record
+ */
+ private function _readImData()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->_getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ // UNDER CONSTRUCTION
+
+ // offset: 0; size: 2; image format
+ $cf = $this->_GetInt2d($recordData, 0);
+
+ // offset: 2; size: 2; environment from which the file was written
+ $env = $this->_GetInt2d($recordData, 2);
+
+ // offset: 4; size: 4; length of the image data
+ $lcb = $this->_GetInt4d($recordData, 4);
+
+ // offset: 8; size: var; image data
+ $iData = substr($recordData, 8);
+
+ switch ($cf) {
+ case 0x09: // Windows bitmap format
+ // BITMAPCOREINFO
+ // 1. BITMAPCOREHEADER
+ // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure
+ $bcSize = $this->_GetInt4d($iData, 0);
+ var_dump($bcSize);
+
+ // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels
+ $bcWidth = $this->_GetInt2d($iData, 4);
+ var_dump($bcWidth);
+
+ // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels.
+ $bcHeight = $this->_GetInt2d($iData, 6);
+ var_dump($bcHeight);
+ $ih = imagecreatetruecolor($bcWidth, $bcHeight);
+
+ // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1
+
+ // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24
+ $bcBitCount = $this->_GetInt2d($iData, 10);
+ var_dump($bcBitCount);
+
+ $rgbString = substr($iData, 12);
+ $rgbTriples = array();
+ while (strlen($rgbString) > 0) {
+ $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString);
+ $rgbString = substr($rgbString, 3);
+ }
+ $x = 0;
+ $y = 0;
+ foreach ($rgbTriples as $i => $rgbTriple) {
+ $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']);
+ imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color);
+ $x = ($x + 1) % $bcWidth;
+ $y = $y + floor(($x + 1) / $bcWidth);
+ }
+ //imagepng($ih, 'image.png');
+
+ $drawing = new PHPExcel_Worksheet_Drawing();
+ $drawing->setPath($filename);
+ $drawing->setWorksheet($this->_phpSheet);
+
+ break;
+
+ case 0x02: // Windows metafile or Macintosh PICT format
+ case 0x0e: // native format
+ default;
+ break;
+
+ }
+
+ // _getSplicedRecordData() takes care of moving current position in data stream
+ }
+
+ /**
+ * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
+ * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
+ * In this case, we must treat the CONTINUE record as a MSODRAWING record
+ */
+ private function _readContinue()
+ {
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $recordData = substr($this->_data, $this->_pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 4 + $length;
+
+ // check if we are reading drawing data
+ // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
+ if ($this->_drawingData == '') {
+ return;
+ }
+
+ // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
+ if (strlen($recordData) < 4) {
+ return;
+ }
+
+ // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
+ // look inside CONTINUE record to see if it looks like a part of an Escher stream
+ // we know that Escher stream may be split at least at
+ // 0xF003 MsofbtSpgrContainer
+ // 0xF004 MsofbtSpContainer
+ // 0xF00D MsofbtClientTextbox
+ $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more
+
+ $splitPoint = $this->_GetInt2d($recordData, 2);
+ if (in_array($splitPoint, $validSplitPoints)) {
+ $this->_drawingData .= $recordData;
+ }
+ }
+
+
+ /**
+ * Reads a record from current position in data stream and continues reading data as long as CONTINUE
+ * records are found. Splices the record data pieces and returns the combined string as if record data
+ * is in one piece.
+ * Moves to next current position in data stream to start of next record different from a CONtINUE record
+ *
+ * @return array
+ */
+ private function _getSplicedRecordData()
+ {
+ $data = '';
+ $spliceOffsets = array();
+
+ $i = 0;
+ $spliceOffsets[0] = 0;
+
+ do {
+ ++$i;
+
+ // offset: 0; size: 2; identifier
+ $identifier = $this->_GetInt2d($this->_data, $this->_pos);
+ // offset: 2; size: 2; length
+ $length = $this->_GetInt2d($this->_data, $this->_pos + 2);
+ $data .= substr($this->_data, $this->_pos + 4, $length);
+
+ $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
+
+ $this->_pos += 4 + $length;
+ $nextIdentifier = $this->_GetInt2d($this->_data, $this->_pos);
+ }
+ while ($nextIdentifier == self::XLS_Type_CONTINUE);
+
+ $splicedData = array(
+ 'recordData' => $data,
+ 'spliceOffsets' => $spliceOffsets,
+ );
+
+ return $splicedData;
+
+ }
+
+ /**
+ * Convert formula structure into human readable Excel formula like 'A3+A5*5'
+ *
+ * @param string $formulaStructure The complete binary data for the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string Human readable formula
+ */
+ private function _getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
+ {
+ // offset: 0; size: 2; size of the following formula data
+ $sz = $this->_GetInt2d($formulaStructure, 0);
+
+ // offset: 2; size: sz
+ $formulaData = substr($formulaStructure, 2, $sz);
+
+ // for debug: dump the formula data
+ //echo '';
+ //echo 'size: ' . $sz . "\n";
+ //echo 'the entire formula data: ';
+ //Debug::dump($formulaData);
+ //echo "\n----\n";
+
+ // offset: 2 + sz; size: variable (optional)
+ if (strlen($formulaStructure) > 2 + $sz) {
+ $additionalData = substr($formulaStructure, 2 + $sz);
+
+ // for debug: dump the additional data
+ //echo 'the entire additional data: ';
+ //Debug::dump($additionalData);
+ //echo "\n----\n";
+
+ } else {
+ $additionalData = '';
+ }
+
+ return $this->_getFormulaFromData($formulaData, $additionalData, $baseCell);
+ }
+
+ /**
+ * Take formula data and additional data for formula and return human readable formula
+ *
+ * @param string $formulaData The binary data for the formula itself
+ * @param string $additionalData Additional binary data going with the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string Human readable formula
+ */
+ private function _getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
+ {
+ // start parsing the formula data
+ $tokens = array();
+
+ while (strlen($formulaData) > 0 and $token = $this->_getNextToken($formulaData, $baseCell)) {
+ $tokens[] = $token;
+ $formulaData = substr($formulaData, $token['size']);
+
+ // for debug: dump the token
+ //var_dump($token);
+ }
+
+ $formulaString = $this->_createFormulaFromTokens($tokens, $additionalData);
+
+ return $formulaString;
+ }
+
+ /**
+ * Take array of tokens together with additional data for formula and return human readable formula
+ *
+ * @param array $tokens
+ * @param array $additionalData Additional binary data going with the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string Human readable formula
+ */
+ private function _createFormulaFromTokens($tokens, $additionalData)
+ {
+ // empty formula?
+ if (count($tokens) == 0) {
+ return '';
+ }
+
+ $formulaStrings = array();
+ foreach ($tokens as $token) {
+ // initialize spaces
+ $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
+ $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
+ $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
+ $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
+ $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
+ $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
+
+ switch ($token['name']) {
+ case 'tAdd': // addition
+ case 'tConcat': // addition
+ case 'tDiv': // division
+ case 'tEQ': // equaltiy
+ case 'tGE': // greater than or equal
+ case 'tGT': // greater than
+ case 'tIsect': // intersection
+ case 'tLE': // less than or equal
+ case 'tList': // less than or equal
+ case 'tLT': // less than
+ case 'tMul': // multiplication
+ case 'tNE': // multiplication
+ case 'tPower': // power
+ case 'tRange': // range
+ case 'tSub': // subtraction
+ $op2 = array_pop($formulaStrings);
+ $op1 = array_pop($formulaStrings);
+ $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
+ unset($space0, $space1);
+ break;
+ case 'tUplus': // unary plus
+ case 'tUminus': // unary minus
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "$space1$space0{$token['data']}$op";
+ unset($space0, $space1);
+ break;
+ case 'tPercent': // percent sign
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "$op$space1$space0{$token['data']}";
+ unset($space0, $space1);
+ break;
+ case 'tAttrVolatile': // indicates volatile function
+ case 'tAttrIf':
+ case 'tAttrSkip':
+ case 'tAttrChoose':
+ // token is only important for Excel formula evaluator
+ // do nothing
+ break;
+ case 'tAttrSpace': // space / carriage return
+ // space will be used when next token arrives, do not alter formulaString stack
+ switch ($token['data']['spacetype']) {
+ case 'type0':
+ $space0 = str_repeat(' ', $token['data']['spacecount']);
+ break;
+ case 'type1':
+ $space1 = str_repeat("\n", $token['data']['spacecount']);
+ break;
+ case 'type2':
+ $space2 = str_repeat(' ', $token['data']['spacecount']);
+ break;
+ case 'type3':
+ $space3 = str_repeat("\n", $token['data']['spacecount']);
+ break;
+ case 'type4':
+ $space4 = str_repeat(' ', $token['data']['spacecount']);
+ break;
+ case 'type5':
+ $space5 = str_repeat("\n", $token['data']['spacecount']);
+ break;
+ }
+ break;
+ case 'tAttrSum': // SUM function with one parameter
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "{$space1}{$space0}SUM($op)";
+ unset($space0, $space1);
+ break;
+ case 'tFunc': // function with fixed number of arguments
+ case 'tFuncV': // function with variable number of arguments
+ $ops = array(); // array of operators
+ for ($i = 0; $i < $token['data']['args']; ++$i) {
+ $ops[] = array_pop($formulaStrings);
+ }
+ $ops = array_reverse($ops);
+ $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")";
+ unset($space0, $space1);
+ break;
+ case 'tParen': // parenthesis
+ $expression = array_pop($formulaStrings);
+ $formulaStrings[] = "$space3$space2($expression$space5$space4)";
+ unset($space2, $space3, $space4, $space5);
+ break;
+ case 'tArray': // array constant
+ $constantArray = $this->_readBIFF8ConstantArray($additionalData);
+ $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
+ $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
+ unset($space0, $space1);
+ break;
+ case 'tMemArea':
+ // bite off chunk of additional data
+ $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($additionalData);
+ $additionalData = substr($additionalData, $cellRangeAddressList['size']);
+ $formulaStrings[] = "$space1$space0{$token['data']}";
+ unset($space0, $space1);
+ break;
+ case 'tArea': // cell range address
+ case 'tBool': // boolean
+ case 'tErr': // error code
+ case 'tInt': // integer
+ case 'tMemErr':
+ case 'tMemFunc':
+ case 'tMissArg':
+ case 'tName':
+ case 'tNum': // number
+ case 'tRef': // single cell reference
+ case 'tRef3d': // 3d cell reference
+ case 'tArea3d': // 3d cell range reference
+ case 'tRefN':
+ case 'tAreaN':
+ case 'tStr': // string
+ $formulaStrings[] = "$space1$space0{$token['data']}";
+ unset($space0, $space1);
+ break;
+ }
+ }
+ $formulaString = $formulaStrings[0];
+
+ // for debug: dump the human readable formula
+ //echo '----' . "\n";
+ //echo 'Formula: ' . $formulaString;
+
+ return $formulaString;
+ }
+
+ /**
+ * Fetch next token from binary formula data
+ *
+ * @param string Formula data
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return array
+ * @throws Exception
+ */
+ private function _getNextToken($formulaData, $baseCell = 'A1')
+ {
+ // offset: 0; size: 1; token id
+ $id = ord($formulaData[0]); // token id
+ $name = false; // initialize token name
+
+ switch ($id) {
+ case 0x03: $name = 'tAdd'; $size = 1; $data = '+'; break;
+ case 0x04: $name = 'tSub'; $size = 1; $data = '-'; break;
+ case 0x05: $name = 'tMul'; $size = 1; $data = '*'; break;
+ case 0x06: $name = 'tDiv'; $size = 1; $data = '/'; break;
+ case 0x07: $name = 'tPower'; $size = 1; $data = '^'; break;
+ case 0x08: $name = 'tConcat'; $size = 1; $data = '&'; break;
+ case 0x09: $name = 'tLT'; $size = 1; $data = '<'; break;
+ case 0x0A: $name = 'tLE'; $size = 1; $data = '<='; break;
+ case 0x0B: $name = 'tEQ'; $size = 1; $data = '='; break;
+ case 0x0C: $name = 'tGE'; $size = 1; $data = '>='; break;
+ case 0x0D: $name = 'tGT'; $size = 1; $data = '>'; break;
+ case 0x0E: $name = 'tNE'; $size = 1; $data = '<>'; break;
+ case 0x0F: $name = 'tIsect'; $size = 1; $data = ' '; break;
+ case 0x10: $name = 'tList'; $size = 1; $data = ','; break;
+ case 0x11: $name = 'tRange'; $size = 1; $data = ':'; break;
+ case 0x12: $name = 'tUplus'; $size = 1; $data = '+'; break;
+ case 0x13: $name = 'tUminus'; $size = 1; $data = '-'; break;
+ case 0x14: $name = 'tPercent'; $size = 1; $data = '%'; break;
+ case 0x15: // parenthesis
+ $name = 'tParen';
+ $size = 1;
+ $data = null;
+ break;
+ case 0x16: // missing argument
+ $name = 'tMissArg';
+ $size = 1;
+ $data = '';
+ break;
+ case 0x17: // string
+ $name = 'tStr';
+ // offset: 1; size: var; Unicode string, 8-bit string length
+ $string = $this->_readUnicodeStringShort(substr($formulaData, 1));
+ $size = 1 + $string['size'];
+ $data = $this->_UTF8toExcelDoubleQuoted($string['value']);
+ break;
+ case 0x19: // Special attribute
+ // offset: 1; size: 1; attribute type flags:
+ switch (ord($formulaData[1])) {
+ case 0x01:
+ $name = 'tAttrVolatile';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x02:
+ $name = 'tAttrIf';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x04:
+ $name = 'tAttrChoose';
+ // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
+ $nc = $this->_GetInt2d($formulaData, 2);
+ // offset: 4; size: 2 * $nc
+ // offset: 4 + 2 * $nc; size: 2
+ $size = 2 * $nc + 6;
+ $data = null;
+ break;
+ case 0x08:
+ $name = 'tAttrSkip';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x10:
+ $name = 'tAttrSum';
+ $size = 4;
+ $data = null;
+ break;
+ case 0x40:
+ case 0x41:
+ $name = 'tAttrSpace';
+ $size = 4;
+ // offset: 2; size: 2; space type and position
+ switch (ord($formulaData[2])) {
+ case 0x00:
+ $spacetype = 'type0';
+ break;
+ case 0x01:
+ $spacetype = 'type1';
+ break;
+ case 0x02:
+ $spacetype = 'type2';
+ break;
+ case 0x03:
+ $spacetype = 'type3';
+ break;
+ case 0x04:
+ $spacetype = 'type4';
+ break;
+ case 0x05:
+ $spacetype = 'type5';
+ break;
+ default:
+ throw new Exception('Unrecognized space type in tAttrSpace token');
+ break;
+ }
+ // offset: 3; size: 1; number of inserted spaces/carriage returns
+ $spacecount = ord($formulaData[3]);
+
+ $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount);
+ break;
+ default:
+ throw new Exception('Unrecognized attribute flag in tAttr token');
+ break;
+ }
+ break;
+ case 0x1C: // error code
+ // offset: 1; size: 1; error code
+ $name = 'tErr';
+ $size = 2;
+ $data = $this->_mapErrorCode(ord($formulaData[1]));
+ break;
+ case 0x1D: // boolean
+ // offset: 1; size: 1; 0 = false, 1 = true;
+ $name = 'tBool';
+ $size = 2;
+ $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
+ break;
+ case 0x1E: // integer
+ // offset: 1; size: 2; unsigned 16-bit integer
+ $name = 'tInt';
+ $size = 3;
+ $data = $this->_GetInt2d($formulaData, 1);
+ break;
+ case 0x1F: // number
+ // offset: 1; size: 8;
+ $name = 'tNum';
+ $size = 9;
+ $data = $this->_extractNumber(substr($formulaData, 1));
+ $data = str_replace(',', '.', (string)$data); // in case non-English locale
+ break;
+ case 0x40: // array constant
+ case 0x60: // array constant
+ // offset: 1; size: 7; not used
+ $name = 'tArray';
+ $size = 8;
+ $data = null;
+ break;
+ case 0x41: // function with fixed number of arguments
+ $name = 'tFunc';
+ $size = 3;
+ // offset: 1; size: 2; index to built-in sheet function
+ switch ($this->_GetInt2d($formulaData, 1)) {
+ case 2: $function = 'ISNA'; $args = 1; break;
+ case 3: $function = 'ISERROR'; $args = 1; break;
+ case 10: $function = 'NA'; $args = 0; break;
+ case 15: $function = 'SIN'; $args = 1; break;
+ case 16: $function = 'COS'; $args = 1; break;
+ case 17: $function = 'TAN'; $args = 1; break;
+ case 18: $function = 'ATAN'; $args = 1; break;
+ case 19: $function = 'PI'; $args = 0; break;
+ case 20: $function = 'SQRT'; $args = 1; break;
+ case 21: $function = 'EXP'; $args = 1; break;
+ case 22: $function = 'LN'; $args = 1; break;
+ case 23: $function = 'LOG10'; $args = 1; break;
+ case 24: $function = 'ABS'; $args = 1; break;
+ case 25: $function = 'INT'; $args = 1; break;
+ case 26: $function = 'SIGN'; $args = 1; break;
+ case 27: $function = 'ROUND'; $args = 2; break;
+ case 30: $function = 'REPT'; $args = 2; break;
+ case 31: $function = 'MID'; $args = 3; break;
+ case 32: $function = 'LEN'; $args = 1; break;
+ case 33: $function = 'VALUE'; $args = 1; break;
+ case 34: $function = 'TRUE'; $args = 0; break;
+ case 35: $function = 'FALSE'; $args = 0; break;
+ case 38: $function = 'NOT'; $args = 1; break;
+ case 39: $function = 'MOD'; $args = 2; break;
+ case 40: $function = 'DCOUNT'; $args = 3; break;
+ case 41: $function = 'DSUM'; $args = 3; break;
+ case 42: $function = 'DAVERAGE'; $args = 3; break;
+ case 43: $function = 'DMIN'; $args = 3; break;
+ case 44: $function = 'DMAX'; $args = 3; break;
+ case 45: $function = 'DSTDEV'; $args = 3; break;
+ case 48: $function = 'TEXT'; $args = 2; break;
+ case 61: $function = 'MIRR'; $args = 3; break;
+ case 63: $function = 'RAND'; $args = 0; break;
+ case 65: $function = 'DATE'; $args = 3; break;
+ case 66: $function = 'TIME'; $args = 3; break;
+ case 67: $function = 'DAY'; $args = 1; break;
+ case 68: $function = 'MONTH'; $args = 1; break;
+ case 69: $function = 'YEAR'; $args = 1; break;
+ case 71: $function = 'HOUR'; $args = 1; break;
+ case 72: $function = 'MINUTE'; $args = 1; break;
+ case 73: $function = 'SECOND'; $args = 1; break;
+ case 74: $function = 'NOW'; $args = 0; break;
+ case 75: $function = 'AREAS'; $args = 1; break;
+ case 76: $function = 'ROWS'; $args = 1; break;
+ case 77: $function = 'COLUMNS'; $args = 1; break;
+ case 83: $function = 'TRANSPOSE'; $args = 1; break;
+ case 86: $function = 'TYPE'; $args = 1; break;
+ case 97: $function = 'ATAN2'; $args = 2; break;
+ case 98: $function = 'ASIN'; $args = 1; break;
+ case 99: $function = 'ACOS'; $args = 1; break;
+ case 105: $function = 'ISREF'; $args = 1; break;
+ case 111: $function = 'CHAR'; $args = 1; break;
+ case 112: $function = 'LOWER'; $args = 1; break;
+ case 113: $function = 'UPPER'; $args = 1; break;
+ case 114: $function = 'PROPER'; $args = 1; break;
+ case 117: $function = 'EXACT'; $args = 2; break;
+ case 118: $function = 'TRIM'; $args = 1; break;
+ case 119: $function = 'REPLACE'; $args = 4; break;
+ case 121: $function = 'CODE'; $args = 1; break;
+ case 126: $function = 'ISERR'; $args = 1; break;
+ case 127: $function = 'ISTEXT'; $args = 1; break;
+ case 128: $function = 'ISNUMBER'; $args = 1; break;
+ case 129: $function = 'ISBLANK'; $args = 1; break;
+ case 130: $function = 'T'; $args = 1; break;
+ case 131: $function = 'N'; $args = 1; break;
+ case 140: $function = 'DATEVALUE'; $args = 1; break;
+ case 141: $function = 'TIMEVALUE'; $args = 1; break;
+ case 142: $function = 'SLN'; $args = 3; break;
+ case 143: $function = 'SYD'; $args = 4; break;
+ case 162: $function = 'CLEAN'; $args = 1; break;
+ case 163: $function = 'MDETERM'; $args = 1; break;
+ case 164: $function = 'MINVERSE'; $args = 1; break;
+ case 165: $function = 'MMULT'; $args = 2; break;
+ case 184: $function = 'FACT'; $args = 1; break;
+ case 189: $function = 'DPRODUCT'; $args = 3; break;
+ case 190: $function = 'ISNONTEXT'; $args = 1; break;
+ case 195: $function = 'DSTDEVP'; $args = 3; break;
+ case 196: $function = 'DVARP'; $args = 3; break;
+ case 198: $function = 'ISLOGICAL'; $args = 1; break;
+ case 199: $function = 'DCOUNTA'; $args = 3; break;
+ case 207: $function = 'REPLACEB'; $args = 4; break;
+ case 210: $function = 'MIDB'; $args = 3; break;
+ case 211: $function = 'LENB'; $args = 1; break;
+ case 212: $function = 'ROUNDUP'; $args = 2; break;
+ case 213: $function = 'ROUNDDOWN'; $args = 2; break;
+ case 214: $function = 'ASC'; $args = 1; break;
+ case 215: $function = 'DBCS'; $args = 1; break;
+ case 221: $function = 'TODAY'; $args = 0; break;
+ case 229: $function = 'SINH'; $args = 1; break;
+ case 230: $function = 'COSH'; $args = 1; break;
+ case 231: $function = 'TANH'; $args = 1; break;
+ case 232: $function = 'ASINH'; $args = 1; break;
+ case 233: $function = 'ACOSH'; $args = 1; break;
+ case 234: $function = 'ATANH'; $args = 1; break;
+ case 235: $function = 'DGET'; $args = 3; break;
+ case 244: $function = 'INFO'; $args = 1; break;
+ case 252: $function = 'FREQUENCY'; $args = 2; break;
+ case 261: $function = 'ERROR.TYPE'; $args = 1; break;
+ case 271: $function = 'GAMMALN'; $args = 1; break;
+ case 273: $function = 'BINOMDIST'; $args = 4; break;
+ case 274: $function = 'CHIDIST'; $args = 2; break;
+ case 275: $function = 'CHIINV'; $args = 2; break;
+ case 276: $function = 'COMBIN'; $args = 2; break;
+ case 277: $function = 'CONFIDENCE'; $args = 3; break;
+ case 278: $function = 'CRITBINOM'; $args = 3; break;
+ case 279: $function = 'EVEN'; $args = 1; break;
+ case 280: $function = 'EXPONDIST'; $args = 3; break;
+ case 281: $function = 'FDIST'; $args = 3; break;
+ case 282: $function = 'FINV'; $args = 3; break;
+ case 283: $function = 'FISHER'; $args = 1; break;
+ case 284: $function = 'FISHERINV'; $args = 1; break;
+ case 285: $function = 'FLOOR'; $args = 2; break;
+ case 286: $function = 'GAMMADIST'; $args = 4; break;
+ case 287: $function = 'GAMMAINV'; $args = 3; break;
+ case 288: $function = 'CEILING'; $args = 2; break;
+ case 289: $function = 'HYPGEOMDIST'; $args = 4; break;
+ case 290: $function = 'LOGNORMDIST'; $args = 3; break;
+ case 291: $function = 'LOGINV'; $args = 3; break;
+ case 292: $function = 'NEGBINOMDIST'; $args = 3; break;
+ case 293: $function = 'NORMDIST'; $args = 4; break;
+ case 294: $function = 'NORMSDIST'; $args = 1; break;
+ case 295: $function = 'NORMINV'; $args = 3; break;
+ case 296: $function = 'NORMSINV'; $args = 1; break;
+ case 297: $function = 'STANDARDIZE'; $args = 3; break;
+ case 298: $function = 'ODD'; $args = 1; break;
+ case 299: $function = 'PERMUT'; $args = 2; break;
+ case 300: $function = 'POISSON'; $args = 3; break;
+ case 301: $function = 'TDIST'; $args = 3; break;
+ case 302: $function = 'WEIBULL'; $args = 4; break;
+ case 303: $function = 'SUMXMY2'; $args = 2; break;
+ case 304: $function = 'SUMX2MY2'; $args = 2; break;
+ case 305: $function = 'SUMX2PY2'; $args = 2; break;
+ case 306: $function = 'CHITEST'; $args = 2; break;
+ case 307: $function = 'CORREL'; $args = 2; break;
+ case 308: $function = 'COVAR'; $args = 2; break;
+ case 309: $function = 'FORECAST'; $args = 3; break;
+ case 310: $function = 'FTEST'; $args = 2; break;
+ case 311: $function = 'INTERCEPT'; $args = 2; break;
+ case 312: $function = 'PEARSON'; $args = 2; break;
+ case 313: $function = 'RSQ'; $args = 2; break;
+ case 314: $function = 'STEYX'; $args = 2; break;
+ case 315: $function = 'SLOPE'; $args = 2; break;
+ case 316: $function = 'TTEST'; $args = 4; break;
+ case 325: $function = 'LARGE'; $args = 2; break;
+ case 326: $function = 'SMALL'; $args = 2; break;
+ case 327: $function = 'QUARTILE'; $args = 2; break;
+ case 328: $function = 'PERCENTILE'; $args = 2; break;
+ case 331: $function = 'TRIMMEAN'; $args = 2; break;
+ case 332: $function = 'TINV'; $args = 2; break;
+ case 337: $function = 'POWER'; $args = 2; break;
+ case 342: $function = 'RADIANS'; $args = 1; break;
+ case 343: $function = 'DEGREES'; $args = 1; break;
+ case 346: $function = 'COUNTIF'; $args = 2; break;
+ case 347: $function = 'COUNTBLANK'; $args = 1; break;
+ case 350: $function = 'ISPMT'; $args = 4; break;
+ case 351: $function = 'DATEDIF'; $args = 3; break;
+ case 352: $function = 'DATESTRING'; $args = 1; break;
+ case 353: $function = 'NUMBERSTRING'; $args = 2; break;
+ case 360: $function = 'PHONETIC'; $args = 1; break;
+ default:
+ throw new Exception('Unrecognized function in formula');
+ break;
+ }
+ $data = array('function' => $function, 'args' => $args);
+ break;
+ case 0x22: // function with variable number of arguments
+ case 0x42: // function with variable number of arguments
+ case 0x62: // function with variable number of arguments
+ $name = 'tFuncV';
+ $size = 4;
+ // offset: 1; size: 1; number of arguments
+ $args = ord($formulaData[1]);
+ // offset: 2: size: 2; index to built-in sheet function
+ switch ($this->_GetInt2d($formulaData, 2)) {
+ case 0: $function = 'COUNT'; break;
+ case 1: $function = 'IF'; break;
+ case 4: $function = 'SUM'; break;
+ case 5: $function = 'AVERAGE'; break;
+ case 6: $function = 'MIN'; break;
+ case 7: $function = 'MAX'; break;
+ case 8: $function = 'ROW'; break;
+ case 9: $function = 'COLUMN'; break;
+ case 11: $function = 'NPV'; break;
+ case 12: $function = 'STDEV'; break;
+ case 13: $function = 'DOLLAR'; break;
+ case 14: $function = 'FIXED'; break;
+ case 28: $function = 'LOOKUP'; break;
+ case 29: $function = 'INDEX'; break;
+ case 36: $function = 'AND'; break;
+ case 37: $function = 'OR'; break;
+ case 46: $function = 'VAR'; break;
+ case 49: $function = 'LINEST'; break;
+ case 50: $function = 'TREND'; break;
+ case 51: $function = 'LOGEST'; break;
+ case 52: $function = 'GROWTH'; break;
+ case 56: $function = 'PV'; break;
+ case 57: $function = 'FV'; break;
+ case 58: $function = 'NPER'; break;
+ case 59: $function = 'PMT'; break;
+ case 60: $function = 'RATE'; break;
+ case 62: $function = 'IRR'; break;
+ case 64: $function = 'MATCH'; break;
+ case 70: $function = 'WEEKDAY'; break;
+ case 78: $function = 'OFFSET'; break;
+ case 82: $function = 'SEARCH'; break;
+ case 100: $function = 'CHOOSE'; break;
+ case 101: $function = 'HLOOKUP'; break;
+ case 102: $function = 'VLOOKUP'; break;
+ case 109: $function = 'LOG'; break;
+ case 115: $function = 'LEFT'; break;
+ case 116: $function = 'RIGHT'; break;
+ case 120: $function = 'SUBSTITUTE'; break;
+ case 124: $function = 'FIND'; break;
+ case 125: $function = 'CELL'; break;
+ case 144: $function = 'DDB'; break;
+ case 148: $function = 'INDIRECT'; break;
+ case 167: $function = 'IPMT'; break;
+ case 168: $function = 'PPMT'; break;
+ case 169: $function = 'COUNTA'; break;
+ case 183: $function = 'PRODUCT'; break;
+ case 193: $function = 'STDEVP'; break;
+ case 194: $function = 'VARP'; break;
+ case 197: $function = 'TRUNC'; break;
+ case 204: $function = 'USDOLLAR'; break;
+ case 205: $function = 'FINDB'; break;
+ case 206: $function = 'SEARCHB'; break;
+ case 208: $function = 'LEFTB'; break;
+ case 209: $function = 'RIGHTB'; break;
+ case 216: $function = 'RANK'; break;
+ case 219: $function = 'ADDRESS'; break;
+ case 220: $function = 'DAYS360'; break;
+ case 222: $function = 'VDB'; break;
+ case 227: $function = 'MEDIAN'; break;
+ case 228: $function = 'SUMPRODUCT'; break;
+ case 247: $function = 'DB'; break;
+ case 269: $function = 'AVEDEV'; break;
+ case 270: $function = 'BETADIST'; break;
+ case 272: $function = 'BETAINV'; break;
+ case 317: $function = 'PROB'; break;
+ case 318: $function = 'DEVSQ'; break;
+ case 319: $function = 'GEOMEAN'; break;
+ case 320: $function = 'HARMEAN'; break;
+ case 321: $function = 'SUMSQ'; break;
+ case 322: $function = 'KURT'; break;
+ case 323: $function = 'SKEW'; break;
+ case 324: $function = 'ZTEST'; break;
+ case 329: $function = 'PERCENTRANK'; break;
+ case 330: $function = 'MODE'; break;
+ case 336: $function = 'CONCATENATE'; break;
+ case 344: $function = 'SUBTOTAL'; break;
+ case 345: $function = 'SUMIF'; break;
+ case 354: $function = 'ROMAN'; break;
+ case 358: $function = 'GETPIVOTDATA'; break;
+ case 359: $function = 'HYPERLINK'; break;
+ case 361: $function = 'AVERAGEA'; break;
+ case 362: $function = 'MAXA'; break;
+ case 363: $function = 'MINA'; break;
+ case 364: $function = 'STDEVPA'; break;
+ case 365: $function = 'VARPA'; break;
+ case 366: $function = 'STDEVA'; break;
+ case 367: $function = 'VARA'; break;
+ default:
+ throw new Exception('Unrecognized function in formula');
+ break;
+ }
+ $data = array('function' => $function, 'args' => $args);
+ break;
+ case 0x23: // index to defined name
+ case 0x43:
+ $name = 'tName';
+ $size = 5;
+ // offset: 1; size: 2; one-based index to definedname record
+ $definedNameIndex = $this->_GetInt2d($formulaData, 1) - 1;
+ // offset: 2; size: 2; not used
+ $data = $this->_definedname[$definedNameIndex]['name'];
+ break;
+ case 0x24: // single cell reference e.g. A5
+ case 0x44:
+ case 0x64:
+ $name = 'tRef';
+ $size = 5;
+ $data = $this->_readBIFF8CellAddress(substr($formulaData, 1, 4));
+ break;
+ case 0x25: // cell range reference to cells in the same sheet
+ case 0x45:
+ case 0x65:
+ $name = 'tArea';
+ $size = 9;
+ $data = $this->_readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
+ break;
+ case 0x26:
+ case 0x46:
+ $name = 'tMemArea';
+ // offset: 1; size: 4; not used
+ // offset: 5; size: 2; size of the following subexpression
+ $subSize = $this->_GetInt2d($formulaData, 5);
+ $size = 7 + $subSize;
+ $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize));
+ break;
+ case 0x47:
+ $name = 'tMemErr';
+ // offset: 1; size: 4; not used
+ // offset: 5; size: 2; size of the following subexpression
+ $subSize = $this->_GetInt2d($formulaData, 5);
+ $size = 7 + $subSize;
+ $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize));
+ break;
+ case 0x29:
+ case 0x49:
+ $name = 'tMemFunc';
+ // offset: 1; size: 2; size of the following subexpression
+ $subSize = $this->_GetInt2d($formulaData, 1);
+ $size = 3 + $subSize;
+ $data = $this->_getFormulaFromData(substr($formulaData, 3, $subSize));
+ break;
+
+ case 0x2C: // Relative reference, used in shared formulas and some other places
+ case 0x4C:
+ case 0x6C:
+ $name = 'tRefN';
+ $size = 5;
+ $data = $this->_readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
+ break;
+
+ case 0x2D:
+ case 0x4D:
+ case 0x6D:
+ $name = 'tAreaN';
+ $size = 9;
+ $data = $this->_readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
+ break;
+
+ case 0x3A: // 3d reference to cell
+ case 0x5A:
+ $name = 'tRef3d';
+ $size = 7;
+ // offset: 1; size: 2; index to REF entry
+ $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1));
+ // offset: 3; size: 4; cell address
+ $cellAddress = $this->_readBIFF8CellAddress(substr($formulaData, 3, 4));
+
+ $data = "$sheetRange!$cellAddress";
+
+ break;
+ case 0x3B: // 3d reference to cell range
+ case 0x5B:
+ $name = 'tArea3d';
+ $size = 11;
+ // offset: 1; size: 2; index to REF entry
+ $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1));
+ // offset: 3; size: 8; cell address
+ $cellRangeAddress = $this->_readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
+
+ $data = "$sheetRange!$cellRangeAddress";
+
+ break;
+ // case 0x39: // don't know how to deal with
+ default:
+ throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
+ break;
+ }
+
+ return array(
+ 'id' => $id,
+ 'name' => $name,
+ 'size' => $size,
+ 'data' => $data,
+ );
+ }
+
+ /**
+ * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
+ * section 3.3.4
+ *
+ * @param string $cellAddressStructure
+ * @return string
+ */
+ private function _readBIFF8CellAddress($cellAddressStructure)
+ {
+ // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+ $row = $this->_GetInt2d($cellAddressStructure, 0) + 1;
+
+ // offset: 2; size: 2; index to column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $column = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($cellAddressStructure, 2));
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $column = '$' . $column;
+ }
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $row = '$' . $row;
+ }
+
+ return $column . $row;
+ }
+
+ /**
+ * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+ * to indicate offsets from a base cell
+ * section 3.3.4
+ *
+ * @param string $cellAddressStructure
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ * @return string
+ */
+ private function _readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
+ {
+ list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell);
+ $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1;
+
+ // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+ $rowIndex = $this->_GetInt2d($cellAddressStructure, 0);
+ $row = $this->_GetInt2d($cellAddressStructure, 0) + 1;
+
+ // offset: 2; size: 2; index to column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $colIndex = 0x00FF & $this->_GetInt2d($cellAddressStructure, 2);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $column = PHPExcel_Cell::stringFromColumnIndex($colIndex);
+ $column = '$' . $column;
+ } else {
+ $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256;
+ $column = PHPExcel_Cell::stringFromColumnIndex($baseCol + $colIndex);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) {
+ $row = '$' . $row;
+ } else {
+ $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
+ $row = $baseRow + $rowIndex;
+ }
+
+ return $column . $row;
+ }
+
+ /**
+ * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
+ * always fixed range
+ * section 2.5.14
+ *
+ * @param string $subData
+ * @return string
+ * @throws Exception
+ */
+ private function _readBIFF5CellRangeAddressFixed($subData)
+ {
+ // offset: 0; size: 2; index to first row
+ $fr = $this->_GetInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row
+ $lr = $this->_GetInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 1; index to first column
+ $fc = ord($subData{4});
+
+ // offset: 5; size: 1; index to last column
+ $lc = ord($subData{5});
+
+ // check values
+ if ($fr > $lr || $fc > $lc) {
+ throw new Exception('Not a cell range address');
+ }
+
+ // column index to letter
+ $fc = PHPExcel_Cell::stringFromColumnIndex($fc);
+ $lc = PHPExcel_Cell::stringFromColumnIndex($lc);
+
+ if ($fr == $lr and $fc == $lc) {
+ return "$fc$fr";
+ }
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
+ * always fixed range
+ * section 2.5.14
+ *
+ * @param string $subData
+ * @return string
+ * @throws Exception
+ */
+ private function _readBIFF8CellRangeAddressFixed($subData)
+ {
+ // offset: 0; size: 2; index to first row
+ $fr = $this->_GetInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row
+ $lr = $this->_GetInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 2; index to first column
+ $fc = $this->_GetInt2d($subData, 4);
+
+ // offset: 6; size: 2; index to last column
+ $lc = $this->_GetInt2d($subData, 6);
+
+ // check values
+ if ($fr > $lr || $fc > $lc) {
+ throw new Exception('Not a cell range address');
+ }
+
+ // column index to letter
+ $fc = PHPExcel_Cell::stringFromColumnIndex($fc);
+ $lc = PHPExcel_Cell::stringFromColumnIndex($lc);
+
+ if ($fr == $lr and $fc == $lc) {
+ return "$fc$fr";
+ }
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
+ * there are flags indicating whether column/row index is relative
+ * section 3.3.4
+ *
+ * @param string $subData
+ * @return string
+ */
+ private function _readBIFF8CellRangeAddress($subData)
+ {
+ // todo: if cell range is just a single cell, should this funciton
+ // not just return e.g. 'A1' and not 'A1:A1' ?
+
+ // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
+ $fr = $this->_GetInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
+ $lr = $this->_GetInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 2; index to first column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $fc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 4));
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 4))) {
+ $fc = '$' . $fc;
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 4))) {
+ $fr = '$' . $fr;
+ }
+
+ // offset: 6; size: 2; index to last column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $lc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 6));
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 6))) {
+ $lc = '$' . $lc;
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 6))) {
+ $lr = '$' . $lr;
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+ * to indicate offsets from a base cell
+ * section 3.3.4
+ *
+ * @param string $subData
+ * @param string $baseCell Base cell
+ * @return string Cell range address
+ */
+ private function _readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
+ {
+ list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell);
+ $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1;
+
+ // TODO: if cell range is just a single cell, should this funciton
+ // not just return e.g. 'A1' and not 'A1:A1' ?
+
+ // offset: 0; size: 2; first row
+ $frIndex = $this->_GetInt2d($subData, 0); // adjust below
+
+ // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
+ $lrIndex = $this->_GetInt2d($subData, 2); // adjust below
+
+ // offset: 4; size: 2; first column with relative/absolute flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $fcIndex = 0x00FF & $this->_GetInt2d($subData, 4);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 4))) {
+ // absolute column index
+ $fc = PHPExcel_Cell::stringFromColumnIndex($fcIndex);
+ $fc = '$' . $fc;
+ } else {
+ // column offset
+ $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256;
+ $fc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $fcIndex);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 4))) {
+ // absolute row index
+ $fr = $frIndex + 1;
+ $fr = '$' . $fr;
+ } else {
+ // row offset
+ $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
+ $fr = $baseRow + $frIndex;
+ }
+
+ // offset: 6; size: 2; last column with relative/absolute flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $lcIndex = 0x00FF & $this->_GetInt2d($subData, 6);
+ $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
+ $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & $this->_GetInt2d($subData, 6))) {
+ // absolute column index
+ $lc = PHPExcel_Cell::stringFromColumnIndex($lcIndex);
+ $lc = '$' . $lc;
+ } else {
+ // column offset
+ $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256;
+ $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & $this->_GetInt2d($subData, 6))) {
+ // absolute row index
+ $lr = $lrIndex + 1;
+ $lr = '$' . $lr;
+ } else {
+ // row offset
+ $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
+ $lr = $baseRow + $lrIndex;
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Read BIFF8 cell range address list
+ * section 2.5.15
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readBIFF8CellRangeAddressList($subData)
+ {
+ $cellRangeAddresses = array();
+
+ // offset: 0; size: 2; number of the following cell range addresses
+ $nm = $this->_GetInt2d($subData, 0);
+
+ $offset = 2;
+ // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
+ for ($i = 0; $i < $nm; ++$i) {
+ $cellRangeAddresses[] = $this->_readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
+ $offset += 8;
+ }
+
+ return array(
+ 'size' => 2 + 8 * $nm,
+ 'cellRangeAddresses' => $cellRangeAddresses,
+ );
+ }
+
+ /**
+ * Read BIFF5 cell range address list
+ * section 2.5.15
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readBIFF5CellRangeAddressList($subData)
+ {
+ $cellRangeAddresses = array();
+
+ // offset: 0; size: 2; number of the following cell range addresses
+ $nm = $this->_GetInt2d($subData, 0);
+
+ $offset = 2;
+ // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
+ for ($i = 0; $i < $nm; ++$i) {
+ $cellRangeAddresses[] = $this->_readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
+ $offset += 6;
+ }
+
+ return array(
+ 'size' => 2 + 6 * $nm,
+ 'cellRangeAddresses' => $cellRangeAddresses,
+ );
+ }
+
+ /**
+ * Get a sheet range like Sheet1:Sheet3 from REF index
+ * Note: If there is only one sheet in the range, one gets e.g Sheet1
+ * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
+ * in which case an exception is thrown
+ *
+ * @param int $index
+ * @return string|false
+ * @throws Exception
+ */
+ private function _readSheetRangeByRefIndex($index)
+ {
+ if (isset($this->_ref[$index])) {
+
+ $type = $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type'];
+
+ switch ($type) {
+ case 'internal':
+ // check if we have a deleted 3d reference
+ if ($this->_ref[$index]['firstSheetIndex'] == 0xFFFF or $this->_ref[$index]['lastSheetIndex'] == 0xFFFF) {
+ throw new Exception('Deleted sheet reference');
+ }
+
+ // we have normal sheet range (collapsed or uncollapsed)
+ $firstSheetName = $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name'];
+ $lastSheetName = $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name'];
+
+ if ($firstSheetName == $lastSheetName) {
+ // collapsed sheet range
+ $sheetRange = $firstSheetName;
+ } else {
+ $sheetRange = "$firstSheetName:$lastSheetName";
+ }
+
+ // escape the single-quotes
+ $sheetRange = str_replace("'", "''", $sheetRange);
+
+ // if there are special characters, we need to enclose the range in single-quotes
+ // todo: check if we have identified the whole set of special characters
+ // it seems that the following characters are not accepted for sheet names
+ // and we may assume that they are not present: []*/:\?
+ if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) {
+ $sheetRange = "'$sheetRange'";
+ }
+
+ return $sheetRange;
+ break;
+
+ default:
+ // TODO: external sheet support
+ throw new Exception('Excel5 reader only supports internal sheets in fomulas');
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * read BIFF8 constant value array from array data
+ * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40}
+ * section 2.5.8
+ *
+ * @param string $arrayData
+ * @return array
+ */
+ private function _readBIFF8ConstantArray($arrayData)
+ {
+ // offset: 0; size: 1; number of columns decreased by 1
+ $nc = ord($arrayData[0]);
+
+ // offset: 1; size: 2; number of rows decreased by 1
+ $nr = $this->_GetInt2d($arrayData, 1);
+ $size = 3; // initialize
+ $arrayData = substr($arrayData, 3);
+
+ // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
+ $matrixChunks = array();
+ for ($r = 1; $r <= $nr + 1; ++$r) {
+ $items = array();
+ for ($c = 1; $c <= $nc + 1; ++$c) {
+ $constant = $this->_readBIFF8Constant($arrayData);
+ $items[] = $constant['value'];
+ $arrayData = substr($arrayData, $constant['size']);
+ $size += $constant['size'];
+ }
+ $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
+ }
+ $matrix = '{' . implode(';', $matrixChunks) . '}';
+
+ return array(
+ 'value' => $matrix,
+ 'size' => $size,
+ );
+ }
+
+ /**
+ * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
+ * section 2.5.7
+ * returns e.g. array('value' => '5', 'size' => 9)
+ *
+ * @param string $valueData
+ * @return array
+ */
+ private function _readBIFF8Constant($valueData)
+ {
+ // offset: 0; size: 1; identifier for type of constant
+ $identifier = ord($valueData[0]);
+
+ switch ($identifier) {
+ case 0x00: // empty constant (what is this?)
+ $value = '';
+ $size = 9;
+ break;
+ case 0x01: // number
+ // offset: 1; size: 8; IEEE 754 floating-point value
+ $value = $this->_extractNumber(substr($valueData, 1, 8));
+ $size = 9;
+ break;
+ case 0x02: // string value
+ // offset: 1; size: var; Unicode string, 16-bit string length
+ $string = $this->_readUnicodeStringLong(substr($valueData, 1));
+ $value = '"' . $string['value'] . '"';
+ $size = 1 + $string['size'];
+ break;
+ case 0x04: // boolean
+ // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
+ if (ord($valueData[1])) {
+ $value = 'TRUE';
+ } else {
+ $value = 'FALSE';
+ }
+ $size = 9;
+ break;
+ case 0x10: // error code
+ // offset: 1; size: 1; error code
+ $value = $this->_mapErrorCode(ord($valueData[1]));
+ $size = 9;
+ break;
+ }
+ return array(
+ 'value' => $value,
+ 'size' => $size,
+ );
+ }
+
+ /**
+ * Extract RGB color
+ * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4
+ *
+ * @param string $rgb Encoded RGB value (4 bytes)
+ * @return array
+ */
+ private function _readRGB($rgb)
+ {
+ // offset: 0; size 1; Red component
+ $r = ord($rgb{0});
+
+ // offset: 1; size: 1; Green component
+ $g = ord($rgb{1});
+
+ // offset: 2; size: 1; Blue component
+ $b = ord($rgb{2});
+
+ // HEX notation, e.g. 'FF00FC'
+ $rgb = sprintf('%02X', $r) . sprintf('%02X', $g) . sprintf('%02X', $b);
+
+ return array('rgb' => $rgb);
+ }
+
+ /**
+ * Read byte string (8-bit string length)
+ * OpenOffice documentation: 2.5.2
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readByteStringShort($subData)
+ {
+ // offset: 0; size: 1; length of the string (character count)
+ $ln = ord($subData[0]);
+
+ // offset: 1: size: var; character array (8-bit characters)
+ $value = $this->_decodeCodepage(substr($subData, 1, $ln));
+
+ return array(
+ 'value' => $value,
+ 'size' => 1 + $ln, // size in bytes of data structure
+ );
+ }
+
+ /**
+ * Read byte string (16-bit string length)
+ * OpenOffice documentation: 2.5.2
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readByteStringLong($subData)
+ {
+ // offset: 0; size: 2; length of the string (character count)
+ $ln = $this->_GetInt2d($subData, 0);
+
+ // offset: 2: size: var; character array (8-bit characters)
+ $value = $this->_decodeCodepage(substr($subData, 2));
+
+ //return $string;
+ return array(
+ 'value' => $value,
+ 'size' => 2 + $ln, // size in bytes of data structure
+ );
+ }
+
+ /**
+ * Extracts an Excel Unicode short string (8-bit string length)
+ * OpenOffice documentation: 2.5.3
+ * function will automatically find out where the Unicode string ends.
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readUnicodeStringShort($subData)
+ {
+ $value = '';
+
+ // offset: 0: size: 1; length of the string (character count)
+ $characterCount = ord($subData[0]);
+
+ $string = $this->_readUnicodeString(substr($subData, 1), $characterCount);
+
+ // add 1 for the string length
+ $string['size'] += 1;
+
+ return $string;
+ }
+
+ /**
+ * Extracts an Excel Unicode long string (16-bit string length)
+ * OpenOffice documentation: 2.5.3
+ * this function is under construction, needs to support rich text, and Asian phonetic settings
+ *
+ * @param string $subData
+ * @return array
+ */
+ private function _readUnicodeStringLong($subData)
+ {
+ $value = '';
+
+ // offset: 0: size: 2; length of the string (character count)
+ $characterCount = $this->_GetInt2d($subData, 0);
+
+ $string = $this->_readUnicodeString(substr($subData, 2), $characterCount);
+
+ // add 2 for the string length
+ $string['size'] += 2;
+
+ return $string;
+ }
+
+ /**
+ * Read Unicode string with no string length field, but with known character count
+ * this function is under construction, needs to support rich text, and Asian phonetic settings
+ * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3
+ *
+ * @param string $subData
+ * @param int $characterCount
+ * @return array
+ */
+ private function _readUnicodeString($subData, $characterCount)
+ {
+ $value = '';
+
+ // offset: 0: size: 1; option flags
+
+ // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
+ $isCompressed = !((0x01 & ord($subData[0])) >> 0);
+
+ // bit: 2; mask: 0x04; Asian phonetic settings
+ $hasAsian = (0x04) & ord($subData[0]) >> 2;
+
+ // bit: 3; mask: 0x08; Rich-Text settings
+ $hasRichText = (0x08) & ord($subData[0]) >> 3;
+
+ // offset: 1: size: var; character array
+ // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
+ // needs to be fixed
+ $value = $this->_encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
+
+ return array(
+ 'value' => $value,
+ 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
+ );
+ }
+
+ /**
+ * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
+ * Example: hello"world --> "hello""world"
+ *
+ * @param string $value UTF-8 encoded string
+ * @return string
+ */
+ private function _UTF8toExcelDoubleQuoted($value)
+ {
+ return '"' . str_replace('"', '""', $value) . '"';
+ }
+
+ /**
+ * Reads first 8 bytes of a string and return IEEE 754 float
+ *
+ * @param string $data Binary string that is at least 8 bytes long
+ * @return float
+ */
+ private function _extractNumber($data)
+ {
+ $rknumhigh = $this->_GetInt4d($data, 4);
+ $rknumlow = $this->_GetInt4d($data, 0);
+ $sign = ($rknumhigh & 0x80000000) >> 31;
+ $exp = ($rknumhigh & 0x7ff00000) >> 20;
+ $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
+ $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
+ $mantissalow2 = ($rknumlow & 0x7fffffff);
+ $value = $mantissa / pow( 2 , (20 - ($exp - 1023)));
+
+ if ($mantissalow1 != 0) {
+ $value += 1 / pow (2 , (21 - ($exp - 1023)));
+ }
+
+ $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023)));
+ if ($sign) {
+ $value = -1 * $value;
+ }
+
+ return $value;
+ }
+
+ private function _GetIEEE754($rknum)
+ {
+ if (($rknum & 0x02) != 0) {
+ $value = $rknum >> 2;
+ }
+ else {
+ // changes by mmp, info on IEEE754 encoding from
+ // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
+ // The RK format calls for using only the most significant 30 bits
+ // of the 64 bit floating point value. The other 34 bits are assumed
+ // to be 0 so we use the upper 30 bits of $rknum as follows...
+ $sign = ($rknum & 0x80000000) >> 31;
+ $exp = ($rknum & 0x7ff00000) >> 20;
+ $mantissa = (0x100000 | ($rknum & 0x000ffffc));
+ $value = $mantissa / pow( 2 , (20- ($exp - 1023)));
+ if ($sign) {
+ $value = -1 * $value;
+ }
+ //end of changes by mmp
+ }
+ if (($rknum & 0x01) != 0) {
+ $value /= 100;
+ }
+ return $value;
+ }
+
+ /**
+ * Get UTF-8 string from (compressed or uncompressed) UTF-16 string
+ *
+ * @param string $string
+ * @param bool $compressed
+ * @return string
+ */
+ private function _encodeUTF16($string, $compressed = '')
+ {
+ if ($compressed) {
+ $string = $this->_uncompressByteString($string);
+ }
+
+ $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', 'UTF-16LE');
+
+ return $result;
+ }
+
+ /**
+ * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
+ *
+ * @param string $string
+ * @return string
+ */
+ private function _uncompressByteString($string)
+ {
+ $uncompressedString = '';
+ for ($i = 0; $i < strlen($string); ++$i) {
+ $uncompressedString .= $string[$i] . "\0";
+ }
+
+ return $uncompressedString;
+ }
+
+ /**
+ * Convert string to UTF-8. Only used for BIFF5.
+ *
+ * @param string $string
+ * @return string
+ */
+ private function _decodeCodepage($string)
+ {
+ $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $this->_codepage);
+ return $result;
+ }
+
+ /**
+ * Read 16-bit unsigned integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt2d($data, $pos)
+ {
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
+ }
+
+ /**
+ * Read 32-bit signed integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt4d($data, $pos)
+ {
+ //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) |
+ // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
+
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+
+ /**
+ * Read color
+ *
+ * @param int $color Indexed color
+ * @return array RGB color value, example: array('rgb' => 'FF0000')
+ */
+ private function _readColor($color)
+ {
+ if ($color <= 0x07 || $color >= 0x40) {
+ // special built-in color
+ $color = $this->_mapBuiltInColor($color);
+ } else if (isset($this->_palette) && isset($this->_palette[$color - 8])) {
+ // palette color, color index 0x08 maps to pallete index 0
+ $color = $this->_palette[$color - 8];
+ } else {
+ // default color table
+ if ($this->_version == self::XLS_BIFF8) {
+ $color = $this->_mapColor($color);
+ } else {
+ // BIFF5
+ $color = $this->_mapColorBIFF5($color);
+ }
+ }
+
+ return $color;
+ }
+
+
+ /**
+ * Map border style
+ * OpenOffice documentation: 2.5.11
+ *
+ * @param int $index
+ * @return string
+ */
+ private function _mapBorderStyle($index)
+ {
+ switch ($index) {
+ case 0x00: return PHPExcel_Style_Border::BORDER_NONE;
+ case 0x01: return PHPExcel_Style_Border::BORDER_THIN;
+ case 0x02: return PHPExcel_Style_Border::BORDER_MEDIUM;
+ case 0x03: return PHPExcel_Style_Border::BORDER_DASHED;
+ case 0x04: return PHPExcel_Style_Border::BORDER_DOTTED;
+ case 0x05: return PHPExcel_Style_Border::BORDER_THICK;
+ case 0x06: return PHPExcel_Style_Border::BORDER_DOUBLE;
+ case 0x07: return PHPExcel_Style_Border::BORDER_HAIR;
+ case 0x08: return PHPExcel_Style_Border::BORDER_MEDIUMDASHED;
+ case 0x09: return PHPExcel_Style_Border::BORDER_DASHDOT;
+ case 0x0A: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT;
+ case 0x0B: return PHPExcel_Style_Border::BORDER_DASHDOTDOT;
+ case 0x0C: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT;
+ case 0x0D: return PHPExcel_Style_Border::BORDER_SLANTDASHDOT;
+ default: return PHPExcel_Style_Border::BORDER_NONE;
+ }
+ }
+
+ /**
+ * Get fill pattern from index
+ * OpenOffice documentation: 2.5.12
+ *
+ * @param int $index
+ * @return string
+ */
+ private function _mapFillPattern($index)
+ {
+ switch ($index) {
+ case 0x00: return PHPExcel_Style_Fill::FILL_NONE;
+ case 0x01: return PHPExcel_Style_Fill::FILL_SOLID;
+ case 0x02: return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY;
+ case 0x03: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY;
+ case 0x04: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY;
+ case 0x05: return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL;
+ case 0x06: return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL;
+ case 0x07: return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN;
+ case 0x08: return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP;
+ case 0x09: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID;
+ case 0x0A: return PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS;
+ case 0x0B: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL;
+ case 0x0C: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL;
+ case 0x0D: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN;
+ case 0x0E: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP;
+ case 0x0F: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID;
+ case 0x10: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS;
+ case 0x11: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125;
+ case 0x12: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625;
+ default: return PHPExcel_Style_Fill::FILL_NONE;
+ }
+ }
+
+ /**
+ * Map error code, e.g. '#N/A'
+ *
+ * @param int $subData
+ * @return string
+ */
+ private function _mapErrorCode($subData)
+ {
+ switch ($subData) {
+ case 0x00: return '#NULL!'; break;
+ case 0x07: return '#DIV/0!'; break;
+ case 0x0F: return '#VALUE!'; break;
+ case 0x17: return '#REF!'; break;
+ case 0x1D: return '#NAME?'; break;
+ case 0x24: return '#NUM!'; break;
+ case 0x2A: return '#N/A'; break;
+ default: return false;
+ }
+ }
+
+ /**
+ * Map built-in color to RGB value
+ *
+ * @param int $color Indexed color
+ * @return array
+ */
+ private function _mapBuiltInColor($color)
+ {
+ switch ($color) {
+ case 0x00: return array('rgb' => '000000');
+ case 0x01: return array('rgb' => 'FFFFFF');
+ case 0x02: return array('rgb' => 'FF0000');
+ case 0x03: return array('rgb' => '00FF00');
+ case 0x04: return array('rgb' => '0000FF');
+ case 0x05: return array('rgb' => 'FFFF00');
+ case 0x06: return array('rgb' => 'FF00FF');
+ case 0x07: return array('rgb' => '00FFFF');
+ case 0x40: return array('rgb' => '000000'); // system window text color
+ case 0x41: return array('rgb' => 'FFFFFF'); // system window background color
+ default: return array('rgb' => '000000');
+ }
+ }
+
+ /**
+ * Map color array from BIFF5 built-in color index
+ *
+ * @param int $subData
+ * @return array
+ */
+ private function _mapColorBIFF5($subData)
+ {
+ switch ($subData) {
+ case 0x08: return array('rgb' => '000000');
+ case 0x09: return array('rgb' => 'FFFFFF');
+ case 0x0A: return array('rgb' => 'FF0000');
+ case 0x0B: return array('rgb' => '00FF00');
+ case 0x0C: return array('rgb' => '0000FF');
+ case 0x0D: return array('rgb' => 'FFFF00');
+ case 0x0E: return array('rgb' => 'FF00FF');
+ case 0x0F: return array('rgb' => '00FFFF');
+ case 0x10: return array('rgb' => '800000');
+ case 0x11: return array('rgb' => '008000');
+ case 0x12: return array('rgb' => '000080');
+ case 0x13: return array('rgb' => '808000');
+ case 0x14: return array('rgb' => '800080');
+ case 0x15: return array('rgb' => '008080');
+ case 0x16: return array('rgb' => 'C0C0C0');
+ case 0x17: return array('rgb' => '808080');
+ case 0x18: return array('rgb' => '8080FF');
+ case 0x19: return array('rgb' => '802060');
+ case 0x1A: return array('rgb' => 'FFFFC0');
+ case 0x1B: return array('rgb' => 'A0E0F0');
+ case 0x1C: return array('rgb' => '600080');
+ case 0x1D: return array('rgb' => 'FF8080');
+ case 0x1E: return array('rgb' => '0080C0');
+ case 0x1F: return array('rgb' => 'C0C0FF');
+ case 0x20: return array('rgb' => '000080');
+ case 0x21: return array('rgb' => 'FF00FF');
+ case 0x22: return array('rgb' => 'FFFF00');
+ case 0x23: return array('rgb' => '00FFFF');
+ case 0x24: return array('rgb' => '800080');
+ case 0x25: return array('rgb' => '800000');
+ case 0x26: return array('rgb' => '008080');
+ case 0x27: return array('rgb' => '0000FF');
+ case 0x28: return array('rgb' => '00CFFF');
+ case 0x29: return array('rgb' => '69FFFF');
+ case 0x2A: return array('rgb' => 'E0FFE0');
+ case 0x2B: return array('rgb' => 'FFFF80');
+ case 0x2C: return array('rgb' => 'A6CAF0');
+ case 0x2D: return array('rgb' => 'DD9CB3');
+ case 0x2E: return array('rgb' => 'B38FEE');
+ case 0x2F: return array('rgb' => 'E3E3E3');
+ case 0x30: return array('rgb' => '2A6FF9');
+ case 0x31: return array('rgb' => '3FB8CD');
+ case 0x32: return array('rgb' => '488436');
+ case 0x33: return array('rgb' => '958C41');
+ case 0x34: return array('rgb' => '8E5E42');
+ case 0x35: return array('rgb' => 'A0627A');
+ case 0x36: return array('rgb' => '624FAC');
+ case 0x37: return array('rgb' => '969696');
+ case 0x38: return array('rgb' => '1D2FBE');
+ case 0x39: return array('rgb' => '286676');
+ case 0x3A: return array('rgb' => '004500');
+ case 0x3B: return array('rgb' => '453E01');
+ case 0x3C: return array('rgb' => '6A2813');
+ case 0x3D: return array('rgb' => '85396A');
+ case 0x3E: return array('rgb' => '4A3285');
+ case 0x3F: return array('rgb' => '424242');
+ default: return array('rgb' => '000000');
+ }
+ }
+
+ /**
+ * Map color array from BIFF8 built-in color index
+ *
+ * @param int $subData
+ * @return array
+ */
+ private function _mapColor($subData)
+ {
+ switch ($subData) {
+ case 0x08: return array('rgb' => '000000');
+ case 0x09: return array('rgb' => 'FFFFFF');
+ case 0x0A: return array('rgb' => 'FF0000');
+ case 0x0B: return array('rgb' => '00FF00');
+ case 0x0C: return array('rgb' => '0000FF');
+ case 0x0D: return array('rgb' => 'FFFF00');
+ case 0x0E: return array('rgb' => 'FF00FF');
+ case 0x0F: return array('rgb' => '00FFFF');
+ case 0x10: return array('rgb' => '800000');
+ case 0x11: return array('rgb' => '008000');
+ case 0x12: return array('rgb' => '000080');
+ case 0x13: return array('rgb' => '808000');
+ case 0x14: return array('rgb' => '800080');
+ case 0x15: return array('rgb' => '008080');
+ case 0x16: return array('rgb' => 'C0C0C0');
+ case 0x17: return array('rgb' => '808080');
+ case 0x18: return array('rgb' => '9999FF');
+ case 0x19: return array('rgb' => '993366');
+ case 0x1A: return array('rgb' => 'FFFFCC');
+ case 0x1B: return array('rgb' => 'CCFFFF');
+ case 0x1C: return array('rgb' => '660066');
+ case 0x1D: return array('rgb' => 'FF8080');
+ case 0x1E: return array('rgb' => '0066CC');
+ case 0x1F: return array('rgb' => 'CCCCFF');
+ case 0x20: return array('rgb' => '000080');
+ case 0x21: return array('rgb' => 'FF00FF');
+ case 0x22: return array('rgb' => 'FFFF00');
+ case 0x23: return array('rgb' => '00FFFF');
+ case 0x24: return array('rgb' => '800080');
+ case 0x25: return array('rgb' => '800000');
+ case 0x26: return array('rgb' => '008080');
+ case 0x27: return array('rgb' => '0000FF');
+ case 0x28: return array('rgb' => '00CCFF');
+ case 0x29: return array('rgb' => 'CCFFFF');
+ case 0x2A: return array('rgb' => 'CCFFCC');
+ case 0x2B: return array('rgb' => 'FFFF99');
+ case 0x2C: return array('rgb' => '99CCFF');
+ case 0x2D: return array('rgb' => 'FF99CC');
+ case 0x2E: return array('rgb' => 'CC99FF');
+ case 0x2F: return array('rgb' => 'FFCC99');
+ case 0x30: return array('rgb' => '3366FF');
+ case 0x31: return array('rgb' => '33CCCC');
+ case 0x32: return array('rgb' => '99CC00');
+ case 0x33: return array('rgb' => 'FFCC00');
+ case 0x34: return array('rgb' => 'FF9900');
+ case 0x35: return array('rgb' => 'FF6600');
+ case 0x36: return array('rgb' => '666699');
+ case 0x37: return array('rgb' => '969696');
+ case 0x38: return array('rgb' => '003366');
+ case 0x39: return array('rgb' => '339966');
+ case 0x3A: return array('rgb' => '003300');
+ case 0x3B: return array('rgb' => '333300');
+ case 0x3C: return array('rgb' => '993300');
+ case 0x3D: return array('rgb' => '993366');
+ case 0x3E: return array('rgb' => '333399');
+ case 0x3F: return array('rgb' => '333333');
+ default: return array('rgb' => '000000');
+ }
+ }
+
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel5/Escher.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel5/Escher.php
new file mode 100644
index 0000000..fd9f138
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/Excel5/Escher.php
@@ -0,0 +1,709 @@
+_object = $object;
+ }
+
+ /**
+ * Load Escher stream data. May be a partial Escher stream.
+ *
+ * @param string $data
+ */
+ public function load($data)
+ {
+ $this->_data = $data;
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->_dataSize = strlen($this->_data);
+
+ $this->_pos = 0;
+
+ // Parse Escher stream
+ while ($this->_pos < $this->_dataSize) {
+
+
+ // offset: 2; size: 2: Record Type
+ $fbt = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ switch ($fbt) {
+ case self::DGGCONTAINER: $this->_readDggContainer(); break;
+ case self::DGG: $this->_readDgg(); break;
+ case self::BSTORECONTAINER: $this->_readBstoreContainer(); break;
+ case self::BSE: $this->_readBSE(); break;
+ case self::BLIPJPEG: $this->_readBlipJPEG(); break;
+ case self::BLIPPNG: $this->_readBlipPNG(); break;
+ case self::OPT: $this->_readOPT(); break;
+ case self::TERTIARYOPT: $this->_readTertiaryOPT(); break;
+ case self::SPLITMENUCOLORS: $this->_readSplitMenuColors(); break;
+ case self::DGCONTAINER: $this->_readDgContainer(); break;
+ case self::DG: $this->_readDg(); break;
+ case self::SPGRCONTAINER: $this->_readSpgrContainer(); break;
+ case self::SPCONTAINER: $this->_readSpContainer(); break;
+ case self::SPGR: $this->_readSpgr(); break;
+ case self::SP: $this->_readSp(); break;
+ case self::CLIENTTEXTBOX: $this->_readClientTextbox(); break;
+ case self::CLIENTANCHOR: $this->_readClientAnchor(); break;
+ case self::CLIENTDATA: $this->_readClientData(); break;
+ default: $this->_readDefault(); break;
+ }
+ }
+
+ return $this->_object;
+ }
+
+ /**
+ * Read a generic record
+ */
+ private function _readDefault()
+ {
+ // offset 0; size: 2; recVer and recInstance
+ $verInstance = $this->_GetInt2d($this->_data, $this->_pos);
+
+ // offset: 2; size: 2: Record Type
+ $fbt = $this->_GetInt2d($this->_data, $this->_pos + 2);
+
+ // bit: 0-3; mask: 0x000F; recVer
+ $recVer = (0x000F & $verInstance) >> 0;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read DggContainer record (Drawing Group Container)
+ */
+ private function _readDggContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $dggContainer = new PHPExcel_Shared_Escher_DggContainer();
+ $this->_object->setDggContainer($dggContainer);
+ $reader = new PHPExcel_Reader_Excel5_Escher($dggContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read Dgg record (Drawing Group)
+ */
+ private function _readDgg()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read BstoreContainer record (Blip Store Container)
+ */
+ private function _readBstoreContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $bstoreContainer = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
+ $this->_object->setBstoreContainer($bstoreContainer);
+ $reader = new PHPExcel_Reader_Excel5_Escher($bstoreContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read BSE record
+ */
+ private function _readBSE()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // add BSE to BstoreContainer
+ $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
+ $this->_object->addBSE($BSE);
+
+ $BSE->setBLIPType($recInstance);
+
+ // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
+ $btWin32 = ord($recordData[0]);
+
+ // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
+ $btMacOS = ord($recordData[1]);
+
+ // offset: 2; size: 16; MD4 digest
+ $rgbUid = substr($recordData, 2, 16);
+
+ // offset: 18; size: 2; tag
+ $tag = $this->_GetInt2d($recordData, 18);
+
+ // offset: 20; size: 4; size of BLIP in bytes
+ $size = $this->_GetInt4d($recordData, 20);
+
+ // offset: 24; size: 4; number of references to this BLIP
+ $cRef = $this->_GetInt4d($recordData, 24);
+
+ // offset: 28; size: 4; MSOFO file offset
+ $foDelay = $this->_GetInt4d($recordData, 28);
+
+ // offset: 32; size: 1; unused1
+ $unused1 = ord($recordData{32});
+
+ // offset: 33; size: 1; size of nameData in bytes (including null terminator)
+ $cbName = ord($recordData{33});
+
+ // offset: 34; size: 1; unused2
+ $unused2 = ord($recordData{34});
+
+ // offset: 35; size: 1; unused3
+ $unused3 = ord($recordData{35});
+
+ // offset: 36; size: $cbName; nameData
+ $nameData = substr($recordData, 36, $cbName);
+
+ // offset: 36 + $cbName, size: var; the BLIP data
+ $blipData = substr($recordData, 36 + $cbName);
+
+ // record is a container, read contents
+ $reader = new PHPExcel_Reader_Excel5_Escher($BSE);
+ $reader->load($blipData);
+ }
+
+ /**
+ * Read BlipJPEG record. Holds raw JPEG image data
+ */
+ private function _readBlipJPEG()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ $pos = 0;
+
+ // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+ $rgbUid1 = substr($recordData, 0, 16);
+ $pos += 16;
+
+ // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+ if (in_array($recInstance, array(0x046B, 0x06E3))) {
+ $rgbUid2 = substr($recordData, 16, 16);
+ $pos += 16;
+ }
+
+ // offset: var; size: 1; tag
+ $tag = ord($recordData{$pos});
+ $pos += 1;
+
+ // offset: var; size: var; the raw image data
+ $data = substr($recordData, $pos);
+
+ $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
+ $blip->setData($data);
+
+ $this->_object->setBlip($blip);
+ }
+
+ /**
+ * Read BlipPNG record. Holds raw PNG image data
+ */
+ private function _readBlipPNG()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ $pos = 0;
+
+ // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+ $rgbUid1 = substr($recordData, 0, 16);
+ $pos += 16;
+
+ // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+ if ($recInstance == 0x06E1) {
+ $rgbUid2 = substr($recordData, 16, 16);
+ $pos += 16;
+ }
+
+ // offset: var; size: 1; tag
+ $tag = ord($recordData{$pos});
+ $pos += 1;
+
+ // offset: var; size: var; the raw image data
+ $data = substr($recordData, $pos);
+
+ $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
+ $blip->setData($data);
+
+ $this->_object->setBlip($blip);
+ }
+
+ /**
+ * Read OPT record. This record may occur within DggContainer record or SpContainer
+ */
+ private function _readOPT()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ $this->_readOfficeArtRGFOPTE($recordData, $recInstance);
+ }
+
+ /**
+ * Read TertiaryOPT record
+ */
+ private function _readTertiaryOPT()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read SplitMenuColors record
+ */
+ private function _readSplitMenuColors()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read DgContainer record (Drawing Container)
+ */
+ private function _readDgContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $dgContainer = new PHPExcel_Shared_Escher_DgContainer();
+ $this->_object->setDgContainer($dgContainer);
+ $reader = new PHPExcel_Reader_Excel5_Escher($dgContainer);
+ $escher = $reader->load($recordData);
+ }
+
+ /**
+ * Read Dg record (Drawing)
+ */
+ private function _readDg()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read SpgrContainer record (Shape Group Container)
+ */
+ private function _readSpgrContainer()
+ {
+ // context is either context DgContainer or SpgrContainer
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $spgrContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer();
+
+ if ($this->_object instanceof PHPExcel_Shared_Escher_DgContainer) {
+ // DgContainer
+ $this->_object->setSpgrContainer($spgrContainer);
+ } else {
+ // SpgrContainer
+ $this->_object->addChild($spgrContainer);
+ }
+
+ $reader = new PHPExcel_Reader_Excel5_Escher($spgrContainer);
+ $escher = $reader->load($recordData);
+ }
+
+ /**
+ * Read SpContainer record (Shape Container)
+ */
+ private function _readSpContainer()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // add spContainer to spgrContainer
+ $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
+ $this->_object->addChild($spContainer);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // record is a container, read contents
+ $reader = new PHPExcel_Reader_Excel5_Escher($spContainer);
+ $escher = $reader->load($recordData);
+ }
+
+ /**
+ * Read Spgr record (Shape Group)
+ */
+ private function _readSpgr()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read Sp record (Shape)
+ */
+ private function _readSp()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read ClientTextbox record
+ */
+ private function _readClientTextbox()
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & $this->_GetInt2d($this->_data, $this->_pos)) >> 4;
+
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet
+ */
+ private function _readClientAnchor()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+
+ // offset: 2; size: 2; upper-left corner column index (0-based)
+ $c1 = $this->_GetInt2d($recordData, 2);
+
+ // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
+ $startOffsetX = $this->_GetInt2d($recordData, 4);
+
+ // offset: 6; size: 2; upper-left corner row index (0-based)
+ $r1 = $this->_GetInt2d($recordData, 6);
+
+ // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
+ $startOffsetY = $this->_GetInt2d($recordData, 8);
+
+ // offset: 10; size: 2; bottom-right corner column index (0-based)
+ $c2 = $this->_GetInt2d($recordData, 10);
+
+ // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
+ $endOffsetX = $this->_GetInt2d($recordData, 12);
+
+ // offset: 14; size: 2; bottom-right corner row index (0-based)
+ $r2 = $this->_GetInt2d($recordData, 14);
+
+ // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
+ $endOffsetY = $this->_GetInt2d($recordData, 16);
+
+ // set the start coordinates
+ $this->_object->setStartCoordinates(PHPExcel_Cell::stringFromColumnIndex($c1) . ($r1 + 1));
+
+ // set the start offsetX
+ $this->_object->setStartOffsetX($startOffsetX);
+
+ // set the start offsetY
+ $this->_object->setStartOffsetY($startOffsetY);
+
+ // set the end coordinates
+ $this->_object->setEndCoordinates(PHPExcel_Cell::stringFromColumnIndex($c2) . ($r2 + 1));
+
+ // set the end offsetX
+ $this->_object->setEndOffsetX($endOffsetX);
+
+ // set the end offsetY
+ $this->_object->setEndOffsetY($endOffsetY);
+ }
+
+ /**
+ * Read ClientData record
+ */
+ private function _readClientData()
+ {
+ $length = $this->_GetInt4d($this->_data, $this->_pos + 4);
+ $recordData = substr($this->_data, $this->_pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->_pos += 8 + $length;
+ }
+
+ /**
+ * Read OfficeArtRGFOPTE table of property-value pairs
+ *
+ * @param string $data Binary data
+ * @param int $n Number of properties
+ */
+ private function _readOfficeArtRGFOPTE($data, $n) {
+
+ $splicedComplexData = substr($data, 6 * $n);
+
+ // loop through property-value pairs
+ for ($i = 0; $i < $n; ++$i) {
+ // read 6 bytes at a time
+ $fopte = substr($data, 6 * $i, 6);
+
+ // offset: 0; size: 2; opid
+ $opid = $this->_GetInt2d($fopte, 0);
+
+ // bit: 0-13; mask: 0x3FFF; opid.opid
+ $opidOpid = (0x3FFF & $opid) >> 0;
+
+ // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
+ $opidFBid = (0x4000 & $opid) >> 14;
+
+ // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
+ $opidFComplex = (0x8000 & $opid) >> 15;
+
+ // offset: 2; size: 4; the value for this property
+ $op = $this->_GetInt4d($fopte, 2);
+
+ if ($opidFComplex) {
+ $complexData = substr($splicedComplexData, 0, $op);
+ $splicedComplexData = substr($splicedComplexData, $op);
+
+ // we store string value with complex data
+ $value = $complexData;
+ } else {
+ // we store integer value
+ $value = $op;
+ }
+
+ $this->_object->setOPT($opidOpid, $value);
+ }
+ }
+
+ /**
+ * Read 16-bit unsigned integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt2d($data, $pos)
+ {
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
+ }
+
+ /**
+ * Read 32-bit signed integer
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt4d($data, $pos)
+ {
+ //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) |
+ // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
+
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/IReadFilter.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/IReadFilter.php
new file mode 100644
index 0000000..c812971
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Reader/IReadFilter.php
@@ -0,0 +1,47 @@
+_readDataOnly;
+ }
+
+ /**
+ * Set read data only
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadDataOnly($pValue = false) {
+ $this->_readDataOnly = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get which sheets to load
+ *
+ * @return mixed
+ */
+ public function getLoadSheetsOnly()
+ {
+ return $this->_loadSheetsOnly;
+ }
+
+ /**
+ * Set which sheets to load
+ *
+ * @param mixed $value
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadSheetsOnly($value = null)
+ {
+ $this->_loadSheetsOnly = is_array($value) ?
+ $value : array($value);
+ return $this;
+ }
+
+ /**
+ * Set all sheets to load
+ *
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setLoadAllSheets()
+ {
+ $this->_loadSheetsOnly = null;
+ return $this;
+ }
+
+ /**
+ * Read filter
+ *
+ * @return PHPExcel_Reader_IReadFilter
+ */
+ public function getReadFilter() {
+ return $this->_readFilter;
+ }
+
+ /**
+ * Set read filter
+ *
+ * @param PHPExcel_Reader_IReadFilter $pValue
+ * @return PHPExcel_Reader_Excel2007
+ */
+ public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) {
+ $this->_readFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Create a new PHPExcel_Reader_OOCalc
+ */
+ public function __construct() {
+ $this->_sheetIndex = 0;
+ $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter();
+ }
+
+ /**
+ * Can the current PHPExcel_Reader_IReader read the file?
+ *
+ * @param string $pFileName
+ * @return boolean
+ */
+ public function canRead($pFilename)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ // Load file
+ $zip = new ZipArchive;
+ if ($zip->open($pFilename) === true) {
+ // check if it is an OOXML archive
+ $mimeType = $zip->getFromName("mimetype");
+
+ $zip->close();
+
+ return ($mimeType === 'application/vnd.oasis.opendocument.spreadsheet');
+ }
+
+ return false;
+ }
+
+ /**
+ * Loads PHPExcel from file
+ *
+ * @param string $pFilename
+ * @throws Exception
+ */
+ public function load($pFilename)
+ {
+ // Create new PHPExcel
+ $objPHPExcel = new PHPExcel();
+
+ // Load into this instance
+ return $this->loadIntoExisting($pFilename, $objPHPExcel);
+ }
+
+ private static function identifyFixedStyleValue($styleList,&$styleAttributeValue) {
+ $styleAttributeValue = strtolower($styleAttributeValue);
+ foreach($styleList as $style) {
+ if ($styleAttributeValue == strtolower($style)) {
+ $styleAttributeValue = $style;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Loads PHPExcel from file into PHPExcel instance
+ *
+ * @param string $pFilename
+ * @param PHPExcel $objPHPExcel
+ * @throws Exception
+ */
+ public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
+ {
+ // Check if file exists
+ if (!file_exists($pFilename)) {
+ throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
+ }
+
+ $zip = new ZipArchive;
+ if ($zip->open($pFilename) === true) {
+// echo '
+The Jama distribution comes with a magic square example that is used to
+test and benchmark the LU, QR, SVD and symmetric Eig decompositions.
+The example outputs a multi-column table with these column headings:
+
+
+
+
+
n
+
Order of magic square.
+
+
+
trace
+
Diagonal sum, should be the magic sum, (n^3 + n)/2.
+
+
+
max_eig
+
Maximum eigenvalue of (A + A')/2, should equal trace.
+
+
+
rank
+
Linear algebraic rank, should equal n if n is odd, be less than n if n is even.
+
+
+
cond
+
L_2 condition number, ratio of singular values.
+
+
+
lu_res
+
test of LU factorization, norm1(L*U-A(p,:))/(n*eps).
+
+
+
qr_res
+
test of QR factorization, norm1(Q*R-A)/(n*eps).
+
+
+
+Running the Java-based version of the matix square example produces these results:
+
+
+
+
+
n
+
trace
+
max_eig
+
rank
+
cond
+
lu_res
+
qr_res
+
+
+
3
15
15.000
3
4.330
0.000
11.333
+
+
+
4
34
34.000
3
Inf
0.000
13.500
+
+
5
65
65.000
5
5.462
0.000
14.400
+
+
+
6
111
111.000
5
Inf
5.333
16.000
+
+
+
7
175
175.000
7
7.111
2.286
37.714
+
+
+
8
260
260.000
3
Inf
0.000
59.000
+
+
+
9
369
369.000
9
9.102
7.111
53.333
+
+
+
10
505
505.000
7
Inf
3.200
159.200
+
+
+
11
671
671.000
11
11.102
2.909
215.273
+
+
+
12
870
870.000
3
Inf
0.000
185.333
+
+
+
13
1105
1105.000
13
13.060
4.923
313.846
+
+
+
14
1379
1379.000
9
Inf
4.571
540.571
+
+
+
15
1695
1695.000
15
15.062
4.267
242.133
+
+
+
16
2056
2056.000
3
Inf
0.000
488.500
+
+
+
17
2465
2465.000
17
17.042
7.529
267.294
+
+
+
18
2925
2925.000
11
Inf
7.111
520.889
+
+
+
19
3439
3439.000
19
19.048
16.842
387.368
+
+
+
20
4010
4010.000
3
Inf
14.400
584.800
+
+
+
21
4641
4641.000
21
21.035
6.095
1158.095
+
+
+
22
5335
5335.000
13
Inf
6.545
1132.364
+
+
+
23
6095
6095.000
23
23.037
11.130
1268.870
+
+
+
24
6924
6924.000
3
Inf
10.667
827.500
+
+
+
25
7825
7825.000
25
25.029
35.840
1190.400
+
+
+
26
8801
8801.000
15
Inf
4.923
1859.077
+
+
+
27
9855
9855.000
27
27.032
37.926
1365.333
+
+
+
28
10990
10990.000
3
Inf
34.286
1365.714
+
+
+
29
12209
12209.000
29
29.025
30.897
1647.448
+
+
+
30
13515
13515.000
17
Inf
8.533
2571.733
+
+
+
31
14911
14911.000
31
31.027
33.032
1426.581
+
+
+
32
16400
16400.000
3
Inf
0.000
1600.125
+
+
+
Elapsed Time = 0.710 seconds
+
+
+The magic square example does not fare well when run as a PHP script. For a 32x32 matrix array
+it takes around a second to complete just the last row of computations in the above table.
+Hopefully this result will spur PHP developers to find optimizations and better attuned algorithms
+to speed things up. Matrix algebra is a great testing ground for ideas about time and memory
+performance optimation. Keep in perspective that PHP JAMA scripts are still plenty fast for use as
+a tool for learning about matrix algebra and quickly extending your knowledge with new scripts
+to apply knowledge.
+
+
+
+To learn more about the subject of magic squares you can visit the Drexel Math Forum on Magic Squares.
+You can also learn more by carefully examining the MagicSquareExample.php source code below.
+
+JAMA is a proposed standard matrix class for Java. The JAMA introduction
+describes "JAMA : A Java Matrix Package" in this way:
+
+
+
+JAMA is a basic linear algebra package for Java. It provides user-level classes for
+constructing and manipulating real, dense matrices. It is meant to provide sufficient
+functionality for routine problems, packaged in a way that is natural and understandable
+to non-experts. It is intended to serve as the standard matrix class for Java, and
+will be proposed as such to the Java Grande Forum and then to Sun. A straightforward
+public-domain reference implementation has been developed by the MathWorks and NIST as
+a strawman for such a class. We are releasing this version in order to obtain public
+comment. There is no guarantee that future versions of JAMA will be compatible with this one.
+
+
+
+The development team below has successfully ported the JAMA API to PHP. You can explore
+this site to learn more about this project and it's current development status.
+
+
+
\ No newline at end of file
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/docs/package.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/docs/package.php
new file mode 100644
index 0000000..07f8492
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/docs/package.php
@@ -0,0 +1,37 @@
+
+
+The first script your should run when you install Jama is the TestMatrix.php script.
+
+
+This will run the unit tests for methods in the Matrix.php class. Because
+the Matrix.php class can be used to invoke all the decomposition methods the TestMatrix.php
+script is a test suite for the whole Jama package.
+
+
+The original TestMatrix.java code uses try/catch error handling. We will
+eventually create a build of JAMA that will take advantage of PHP5's new try/catch error
+handling capabilities. This will improve our ability to replicate all the unit tests that
+appeared in the original (except for some print methods that may not be worth porting).
+
+
+You can run the TestMatrix.php script to see what
+unit tests are currently implemented. The source of the TestMatrix.php script
+is provided below. It is worth studying carefully for an example of how to do matrix algebra
+programming with Jama.
+
+
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LMQuadTest.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LMQuadTest.php
new file mode 100644
index 0000000..2f316de
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LMQuadTest.php
@@ -0,0 +1,116 @@
+val($x[$i], $a);
+ print("Quad ".$c.",".$r." -> ".$y[$i]." ");
+ $s[$i] = 1.;
+ ++$i;
+ }
+ }
+ print("quad x= ");
+
+ $qx = new Matrix($x);
+ $qx->print(10, 2);
+
+ print("quad y= ");
+ $qy = new Matrix($y, $npts);
+ $qy->print(10, 2);
+
+ $o[0] = $x;
+ $o[1] = $a;
+ $o[2] = $y;
+ $o[3] = $s;
+
+ return $o;
+ } // function testdata()
+
+} // class LMQuadTest
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation.php
new file mode 100644
index 0000000..5b74286
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation.php
@@ -0,0 +1,59 @@
+solve($b);
+
+ return $s->getRowPackedCopy();
+ } // function findPolynomialFactors()
+
+} // class LagrangeInterpolation
+
+
+$x = array(2.0, 1.0, 3.0);
+$y = array(3.0, 4.0, 7.0);
+
+$li = new LagrangeInterpolation;
+$f = $li->findPolynomialFactors($x, $y);
+
+
+for ($i = 0; $i < 3; ++$i) {
+ echo $f[$i]." ";
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation2.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation2.php
new file mode 100644
index 0000000..e7529c5
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LagrangeInterpolation2.php
@@ -0,0 +1,59 @@
+solve($b);
+
+ return $s->getRowPackedCopy();
+ } // function findPolynomialFactors()
+
+} // class LagrangeInterpolation
+
+
+$x = array(2.0, 1.0, 3.0);
+$y = array(3.0, 4.0, 7.0);
+
+$li = new LagrangeInterpolation;
+$f = $li->findPolynomialFactors($x, $y);
+
+for ($i = 0; $i < 3; ++$i) {
+ echo $f[$i]." ";
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LevenbergMarquardt.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LevenbergMarquardt.php
new file mode 100644
index 0000000..7cfd5f8
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/LevenbergMarquardt.php
@@ -0,0 +1,185 @@
+val($x[$i], $a);
+ $d = $d / $s[$i];
+ $sum = $sum + ($d*$d);
+ }
+
+ return $sum;
+ } // function chiSquared()
+
+
+ /**
+ * Minimize E = sum {(y[k] - f(x[k],a)) / s[k]}^2
+ * The individual errors are optionally scaled by s[k].
+ * Note that LMfunc implements the value and gradient of f(x,a),
+ * NOT the value and gradient of E with respect to a!
+ *
+ * @param x array of domain points, each may be multidimensional
+ * @param y corresponding array of values
+ * @param a the parameters/state of the model
+ * @param vary false to indicate the corresponding a[k] is to be held fixed
+ * @param s2 sigma^2 for point i
+ * @param lambda blend between steepest descent (lambda high) and
+ * jump to bottom of quadratic (lambda zero).
+ * Start with 0.001.
+ * @param termepsilon termination accuracy (0.01)
+ * @param maxiter stop and return after this many iterations if not done
+ * @param verbose set to zero (no prints), 1, 2
+ *
+ * @return the new lambda for future iterations.
+ * Can use this and maxiter to interleave the LM descent with some other
+ * task, setting maxiter to something small.
+ */
+ function solve($x, $a, $y, $s, $vary, $f, $lambda, $termepsilon, $maxiter, $verbose) {
+ $npts = count($y);
+ $nparm = count($a);
+
+ if ($verbose > 0) {
+ print("solve x[".count($x)."][".count($x[0])."]");
+ print(" a[".count($a)."]");
+ println(" y[".count(length)."]");
+ }
+
+ $e0 = $this->chiSquared($x, $a, $y, $s, $f);
+
+ //double lambda = 0.001;
+ $done = false;
+
+ // g = gradient, H = hessian, d = step to minimum
+ // H d = -g, solve for d
+ $H = array();
+ $g = array();
+
+ //double[] d = new double[nparm];
+
+ $oos2 = array();
+
+ for($i = 0; $i < $npts; ++$i) {
+ $oos2[$i] = 1./($s[$i]*$s[$i]);
+ }
+ $iter = 0;
+ $term = 0; // termination count test
+
+ do {
+ ++$iter;
+
+ // hessian approximation
+ for( $r = 0; $r < $nparm; ++$r) {
+ for( $c = 0; $c < $nparm; ++$c) {
+ for( $i = 0; $i < $npts; ++$i) {
+ if ($i == 0) $H[$r][$c] = 0.;
+ $xi = $x[$i];
+ $H[$r][$c] += ($oos2[$i] * $f->grad($xi, $a, $r) * $f->grad($xi, $a, $c));
+ } //npts
+ } //c
+ } //r
+
+ // boost diagonal towards gradient descent
+ for( $r = 0; $r < $nparm; ++$r)
+ $H[$r][$r] *= (1. + $lambda);
+
+ // gradient
+ for( $r = 0; $r < $nparm; ++$r) {
+ for( $i = 0; $i < $npts; ++$i) {
+ if ($i == 0) $g[$r] = 0.;
+ $xi = $x[$i];
+ $g[$r] += ($oos2[$i] * ($y[$i]-$f->val($xi,$a)) * $f->grad($xi, $a, $r));
+ }
+ } //npts
+
+ // scale (for consistency with NR, not necessary)
+ if ($false) {
+ for( $r = 0; $r < $nparm; ++$r) {
+ $g[$r] = -0.5 * $g[$r];
+ for( $c = 0; $c < $nparm; ++$c) {
+ $H[$r][$c] *= 0.5;
+ }
+ }
+ }
+
+ // solve H d = -g, evaluate error at new location
+ //double[] d = DoubleMatrix.solve(H, g);
+// double[] d = (new Matrix(H)).lu().solve(new Matrix(g, nparm)).getRowPackedCopy();
+ //double[] na = DoubleVector.add(a, d);
+// double[] na = (new Matrix(a, nparm)).plus(new Matrix(d, nparm)).getRowPackedCopy();
+// double e1 = chiSquared(x, na, y, s, f);
+
+// if (verbose > 0) {
+// System.out.println("\n\niteration "+iter+" lambda = "+lambda);
+// System.out.print("a = ");
+// (new Matrix(a, nparm)).print(10, 2);
+// if (verbose > 1) {
+// System.out.print("H = ");
+// (new Matrix(H)).print(10, 2);
+// System.out.print("g = ");
+// (new Matrix(g, nparm)).print(10, 2);
+// System.out.print("d = ");
+// (new Matrix(d, nparm)).print(10, 2);
+// }
+// System.out.print("e0 = " + e0 + ": ");
+// System.out.print("moved from ");
+// (new Matrix(a, nparm)).print(10, 2);
+// System.out.print("e1 = " + e1 + ": ");
+// if (e1 < e0) {
+// System.out.print("to ");
+// (new Matrix(na, nparm)).print(10, 2);
+// } else {
+// System.out.println("move rejected");
+// }
+// }
+
+ // termination test (slightly different than NR)
+// if (Math.abs(e1-e0) > termepsilon) {
+// term = 0;
+// } else {
+// term++;
+// if (term == 4) {
+// System.out.println("terminating after " + iter + " iterations");
+// done = true;
+// }
+// }
+// if (iter >= maxiter) done = true;
+
+ // in the C++ version, found that changing this to e1 >= e0
+ // was not a good idea. See comment there.
+ //
+// if (e1 > e0 || Double.isNaN(e1)) { // new location worse than before
+// lambda *= 10.;
+// } else { // new location better, accept new parameters
+// lambda *= 0.1;
+// e0 = e1;
+// // simply assigning a = na will not get results copied back to caller
+// for( int i = 0; i < nparm; i++ ) {
+// if (vary[i]) a[i] = na[i];
+// }
+// }
+ } while(!$done);
+
+ return $lambda;
+ } // function solve()
+
+} // class LevenbergMarquardt
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/MagicSquareExample.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/MagicSquareExample.php
new file mode 100644
index 0000000..e6c93d0
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/MagicSquareExample.php
@@ -0,0 +1,182 @@
+magic($p);
+ $M = array();
+ for ($j = 0; $j < $p; ++$j) {
+ for ($i = 0; $i < $p; ++$i) {
+ $aij = $A->get($i,$j);
+ $M[$i][$j] = $aij;
+ $M[$i][$j+$p] = $aij + 2*$p*$p;
+ $M[$i+$p][$j] = $aij + 3*$p*$p;
+ $M[$i+$p][$j+$p] = $aij + $p*$p;
+ }
+ }
+
+ for ($i = 0; $i < $p; ++$i) {
+ for ($j = 0; $j < $k; ++$j) {
+ $t = $M[$i][$j];
+ $M[$i][$j] = $M[$i+$p][$j];
+ $M[$i+$p][$j] = $t;
+ }
+ for ($j = $n-$k+1; $j < $n; ++$j) {
+ $t = $M[$i][$j];
+ $M[$i][$j] = $M[$i+$p][$j];
+ $M[$i+$p][$j] = $t;
+ }
+ }
+
+ $t = $M[$k][0]; $M[$k][0] = $M[$k+$p][0]; $M[$k+$p][0] = $t;
+ $t = $M[$k][$k]; $M[$k][$k] = $M[$k+$p][$k]; $M[$k+$p][$k] = $t;
+
+ }
+
+ return new Matrix($M);
+
+ }
+
+ /**
+ * Simple function to replicate PHP 5 behaviour
+ */
+ function microtime_float() {
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
+ }
+
+ /**
+ * Tests LU, QR, SVD and symmetric Eig decompositions.
+ *
+ * n = order of magic square.
+ * trace = diagonal sum, should be the magic sum, (n^3 + n)/2.
+ * max_eig = maximum eigenvalue of (A + A')/2, should equal trace.
+ * rank = linear algebraic rank, should equal n if n is odd,
+ * be less than n if n is even.
+ * cond = L_2 condition number, ratio of singular values.
+ * lu_res = test of LU factorization, norm1(L*U-A(p,:))/(n*eps).
+ * qr_res = test of QR factorization, norm1(Q*R-A)/(n*eps).
+ */
+ function main() {
+ ?>
+
Elapsed time is ". sprintf("%.4f",$etime) ." seconds.
";
+
+ }
+
+}
+
+$magic = new MagicSquareExample();
+$magic->main();
+
+?>
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/Stats.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/Stats.php
new file mode 100644
index 0000000..2bb9af1
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/examples/Stats.php
@@ -0,0 +1,1605 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Stats.php,v 1.15 2003/06/01 11:40:30 jmcastagnetto Exp $
+//
+
+include_once 'PEAR.php';
+
+/**
+* @package Math_Stats
+*/
+
+// Constants for defining the statistics to calculate /*{{{*/
+/**
+* STATS_BASIC to generate the basic descriptive statistics
+*/
+define('STATS_BASIC', 1);
+/**
+* STATS_FULL to generate also higher moments, mode, median, etc.
+*/
+define('STATS_FULL', 2);
+/*}}}*/
+
+// Constants describing the data set format /*{{{*/
+/**
+* STATS_DATA_SIMPLE for an array of numeric values. This is the default.
+* e.g. $data = array(2,3,4,5,1,1,6);
+*/
+define('STATS_DATA_SIMPLE', 0);
+/**
+* STATS_DATA_CUMMULATIVE for an associative array of frequency values,
+* where in each array entry, the index is the data point and the
+* value the count (frequency):
+* e.g. $data = array(3=>4, 2.3=>5, 1.25=>6, 0.5=>3)
+*/
+define('STATS_DATA_CUMMULATIVE', 1);
+/*}}}*/
+
+// Constants defining how to handle nulls /*{{{*/
+/**
+* STATS_REJECT_NULL, reject data sets with null values. This is the default.
+* Any non-numeric value is considered a null in this context.
+*/
+define('STATS_REJECT_NULL', -1);
+/**
+* STATS_IGNORE_NULL, ignore null values and prune them from the data.
+* Any non-numeric value is considered a null in this context.
+*/
+define('STATS_IGNORE_NULL', -2);
+/**
+* STATS_USE_NULL_AS_ZERO, assign the value of 0 (zero) to null values.
+* Any non-numeric value is considered a null in this context.
+*/
+define('STATS_USE_NULL_AS_ZERO', -3);
+/*}}}*/
+
+/**
+* A class to calculate descriptive statistics from a data set.
+* Data sets can be simple arrays of data, or a cummulative hash.
+* The second form is useful when passing large data set,
+* for example the data set:
+*
+*
";
+
+ /**
+ * Test linear algebra methods
+ */
+ echo "
Testing linear algebra methods...
";
+
+ $A = new Matrix($columnwise, 3);
+ if( $this->checkMatrices($A->transpose(), $T) )
+ $this->try_success("Transpose check...");
+ else
+ $errorCount = $this->try_failure($errorCount, "Transpose check...", "Matrices are not equal");
+
+ if($this->checkScalars($A->norm1(), $columnsummax))
+ $this->try_success("Maximum column sum...");
+ else
+ $errorCount = $this->try_failure($errorCount, "Maximum column sum...", "Incorrect: " . $A->norm1() . " != " . $columnsummax);
+
+ if($this->checkScalars($A->normInf(), $rowsummax))
+ $this->try_success("Maximum row sum...");
+ else
+ $errorCount = $this->try_failure($errorCount, "Maximum row sum...", "Incorrect: " . $A->normInf() . " != " . $rowsummax );
+
+ if($this->checkScalars($A->normF(), sqrt($sumofsquares)))
+ $this->try_success("Frobenius norm...");
+ else
+ $errorCount = $this->try_failure($errorCount, "Frobenius norm...", "Incorrect:" . $A->normF() . " != " . sqrt($sumofsquares));
+
+ if($this->checkScalars($A->trace(), $sumofdiagonals))
+ $this->try_success("Matrix trace...");
+ else
+ $errorCount = $this->try_failure($errorCount, "Matrix trace...", "Incorrect: " . $A->trace() . " != " . $sumofdiagonals);
+
+ $B = $A->getMatrix(0, $A->getRowDimension(), 0, $A->getRowDimension());
+ if( $B->det() == 0 )
+ $this->try_success("Matrix determinant...");
+ else
+ $errorCount = $this->try_failure($errorCount, "Matrix determinant...", "Incorrect: " . $B->det() . " != " . 0);
+
+ $A = new Matrix($columnwise,3);
+ $SQ = new Matrix($square);
+ if ($this->checkMatrices($SQ, $A->times($A->transpose())))
+ $this->try_success("times(Matrix)...");
+ else {
+ $errorCount = $this->try_failure($errorCount, "times(Matrix)...", "Unable to multiply matrices");
+ $SQ->toHTML();
+ $AT->toHTML();
+ }
+
+ $A = new Matrix($columnwise, 4);
+
+ $QR = $A->qr();
+ $R = $QR->getR();
+ $Q = $QR->getQ();
+ if($this->checkMatrices($A, $Q->times($R)))
+ $this->try_success("QRDecomposition...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"QRDecomposition...","incorrect qr decomposition calculation");
+
+ $A = new Matrix($columnwise, 4);
+ $SVD = $A->svd();
+ $U = $SVD->getU();
+ $S = $SVD->getS();
+ $V = $SVD->getV();
+ if ($this->checkMatrices($A, $U->times($S->times($V->transpose()))))
+ $this->try_success("SingularValueDecomposition...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"SingularValueDecomposition...","incorrect singular value decomposition calculation");
+
+ $n = $A->getColumnDimension();
+ $A = $A->getMatrix(0,$n-1,0,$n-1);
+ $A->set(0,0,0.);
+
+ $LU = $A->lu();
+ $L = $LU->getL();
+ if ( $this->checkMatrices($A->getMatrix($LU->getPivot(),0,$n-1), $L->times($LU->getU())) )
+ $this->try_success("LUDecomposition...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"LUDecomposition...","incorrect LU decomposition calculation");
+
+ $X = $A->inverse();
+ if ( $this->checkMatrices($A->times($X),Matrix::identity(3,3)) )
+ $this->try_success("inverse()...","");
+ else
+ $errorCount = $this->try_failure($errorCount, "inverse()...","incorrect inverse calculation");
+
+ $DEF = new Matrix($rankdef);
+ if($this->checkScalars($DEF->rank(), min($DEF->getRowDimension(), $DEF->getColumnDimension())-1))
+ $this->try_success("Rank...");
+ else
+ $this->try_failure("Rank...", "incorrect rank calculation");
+
+ $B = new Matrix($condmat);
+ $SVD = $B->svd();
+ $singularvalues = $SVD->getSingularValues();
+ if($this->checkScalars($B->cond(), $singularvalues[0]/$singularvalues[min($B->getRowDimension(), $B->getColumnDimension())-1]))
+ $this->try_success("Condition number...");
+ else
+ $this->try_failure("Condition number...", "incorrect condition number calculation");
+
+ $SUB = new Matrix($subavals);
+ $O = new Matrix($SUB->getRowDimension(),1,1.0);
+ $SOL = new Matrix($sqSolution);
+ $SQ = $SUB->getMatrix(0,$SUB->getRowDimension()-1,0,$SUB->getRowDimension()-1);
+ if ( $this->checkMatrices($SQ->solve($SOL),$O) )
+ $this->try_success("solve()...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"solve()...","incorrect lu solve calculation");
+
+ $A = new Matrix($pvals);
+ $Chol = $A->chol();
+ $L = $Chol->getL();
+ if ( $this->checkMatrices($A, $L->times($L->transpose())) )
+ $this->try_success("CholeskyDecomposition...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"CholeskyDecomposition...","incorrect Cholesky decomposition calculation");
+
+ $X = $Chol->solve(Matrix::identity(3,3));
+ if ( $this->checkMatrices($A->times($X), Matrix::identity(3,3)) )
+ $this->try_success("CholeskyDecomposition solve()...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"CholeskyDecomposition solve()...","incorrect Choleskydecomposition solve calculation");
+
+ $Eig = $A->eig();
+ $D = $Eig->getD();
+ $V = $Eig->getV();
+ if( $this->checkMatrices($A->times($V),$V->times($D)) )
+ $this->try_success("EigenvalueDecomposition (symmetric)...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"EigenvalueDecomposition (symmetric)...","incorrect symmetric Eigenvalue decomposition calculation");
+
+ $A = new Matrix($evals);
+ $Eig = $A->eig();
+ $D = $Eig->getD();
+ $V = $Eig->getV();
+ if ( $this->checkMatrices($A->times($V),$V->times($D)) )
+ $this->try_success("EigenvalueDecomposition (nonsymmetric)...","");
+ else
+ $errorCount = $this->try_failure($errorCount,"EigenvalueDecomposition (nonsymmetric)...","incorrect nonsymmetric Eigenvalue decomposition calculation");
+
+ print("{$errorCount} total errors.");
+ }
+
+ /**
+ * Print appropriate messages for successful outcome try
+ * @param string $s
+ * @param string $e
+ */
+ function try_success($s, $e = "") {
+ print "> ". $s ."success ";
+ if ($e != "")
+ print "> Message: ". $e ." ";
+ }
+
+ /**
+ * Print appropriate messages for unsuccessful outcome try
+ * @param int $count
+ * @param string $s
+ * @param string $e
+ * @return int incremented counter
+ */
+ function try_failure($count, $s, $e="") {
+ print "> ". $s ."*** failure *** > Message: ". $e ." ";
+ return ++$count;
+ }
+
+ /**
+ * Print appropriate messages for unsuccessful outcome try
+ * @param int $count
+ * @param string $s
+ * @param string $e
+ * @return int incremented counter
+ */
+ function try_warning($count, $s, $e="") {
+ print "> ". $s ."*** warning *** > Message: ". $e ." ";
+ return ++$count;
+ }
+
+ /**
+ * Check magnitude of difference of "scalars".
+ * @param float $x
+ * @param float $y
+ */
+ function checkScalars($x, $y) {
+ $eps = pow(2.0,-52.0);
+ if ($x == 0 & abs($y) < 10*$eps) return;
+ if ($y == 0 & abs($x) < 10*$eps) return;
+ if (abs($x-$y) > 10 * $eps * max(abs($x),abs($y)))
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Check norm of difference of "vectors".
+ * @param float $x[]
+ * @param float $y[]
+ */
+ function checkVectors($x, $y) {
+ $nx = count($x);
+ $ny = count($y);
+ if ($nx == $ny)
+ for($i=0; $i < $nx; ++$i)
+ $this->checkScalars($x[$i],$y[$i]);
+ else
+ die("Attempt to compare vectors of different lengths");
+ }
+
+ /**
+ * Check norm of difference of "arrays".
+ * @param float $x[][]
+ * @param float $y[][]
+ */
+ function checkArrays($x, $y) {
+ $A = new Matrix($x);
+ $B = new Matrix($y);
+ return $this->checkMatrices($A,$B);
+ }
+
+ /**
+ * Check norm of difference of "matrices".
+ * @param matrix $X
+ * @param matrix $Y
+ */
+ function checkMatrices($X = null, $Y = null) {
+ if( $X == null || $Y == null )
+ return false;
+
+ $eps = pow(2.0,-52.0);
+ if ($X->norm1() == 0. & $Y->norm1() < 10*$eps) return true;
+ if ($Y->norm1() == 0. & $X->norm1() < 10*$eps) return true;
+
+ $A = $X->minus($Y);
+
+ if ($A->norm1() > 1000 * $eps * max($X->norm1(),$Y->norm1()))
+ die("The norm of (X-Y) is too large: ".$A->norm1());
+ else
+ return true;
+ }
+
+}
+
+$test = new TestMatrix;
+?>
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/utils/Error.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/utils/Error.php
new file mode 100644
index 0000000..e73252b
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/JAMA/utils/Error.php
@@ -0,0 +1,82 @@
+ abs($b)) {
+ $r = $b / $a;
+ $r = abs($a) * sqrt(1 + $r * $r);
+ } elseif ($b != 0) {
+ $r = $a / $b;
+ $r = abs($b) * sqrt(1 + $r * $r);
+ } else {
+ $r = 0.0;
+ }
+ return $r;
+} // function hypo()
+
+
+/**
+ * Mike Bommarito's version.
+ * Compute n-dimensional hyotheneuse.
+ *
+function hypot() {
+ $s = 0;
+ foreach (func_get_args() as $d) {
+ if (is_numeric($d)) {
+ $s += pow($d, 2);
+ } else {
+ throw new Exception(JAMAError(ArgumentTypeException));
+ }
+ }
+ return sqrt($s);
+}
+*/
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE.php
new file mode 100644
index 0000000..f65c24f
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE.php
@@ -0,0 +1,555 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+// $Id: OLE.php,v 1.13 2007/03/07 14:38:25 schmidt Exp $
+
+/** PHPExcel root directory */
+if (!defined('PHPEXCEL_ROOT')) {
+ /**
+ * @ignore
+ */
+ define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
+}
+
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE.php';
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE/OLE_PPS.php';
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE/OLE_File.php';
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE/OLE_Root.php';
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE/ChainedBlockStream.php';
+
+/**
+* Array for storing OLE instances that are accessed from
+* OLE_ChainedBlockStream::stream_open().
+* @var array
+*/
+$GLOBALS['_OLE_INSTANCES'] = array();
+
+/**
+* OLE package base class.
+*
+* @author Xavier Noguer
+* @author Christian Schmidt
+* @category PHPExcel
+* @package PHPExcel_Shared_OLE
+*/
+class PHPExcel_Shared_OLE
+{
+ const OLE_PPS_TYPE_ROOT = 5;
+ const OLE_PPS_TYPE_DIR = 1;
+ const OLE_PPS_TYPE_FILE = 2;
+ const OLE_DATA_SIZE_SMALL = 0x1000;
+ const OLE_LONG_INT_SIZE = 4;
+ const OLE_PPS_SIZE = 0x80;
+
+ /**
+ * The file handle for reading an OLE container
+ * @var resource
+ */
+ public $_file_handle;
+
+ /**
+ * Array of PPS's found on the OLE container
+ * @var array
+ */
+ public $_list = array();
+
+ /**
+ * Root directory of OLE container
+ * @var OLE_PPS_Root
+ */
+ public $root;
+
+ /**
+ * Big Block Allocation Table
+ * @var array (blockId => nextBlockId)
+ */
+ public $bbat;
+
+ /**
+ * Short Block Allocation Table
+ * @var array (blockId => nextBlockId)
+ */
+ public $sbat;
+
+ /**
+ * Size of big blocks. This is usually 512.
+ * @var int number of octets per block.
+ */
+ public $bigBlockSize;
+
+ /**
+ * Size of small blocks. This is usually 64.
+ * @var int number of octets per block
+ */
+ public $smallBlockSize;
+
+ /**
+ * Reads an OLE container from the contents of the file given.
+ *
+ * @acces public
+ * @param string $file
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ public function read($file)
+ {
+ $fh = fopen($file, "r");
+ if (!$fh) {
+ throw new Exception("Can't open file $file");
+ }
+ $this->_file_handle = $fh;
+
+ $signature = fread($fh, 8);
+ if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
+ throw new Exception("File doesn't seem to be an OLE container.");
+ }
+ fseek($fh, 28);
+ if (fread($fh, 2) != "\xFE\xFF") {
+ // This shouldn't be a problem in practice
+ throw new Exception("Only Little-Endian encoding is supported.");
+ }
+ // Size of blocks and short blocks in bytes
+ $this->bigBlockSize = pow(2, $this->_readInt2($fh));
+ $this->smallBlockSize = pow(2, $this->_readInt2($fh));
+
+ // Skip UID, revision number and version number
+ fseek($fh, 44);
+ // Number of blocks in Big Block Allocation Table
+ $bbatBlockCount = $this->_readInt4($fh);
+
+ // Root chain 1st block
+ $directoryFirstBlockId = $this->_readInt4($fh);
+
+ // Skip unused bytes
+ fseek($fh, 56);
+ // Streams shorter than this are stored using small blocks
+ $this->bigBlockThreshold = $this->_readInt4($fh);
+ // Block id of first sector in Short Block Allocation Table
+ $sbatFirstBlockId = $this->_readInt4($fh);
+ // Number of blocks in Short Block Allocation Table
+ $sbbatBlockCount = $this->_readInt4($fh);
+ // Block id of first sector in Master Block Allocation Table
+ $mbatFirstBlockId = $this->_readInt4($fh);
+ // Number of blocks in Master Block Allocation Table
+ $mbbatBlockCount = $this->_readInt4($fh);
+ $this->bbat = array();
+
+ // Remaining 4 * 109 bytes of current block is beginning of Master
+ // Block Allocation Table
+ $mbatBlocks = array();
+ for ($i = 0; $i < 109; ++$i) {
+ $mbatBlocks[] = $this->_readInt4($fh);
+ }
+
+ // Read rest of Master Block Allocation Table (if any is left)
+ $pos = $this->_getBlockOffset($mbatFirstBlockId);
+ for ($i = 0; $i < $mbbatBlockCount; ++$i) {
+ fseek($fh, $pos);
+ for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
+ $mbatBlocks[] = $this->_readInt4($fh);
+ }
+ // Last block id in each block points to next block
+ $pos = $this->_getBlockOffset($this->_readInt4($fh));
+ }
+
+ // Read Big Block Allocation Table according to chain specified by
+ // $mbatBlocks
+ for ($i = 0; $i < $bbatBlockCount; ++$i) {
+ $pos = $this->_getBlockOffset($mbatBlocks[$i]);
+ fseek($fh, $pos);
+ for ($j = 0 ; $j < $this->bigBlockSize / 4; ++$j) {
+ $this->bbat[] = $this->_readInt4($fh);
+ }
+ }
+
+ // Read short block allocation table (SBAT)
+ $this->sbat = array();
+ $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
+ $sbatFh = $this->getStream($sbatFirstBlockId);
+ for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
+ $this->sbat[$blockId] = $this->_readInt4($sbatFh);
+ }
+ fclose($sbatFh);
+
+ $this->_readPpsWks($directoryFirstBlockId);
+
+ return true;
+ }
+
+ /**
+ * @param int block id
+ * @param int byte offset from beginning of file
+ * @access public
+ */
+ public function _getBlockOffset($blockId)
+ {
+ return 512 + $blockId * $this->bigBlockSize;
+ }
+
+ /**
+ * Returns a stream for use with fread() etc. External callers should
+ * use PHPExcel_Shared_OLE_PPS_File::getStream().
+ * @param int|PPS block id or PPS
+ * @return resource read-only stream
+ */
+ public function getStream($blockIdOrPps)
+ {
+ static $isRegistered = false;
+ if (!$isRegistered) {
+ stream_wrapper_register('ole-chainedblockstream',
+ 'PHPExcel_Shared_OLE_ChainedBlockStream');
+ $isRegistered = true;
+ }
+
+ // Store current instance in global array, so that it can be accessed
+ // in OLE_ChainedBlockStream::stream_open().
+ // Object is removed from self::$instances in OLE_Stream::close().
+ $GLOBALS['_OLE_INSTANCES'][] = $this;
+ $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES']));
+
+ $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
+ if ($blockIdOrPps instanceof PHPExcel_Shared_OLE_PPS) {
+ $path .= '&blockId=' . $blockIdOrPps->_StartBlock;
+ $path .= '&size=' . $blockIdOrPps->Size;
+ } else {
+ $path .= '&blockId=' . $blockIdOrPps;
+ }
+ return fopen($path, 'r');
+ }
+
+ /**
+ * Reads a signed char.
+ * @param resource file handle
+ * @return int
+ * @access public
+ */
+ public function _readInt1($fh)
+ {
+ list(, $tmp) = unpack("c", fread($fh, 1));
+ return $tmp;
+ }
+
+ /**
+ * Reads an unsigned short (2 octets).
+ * @param resource file handle
+ * @return int
+ * @access public
+ */
+ public function _readInt2($fh)
+ {
+ list(, $tmp) = unpack("v", fread($fh, 2));
+ return $tmp;
+ }
+
+ /**
+ * Reads an unsigned long (4 octets).
+ * @param resource file handle
+ * @return int
+ * @access public
+ */
+ public function _readInt4($fh)
+ {
+ list(, $tmp) = unpack("V", fread($fh, 4));
+ return $tmp;
+ }
+
+ /**
+ * Gets information about all PPS's on the OLE container from the PPS WK's
+ * creates an OLE_PPS object for each one.
+ *
+ * @access public
+ * @param integer the block id of the first block
+ * @return mixed true on success, PEAR_Error on failure
+ */
+ public function _readPpsWks($blockId)
+ {
+ $fh = $this->getStream($blockId);
+ for ($pos = 0; ; $pos += 128) {
+ fseek($fh, $pos, SEEK_SET);
+ $nameUtf16 = fread($fh, 64);
+ $nameLength = $this->_readInt2($fh);
+ $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
+ // Simple conversion from UTF-16LE to ISO-8859-1
+ $name = str_replace("\x00", "", $nameUtf16);
+ $type = $this->_readInt1($fh);
+ switch ($type) {
+ case self::OLE_PPS_TYPE_ROOT:
+ $pps = new PHPExcel_Shared_OLE_PPS_Root(null, null, array());
+ $this->root = $pps;
+ break;
+ case self::OLE_PPS_TYPE_DIR:
+ $pps = new PHPExcel_Shared_OLE_PPS(null, null, null, null, null,
+ null, null, null, null, array());
+ break;
+ case self::OLE_PPS_TYPE_FILE:
+ $pps = new PHPExcel_Shared_OLE_PPS_File($name);
+ break;
+ default:
+ continue;
+ }
+ fseek($fh, 1, SEEK_CUR);
+ $pps->Type = $type;
+ $pps->Name = $name;
+ $pps->PrevPps = $this->_readInt4($fh);
+ $pps->NextPps = $this->_readInt4($fh);
+ $pps->DirPps = $this->_readInt4($fh);
+ fseek($fh, 20, SEEK_CUR);
+ $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
+ $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
+ $pps->_StartBlock = $this->_readInt4($fh);
+ $pps->Size = $this->_readInt4($fh);
+ $pps->No = count($this->_list);
+ $this->_list[] = $pps;
+
+ // check if the PPS tree (starting from root) is complete
+ if (isset($this->root) &&
+ $this->_ppsTreeComplete($this->root->No)) {
+
+ break;
+ }
+ }
+ fclose($fh);
+
+ // Initialize $pps->children on directories
+ foreach ($this->_list as $pps) {
+ if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
+ $nos = array($pps->DirPps);
+ $pps->children = array();
+ while ($nos) {
+ $no = array_pop($nos);
+ if ($no != -1) {
+ $childPps = $this->_list[$no];
+ $nos[] = $childPps->PrevPps;
+ $nos[] = $childPps->NextPps;
+ $pps->children[] = $childPps;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * It checks whether the PPS tree is complete (all PPS's read)
+ * starting with the given PPS (not necessarily root)
+ *
+ * @access public
+ * @param integer $index The index of the PPS from which we are checking
+ * @return boolean Whether the PPS tree for the given PPS is complete
+ */
+ public function _ppsTreeComplete($index)
+ {
+ return isset($this->_list[$index]) &&
+ ($pps = $this->_list[$index]) &&
+ ($pps->PrevPps == -1 ||
+ $this->_ppsTreeComplete($pps->PrevPps)) &&
+ ($pps->NextPps == -1 ||
+ $this->_ppsTreeComplete($pps->NextPps)) &&
+ ($pps->DirPps == -1 ||
+ $this->_ppsTreeComplete($pps->DirPps));
+ }
+
+ /**
+ * Checks whether a PPS is a File PPS or not.
+ * If there is no PPS for the index given, it will return false.
+ *
+ * @access public
+ * @param integer $index The index for the PPS
+ * @return bool true if it's a File PPS, false otherwise
+ */
+ public function isFile($index)
+ {
+ if (isset($this->_list[$index])) {
+ return ($this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE);
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether a PPS is a Root PPS or not.
+ * If there is no PPS for the index given, it will return false.
+ *
+ * @access public
+ * @param integer $index The index for the PPS.
+ * @return bool true if it's a Root PPS, false otherwise
+ */
+ public function isRoot($index)
+ {
+ if (isset($this->_list[$index])) {
+ return ($this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT);
+ }
+ return false;
+ }
+
+ /**
+ * Gives the total number of PPS's found in the OLE container.
+ *
+ * @access public
+ * @return integer The total number of PPS's found in the OLE container
+ */
+ public function ppsTotal()
+ {
+ return count($this->_list);
+ }
+
+ /**
+ * Gets data from a PPS
+ * If there is no PPS for the index given, it will return an empty string.
+ *
+ * @access public
+ * @param integer $index The index for the PPS
+ * @param integer $position The position from which to start reading
+ * (relative to the PPS)
+ * @param integer $length The amount of bytes to read (at most)
+ * @return string The binary string containing the data requested
+ * @see OLE_PPS_File::getStream()
+ */
+ public function getData($index, $position, $length)
+ {
+ // if position is not valid return empty string
+ if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
+ return '';
+ }
+ $fh = $this->getStream($this->_list[$index]);
+ $data = stream_get_contents($fh, $length, $position);
+ fclose($fh);
+ return $data;
+ }
+
+ /**
+ * Gets the data length from a PPS
+ * If there is no PPS for the index given, it will return 0.
+ *
+ * @access public
+ * @param integer $index The index for the PPS
+ * @return integer The amount of bytes in data the PPS has
+ */
+ public function getDataLength($index)
+ {
+ if (isset($this->_list[$index])) {
+ return $this->_list[$index]->Size;
+ }
+ return 0;
+ }
+
+ /**
+ * Utility function to transform ASCII text to Unicode
+ *
+ * @access public
+ * @static
+ * @param string $ascii The ASCII string to transform
+ * @return string The string in Unicode
+ */
+ public static function Asc2Ucs($ascii)
+ {
+ $rawname = '';
+ for ($i = 0; $i < strlen($ascii); ++$i) {
+ $rawname .= $ascii{$i} . "\x00";
+ }
+ return $rawname;
+ }
+
+ /**
+ * Utility function
+ * Returns a string for the OLE container with the date given
+ *
+ * @access public
+ * @static
+ * @param integer $date A timestamp
+ * @return string The string for the OLE container
+ */
+ public static function LocalDate2OLE($date = null)
+ {
+ if (!isset($date)) {
+ return "\x00\x00\x00\x00\x00\x00\x00\x00";
+ }
+
+ // factor used for separating numbers into 4 bytes parts
+ $factor = pow(2, 32);
+
+ // days from 1-1-1601 until the beggining of UNIX era
+ $days = 134774;
+ // calculate seconds
+ $big_date = $days*24*3600 + gmmktime(date("H",$date),date("i",$date),date("s",$date),
+ date("m",$date),date("d",$date),date("Y",$date));
+ // multiply just to make MS happy
+ $big_date *= 10000000;
+
+ $high_part = floor($big_date / $factor);
+ // lower 4 bytes
+ $low_part = floor((($big_date / $factor) - $high_part) * $factor);
+
+ // Make HEX string
+ $res = '';
+
+ for ($i = 0; $i < 4; ++$i) {
+ $hex = $low_part % 0x100;
+ $res .= pack('c', $hex);
+ $low_part /= 0x100;
+ }
+ for ($i = 0; $i < 4; ++$i) {
+ $hex = $high_part % 0x100;
+ $res .= pack('c', $hex);
+ $high_part /= 0x100;
+ }
+ return $res;
+ }
+
+ /**
+ * Returns a timestamp from an OLE container's date
+ *
+ * @access public
+ * @static
+ * @param integer $string A binary string with the encoded date
+ * @return string The timestamp corresponding to the string
+ */
+ public static function OLE2LocalDate($string)
+ {
+ if (strlen($string) != 8) {
+ return new PEAR_Error("Expecting 8 byte string");
+ }
+
+ // factor used for separating numbers into 4 bytes parts
+ $factor = pow(2,32);
+ $high_part = 0;
+ for ($i = 0; $i < 4; ++$i) {
+ list(, $high_part) = unpack('C', $string{(7 - $i)});
+ if ($i < 3) {
+ $high_part *= 0x100;
+ }
+ }
+ $low_part = 0;
+ for ($i = 4; $i < 8; ++$i) {
+ list(, $low_part) = unpack('C', $string{(7 - $i)});
+ if ($i < 7) {
+ $low_part *= 0x100;
+ }
+ }
+ $big_date = ($high_part * $factor) + $low_part;
+ // translate to seconds
+ $big_date /= 10000000;
+
+ // days from 1-1-1601 until the beggining of UNIX era
+ $days = 134774;
+
+ // translate to seconds from beggining of UNIX era
+ $big_date -= $days * 24 * 3600;
+ return floor($big_date);
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/ChainedBlockStream.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/ChainedBlockStream.php
new file mode 100644
index 0000000..df96995
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/ChainedBlockStream.php
@@ -0,0 +1,234 @@
+params);
+ if (!isset($this->params['oleInstanceId'],
+ $this->params['blockId'],
+ $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) {
+
+ if ($options & STREAM_REPORT_ERRORS) {
+ trigger_error('OLE stream not found', E_USER_WARNING);
+ }
+ return false;
+ }
+ $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']];
+
+ $blockId = $this->params['blockId'];
+ $this->data = '';
+ if (isset($this->params['size']) &&
+ $this->params['size'] < $this->ole->bigBlockThreshold &&
+ $blockId != $this->ole->root->_StartBlock) {
+
+ // Block id refers to small blocks
+ $rootPos = $this->ole->_getBlockOffset($this->ole->root->_StartBlock);
+ while ($blockId != -2) {
+ $pos = $rootPos + $blockId * $this->ole->bigBlockSize;
+ $blockId = $this->ole->sbat[$blockId];
+ fseek($this->ole->_file_handle, $pos);
+ $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
+ }
+ } else {
+ // Block id refers to big blocks
+ while ($blockId != -2) {
+ $pos = $this->ole->_getBlockOffset($blockId);
+ fseek($this->ole->_file_handle, $pos);
+ $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
+ $blockId = $this->ole->bbat[$blockId];
+ }
+ }
+ if (isset($this->params['size'])) {
+ $this->data = substr($this->data, 0, $this->params['size']);
+ }
+
+ if ($options & STREAM_USE_PATH) {
+ $openedPath = $path;
+ }
+
+ return true;
+ }
+
+ /**
+ * Implements support for fclose().
+ * @return string
+ */
+ public function stream_close()
+ {
+ $this->ole = null;
+ unset($GLOBALS['_OLE_INSTANCES']);
+ }
+
+ /**
+ * Implements support for fread(), fgets() etc.
+ * @param int maximum number of bytes to read
+ * @return string
+ */
+ public function stream_read($count)
+ {
+ if ($this->stream_eof()) {
+ return false;
+ }
+ $s = substr($this->data, $this->pos, $count);
+ $this->pos += $count;
+ return $s;
+ }
+
+ /**
+ * Implements support for feof().
+ * @return bool TRUE if the file pointer is at EOF; otherwise FALSE
+ */
+ public function stream_eof()
+ {
+ $eof = $this->pos >= strlen($this->data);
+ // Workaround for bug in PHP 5.0.x: http://bugs.php.net/27508
+ if (version_compare(PHP_VERSION, '5.0', '>=') &&
+ version_compare(PHP_VERSION, '5.1', '<')) {
+
+ $eof = !$eof;
+ }
+ return $eof;
+ }
+
+ /**
+ * Returns the position of the file pointer, i.e. its offset into the file
+ * stream. Implements support for ftell().
+ * @return int
+ */
+ public function stream_tell()
+ {
+ return $this->pos;
+ }
+
+ /**
+ * Implements support for fseek().
+ * @param int byte offset
+ * @param int SEEK_SET, SEEK_CUR or SEEK_END
+ * @return bool
+ */
+ public function stream_seek($offset, $whence)
+ {
+ if ($whence == SEEK_SET && $offset >= 0) {
+ $this->pos = $offset;
+ } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) {
+ $this->pos += $offset;
+ } elseif ($whence == SEEK_END && -$offset <= sizeof($this->data)) {
+ $this->pos = strlen($this->data) + $offset;
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Implements support for fstat(). Currently the only supported field is
+ * "size".
+ * @return array
+ */
+ public function stream_stat()
+ {
+ return array(
+ 'size' => strlen($this->data),
+ );
+ }
+
+ // Methods used by stream_wrapper_register() that are not implemented:
+ // bool stream_flush ( void )
+ // int stream_write ( string data )
+ // bool rename ( string path_from, string path_to )
+ // bool mkdir ( string path, int mode, int options )
+ // bool rmdir ( string path, int options )
+ // bool dir_opendir ( string path, int options )
+ // array url_stat ( string path, int flags )
+ // string dir_readdir ( void )
+ // bool dir_rewinddir ( void )
+ // bool dir_closedir ( void )
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_File.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_File.php
new file mode 100644
index 0000000..02ec4c9
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_File.php
@@ -0,0 +1,130 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+// $Id: File.php,v 1.11 2007/02/13 21:00:42 schmidt Exp $
+
+
+/** PHPExcel root directory */
+if (!defined('PHPEXCEL_ROOT')) {
+ /**
+ * @ignore
+ */
+ define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../../');
+}
+
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE/OLE_PPS.php';
+
+/**
+* Class for creating File PPS's for OLE containers
+*
+* @author Xavier Noguer
+* @category PHPExcel
+* @package PHPExcel_Shared_OLE
+*/
+class PHPExcel_Shared_OLE_PPS_File extends PHPExcel_Shared_OLE_PPS
+ {
+ /**
+ * The temporary dir for storing the OLE file
+ * @var string
+ */
+ public $_tmp_dir;
+
+ /**
+ * The constructor
+ *
+ * @access public
+ * @param string $name The name of the file (in Unicode)
+ * @see OLE::Asc2Ucs()
+ */
+ public function __construct($name)
+ {
+ $this->_tmp_dir = '';
+ parent::__construct(
+ null,
+ $name,
+ PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE,
+ null,
+ null,
+ null,
+ null,
+ null,
+ '',
+ array());
+ }
+
+ /**
+ * Sets the temp dir used for storing the OLE file
+ *
+ * @access public
+ * @param string $dir The dir to be used as temp dir
+ * @return true if given dir is valid, false otherwise
+ */
+ public function setTempDir($dir)
+ {
+ if (is_dir($dir)) {
+ $this->_tmp_dir = $dir;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Initialization method. Has to be called right after OLE_PPS_File().
+ *
+ * @access public
+ * @return mixed true on success
+ */
+ public function init()
+ {
+ $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_File");
+ $fh = fopen($this->_tmp_filename, "w+b");
+ if ($fh === false) {
+ throw new Exception("Can't create temporary file");
+ }
+ $this->_PPS_FILE = $fh;
+ if ($this->_PPS_FILE) {
+ fseek($this->_PPS_FILE, 0);
+ }
+ return true;
+ }
+
+ /**
+ * Append data to PPS
+ *
+ * @access public
+ * @param string $data The data to append
+ */
+ public function append($data)
+ {
+ if ($this->_PPS_FILE) {
+ fwrite($this->_PPS_FILE, $data);
+ } else {
+ $this->_data .= $data;
+ }
+ }
+
+ /**
+ * Returns a stream for reading this file using fread() etc.
+ * @return resource a read-only stream
+ */
+ public function getStream()
+ {
+ $this->ole->getStream($this);
+ }
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_PPS.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_PPS.php
new file mode 100644
index 0000000..a0d6ad2
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_PPS.php
@@ -0,0 +1,228 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+// $Id: PPS.php,v 1.7 2007/02/13 21:00:42 schmidt Exp $
+
+
+/** PHPExcel root directory */
+if (!defined('PHPEXCEL_ROOT')) {
+ /**
+ * @ignore
+ */
+ define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../../');
+}
+
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE.php';
+
+/**
+* Class for creating PPS's for OLE containers
+*
+* @author Xavier Noguer
+* @category PHPExcel
+* @package PHPExcel_Shared_OLE
+*/
+class PHPExcel_Shared_OLE_PPS
+{
+ /**
+ * The PPS index
+ * @var integer
+ */
+ public $No;
+
+ /**
+ * The PPS name (in Unicode)
+ * @var string
+ */
+ public $Name;
+
+ /**
+ * The PPS type. Dir, Root or File
+ * @var integer
+ */
+ public $Type;
+
+ /**
+ * The index of the previous PPS
+ * @var integer
+ */
+ public $PrevPps;
+
+ /**
+ * The index of the next PPS
+ * @var integer
+ */
+ public $NextPps;
+
+ /**
+ * The index of it's first child if this is a Dir or Root PPS
+ * @var integer
+ */
+ public $DirPps;
+
+ /**
+ * A timestamp
+ * @var integer
+ */
+ public $Time1st;
+
+ /**
+ * A timestamp
+ * @var integer
+ */
+ public $Time2nd;
+
+ /**
+ * Starting block (small or big) for this PPS's data inside the container
+ * @var integer
+ */
+ public $_StartBlock;
+
+ /**
+ * The size of the PPS's data (in bytes)
+ * @var integer
+ */
+ public $Size;
+
+ /**
+ * The PPS's data (only used if it's not using a temporary file)
+ * @var string
+ */
+ public $_data;
+
+ /**
+ * Array of child PPS's (only used by Root and Dir PPS's)
+ * @var array
+ */
+ public $children = array();
+
+ /**
+ * Pointer to OLE container
+ * @var OLE
+ */
+ public $ole;
+
+ /**
+ * The constructor
+ *
+ * @access public
+ * @param integer $No The PPS index
+ * @param string $name The PPS name
+ * @param integer $type The PPS type. Dir, Root or File
+ * @param integer $prev The index of the previous PPS
+ * @param integer $next The index of the next PPS
+ * @param integer $dir The index of it's first child if this is a Dir or Root PPS
+ * @param integer $time_1st A timestamp
+ * @param integer $time_2nd A timestamp
+ * @param string $data The (usually binary) source data of the PPS
+ * @param array $children Array containing children PPS for this PPS
+ */
+ public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
+ {
+ $this->No = $No;
+ $this->Name = $name;
+ $this->Type = $type;
+ $this->PrevPps = $prev;
+ $this->NextPps = $next;
+ $this->DirPps = $dir;
+ $this->Time1st = $time_1st;
+ $this->Time2nd = $time_2nd;
+ $this->_data = $data;
+ $this->children = $children;
+ if ($data != '') {
+ $this->Size = strlen($data);
+ } else {
+ $this->Size = 0;
+ }
+ }
+
+ /**
+ * Returns the amount of data saved for this PPS
+ *
+ * @access public
+ * @return integer The amount of data (in bytes)
+ */
+ public function _DataLen()
+ {
+ if (!isset($this->_data)) {
+ return 0;
+ }
+ if (isset($this->_PPS_FILE)) {
+ fseek($this->_PPS_FILE, 0);
+ $stats = fstat($this->_PPS_FILE);
+ return $stats[7];
+ } else {
+ return strlen($this->_data);
+ }
+ }
+
+ /**
+ * Returns a string with the PPS's WK (What is a WK?)
+ *
+ * @access public
+ * @return string The binary string
+ */
+ public function _getPpsWk()
+ {
+ $ret = $this->Name;
+ for ($i = 0; $i < (64 - strlen($this->Name)); ++$i) {
+ $ret .= "\x00";
+ }
+ $ret .= pack("v", strlen($this->Name) + 2) // 66
+ . pack("c", $this->Type) // 67
+ . pack("c", 0x00) //UK // 68
+ . pack("V", $this->PrevPps) //Prev // 72
+ . pack("V", $this->NextPps) //Next // 76
+ . pack("V", $this->DirPps) //Dir // 80
+ . "\x00\x09\x02\x00" // 84
+ . "\x00\x00\x00\x00" // 88
+ . "\xc0\x00\x00\x00" // 92
+ . "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root
+ . "\x00\x00\x00\x00" // 100
+ . PHPExcel_Shared_OLE::LocalDate2OLE($this->Time1st) // 108
+ . PHPExcel_Shared_OLE::LocalDate2OLE($this->Time2nd) // 116
+ . pack("V", isset($this->_StartBlock)?
+ $this->_StartBlock:0) // 120
+ . pack("V", $this->Size) // 124
+ . pack("V", 0); // 128
+ return $ret;
+ }
+
+ /**
+ * Updates index and pointers to previous, next and children PPS's for this
+ * PPS. I don't think it'll work with Dir PPS's.
+ *
+ * @access public
+ * @param array &$pps_array Reference to the array of PPS's for the whole OLE
+ * container
+ * @return integer The index for this PPS
+ */
+ public function _savePpsSetPnt(&$pps_array)
+ {
+ $pps_array[count($pps_array)] = &$this;
+ $this->No = count($pps_array) - 1;
+ $this->PrevPps = 0xFFFFFFFF;
+ $this->NextPps = 0xFFFFFFFF;
+ if (count($this->children) > 0) {
+ $this->DirPps = $this->children[0]->_savePpsSetPnt($pps_array);
+ } else {
+ $this->DirPps = 0xFFFFFFFF;
+ }
+ return $this->No;
+ }
+ }
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_Root.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_Root.php
new file mode 100644
index 0000000..3cf1d1b
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLE/OLE_Root.php
@@ -0,0 +1,489 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+// $Id: Root.php,v 1.9 2005/04/23 21:53:49 dufuz Exp $
+
+
+/** PHPExcel root directory */
+if (!defined('PHPEXCEL_ROOT')) {
+ /**
+ * @ignore
+ */
+ define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../../');
+}
+
+require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/OLE/OLE_PPS.php';
+
+/**
+* Class for creating Root PPS's for OLE containers
+*
+* @author Xavier Noguer
+* @category PHPExcel
+* @package PHPExcel_Shared_OLE
+*/
+class PHPExcel_Shared_OLE_PPS_Root extends PHPExcel_Shared_OLE_PPS
+ {
+ /**
+ * The temporary dir for storing the OLE file
+ * @var string
+ */
+ public $_tmp_dir;
+
+ /**
+ * @param integer $time_1st A timestamp
+ * @param integer $time_2nd A timestamp
+ */
+ public function __construct($time_1st, $time_2nd, $raChild)
+ {
+ $this->_tmp_dir = '';
+ parent::__construct(
+ null,
+ PHPExcel_Shared_OLE::Asc2Ucs('Root Entry'),
+ PHPExcel_Shared_OLE::OLE_PPS_TYPE_ROOT,
+ null,
+ null,
+ null,
+ $time_1st,
+ $time_2nd,
+ null,
+ $raChild);
+ }
+
+ /**
+ * Sets the temp dir used for storing the OLE file
+ *
+ * @access public
+ * @param string $dir The dir to be used as temp dir
+ * @return true if given dir is valid, false otherwise
+ */
+ public function setTempDir($dir)
+ {
+ if (is_dir($dir)) {
+ $this->_tmp_dir = $dir;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Method for saving the whole OLE container (including files).
+ * In fact, if called with an empty argument (or '-'), it saves to a
+ * temporary file and then outputs it's contents to stdout.
+ *
+ * @param string $filename The name of the file where to save the OLE container
+ * @access public
+ * @return mixed true on success
+ */
+ public function save($filename)
+ {
+ // Initial Setting for saving
+ $this->_BIG_BLOCK_SIZE = pow(2,
+ ((isset($this->_BIG_BLOCK_SIZE))? $this->_adjust2($this->_BIG_BLOCK_SIZE) : 9));
+ $this->_SMALL_BLOCK_SIZE= pow(2,
+ ((isset($this->_SMALL_BLOCK_SIZE))? $this->_adjust2($this->_SMALL_BLOCK_SIZE): 6));
+
+ // Open temp file if we are sending output to stdout
+ if ($filename == '-' || $filename == '') {
+ $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root");
+ $this->_FILEH_ = fopen($this->_tmp_filename,"w+b");
+ if ($this->_FILEH_ == false) {
+ throw new Exception("Can't create temporary file.");
+ }
+ } else {
+ $this->_FILEH_ = fopen($filename, "wb");
+ if ($this->_FILEH_ == false) {
+ throw new Exception("Can't open $filename. It may be in use or protected.");
+ }
+ }
+ // Make an array of PPS's (for Save)
+ $aList = array();
+ $this->_savePpsSetPnt($aList);
+ // calculate values for header
+ list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo);
+ // Save Header
+ $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
+
+ // Make Small Data string (write SBD)
+ $this->_data = $this->_makeSmallData($aList);
+
+ // Write BB
+ $this->_saveBigData($iSBDcnt, $aList);
+ // Write PPS
+ $this->_savePps($aList);
+ // Write Big Block Depot and BDList and Adding Header informations
+ $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);
+ // Close File, send it to stdout if necessary
+ if (($filename == '-') || ($filename == '')) {
+ fseek($this->_FILEH_, 0);
+ fpassthru($this->_FILEH_);
+ fclose($this->_FILEH_);
+ // Delete the temporary file.
+ unlink($this->_tmp_filename);
+ } else {
+ fclose($this->_FILEH_);
+ }
+
+ return true;
+ }
+
+ /**
+ * Calculate some numbers
+ *
+ * @access public
+ * @param array $raList Reference to an array of PPS's
+ * @return array The array of numbers
+ */
+ public function _calcSize(&$raList)
+ {
+ // Calculate Basic Setting
+ list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0);
+ $iSmallLen = 0;
+ $iSBcnt = 0;
+ for ($i = 0; $i < count($raList); ++$i) {
+ if ($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE) {
+ $raList[$i]->Size = $raList[$i]->_DataLen();
+ if ($raList[$i]->Size < PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) {
+ $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE)
+ + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0);
+ } else {
+ $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) +
+ (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0));
+ }
+ }
+ }
+ $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE;
+ $iSlCnt = floor($this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE);
+ $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0);
+ $iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) +
+ (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0));
+ $iCnt = count($raList);
+ $iBdCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_PPS_SIZE;
+ $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0));
+
+ return array($iSBDcnt, $iBBcnt, $iPPScnt);
+ }
+
+ /**
+ * Helper function for caculating a magic value for block sizes
+ *
+ * @access public
+ * @param integer $i2 The argument
+ * @see save()
+ * @return integer
+ */
+ public function _adjust2($i2)
+ {
+ $iWk = log($i2)/log(2);
+ return ($iWk > floor($iWk))? floor($iWk)+1:$iWk;
+ }
+
+ /**
+ * Save OLE header
+ *
+ * @access public
+ * @param integer $iSBDcnt
+ * @param integer $iBBcnt
+ * @param integer $iPPScnt
+ */
+ public function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt)
+ {
+ $FILE = $this->_FILEH_;
+
+ // Calculate Basic Setting
+ $iBlCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
+ $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
+
+ $iBdExL = 0;
+ $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
+ $iAllW = $iAll;
+ $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
+ $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
+
+ // Calculate BD count
+ if ($iBdCnt > $i1stBdL) {
+ while (1) {
+ ++$iBdExL;
+ ++$iAllW;
+ $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
+ $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
+ if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) {
+ break;
+ }
+ }
+ }
+
+ // Save Header
+ fwrite($FILE,
+ "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . pack("v", 0x3b)
+ . pack("v", 0x03)
+ . pack("v", -2)
+ . pack("v", 9)
+ . pack("v", 6)
+ . pack("v", 0)
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . pack("V", $iBdCnt)
+ . pack("V", $iBBcnt+$iSBDcnt) //ROOT START
+ . pack("V", 0)
+ . pack("V", 0x1000)
+ . pack("V", $iSBDcnt ? 0 : -2) //Small Block Depot
+ . pack("V", $iSBDcnt)
+ );
+ // Extra BDList Start, Count
+ if ($iBdCnt < $i1stBdL) {
+ fwrite($FILE,
+ pack("V", -2). // Extra BDList Start
+ pack("V", 0) // Extra BDList Count
+ );
+ } else {
+ fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL));
+ }
+
+ // BDList
+ for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
+ fwrite($FILE, pack("V", $iAll+$i));
+ }
+ if ($i < $i1stBdL) {
+ for ($j = 0; $j < ($i1stBdL-$i); ++$j) {
+ fwrite($FILE, (pack("V", -1)));
+ }
+ }
+ }
+
+ /**
+ * Saving big data (PPS's with data bigger than PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL)
+ *
+ * @access public
+ * @param integer $iStBlk
+ * @param array &$raList Reference to array of PPS's
+ */
+ public function _saveBigData($iStBlk, &$raList)
+ {
+ $FILE = $this->_FILEH_;
+
+ // cycle through PPS's
+ for ($i = 0; $i < count($raList); ++$i) {
+ if ($raList[$i]->Type != PHPExcel_Shared_OLE::OLE_PPS_TYPE_DIR) {
+ $raList[$i]->Size = $raList[$i]->_DataLen();
+ if (($raList[$i]->Size >= PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) ||
+ (($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data)))
+ {
+ // Write Data
+ if (isset($raList[$i]->_PPS_FILE)) {
+ $iLen = 0;
+ fseek($raList[$i]->_PPS_FILE, 0); // To The Top
+ while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) {
+ $iLen += strlen($sBuff);
+ fwrite($FILE, $sBuff);
+ }
+ } else {
+ fwrite($FILE, $raList[$i]->_data);
+ }
+
+ if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) {
+ for ($j = 0; $j < ($this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)); ++$j) {
+ fwrite($FILE, "\x00");
+ }
+ }
+ // Set For PPS
+ $raList[$i]->_StartBlock = $iStBlk;
+ $iStBlk +=
+ (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) +
+ (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0));
+ }
+ // Close file for each PPS, and unlink it
+ if (isset($raList[$i]->_PPS_FILE)) {
+ fclose($raList[$i]->_PPS_FILE);
+ $raList[$i]->_PPS_FILE = null;
+ unlink($raList[$i]->_tmp_filename);
+ }
+ }
+ }
+ }
+
+ /**
+ * get small data (PPS's with data smaller than PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL)
+ *
+ * @access public
+ * @param array &$raList Reference to array of PPS's
+ */
+ public function _makeSmallData(&$raList)
+ {
+ $sRes = '';
+ $FILE = $this->_FILEH_;
+ $iSmBlk = 0;
+
+ for ($i = 0; $i < count($raList); ++$i) {
+ // Make SBD, small data string
+ if ($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE) {
+ if ($raList[$i]->Size <= 0) {
+ continue;
+ }
+ if ($raList[$i]->Size < PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) {
+ $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE)
+ + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0);
+ // Add to SBD
+ for ($j = 0; $j < ($iSmbCnt-1); ++$j) {
+ fwrite($FILE, pack("V", $j+$iSmBlk+1));
+ }
+ fwrite($FILE, pack("V", -2));
+
+ // Add to Data String(this will be written for RootEntry)
+ if ($raList[$i]->_PPS_FILE) {
+ fseek($raList[$i]->_PPS_FILE, 0); // To The Top
+ while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) {
+ $sRes .= $sBuff;
+ }
+ } else {
+ $sRes .= $raList[$i]->_data;
+ }
+ if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) {
+ for ($j = 0; $j < ($this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); ++$j) {
+ $sRes .= "\x00";
+ }
+ }
+ // Set for PPS
+ $raList[$i]->_StartBlock = $iSmBlk;
+ $iSmBlk += $iSmbCnt;
+ }
+ }
+ }
+ $iSbCnt = floor($this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE);
+ if ($iSmBlk % $iSbCnt) {
+ for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); ++$i) {
+ fwrite($FILE, pack("V", -1));
+ }
+ }
+ return $sRes;
+ }
+
+ /**
+ * Saves all the PPS's WKs
+ *
+ * @access public
+ * @param array $raList Reference to an array with all PPS's
+ */
+ public function _savePps(&$raList)
+ {
+ // Save each PPS WK
+ for ($i = 0; $i < count($raList); ++$i) {
+ fwrite($this->_FILEH_, $raList[$i]->_getPpsWk());
+ }
+ // Adjust for Block
+ $iCnt = count($raList);
+ $iBCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_PPS_SIZE;
+ if ($iCnt % $iBCnt) {
+ for ($i = 0; $i < (($iBCnt - ($iCnt % $iBCnt)) * PHPExcel_Shared_OLE::OLE_PPS_SIZE); ++$i) {
+ fwrite($this->_FILEH_, "\x00");
+ }
+ }
+ }
+
+ /**
+ * Saving Big Block Depot
+ *
+ * @access public
+ * @param integer $iSbdSize
+ * @param integer $iBsize
+ * @param integer $iPpsCnt
+ */
+ public function _saveBbd($iSbdSize, $iBsize, $iPpsCnt)
+ {
+ $FILE = $this->_FILEH_;
+ // Calculate Basic Setting
+ $iBbCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
+ $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
+
+ $iBdExL = 0;
+ $iAll = $iBsize + $iPpsCnt + $iSbdSize;
+ $iAllW = $iAll;
+ $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
+ $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
+ // Calculate BD count
+ if ($iBdCnt >$i1stBdL) {
+ while (1) {
+ ++$iBdExL;
+ ++$iAllW;
+ $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
+ $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
+ if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) {
+ break;
+ }
+ }
+ }
+
+ // Making BD
+ // Set for SBD
+ if ($iSbdSize > 0) {
+ for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
+ fwrite($FILE, pack("V", $i+1));
+ }
+ fwrite($FILE, pack("V", -2));
+ }
+ // Set for B
+ for ($i = 0; $i < ($iBsize - 1); ++$i) {
+ fwrite($FILE, pack("V", $i+$iSbdSize+1));
+ }
+ fwrite($FILE, pack("V", -2));
+
+ // Set for PPS
+ for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
+ fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1));
+ }
+ fwrite($FILE, pack("V", -2));
+ // Set for BBD itself ( 0xFFFFFFFD : BBD)
+ for ($i = 0; $i < $iBdCnt; ++$i) {
+ fwrite($FILE, pack("V", 0xFFFFFFFD));
+ }
+ // Set for ExtraBDList
+ for ($i = 0; $i < $iBdExL; ++$i) {
+ fwrite($FILE, pack("V", 0xFFFFFFFC));
+ }
+ // Adjust for Block
+ if (($iAllW + $iBdCnt) % $iBbCnt) {
+ for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); ++$i) {
+ fwrite($FILE, pack("V", -1));
+ }
+ }
+ // Extra BDList
+ if ($iBdCnt > $i1stBdL) {
+ $iN=0;
+ $iNb=0;
+ for ($i = $i1stBdL;$i < $iBdCnt; $i++, ++$iN) {
+ if ($iN >= ($iBbCnt - 1)) {
+ $iN = 0;
+ ++$iNb;
+ fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb));
+ }
+ fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i));
+ }
+ if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) {
+ for ($i = 0; $i < (($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1))); ++$i) {
+ fwrite($FILE, pack("V", -1));
+ }
+ }
+ fwrite($FILE, pack("V", -2));
+ }
+ }
+ }
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLERead.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLERead.php
new file mode 100644
index 0000000..46cb5ad
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/OLERead.php
@@ -0,0 +1,305 @@
+data = file_get_contents($sFileName);
+
+ // Check OLE identifier
+ if (substr($this->data, 0, 8) != self::IDENTIFIER_OLE) {
+ throw new Exception('The filename ' . $sFileName . ' is not recognised as an OLE file');
+ }
+
+ // Total number of sectors used for the SAT
+ $this->numBigBlockDepotBlocks = $this->_GetInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
+
+ // SecID of the first sector of the directory stream
+ $this->rootStartBlock = $this->_GetInt4d($this->data, self::ROOT_START_BLOCK_POS);
+
+ // SecID of the first sector of the SSAT (or -2 if not extant)
+ $this->sbdStartBlock = $this->_GetInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
+
+ // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
+ $this->extensionBlock = $this->_GetInt4d($this->data, self::EXTENSION_BLOCK_POS);
+
+ // Total number of sectors used by MSAT
+ $this->numExtensionBlocks = $this->_GetInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
+
+ $bigBlockDepotBlocks = array();
+ $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
+
+ $bbdBlocks = $this->numBigBlockDepotBlocks;
+
+ if ($this->numExtensionBlocks != 0) {
+ $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
+ }
+
+ for ($i = 0; $i < $bbdBlocks; ++$i) {
+ $bigBlockDepotBlocks[$i] = $this->_GetInt4d($this->data, $pos);
+ $pos += 4;
+ }
+
+ for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
+ $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
+ $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
+
+ for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
+ $bigBlockDepotBlocks[$i] = $this->_GetInt4d($this->data, $pos);
+ $pos += 4;
+ }
+
+ $bbdBlocks += $blocksToRead;
+ if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
+ $this->extensionBlock = $this->_GetInt4d($this->data, $pos);
+ }
+ }
+
+ $pos = 0;
+ $index = 0;
+ $this->bigBlockChain = array();
+
+ for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
+ $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
+
+ for ($j = 0 ; $j < self::BIG_BLOCK_SIZE / 4; ++$j) {
+ $this->bigBlockChain[$index] = $this->_GetInt4d($this->data, $pos);
+ $pos += 4 ;
+ ++$index;
+ }
+ }
+
+ $pos = 0;
+ $index = 0;
+ $sbdBlock = $this->sbdStartBlock;
+ $this->smallBlockChain = array();
+
+ while ($sbdBlock != -2) {
+ $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
+
+ for ($j = 0; $j < self::BIG_BLOCK_SIZE / 4; ++$j) {
+ $this->smallBlockChain[$index] = $this->_GetInt4d($this->data, $pos);
+ $pos += 4;
+ ++$index;
+ }
+
+ $sbdBlock = $this->bigBlockChain[$sbdBlock];
+ }
+
+ $block = $this->rootStartBlock;
+ $pos = 0;
+
+ // read the directory stream
+ $this->entry = $this->_readData($block);
+
+ $this->_readPropertySets();
+
+ }
+
+ /**
+ * Extract binary stream data, workbook stream + sheet streams
+ *
+ * @return string
+ */
+ public function getWorkBook()
+ {
+ if ($this->props[$this->wrkbook]['size'] < self::SMALL_BLOCK_THRESHOLD){
+ $rootdata = $this->_readData($this->props[$this->rootentry]['startBlock']);
+
+ $streamData = '';
+ $block = $this->props[$this->wrkbook]['startBlock'];
+
+ $pos = 0;
+ while ($block != -2) {
+ $pos = $block * self::SMALL_BLOCK_SIZE;
+ $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
+
+ $block = $this->smallBlockChain[$block];
+ }
+
+ return $streamData;
+
+
+ } else {
+ $numBlocks = $this->props[$this->wrkbook]['size'] / self::BIG_BLOCK_SIZE;
+ if ($this->props[$this->wrkbook]['size'] % self::BIG_BLOCK_SIZE != 0) {
+ ++$numBlocks;
+ }
+
+ if ($numBlocks == 0) return '';
+
+
+ $streamData = '';
+ $block = $this->props[$this->wrkbook]['startBlock'];
+
+ $pos = 0;
+
+ while ($block != -2) {
+ $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+ $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+ $block = $this->bigBlockChain[$block];
+ }
+
+ return $streamData;
+ }
+ }
+
+ /**
+ * Read a standard stream (by joining sectors using information from SAT)
+ *
+ * @param int $bl Sector ID where the stream starts
+ * @return string Data for standard stream
+ */
+ private function _readData($bl)
+ {
+ $block = $bl;
+ $pos = 0;
+ $data = '';
+
+ while ($block != -2) {
+ $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+ $data = $data . substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+ $block = $this->bigBlockChain[$block];
+ }
+ return $data;
+ }
+
+ /**
+ * Read entries in the directory stream.
+ */
+ private function _readPropertySets()
+ {
+ $offset = 0;
+
+ // loop through entires, each entry is 128 bytes
+ while ($offset < strlen($this->entry)) {
+ // entry data (128 bytes)
+ $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
+
+ // size in bytes of name
+ $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS+1]) << 8);
+
+ // type of entry
+ $type = ord($d[self::TYPE_POS]);
+
+ // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
+ // sectorID of first sector of the short-stream container stream, if this entry is root entry
+ $startBlock = $this->_GetInt4d($d, self::START_BLOCK_POS);
+
+ $size = $this->_GetInt4d($d, self::SIZE_POS);
+
+ $name = '';
+ for ($i = 0; $i < $nameSize ; ++$i) {
+ $name .= $d[$i];
+ }
+
+ $name = str_replace("\x00", "", $name);
+
+ $this->props[] = array (
+ 'name' => $name,
+ 'type' => $type,
+ 'startBlock' => $startBlock,
+ 'size' => $size);
+
+ // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
+ if (($name == 'Workbook') || ($name == 'Book') || ($name == 'WORKBOOK')) {
+ $this->wrkbook = count($this->props) - 1;
+ }
+
+ // Root entry
+ if ($name == 'Root Entry' || $name == 'ROOT ENTRY' || $name == 'R') {
+ $this->rootentry = count($this->props) - 1;
+ }
+
+ $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
+ }
+
+ }
+
+ /**
+ * Read 4 bytes of data at specified position
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private function _GetInt4d($data, $pos)
+ {
+ // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
+ $_or_24 = ord($data[$pos+3]);
+ if ($_or_24>=128) $_ord_24 = -abs((256-$_or_24) << 24);
+ else $_ord_24 = ($_or_24&127) << 24;
+
+ return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $_ord_24;
+ }
+
+}
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF.php
new file mode 100644
index 0000000..b66e8e0
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF.php
@@ -0,0 +1,39 @@
+.
+//
+// See LICENSE.TXT file for more information.
+// ----------------------------------------------------------------------------
+//
+// Description : PHP class to creates array representations for
+// 2D barcodes to be used with TCPDF.
+//
+// Author: Nicola Asuni
+//
+// (c) Copyright:
+// Nicola Asuni
+// Tecnick.com S.r.l.
+// Via della Pace, 11
+// 09044 Quartucciu (CA)
+// ITALY
+// www.tecnick.com
+// info@tecnick.com
+//============================================================+
+
+/**
+ * PHP class to creates array representations for 2D barcodes to be used with TCPDF.
+ * @package com.tecnick.tcpdf
+ * @abstract Functions for generating string representation of 2D barcodes.
+ * @author Nicola Asuni
+ * @copyright 2008-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
+ * @link http://www.tcpdf.org
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ * @version 1.0.000
+ */
+
+ /**
+ * PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www.tcpdf.org).
+ * @name TCPDFBarcode
+ * @package com.tecnick.tcpdf
+ * @version 1.0.000
+ * @author Nicola Asuni
+ * @link http://www.tcpdf.org
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ */
+class TCPDF2DBarcode {
+
+ /**
+ * @var array representation of barcode.
+ * @access protected
+ */
+ protected $barcode_array;
+
+ /**
+ * This is the class constructor.
+ * Return an array representations for 2D barcodes:
+ *
$arrcode['code'] code to be printed on text label
+ *
$arrcode['num_rows'] required number of rows
+ *
$arrcode['num_cols'] required number of columns
+ *
$arrcode['bcode'][$r][$c] value of the cell is $r row and $c column (0 = transparent, 1 = black)
+ * @param string $code code to print
+ * @param string $type type of barcode:
TEST
...TO BE IMPLEMENTED
+ */
+ public function __construct($code, $type) {
+ $this->setBarcode($code, $type);
+ }
+
+ /**
+ * Return an array representations of barcode.
+ * @return array
+ */
+ public function getBarcodeArray() {
+ return $this->barcode_array;
+ }
+
+ /**
+ * Set the barcode.
+ * @param string $code code to print
+ * @param string $type type of barcode:
TEST
...TO BE IMPLEMENTED
+ * @return array
+ */
+ public function setBarcode($code, $type) {
+ $mode = explode(',', $type);
+ switch (strtoupper($mode[0])) {
+ case 'TEST': { // TEST MODE
+ $this->barcode_array['num_rows'] = 5;
+ $this->barcode_array['num_cols'] = 15;
+ $this->barcode_array['bcode'] = array(
+ array(1,1,1,0,1,1,1,0,1,1,1,0,1,1,1),
+ array(0,1,0,0,1,0,0,0,1,0,0,0,0,1,0),
+ array(0,1,0,0,1,1,0,0,1,1,1,0,0,1,0),
+ array(0,1,0,0,1,0,0,0,0,0,1,0,0,1,0),
+ array(0,1,0,0,1,1,1,0,1,1,1,0,0,1,0)
+ );
+ break;
+ }
+
+ // ... Add here real 2D barcodes ...
+
+ default: {
+ $this->barcode_array = false;
+ }
+ }
+ }
+} // end of class
+
+//============================================================+
+// END OF FILE
+//============================================================+
+?>
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/CHANGELOG.TXT b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/CHANGELOG.TXT
new file mode 100644
index 0000000..35bb65e
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/CHANGELOG.TXT
@@ -0,0 +1,1141 @@
+4.8.009 (2009-09-30)
+ - Compatibility with PHP 5.3 was improved.
+ - All examples were updated.
+ - Index file for examples was added.
+
+4.8.008 (2009-09-29)
+ - Example 49 was updated.
+ - Underline and linethrough now works with cell stretching mode.
+
+4.8.007 (2009-09-23)
+ - Infinite loop problem caused by nobr attribute was fixed.
+
+4.8.006 (2009-09-23)
+ - Bug item #2864522 "No images if DOCUMENT_ROOT=='/'" was fixed.
+ - Support for text-indent CSS attribute was added.
+ - Method rollbackTransaction() was changed to support self-reassigment of previous object (check source code documentation).
+ - Support for the HTML "nobr" attribute was added to avoid splitting a table or a table row on two pages (i.e.:
...
).
+
+4.8.005 (2009-09-17)
+ - A bug relative to multiple transformations and annotations was fixed.
+
+4.8.004 (2009-09-16)
+ - A bug on _putannotsrefs() method was fixed.
+
+4.8.003 (2009-09-15)
+ - Bug item #2858754 "Division by zero" was fixed.
+ - A bug relative to HTML list items was fixed.
+ - A bug relative to form fields on multiple pages was fixed.
+ - PolyLine() method was added (see example n. 12).
+ - Signature of Polygon() method was changed.
+
+4.8.002 (2009-09-12)
+ - A problem related to CID-0 fonts offset was fixed: if the $cw[1] entry on the CID-0 font file is not defined, then a CID keys offset is introduced.
+
+4.8.001 (2009-09-09)
+ - The appearance streams (AP) for anotations form fields was fixed (see examples n. 14 and 54).
+ - Radiobuttons were fixed.
+
+4.8.000 (2009-09-07)
+ - This version includes some support for Forms fields (see example n. 14) and XHTML forms (see example n. 54).
+ - The following methods were changed to work without JavaScript: TextField(), RadioButton(), ListBox(), ComboBox(), CheckBox(), Button().
+ - Support for Widget annotations was improved.
+ - Alignment of annotation objects was fixed (examples 36 and 41 were updated).
+ - addJavascriptObject() method was added.
+ - Signature of Image() method was changed.
+ - htmlcolors.php file was updated.
+
+--------------------------------------------------------------------------------
+
+4.7.003 (2009-09-03)
+ - Support for TCPDF methods on HTML was improved (see example n. 49).
+
+4.7.002 (2009-09-02)
+ - Bug item #2848892 "writeHTML + table: Gaps between rows" was fixed.
+ - JavaScript support was fixed (see example n. 53).
+
+4.7.001 (2009-08-30)
+ - The Polygon() and Arrow() methods were fixed and improved (see example n. 12).
+
+4.7.000 (2009-08-29)
+ - This is a major release.
+ - Some procedures were internally optimized.
+ - The problem of mixed signature and annotations was fixed (example n. 52).
+
+4.6.030 (2009-08-29)
+ - IMPORTANT: percentages on table cell widths are now relative to the full table width (as in standard HTML).
+ - Various minor bugs were fixed.
+ - Example n. 52 (digital signature) was updated.
+
+4.6.029 (2009-08-26)
+ - PHP4 version was fixed.
+
+4.6.028 (2009-08-25)
+ - Signature algorithm was finally fixed (see example n. 52).
+
+4.6.027 (2009-08-24)
+ - TCPDF now supports unembedded TrueTypeUnicode Fonts (just comment the $file entry on the fonts' php file.
+
+4.6.026 (2009-08-21)
+ - Bug #2841693 "Problem with MultiCell and ishtml and justification" was fixed.
+ - Signature functions were improved but not yet fixed (tcpdf.crt and example n. 52 were updated).
+
+4.6.025 (2009-08-17)
+ - Carriage returns (\r) were removed from source code.
+ - Problem related to set_magic_quotes_runtime() depracated was fixed.
+
+4.6.024 (2009-08-07)
+ - Bug item #2833556 "justification using other units than mm" was fixed.
+ - Documentation was fixed/updated.
+
+4.6.023 (2009-08-02)
+ - Bug item #2830537 "MirrorH can show mask for transparent PNGs" was fixed.
+
+4.6.022 (2009-07-24)
+ - A bug relative to single line printing when using WriteHTMLCell() was fixed.
+ - Signature support were improved but is still experimental.
+ - Fonts Free and Dejavu were updated to latest versions.
+
+4.6.021 (2009-07-20)
+ - Bug item #2824015 "XHTML Ampersand & in hyperlink bug" was fixed.
+ - Bug item #2824036 "Image as hyperlink in table, text displaced at page break" was fixed.
+ - Links alignment on justified text was fixed.
+ - Unicode "\u" modifier was added to re_spaces variable by default.
+
+4.6.020 (2009-07-16)
+ - Bug item #2821921 "issue in example 18" was fixed.
+ - Signature of SetRTL() method was changed.
+
+4.6.019 (2009-07-13)
+ - Bug item #2820703 "xref table broken" was fixed.
+
+4.6.018 (2009-07-10)
+ - Bug item #2819319 "Text over text" was fixed.
+ - Method Arrow() was added to print graphic arrows (example 12 was updated).
+
+4.6.017 (2009-07-05)
+ - Bug item #2816079 "Example 48 not working" was fixed.
+ - The signature of the checkPageBreak() was changed. The parameter $addpage was added to turn off the automatic page creation.
+
+4.6.016 (2009-06-16)
+ - Method setSpacesRE() was added to set the regular expression used for detecting withespaces or word separators. If you are using chinese, try: setSpacesRE('/[\s\p{Z}\p{Lo}]/');, otherwise you can use setSpacesRE('/[\s\p{Z}]/');
+ - The method _putinfo() now automatically fills the metadata with '?' in case of empty string.
+
+4.6.015 (2009-06-11)
+ - Bug #2804667 "word wrap bug" was fixed.
+
+4.6.014 (2009-06-04)
+ - Bug #2800931 "Table thead tag bug" was fixed.
+ - A bug related to
tag was fixed.
+
+4.6.013 (2009-05-28)
+ - List bullets position was fixed for RTL languages.
+
+4.6.012 (2009-05-23)
+ - setUserRights() method doesn't work anymore unless you call the setSignature() method with the Adobe private key!
+
+4.6.011 (2009-05-18)
+ - Signature of the Image() method was changed to include the new $fitbox parameter (see source code documentation).
+
+4.6.010 (2009-05-17)
+ - Image() method was improved: now is possible to specify the maximum dimensions for a constraint box defined by $w and $h parameters, and setting the $resize parameter to null.
+ - tag indent problem was fixed.
+ - $y parameter was added to checkPageBreak() method.
+ - Bug n. 2791773 "writeHTML" was fixed.
+
+4.6.009 (2009-05-13)
+ - xref table for embedded files was fixed.
+
+4.6.008 (2009-05-07)
+ - setSignature() method was improved (but is still experimental).
+ - Example n. 52 was added.
+
+4.6.007 (2009-05-05)
+ - Bug #2786685 "writeHtmlCell and in custom footer" was fixed.
+ - Table header repeating bug was fixed.
+ - Some newlines and tabs are now automatically removed from HTML strings.
+
+4.6.006 (2009-04-28)
+ - Support for "..." was added.
+ - By default TCPDF requires PCRE Unicode support turned on but now works also without it (with limited ability to detect some Unicode blank spaces).
+
+4.6.005 (2009-04-25)
+ - Points (pt) conversion in getHTMLUnitToUnits() was fixed.
+ - Default tcpdf.pem certificate file was added.
+ - Experimental support for signing document was added but it is not yet completed (some help is needed - I think that the calculation of the ByteRange is OK and the problem is on the signature calculation).
+
+4.6.004 (2009-04-23)
+ - Method deletePage() was added to delete pages (see example n. 44).
+
+4.6.003 (2009-04-21)
+ - The caching mechanism of the UTF8StringToArray() method was fixed.
+
+4.6.002 (2009-04-20)
+ - Documentation of rollbackTransaction() method was fixed.
+ - The setImageScale() and getImageScale() methods now set and get the adjusting parameter used by pixelsToUnits() method.
+ - HTML images now support other units of measure than pixels (getHTMLUnitToUnits() is now used instead of pixelsToUnits()).
+ - WARNING: PDF_IMAGE_SCALE_RATIO has been changed by default to 1.
+
+4.6.001 (2009-04-17)
+ - Spaces between HTML block tags are now automatically removed.
+ - The bug related to cMargin changes between tables was fixed.
+
+4.6.000 (2009-04-16)
+ - WARNING: THIS VERSION CHANGES THE BEHAVIOUR OF $x and $y parameters for several TCPDF methods:
+ zero coordinates for $x and $y are now valid coordinates;
+ set $x and $y as empty strings to get the current value.
+ - Some error caused by 'empty' funtion were fixed.
+ - Default color for convertHTMLColorToDec() method was changed to white and the return value for invalid color is false.
+ - HTML on footer bug was fixed.
+ - The following examples were fixed: 5,7,10,17,19,20,21,33,42,43.
+
+4.5.043 (2009-04-15)
+ - Barcode class (barcode.php) was extended to include new linear barcode types (see example n. 27):
+ C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9
+ C39+ : CODE 39 with checksum
+ C39E : CODE 39 EXTENDED
+ C39E+ : CODE 39 EXTENDED + CHECKSUM
+ C93 : CODE 93 - USS-93
+ S25 : Standard 2 of 5
+ S25+ : Standard 2 of 5 + CHECKSUM
+ I25 : Interleaved 2 of 5
+ I25+ : Interleaved 2 of 5 + CHECKSUM
+ C128A : CODE 128 A
+ C128B : CODE 128 B
+ C128C : CODE 128 C
+ EAN2 : 2-Digits UPC-Based Extention
+ EAN5 : 5-Digits UPC-Based Extention
+ EAN8 : EAN 8
+ EAN13 : EAN 13
+ UPCA : UPC-A
+ UPCE : UPC-E
+ MSI : MSI (Variation of Plessey code)
+ MSI+ : MSI + CHECKSUM (modulo 11)
+ POSTNET : POSTNET
+ PLANET : PLANET
+ RMS4CC : RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
+ KIX : KIX (Klant index - Customer index)
+ IMB: Intelligent Mail Barcode - Onecode - USPS-B-3200 (NOTE: requires BCMath PHP extension)
+ CODABAR : CODABAR
+ CODE11 : CODE 11
+ PHARMA : PHARMACODE
+ PHARMA2T : PHARMACODE TWO-TRACKS
+
+4.5.042 (2009-04-15)
+ - Method Write() was fixed for the strings containing only zero value.
+
+4.5.041 (2009-04-14)
+ - Barcode methods were fixed.
+
+4.5.040 (2009-04-14)
+ - Method Write() was fixed to handle empty strings.
+
+4.5.039 (2009-04-11)
+ - Support for linear barcodes was extended (see example n. 27 and barcodes.php documentation).
+
+4.5.038 (2009-04-10)
+ - Write() method was improved to support separators for Japanese, Korean, Chinese Traditional and Chinese Simplified.
+
+4.5.037 (2009-04-09)
+ - General performances were improved.
+ - The signature of the method utf8Bidi() was changed.
+ - The method UniArrSubString() was added.
+ - Experimental support for 2D barcodes were added (see example n. 50 and 2dbarcodes.php class).
+
+4.5.036 (2009-04-03)
+ - TCPDF methods can be called inside the HTML code (see example n. 49).
+ - All tag attributes, such as
must be enclosed within double quotes.
+
+4.5.035 (2009-03-28)
+ - Bug #2717436 "writeHTML rowspan problem (continued)" was fixed.
+ - Bug #2719090 "writeHTML fix follow up" was fixed.
+ - The method _putuserrights() was changed to avoid Adobe Reader 9.1 crash. This broken the 'trick' that was used to display forms in Acrobat Reader.
+
+4.5.034 (2009-03-27)
+ - Bug #2716914 "Bug writeHTML of a table in body and footer related with pb" was fixed.
+ - Bug #2717056 ] "writeHTML problem when setting tr style" was fixed.
+ - The signature of the Cell() method was changed.
+
+4.5.033 (2009-03-27)
+ - The support for rowspan/colspan on HTML tables was improved (see example n. 48).
+
+4.5.032 (2009-03-23)
+ - setPrintFooter(false) bug was fixed.
+
+4.5.031 (2009-03-20)
+ - Table header support was extended to multiple pages.
+
+4.5.030 (2009-03-20)
+ - thead tag is now supported on HTML tables (header rows are repeated after page breaks).
+ - The startTransaction() was improved to autocommit.
+ - List bullets now uses the foreground color (putHtmlListBullet()).
+
+4.5.029 (2009-03-19)
+ - The following methods were added to UNDO commands (see example 47): startTransaction(), commitTransaction(), rollbackTransaction().
+ - All examples were updated.
+
+4.5.028 (2009-03-18)
+ - Bug #2690945 "List Bugs" was fixed.
+ - HTML text alignment on lists was fixed.
+ - The constant PDF_FONT_MONOSPACED was added to the configuration file to define the default monospaced font.
+ - The following methods were fixed: getPageWidth(), getPageHeight(), getBreakMargin().
+ - All examples were updated.
+
+4.5.027 (2009-03-16)
+ - Method getPageDimensions() was added to get page dimensions.
+ - The signature of the following methos were changed: getPageWidth(), getPageHeight(), getBreakMargin().
+ - _parsepng() method was fixed for PNG URL images (fread bug).
+
+4.5.026 (2009-03-11)
+ - Bug #2681793 affecting URL images with spaces was fixed.
+
+4.5.025 (2009-03-10)
+ - A small bug affecting hyphenation support was fixed.
+ - The method SetDefaultMonospacedFont() was added to define the default monospaced font.
+
+4.5.024 (2009-03-07)
+ - The bug #2666493 was fixed "Footer corrupts document".
+
+4.5.023 (2009-03-06)
+ - The bug #2666688 was fixed "Rowspan in tables".
+
+4.5.022 (2009-03-05)
+ - The bug #2659676 was fixed "refer to #2157099 test 4 < BR > problem still not fixed".
+ - addTOC() function bug was fixed.
+
+4.5.020 (2009-03-03)
+ - The following bug was fixed: "function removeSHY corrupts unicode".
+
+4.5.019 (2009-02-28)
+ - The problem of decimal separator using different locale was fixed.
+ - The text hyphenation is now supported (see example n. 46).
+
+4.5.018 (2009-02-26)
+ - The _destroy() method was added to unset all class variables and frees memory.
+ - Now it's possible to call Output() method multiple times.
+
+4.5.017 (2009-02-24)
+ - A minor bug that raises a PHP warning was fixed.
+
+4.5.016 (2009-02-24)
+ - Bug item #2631200 "getNumLines() counts wrong" was fixed.
+ - Multiple attachments bug was fixed.
+ - All class variables are now cleared on Output() for memory otpimization.
+
+4.5.015 (2009-02-18)
+ - Bug item #2612553 "function Write() must not break a line on character" was fixed.
+
+4.5.014 (2009-02-13)
+ - Bug item #2595015 "POSTNET Barcode Checksum Error" was fixed (on barcode.php).
+ - Pagebreak bug for barcode was fixed.
+
+4.5.013 (2009-02-12)
+ - border attribute is now supported on HTML images (only accepts the same values accepted by Cell()).
+
+4.5.012 (2009-02-12)
+ - An error on image border feature was fixed.
+
+4.5.011 (2009-02-12)
+ - HTML links for images are now supported.
+ - height attribute is now supported on HTML cells.
+ - $border parameter was added to Image() and ImageEps() methods.
+ - The method getNumLines() was added to estimate the number of lines required for the specified text.
+
+4.5.010 (2009-01-29)
+ - Bug n. 2546108 "BarCode Y position" was fixed.
+
+4.5.009 (2009-01-26)
+ - Bug n. 2538094 "Empty pdf file created" was fixed.
+
+4.5.008 (2009-01-26)
+ - setPage() method was fixed to correctly restore graphic states.
+ - Source code was cleaned up for performances.
+
+4.5.007 (2009-01-24)
+ - checkPageBreak() and write1DBarcode() methods were fixed.
+ - Source code was cleaned up for performances.
+ - barcodes.php was updated.
+
+4.5.006 (2009-01-23)
+ - getHTMLUnitToPoints() method was replaced by getHTMLUnitToUnits() to fix HTML units bugs.
+
+4.5.005 (2009-01-23)
+ - Page closing bug was fixed.
+
+4.5.004 (2009-01-21)
+ - The access of convertHTMLColorToDec() method was changed to public
+ - Fixed bug on UL tag.
+
+4.5.003 (2009-01-19)
+ - Fonts on different folders are now supported.
+
+4.5.002 (2009-01-07)
+ - addTOC() function was improved (see example n. 45).
+
+4.5.001 (2009-01-04)
+ - The signature of startPageGroup() function was changed.
+ - Method Footer() was improved to automatically print page or page-group number (see example n. 23).
+ - Protected method formatTOCPageNumber() was added to customize the format of page numbers on the Table Of Content.
+ - The signature of addTOC() was changed to include the font used for page numbers.
+
+4.5.000 (2009-01-03)
+ - A new $diskcache parameter was added to class constructor to enable disk caching and reduce RAM memory usage (see example n. 43).
+ - The method movePageTo() was added to move pages to previous positions (see example n. 44).
+ - The methods getAliasNumPage() and getPageNumGroupAlias() were added to get the alias for page number (needed when using movepageTo()).
+ - The methods addTOC() was added to print a Table Of Content (see example n. 45).
+ - Imagick class constant was removed for better compatibility with PHP4.
+ - All existing examples were updated and new examples were added.
+
+4.4.009 (2008-12-29)
+ - Examples 1 and 35 were fixed.
+
+4.4.008 (2008-12-28)
+ - Bug #2472169 "Unordered bullet size not adjusted for unit type" was fixed.
+
+4.4.007 (2008-12-23)
+ - Bug #2459935 "no unit conversion for header line" was fixed.
+ - Example n. 42 for image alpha channel was added.
+ - All examples were updated.
+
+4.4.006 (2008-12-11)
+ - Method setLIsymbol() was changed to reflect latest changes in HTML list handling.
+
+4.4.005 (2008-12-10)
+ - Bug item #2413870 "ordered list override value" was fixed.
+
+4.4.004 (2008-12-10)
+ - The protected method getHTMLUnitToPoints() was added to accept various HTML units of measure (em, ex, px, in, cm, mm, pt, pc, %).
+ - The method intToRoman() was added to convert integer number to Roman representation.
+ - Support fot HTML lists was improved: the CSS property list-style-type is now supported.
+
+4.4.003 (2008-12-09)
+ - Bug item #2412147 "Warning on line 3367" was fixed.
+ - Method setHtmlLinksStyle() was added to set default HTML link colors and font style.
+ - Method addHtmlLink() was changed to use color and style defined on the inline CSS.
+
+4.4.002 (2008-12-09)
+ - Borders on Multicell() were fixed.
+ - Problem of Multicell() on Header function (Bug item #2407579) was fixed.
+ - Problem on graphics tranformations applied to Multicell() was fixed.
+ - Support for ImageMagick was added.
+ - Width calculation for nested tables was fixed.
+
+4.4.001 (2008-12-08)
+ - Some missing core fonts were added on fonts directory.
+ - CID0 fonts rendering was fixed.
+ - HTML support was improved (
and tags are now supported).
+ - Bug item #2406022 "Left padding bug in MultiCell with maxh" was fixed.
+
+4.4.000 (2008-12-07)
+ - File attachments are now supported (see example n. 41).
+ - Font functions were optimized to reduce document size.
+ - makefont.php was updated.
+ - Linux binaries were added on /fonts/utils
+ - All fonts were updated.
+ - $autopadding parameter was added to Multicell() to disable automatic padding features.
+ - $maxh parameter was added to Multicell() and Write() to set a maximum height.
+
+4.3.009 (2008-12-05)
+ - Bug item #2392989 (Custom header + setlinewidth + cell border bug) was fixed.
+
+4.3.008 (2008-12-05)
+ - Bug item #2390566 "rect bug" was fixed.
+ - File path was fixed for font embedded files.
+ - SetFont() method signature was changed to include the font filename.
+ - Some font-related methods were improved.
+ - Methods getFontFamily() and getFontStyle() were added.
+
+4.3.007 (2008-12-03)
+ - PNG alpha channel is now supported (GD library is required).
+ - AddFont() function now support custom font file path on $file parameter.
+ - The default width variable ($dw) is now always defined for any font.
+ - The 'Style' attribute on CID-0 fonts was removed because of protection bug.
+
+4.3.006 (2008-12-01)
+ - A regular expression on getHtmlDomArray() to find HTML tags was fixed.
+
+4.3.005 (2008-11-25)
+ - makefont.php was fixed.
+ - Bug item #2339877 was fixed (false loop condition detected on WriteHTML()).
+ - Bug item #2336733 was fixed (lasth value update on Multicell() when border and fill are disabled).
+ - Bug item #2342303 was fixed (automatic page-break on Image() and ImageEPS()).
+
+4.3.004 (2008-11-19)
+ - Function _textstring() was fixed (bug 2309051).
+ - All examples were updated.
+
+4.3.003 (2008-11-18)
+ - CID-0 font bug was fixed.
+ - Some functions were optimized.
+ - Function getGroupPageNoFormatted() was added.
+ - Example n. 23 was updated.
+
+4.3.002 (2008-11-17)
+ - Bug item #2305518 "CID-0 font don't work with encryption" was fixed.
+
+4.3.001 (2008-11-17)
+ - Bug item #2300007 "download mimetype pdf" was fixed.
+ - Double quotes were replaced by single quotes to improve PHP performances.
+ - A bug relative to HTML cell borders was fixed.
+
+4.3.000 (2008-11-14)
+ - The function setOpenCell() was added to set the top/bottom cell sides to be open or closed when the cell cross the page.
+ - A bug relative to list items indentation was fixed.
+ - A bug relative to borders on HTML tables and Multicell was fixed.
+ - A bug relative to rowspanned cells was fixed.
+ - A bug relative to html images across pages was fixed.
+
+4.2.009 (2008-11-13)
+ - Spaces between li tags are now automatically removed.
+
+4.2.008 (2008-11-12)
+ - A bug relative to fill color on next page was fixed.
+
+4.2.007 (2008-11-12)
+ - The function setListIndentWidth() was added to set custom indentation widht for HTML lists.
+
+4.2.006 (2008-11-06)
+ - A bug relative to HTML justification was fixed.
+
+4.2.005 (2008-11-06)
+ - A bug relative to HTML justification was fixed.
+ - The methods formatPageNumber() and PageNoFormatted() were added to format page numbers.
+ - Default Footer() method was changed to use PageNoFormatted() instead of PageNo().
+ - Example 6 was updated.
+
+4.2.004 (2008-11-04)
+ - Bug item n. 2217039 "filename handling improvement" was fixed.
+
+4.2.003 (2008-10-31)
+ - Font style bug was fixed.
+
+4.2.002 (2008-10-31)
+ - Bug item #2210922 (htm element br not work) was fixed.
+ - Write() function was improved to support margin changes.
+
+4.2.001 (2008-10-30)
+ - setHtmlVSpace($tagvs) function was added to set custom vertical spaces for HTML tags.
+ - writeHTML() function now support margin changes during execution.
+ - Signature of addHTMLVertSpace() function is changed.
+
+4.2.000 (2008-10-29)
+ - htmlcolors.php was changed to support class-loaders.
+ - ImageEps() function was improved in performances.
+ - Signature of Link() And Annotation() functions were changed.
+ - (Bug item #2198926) Links and Annotations alignment were fixed (support for geometric tranformations was added).
+ - rowspan mode for HTML table cells was improved and fixed.
+ - Booklet mode for double-sided pages was added; see SetBooklet() function and example n. 40.
+ - lastPage() signature is changed.
+ - Signature of Write() function is changed.
+ - Some HTML justification problems were fixed.
+ - Some functions were fixed to better support RTL mode.
+ - Example n. 10 was changed to support RTL mode.
+ - All examples were updated.
+
+4.1.004 (2008-10-23)
+ - unicode_data.php was changed to support class-loaders.
+ - Bug item #2186040/2 (writeHTML margin problem) was fixed.
+
+4.1.003 (2008-10-22)
+ - Bug item #2185399 was fixed (rowspan and page break).
+ - Bugs item #2186040 was fixed (writeHTML margin problem).
+ - Newline after table was removed.
+
+4.1.002 (2008-10-21)
+ - Bug item #2184525 was fixed (rowspan on HTML cell).
+
+4.1.001 (2008-10-21)
+ - Support for "start" attribute was added to HTML ordered list.
+ - unicode_data.php file was changed to include UTF-8 to ASCII table.
+ - Some functions were modified to better support UTF-8 extensions to core fonts.
+ - Support for images on HTML lists was improved.
+ - Examples n. 1 and 6 were updated.
+
+4.1.000 (2008-10-18)
+ - Page-break bug using HTML content was fixed.
+ - The "false" parameter was reintroduced to class_exists function on PHP5 version to avoid autoload.
+ - addHtmlLink() function was improved to support internal links (i.e.: link to page 23).
+ - Justification alignment is now supported on HTML (see example n. 39).
+ - example_006.php was updated.
+
+4.0.033 (2008-10-13)
+ - Bug n. 2157099 was fixed.
+ - SetX() and SetY() functions were improved.
+ - SetY() includes a new parameter to avoid the X reset.
+
+4.0.032 (2008-10-10)
+ - Bug n. 2156926 was fixed (bold, italic, underlined, linethrough).
+ - setStyle() method was removed.
+ - Configuration file was changed to use helvetica (non-unicode) font by default.
+ - The use of mixed font types was improved.
+ - All examples were updated.
+
+4.0.031 (2008-10-09)
+ - _putannots() and _putbookmarks() links alignments were fixed.
+
+4.0.030 (2008-10-07)
+ - _putbookmarks() function was fixed.
+ - _putannots() was fixed to include internal links.
+
+4.0.029 (2008-09-27)
+ - Infinite loop bug was fixed [Bug item #130309].
+ - Multicell() problem on Header() was fixed.
+
+4.0.028 (2008-09-26)
+ - setLIsymbol() was added to set the LI symbol used on UL lists.
+ - Missing $padding and $encryption_key variables declarations were added [Bug item #2129058].
+
+4.0.027 (2008-09-19)
+ - Bug #2118588 "Undefined offset in tcpdf.php on line 9581" was fixed.
+ - arailunicid0.php font was updated.
+ - The problem of javascript form fields duplication after saving was fixed.
+
+4.0.026 (2008-09-17)
+ - convertHTMLColorToDec() function was improved to support rgb(RR,GG,BB) notation.
+ - The following inline CSS attributes are now supported: text-decoration, color, background-color and font-size names: xx-small, x-small, small, medium, large, x-large, xx-large
+ - Example n. 6 was updated.
+
+4.0.025 (2008-09-15)
+ - _putcidfont0 function was improved to include CJK fonts (Chinese, Japanese, Korean, CJK, Asian fonts) without embedding.
+ - arialunicid0 font was added (see the new example n. 38).
+ - The following Unicode to CID-0 tables were added on fonts folder: uni2cid_ak12.php, uni2cid_aj16.php, uni2cid_ag15.php, uni2cid_ac15.php.
+
+4.0.024 (2008-09-12)
+ - "stripos" function was replaced with "strpos + strtolower" for backward compatibility with PHP4.
+ - support for Spot Colors were added. Check the new example n. 37 and the following new functions:
+ AddSpotColor()
+ SetDrawSpotColor()
+ SetFillSpotColor()
+ SetTextSpotColor()
+ _putspotcolors()
+ - Bookmark() function was improved to fix wrong levels.
+ - $lasth changes after header/footer calls were fixed.
+
+4.0.023 (2008-09-05)
+ - Some HTML related problems were fixed.
+ - Image alignment on HTML was changed, now it always defaults to the normal mode (see example_006.php).
+
+4.0.022 (2008-08-28)
+ - Line height on HTML was fixed.
+ - Image inside an HTML cell problem was fixed.
+ - A new "zarbold" persian font was added.
+
+4.0.021 (2008-08-24)
+ - HTTP headers were fixed on Output function().
+ - getAliasNbPages() and getPageGroupAlias() functions were changed to support non-unicode fonts on unicode documents.
+ - Function Write() was fixed.
+ - The problem of additional vertical spaces on HTML was fixed.
+ - The problem of frame around HTML links was fixed.
+
+4.0.020 (2008-08-15)
+ - "[2052259] WriteHTML & " bug was fixed.
+
+4.0.019 (2008-08-13)
+ - "Rowspan on first cell" bug was fixed.
+
+4.0.018 (2008-08-08)
+ - Default cellpadding for HTML tables was fixed.
+ - Annotation() function was added to support some PDF annotations (see example_036.php and section 8.4 of PDF reference 1.7).
+ - HTML links are now correclty shifted during line alignments.
+ - function getAliasNbPages() was added and Footer() was updated.
+ - RowSpan mode for HTML tables was fixed.
+ - Bugs item #2043610 "Multiple sizes vertical align wrong" was fixed.
+ - ImageEPS() function was improved and RTL alignment was fixed (see example_032.php).
+
+4.0.017 (2008-08-05)
+ - Missing CNZ and CEO style modes were added to Rect() function.
+ - Fonts utils were updated to include support for OpenType fonts.
+ - getLastH() function was added.
+
+4.0.016 (2008-07-30)
+ - setPageMark() function was added. This function must be called after calling Image() function for a background image.
+
+4.0.015 (2008-07-29)
+ - Some functions were changed to support different page formats (see example_028.php).
+ - The signature of setPage() function is changed.
+
+4.0.014 (2008-07-29)
+ - K_PATH_MAIN calculation on tcpdf_config.php was fixed.
+ - HTML support for EPS/AI images was added (see example_006.php).
+ - Bugs item #2030807 "Truncated text on multipage html fields" was fixed.
+ - PDF header bug was fixed.
+ - helvetica was added as default font family.
+ - Stroke mode was fixed on Text function.
+ - several minor bugs were fixed.
+
+4.0.013 (2008-07-27)
+ - Bugs item #2027799 " Big spaces between lines after page break" was fixed.
+ - K_PATH_MAIN calculation on tcpdf_config.php was changed.
+ - Function setVisibility() was fixed to avoid the "Incorrect PDEObject type" error message.
+
+4.0.012 (2008-07-24)
+ - Addpage(), Header() and Footer() functions were changed to simplify the implementation of external header/footer functions.
+ - The following functions were added:
+ setHeader()
+ setFooter()
+ getImageRBX()
+ getImageRBY()
+ getCellHeightRatio()
+ getHeaderFont()
+ getFooterFont()
+ getRTL()
+ getBarcode()
+ getHeaderData()
+ getHeaderMargin()
+ getFooterMargin()
+
+4.0.011 (2008-07-23)
+ - Font support was improved.
+ - The folder /fonts/utils contains new utilities and instructions for embedd font files.
+ - Documentation was updated.
+
+4.0.010 (2008-07-22)
+ - HTML tables were fixed to work across pages.
+ - Header() and Footer() functions were updated to preserve previous settings.
+ - example_035.php was added.
+
+4.0.009 (2008-07-21)
+ - UTF8StringToArray() function was fixed for non-unicode mode.
+
+4.0.008 (2008-07-21)
+ - Barcodes alignment was fixed (see example_027.php).
+ - unicode_data.php was updated.
+ - Arabic shaping for "Zero-Width Non-Joiner" character (U+200C) was fixed.
+
+4.0.007 (2008-07-18)
+ - str_split was replaced by preg_split for compatibility with PHP4 version.
+ - Clipping mode was added to all graphic functions by using parameter $style = "CNZ" or "CEO" (see example_034.php).
+
+4.0.006 (2008-07-16)
+ - HTML rowspan bug was fixed.
+ - Line style for MultiCell() was fixed.
+ - WriteHTML() function was improved.
+ - CODE128C barcode was fixed (barcodes.php).
+
+4.0.005 (2008-07-11)
+ - Bug [2015715] "PHP Error/Warning" was fixed.
+
+4.0.004 (2008-07-09)
+ - HTML cell internal padding was fixed.
+
+4.0.003 (2008-07-08)
+ - Removed URL encoding when F option is selected on Output() function.
+ - fixed some minor bugs in html tables.
+
+4.0.002 (2008-07-07)
+ - Bug [2000861] was still unfixed and has been fixed.
+
+4.0.001 (2008-07-05)
+ - Bug [2000861] was fixed.
+
+4.0.000 (2008-07-03)
+ - THIS IS A MAIN RELEASE THAT INCLUDES SEVERAL NEW FEATURES AND BUGFIXES
+ - Signature fo SetTextColor() and SetFillColor() functions was changed (parameter $storeprev was removed).
+ - HTML support was completely rewritten and improved (see example 6).
+ - Alignments parameters were fixed.
+ - Functions GetArrStringWidth() and GetStringWidth() now include font parameters.
+ - Fonts support was improved.
+ - All core fonts were replaced and moved to fonts/ directory.
+ - The following functions were added: getMargins(), getFontSize(), getFontSizePt().
+ - File config/tcpdf_config_old.php was renamed tcpdf_config_alt.php and updated.
+ - Multicell and WriteHTMLCell fill function was fixed.
+ - Several minor bugs were fixed.
+ - barcodes.php was updated.
+ - All examples were updated.
+
+------------------------------------------------------------
+
+3.1.001 (2008-06-13)
+ - Bug [1992515] "K_PATH_FONTS default value wrong" was fixed.
+ - Vera font was removed, DejaVu font and Free fonts were updated.
+ - Image handling was improved.
+ - All examples were updated.
+
+3.1.000 (2008-06-11)
+ - setPDFVersion() was added to change the default PDF version (currently 1.7).
+ - setViewerPreferences() was added to control the way the document is to be presented on the screen or printed (see example 29).
+ - SetDisplayMode() signature was changed (new options were added).
+ - LinearGradient(), RadialGradient(), CoonsPatchMesh() functions were added to print various color gradients (see example 30).
+ - PieSector() function was added to render render pie charts (see example 31).
+ - ImageEps() was added to display EPS and AI images with limited support (see example 32).
+ - writeBarcode() function is now depracated, a new write1DBarcode() function was added. The barcode directory was removed and a new barcodes.php file was added.
+ - The new write1DBarcode() function support more barcodes and do not need the GD library (see example 027). All barcodes are directly written to PDF using graphic functions.
+ - HTML lists were improved and could be nested (you may now represent trees).
+ - AddFont() bug was fixed.
+ - _putfonts() bug was fixed.
+ - graphics functions were fixed.
+ - unicode_data.php file was updated (fixed).
+ - almohanad font was updated.
+ - example 18 was updated (Farsi and Arabic languages).
+ - source code cleanup.
+ - All examples were updated and new examples were added.
+
+3.0.015 (2008-06-06)
+ - AddPage() function signature is changed to include page format.
+ - example 28 was added to show page format changes.
+ - setPageUnit() function was added to change the page units of measure.
+ - setPageFormat() function was added to change the page format and orientation between pages.
+ - setPageOrientation() function was added to change the page orientation.
+ - Arabic font shaping was fixed for laa letter and square boxes (see the example 18).
+
+3.0.014 (2008-06-04)
+ - Arabic font shaping was fixed.
+ - setDefaultTableColumns() function was added.
+ - $cell_height_ratio variable was added.
+ - setCellHeightRatio() function was added to define the default height of cell repect font height.
+
+3.0.013 (2008-06-03)
+ - Multicell height parameter was fixed.
+ - Arabic font shaping was improved.
+ - unicode_data.php was updated.
+
+3.0.012 (2008-05-30)
+ - K_PATH_MAIN and K_PATH_URL constants are now automatically set on config file.
+ - DOCUMENT_ROOT constant was fixed for IIS Webserver (config file was updated).
+ - Arabic font shaping was improved.
+ - TranslateY() function was fixed (bug [1977962]).
+ - setVisibility() function was fixed.
+ - writeBarcode() function was fixed to scale using $xref parameter.
+ - All examples were updated.
+
+3.0.011 (2008-05-23)
+ - CMYK color support was added to all graphic functions.
+ - HTML table support was improved:
+ -- now it's possible to include additional html tags inside a cell;
+ -- colspan attribute was added.
+ - example 006 was updated.
+
+3.0.010 (2008-05-21)
+ - fixed $laa_array inclusion on utf8Bidi() function.
+
+3.0.009 (2008-05-20)
+ - unicode_data.php was updated.
+ - Arabic laa letter problem was fixed.
+
+3.0.008 (2008-05-12)
+ - Arabic support was fixed and improved (unicode_data.php was updated).
+ - Polycurve() function was added to draw a poly-Bezier curve.
+ - list items alignment was fixed.
+ - example 6 was updated.
+
+3.0.007 (2008-05-06)
+ - Arabic support was fixed and improved.
+ - AlMohanad (arabic) font was added.
+ - C128 barcode bugs were fixed.
+
+3.0.006 (2008-04-21)
+ - Condition to check negative width values was added.
+
+3.0.005 (2008-04-18)
+ - back-Slash character escape was fixed on writeHTML() function.
+ - Exampe 6 was updated.
+
+3.0.004 (2008-04-11)
+ - Bug [1939304] (Right to Left Issue) was fixed.
+
+3.0.003 (2008-04-07)
+ - Bug [1934523](Words between HTML tags in cell not kept on one line) was fixed.
+ - "face" attribute of "font" tag is now fully supported.
+
+3.0.002 (2008-04-01)
+ - Write() functions now return the number of cells and not the number of lines.
+ - TCPDF is released under LGPL 2.1, or any later version.
+
+3.0.001 (2008-05-28)
+ - _legacyparsejpeg() and _legacyparsepng() were renamed _parsejpeg() and _parsepng().
+ - function writeBarcode() was fixed.
+ - all examples were updated.
+ - example 27 was added to show various barcodes.
+
+3.0.000 (2008-03-27)
+ - private function pixelsToMillimeters() was changed to public function pixelsToUnits() to fix html image size bug.
+ - Image-related functions were rewritten.
+ - resize parameter was added to Image() signature to reduce the image size and fit width and height (see example 9).
+ - TCPDF now supports all images supported by GD library: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM.
+ - CMYK support was added to SetDrawColor(), SetFillColor(), SetTextColor() (see example 22).
+ - Page Groups were added (see example 23).
+ - setVisibility() function was added to restrict the rendering of some elements to screen or printout (see example 24).
+ - All private variables and functions were changed to protected.
+ - setAlpha() function was added to give transparency support for all objects (see example 25).
+ - Clipping and stroke modes were added to Text() function (see example 26).
+ - All examples were moved to "examples" directory.
+ - function setJPEGQuality() was added to set the JPEG image comrpession (see example 9).
+
+2.9.000 (2008-03-26)
+ - htmlcolors.php file was added to include html colors.
+ - Support for HTML color names and three-digit hexadecimal color codes was added.
+ - private function convertColorHexToDec() was renamed convertHTMLColorToDec().
+ - color and bgcolor attributes are now supported on all HTML tags (color nesting is also supported).
+ - Write() function were fixed.
+ - example_006.php was updated.
+ - private function setUserRights() was added to release user rights on Acrobat Reader (this allows to display forms, see example 14)
+
+2.8.000 (2008-03-20)
+ - Private variables were changed to protected.
+ - Function Write() was fixed and improved.
+ - Support for dl, dt, dd, del HTML tags was introduced.
+ - Line-trought mode was added for HTML and text.
+ - Text vertical alignment on cells were fixed.
+ - Examples were updated to reflect changes.
+
+2.7.002 (2008-03-13)
+ - Bug "[1912142] Encrypted PDF created/modified date" was fixed.
+
+2.7.001 (2008-03-10)
+ - Cell justification was fixed for non-unicode mode.
+
+2.7.000 (2008-03-09)
+ - Cell() stretching mode 4 (forced character spacing) was fixed.
+ - writeHTMLCell() now uses Multicell() to write.
+ - Multicell() has a new parameter $ishtml to act as writeHTMLCell().
+ - Write() speed was improved for non-arabic strings.
+ - Example n. 20 was changed.
+
+2.6.000 (2008-03-07)
+ - various alignments bugs were fixed.
+
+2.5.000 (2008-03-07)
+ - Several bugs were fixed.
+ - example_019.php was added to test non-unicode mode using old fonts.
+
+2.4.000 (2008-03-06)
+ - RTL support was deeply improved.
+ - GetStringWidth() was fixed to support RTL languages.
+ - Text() RTL alignment was fixed.
+ - Some functions were added: GetArrStringWidth(), GetCharWidth(), uniord(), utf8Bidi().
+ - example_018.php was added and test_unicode.php was removed.
+
+2.3.000 (2008-03-05)
+ - MultiCell() signature is changed. Now support multiple columns across pages (see example_017).
+ - Write() signature is changed. Now support the cell mode to be used with MultiCell.
+ - Header() and Footer() were changed.
+ - The following functions were added: UTF8ArrSubString() and unichr().
+ - Examples were updated to reflect last changes.
+
+2.2.004 (2008-03-04)
+ - Several examples were added.
+ - AddPage() Header() and Footer() were fixed.
+ - Documentation is now available on http://www.tcpdf.org
+
+2.2.003 (2008-03-03)
+ - [1894853] Performance of MultiCell() was improved.
+ - RadioButton and ListBox functions were added.
+ - javascript form functions were rewritten and properties names are changed. The properties function supported by form fields are listed on Possible values are listed on http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf.
+
+2.2.002 (2008-02-28)
+ - [1900495] html images path was fixed.
+ - Legacy image functions were reintroduced to allow PNG and JPEG support without GD library.
+
+2.2.001 (2008-02-16)
+ - The bug "[1894700] bug with replace relative path" was fixed
+ - Justification was fixed
+
+2.2.000 (2008-02-12)
+ - fixed javascript bug introduced with latest release
+
+2.1.002 (2008-02-12)
+ - Justify function was fixed on PHP4 version.
+ - Bookmank function was added ([1578250] Table of contents).
+ - Javascript and Form fields support was added ([1796359] Form fields).
+
+2.1.001 (2008-02-10)
+ - The bug "[1885776] Race Condition in function justitfy" was fixed.
+ - The bug "[1890217] xpdf complains that pdf is incorrect" was fixed.
+
+2.1.000 (2008-01-07)
+ - FPDF_FONTPATH constant was changed to K_PATH_FONTS on config file
+ - Bidirectional Algorithm to correctly reverse bidirectional languages was added.
+ - SetLeftMargin, SetTopMargin, SetRightMargin functions were fixed.
+ - SetCellPadding function was added.
+ - writeHTML was updated with new parameters.
+ - Text function was fixed.
+ - MultiCell function was fixed, now works also across multiple pages.
+ - Line width was fixed on Header and Footer functions and tag.
+ - "GetImageSize" was renamed "getimagesize".
+ - Document version was changed from 1.3 to 1.5.
+ - _begindoc() function was fixed.
+ - ChangeDate was fixed and ModDate was added.
+ - The following functions were added:
+ setPage() : Move pointer to the specified document page.
+ getPage() : Get current document page number.
+ lastpage() : Reset pointer to the last document page.
+ getNumPages() : Get the total number of inserted pages.
+ GetNumChars() : count the number of (UTF-8) characters in a string.
+ - $stretch parameter was added to Cell() function to fit text on cell:
+ 0 = disabled
+ 1 = horizontal scaling only if necessary
+ 2 = forced horizontal scaling
+ 3 = character spacing only if necessary
+ 4 = forced character spacing
+ - Line function was fixed for RTL.
+ - Graphic transformation functions were added [1811158]:
+ StartTransform()
+ StopTransform()
+ ScaleX()
+ ScaleY()
+ ScaleXY()
+ Scale()
+ MirrorH()
+ MirrorV()
+ MirrorP()
+ MirrorL()
+ TranslateX()
+ TranslateY()
+ Translate()
+ Rotate()
+ SkewX()
+ SkewY()
+ Skew()
+ - Graphic function were added/updated [1688549]:
+ SetLineStyle()
+ _outPoint()
+ _outLine()
+ _outRect()
+ _outCurve()
+ Line()
+ Rect()
+ Curve
+ Ellipse
+ Circle
+ Polygon
+ RegularPolygon
+
+2.0.000 (2008-01-04)
+ - RTL (Right-To-Left) languages support was added. Language direction is set using the $l['a_meta_dir'] setting on /configure/language/xxx.php language files.
+ - setRTL($enable) method was added to manually enable/disable the RTL text direction.
+ - The attribute "dir" was added to support custom text direction on HTML tags. Possible values are: ltr - for Left-To-Right and RTL for Right-To-Left.
+ - RC4 40bit encryption was added. Check the SetProtection method.
+ - [1815213] Improved image support for GIF, JPEG, PNG formats.
+ - [1800094] Attribute "value" was added to ordered list items
.
+ - Image function now has a new "align" parameter that indicates the alignment of the pointer next to image insertion and relative to image height. The value can be:
+ T: top-right for LTR or top-left for RTL
+ M: middle-right for LTR or middle-left for RTL
+ B: bottom-right for LTR or bottom-left for RTL
+ N: next line
+ - Attribute "align" was added to html tag to set the above image "align" parameter. Possible values are:
+ top: top-right for LTR or top-left for RTL
+ middle: middle-right for LTR or middle-left for RTL
+ bottom: bottom-right for LTR or bottom-left for RTL
+ - [1798103] newline was added after , and
tages.
+ - [1816393] Documentation was updated.
+ - 'ln' parameter was fixed on writeHTMLCell. Now it's possible to print two or more columns across several pages;
+ - The method lastPage() was added to move the pointer on the last page;
+
+------------------------------------------------------------
+
+1.53.0.TC034 (2007-07-30)
+ - fixed htmlentities conversion.
+ - MultiCell() function returns the number of cells.
+
+1.53.0.TC033 (2007-07-30)
+ - fixed bug 1762550: case sensitive for font files
+ - NOTE: all fonts files names must be in lowercase!
+
+1.53.0.TC032 (2007-07-27)
+ - setLastH method was added to resolve bug 1689071.
+ - all fonts names were converted in lowercase (bug 1713005).
+ - bug 1740954 was fixed.
+ - justification was added as Cell option.
+
+1.53.0.TC031 (2007-03-20)
+ - ToUnicode CMap were added on _puttruetypeunicode function. Now you may search and copy unicode text.
+
+1.53.0.TC030 (2007-03-06)
+ - fixed bug on PHP4 version.
+
+1.53.0.TC029 (2007-03-06)
+ - DejaVu Fonts were added.
+
+1.53.0.TC028 (2007-03-03)
+ - MultiCell function signature were changed: the $ln parameter were added. Check documentation for further information.
+ - Greek language were added on example sentences.
+ - setPrintHeader() and setPrintFooter() functions were added to enable or disable page header and footer.
+
+1.53.0.TC027 (2006-12-14)
+ - $attr['face'] bug were fixed.
+ - K_TCPDF_EXTERNAL_CONFIG control where introduced on /config/tcpdf_config.php to use external configuration files.
+
+1.53.0.TC026 (2006-10-28)
+ - writeHTML function call were fixed on examples.
+
+1.53.0.TC025 (2006-10-27)
+ - Bugs item #1421290 were fixed (0D - 0A substitution in some characters)
+ - Bugs item #1573174 were fixed (MultiCell documentation)
+
+1.53.0.TC024 (2006-09-26)
+ - getPageHeight() function were fixed (bug 1543476).
+ - fixed missing breaks on closedHTMLTagHandler function (bug 1535263).
+ - fixed extra spaces on Write function (bug 1535262).
+
+1.53.0.TC023 (2006-08-04)
+ - paths to barcode directory were fixed.
+ - documentation were updated.
+
+1.53.0.TC022 (2006-07-16)
+ - fixed bug: [ 1516858 ] Probs with PHP autoloader and class_exists()
+
+1.53.0.TC021 (2006-07-01)
+ - HTML attributes with whitespaces are now supported (thanks to Nelson Benitez for his support)
+
+1.53.0.TC020 (2006-06-23)
+ - code cleanup
+
+1.53.0.TC019 (2006-05-21)
+ - fixed and closing tags
+
+1.53.0.TC018 (2006-05-18)
+ - fixed font names bug
+
+1.53.0.TC017 (2006-05-18)
+ - the TTF2UFM utility to convert True Type fonts for TCPDF were included on fonts folder.
+ - new free unicode fonts were included on /fonts/freefont.
+ - test_unicode.php example were exended.
+ - parameter $fill were added on Write, writeHTML and writeHTMLCell functions.
+ - documentation were updated.
+
+1.53.0.TC016 (2006-03-09)
+ - fixed closing tag on html parser.
+
+1.53.0.TC016 (2005-08-28)
+ - fpdf.php and tcpdf.php files were joined in one single class (you can still extend TCPDF with your own class).
+ - fixed problem when mb_internal_encoding is set.
+
+1.53.0.TC014 (2005-05-29)
+ - fixed WriteHTMLCell new page issue.
+
+1.53.0.TC013 (2005-05-29)
+ - fixed WriteHTMLCell across pages.
+
+1.53.0.TC012 (2005-05-29)
+ - font color attribute bug were fixed.
+
+1.53.0.TC011 (2005-03-31)
+ - SetFont function were fixed (thank Sjaak Lauwers for bug notice).
+
+1.53.0.TC010 (2005-03-22)
+ - the html functions were improved (thanks to Manfred Vervuert for bug reporting).
+
+1.53.0.TC009 (2005-03-19)
+ - a wrong reference to convertColorHexToDec were fixed.
+
+1.53.0.TC008 (2005-02-07)
+ - removed some extra bytes from PHP files.
+
+1.53.0.TC007 (2005-01-08)
+ - fill attribute were removed from writeHTMLCell method.
+
+1.53.0.TC006 (2005-01-08)
+ - the documentation were updated.
+
+1.53.0.TC005 (2005-01-05)
+ - Steven Wittens's unicode methods were removed.
+ - All unicode methods were rewritten from scratch.
+ - TCPDF is now licensed as LGPL.
+
+1.53.0.TC004 (2005-01-04)
+ - this changelog were added.
+ - removed commercial fonts for licensing issue.
+ - Bitstream Vera Fonts were added (http://www.bitstream.com/font_rendering/products/dev_fonts/vera.html).
+ - Now the AddFont and SetFont functions returns the basic font if the styled version do not exist.
+
+EOF ----------------------------------------------------------------------------
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/LICENSE.TXT b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/LICENSE.TXT
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/LICENSE.TXT
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/README.TXT b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/README.TXT
new file mode 100644
index 0000000..3ae0ac8
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/README.TXT
@@ -0,0 +1,87 @@
+TCPDF - README
+============================================================
+
+IF YOU'D LIKE TO SUPPORT TCPDF, PLEASE CONSIDER MAKING A
+DONATION:
+http://sourceforge.net/donate/index.php?group_id=128076
+
+------------------------------------------------------------
+
+Name: TCPDF
+Version: 4.8.009
+Release date: 2009-09-30
+Author: Nicola Asuni
+
+Copyright (c) 2001-2009:
+ Nicola Asuni
+ Tecnick.com s.r.l.
+ Via Della Pace, 11
+ 09044 Quartucciu (CA)
+ ITALY
+ www.tecnick.com
+
+URLs:
+ http://www.tcpdf.org
+ http://www.sourceforge.net/projects/tcpdf
+
+Description:
+ TCPDF is a PHP class for generating PDF files on-the-fly without requiring external extensions.
+ TCPDF has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).
+
+Main Features:
+// * no external libraries are required for the basic functions;
+// * supports all ISO page formats;
+// * supports custom page formats, margins and units of measure;
+// * supports UTF-8 Unicode and Right-To-Left languages;
+// * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
+// * supports document encryption;
+// * includes methods to publish some XHTML code, including forms;
+// * includes graphic (geometric) and transformation methods;
+// * includes Javascript and Forms support;
+// * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;
+// * includes methods to set Bookmarks and print a Table of Content;
+// * includes methods to move and delete pages;
+// * includes methods for automatic page header and footer management;
+// * supports automatic page break;
+// * supports automatic page numbering and page groups;
+// * supports automatic line break and text justification;
+// * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
+// * supports stroke and clipping mode for text;
+// * supports clipping masks;
+// * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
+// * supports several annotations, including links, text and file attachments;
+// * supports page compression (requires zlib extension);
+// * supports text hyphenation.
+// * supports transactions to UNDO commands.
+// * supports signature certifications.
+
+Installation (full instructions on http://www.tcpdf.org):
+ 1. copy the folder on your Web server
+ 2. set your installation path and other parameters on the config/tcpdf_config.php
+ 3. call the examples/example_001.php page with your browser to see an example
+
+Source Code Documentation:
+ doc/index.html
+
+For Additional Documentation:
+ http://www.tcpdf.org
+
+License
+ Copyright (C) 2002-2009 Nicola Asuni - Tecnick.com S.r.l.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see .
+
+ See LICENSE.TXT file for more information.
+
+============================================================
diff --git a/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/barcodes.php b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/barcodes.php
new file mode 100644
index 0000000..c859595
--- /dev/null
+++ b/flaxil/inc/cube/externals/PHPExcel/PHPExcel/Shared/PDF/barcodes.php
@@ -0,0 +1,1978 @@
+.
+//
+// See LICENSE.TXT file for more information.
+// ----------------------------------------------------------------------------
+//
+// Description : PHP class to creates array representations for
+// common 1D barcodes to be used with TCPDF.
+//
+// Author: Nicola Asuni
+//
+// (c) Copyright:
+// Nicola Asuni
+// Tecnick.com S.r.l.
+// Via della Pace, 11
+// 09044 Quartucciu (CA)
+// ITALY
+// www.tecnick.com
+// info@tecnick.com
+//============================================================+
+
+/**
+ * PHP class to creates array representations for common 1D barcodes to be used with TCPDF.
+ * @package com.tecnick.tcpdf
+ * @abstract Functions for generating string representation of common 1D barcodes.
+ * @author Nicola Asuni
+ * @copyright 2008-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
+ * @link http://www.tcpdf.org
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ * @version 1.0.008
+ */
+
+ /**
+ * PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www.tcpdf.org).
+ * @name TCPDFBarcode
+ * @package com.tecnick.tcpdf
+ * @version 1.0.008
+ * @author Nicola Asuni
+ * @link http://www.tcpdf.org
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ */
+class TCPDFBarcode {
+
+ /**
+ * @var array representation of barcode.
+ * @access protected
+ */
+ protected $barcode_array;
+
+ /**
+ * This is the class constructor.
+ * Return an array representations for common 1D barcodes:
+ *
$arrcode['code'] code to be printed on text label
+ *
$arrcode['maxh'] max bar height
+ *
$arrcode['maxw'] max bar width
+ *
$arrcode['bcode'][$k] single bar or space in $k position
+ *
$arrcode['bcode'][$k]['t'] bar type: true = bar, false = space.
+ *
$arrcode['bcode'][$k]['w'] bar width in units.
+ *
$arrcode['bcode'][$k]['h'] bar height in units.
+ *
$arrcode['bcode'][$k]['p'] bar top position (0 = top, 1 = middle)
+ * @param string $code code to print
+ * @param string $type type of barcode:
The Barcode Identifier shall be assigned by USPS to encode the presort identification that is currently printed in human readable form on the optional endorsement line (OEL) as well as for future USPS use. This shall be two digits, with the second digit in the range of 0â4. The allowable encoding ranges shall be 00â04, 10â14, 20â24, 30â34, 40â44, 50â54, 60â64, 70â74, 80â84, and 90â94.
The Service Type Identifier shall be assigned by USPS for any combination of services requested on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.phpâ999. Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier values.
The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000- 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.
The Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The allowable encoding range shall be 000000000â999999999 when used with a 6 digit Mailer ID and 000000-999999 when used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000â99999, 000000000â999999999, and 00000000000â99999999999.