/** * @param string Path to file * @param array Options: * TempDir => string Temporary directory path * ReturnDateTimeObjects => bool True => dates and times will be returned as PHP DateTime objects, false => as strings */ public function __construct($Filepath, array $Options = null) { if (!is_readable($Filepath)) { throw new Exception('SpreadsheetReader_XLSX: File not readable (' . $Filepath . ')'); } $this->TempDir = isset($Options['TempDir']) && is_writable($Options['TempDir']) ? $Options['TempDir'] : sys_get_temp_dir(); $this->TempDir = rtrim($this->TempDir, DIRECTORY_SEPARATOR); $this->TempDir = $this->TempDir . DIRECTORY_SEPARATOR . uniqid() . DIRECTORY_SEPARATOR; $Zip = new ZipArchive(); $Status = $Zip->open($Filepath); if ($Status !== true) { throw new Exception('SpreadsheetReader_XLSX: File not readable (' . $Filepath . ') (Error ' . $Status . ')'); } // Getting the general workbook information if ($Zip->locateName('xl/workbook.xml') !== false) { $this->WorkbookXML = new SimpleXMLElement($Zip->getFromName('xl/workbook.xml')); } // Extracting the XMLs from the XLSX zip file if ($Zip->locateName('xl/sharedStrings.xml') !== false) { $this->SharedStringsPath = $this->TempDir . 'xl' . DIRECTORY_SEPARATOR . 'sharedStrings.xml'; $Zip->extractTo($this->TempDir, 'xl/sharedStrings.xml'); $this->TempFiles[] = $this->TempDir . 'xl' . DIRECTORY_SEPARATOR . 'sharedStrings.xml'; if (is_readable($this->SharedStringsPath)) { $this->SharedStrings = new XMLReader(); $this->SharedStrings->open($this->SharedStringsPath); $this->PrepareSharedStringCache(); } } $Sheets = $this->Sheets(); foreach ($this->Sheets as $Index => $Name) { if ($Zip->locateName('xl/worksheets/sheet' . $Index . '.xml') !== false) { $Zip->extractTo($this->TempDir, 'xl/worksheets/sheet' . $Index . '.xml'); $this->TempFiles[] = $this->TempDir . 'xl' . DIRECTORY_SEPARATOR . 'worksheets' . DIRECTORY_SEPARATOR . 'sheet' . $Index . '.xml'; } } $this->ChangeSheet(0); // If worksheet is present and is OK, parse the styles already if ($Zip->locateName('xl/styles.xml') !== false) { $this->StylesXML = new SimpleXMLElement($Zip->getFromName('xl/styles.xml')); if ($this->StylesXML && $this->StylesXML->cellXfs && $this->StylesXML->cellXfs->xf) { foreach ($this->StylesXML->cellXfs->xf as $Index => $XF) { if ($XF->attributes()->applyNumberFormat) { $FormatId = (int) $XF->attributes()->numFmtId; // If format ID >= 164, it is a custom format and should be read from styleSheet\numFmts $this->Styles[] = $FormatId; } else { $this->Styles[] = false; } } } if ($this->StylesXML->numFmts && $this->StylesXML->numFmts->numFmt) { foreach ($this->StylesXML->numFmts->numFmt as $Index => $NumFmt) { $this->Formats[(int) $NumFmt->attributes()->numFmtId] = (string) $NumFmt->attributes()->formatCode; } } unset($this->StylesXML); } $Zip->close(); // Setting base date if (!self::$BaseDate) { self::$BaseDate = new DateTime(); self::$BaseDate->setTimezone(new DateTimeZone('UTC')); self::$BaseDate->setDate(1900, 1, 0); self::$BaseDate->setTime(0, 0, 0); } // Decimal and thousand separators if (!self::$DecimalSeparator && !self::$ThousandSeparator && !self::$CurrencyCode) { $Locale = localeconv(); self::$DecimalSeparator = $Locale['decimal_point']; self::$ThousandSeparator = $Locale['thousands_sep']; self::$CurrencyCode = $Locale['int_curr_symbol']; } if (function_exists('gmp_gcd')) { self::$RuntimeInfo['GMPSupported'] = true; } }
/** * @param string Path to file * @param array Options: * TempDir => string Temporary directory path * ReturnDateTimeObjects => bool True => dates and times will be returned as PHP DateTime objects, false => as strings */ public function __construct($Filepath, array $Options = null) { if (!is_readable($Filepath)) { throw new Exception('SpreadsheetReader_XLSX: File not readable (' . $Filepath . ')'); } $this->TempDir = isset($Options['TempDir']) && is_writable($Options['TempDir']) ? $Options['TempDir'] : sys_get_temp_dir(); $this->TempDir = rtrim($this->TempDir, DIRECTORY_SEPARATOR); $this->TempDir = $this->TempDir . DIRECTORY_SEPARATOR . uniqid('xlsx', false) . DIRECTORY_SEPARATOR; $Zip = new ZipArchive(); $Status = $Zip->open($Filepath); if ($Status !== true) { throw new Exception('SpreadsheetReader_XLSX: File not readable (' . $Filepath . ') (Error ' . $Status . ')'); } // Getting the general workbook information if ($Zip->locateName('xl/workbook.xml') !== false) { $this->WorkbookXML = $workbook = new SimpleXMLElement($Zip->getFromName('xl/workbook.xml')); $workbook->registerXPathNamespace('w', self::workbook_ns); $workbook->registerXPathNamespace('r', self::doc_rels_ns); $elements = $workbook->xpath('/w:workbook/w:sheets/w:sheet[@r:id and @name]'); $relation_prefix = array_search(self::doc_rels_ns, $workbook->getDocNamespaces()); $index = []; $map = []; foreach ($elements as $elem) { $id = (string) $elem->attributes($relation_prefix, true)['id']; $name = (string) $elem['name']; $map[$id] = $name; $index[] = $id; } $this->Sheets = $map; $this->SheetIndexesToIds = $index; } // Getting the general workbook information if ($Zip->locateName('xl/_rels/workbook.xml.rels') !== false) { $this->WorkbookRels = $relations = new SimpleXMLElement($Zip->getFromName('xl/_rels/workbook.xml.rels')); $relations->registerXPathNamespace('r', self::pkg_rels_ns); $elements = $relations->xpath('/r:Relationships/r:Relationship[@Id and @Target and ' . '@Type = \'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\']'); $map = []; foreach ($elements as $elem) { $id = (string) $elem['Id']; $target = (string) $elem['Target']; $inZipPath = 'xl/' . $target; if ($Zip->locateName($inZipPath) !== false) { $Zip->extractTo($this->TempDir, $inZipPath); $map[$id] = $target; } } $this->SheetIdsToFiles = $map; } // Extracting the XMLs from the XLSX zip file if ($Zip->locateName('xl/sharedStrings.xml') !== false) { $this->SharedStringsPath = $this->TempDir . 'xl' . DIRECTORY_SEPARATOR . 'sharedStrings.xml'; $Zip->extractTo($this->TempDir, 'xl/sharedStrings.xml'); $this->TempFiles[] = $this->TempDir . 'xl' . DIRECTORY_SEPARATOR . 'sharedStrings.xml'; if (is_readable($this->SharedStringsPath)) { $this->SharedStrings = new XMLReader(); $this->SharedStrings->open($this->SharedStringsPath); $this->PrepareSharedStringCache(); } } //$this -> Sheets = $this -> Sheets(); /* bullhockey foreach ($this -> Sheets as $Index => $Name) { if ($Zip -> locateName('xl/worksheets/sheet'.$Index.'.xml') !== false) { $Zip -> extractTo($this -> TempDir, 'xl/worksheets/sheet'.$Index.'.xml'); $this -> TempFiles[] = $this -> TempDir.'xl'.DIRECTORY_SEPARATOR.'worksheets'.DIRECTORY_SEPARATOR.'sheet'.$Index.'.xml'; } } */ $this->ChangeSheet(0); // If worksheet is present and is OK, parse the styles already if ($Zip->locateName('xl/styles.xml') !== false) { $this->StylesXML = new SimpleXMLElement($Zip->getFromName('xl/styles.xml')); if ($this->StylesXML && $this->StylesXML->cellXfs && $this->StylesXML->cellXfs->xf) { foreach ($this->StylesXML->cellXfs->xf as $Index => $XF) { // Format #0 is a special case - it is the "General" format that is applied regardless of applyNumberFormat if ($XF->attributes()->applyNumberFormat || 0 == (int) $XF->attributes()->numFmtId) { $FormatId = (int) $XF->attributes()->numFmtId; // If format ID >= 164, it is a custom format and should be read from styleSheet\numFmts $this->Styles[] = $FormatId; } else { // 0 for "General" format $this->Styles[] = 0; } } } if ($this->StylesXML->numFmts && $this->StylesXML->numFmts->numFmt) { foreach ($this->StylesXML->numFmts->numFmt as $Index => $NumFmt) { $this->Formats[(int) $NumFmt->attributes()->numFmtId] = (string) $NumFmt->attributes()->formatCode; } } unset($this->StylesXML); } $Zip->close(); // Setting base date if (!self::$BaseDate) { self::$BaseDate = new DateTime(); self::$BaseDate->setTimezone(new DateTimeZone('UTC')); self::$BaseDate->setDate(1900, 1, 0); self::$BaseDate->setTime(0, 0, 0); } // Decimal and thousand separators if (!self::$DecimalSeparator && !self::$ThousandSeparator && !self::$CurrencyCode) { $Locale = localeconv(); self::$DecimalSeparator = $Locale['decimal_point']; self::$ThousandSeparator = $Locale['thousands_sep']; self::$CurrencyCode = $Locale['int_curr_symbol']; } if (function_exists('gmp_gcd')) { self::$RuntimeInfo['GMPSupported'] = true; } }