diff --git a/simpleXMLElement.php b/spreadSheetReader/SimpleXLSX.php similarity index 99% rename from simpleXMLElement.php rename to spreadSheetReader/SimpleXLSX.php index cc839ec..df426d4 100644 --- a/simpleXMLElement.php +++ b/spreadSheetReader/SimpleXLSX.php @@ -1,7 +1,5 @@ datetimeFormat = is_string($value) ? $value : false; } } - - -?> \ No newline at end of file diff --git a/spreadSheetReader/SimpleXLSXGen.php b/spreadSheetReader/SimpleXLSXGen.php new file mode 100644 index 0000000..110fd6f --- /dev/null +++ b/spreadSheetReader/SimpleXLSXGen.php @@ -0,0 +1,1015 @@ +curSheet = -1; + $this->defaultFont = 'Calibri'; + $this->sheets = [['name' => 'Sheet1', 'rows' => [], 'hyperlinks' => [], 'mergecells' => [], 'colwidth' => [], 'autofilter' => '']]; + $this->extLinkId = 0; + $this->SI = []; // sharedStrings index + $this->SI_KEYS = []; // & keys + + // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_numFmts_topic_ID0E6KK6.html + $this->NF = [ + self::N_RUB => '#,##0.00\ "₽"', + self::N_DOLLAR => '[$$-1]#,##0.00', + self::N_EURO => '#,##0.00\ [$€-1]' + ]; + $this->NF_KEYS = array_flip($this->NF); + + $this->BR_STYLE = [ + self::B_NONE => 'none', + self::B_THIN => 'thin', + self::B_MEDIUM => 'medium', + self::B_DASHED => 'dashed', + self::B_DOTTED => 'dotted', + self::B_THICK => 'thick', + self::B_DOUBLE => 'double', + self::B_HAIR => 'hair', + self::B_MEDIUM_DASHED => 'mediumDashed', + self::B_DASH_DOT => 'dashDot', + self::B_MEDIUM_DASH_DOT => 'mediumDashDot', + self::B_DASH_DOT_DOT => 'dashDotDot', + self::B_MEDIUM_DASH_DOT_DOT => 'mediumDashDotDot', + self::B_SLANT_DASH_DOT => 'slantDashDot' + ]; + + + $this->XF = [ // styles 0 - num fmt, 1 - align, 2 - font, 3 - fill, 4 - font color, 5 - bgcolor, 6 - border + [self::N_NORMAL, self::A_DEFAULT, self::F_NORMAL, self::FL_NONE, 0, 0, ''], + [self::N_NORMAL, self::A_DEFAULT, self::F_NORMAL, self::FL_GRAY_125, 0, 0, ''], // hack + ]; + $this->XF_KEYS[implode('-', $this->XF[0])] = 0; // & keys + $this->XF_KEYS[implode('-', $this->XF[1])] = 1; + + + $this->template = [ + '_rels/.rels' => ' + + + + +', + 'docProps/app.xml' => ' + +0 +' . __CLASS__ . '', + 'docProps/core.xml' => ' + +{DATE} +en-US +{DATE} +1 +', + 'xl/_rels/workbook.xml.rels' => ' + + +{SHEETS}', + 'xl/worksheets/sheet1.xml' => ' +{COLS}{ROWS}{AUTOFILTER}{MERGECELLS}{HYPERLINKS}', + 'xl/worksheets/_rels/sheet1.xml.rels' => ' +{HYPERLINKS}', + 'xl/sharedStrings.xml' => ' +{STRINGS}', + 'xl/styles.xml' => ' + +{NUMFMTS} +{FONTS} +{FILLS} +{BORDERS} + +{XF} + + + +', + 'xl/workbook.xml' => ' + + +{SHEETS} +', + '[Content_Types].xml' => ' + + + + + + + + +{TYPES} +', + ]; + + // + // 01001200 + // Простой шаблонБудем делать генератор + } + + public static function fromArray(array $rows, $sheetName = null) + { + return (new static())->addSheet($rows, $sheetName); + } + + public function addSheet(array $rows, $name = null) + { + + $this->curSheet++; + if ($name === null) { // autogenerated sheet names + $name = 'Sheet' . ($this->curSheet + 1); + } else { + $name = mb_substr($name, 0, 31); + $names = []; + foreach ($this->sheets as $sh) { + $names[mb_strtoupper($sh['name'])] = 1; + } + for ($i = 0; $i < 100; $i++) { + $postfix = ' (' . $i . ')'; + $new_name = ($i === 0) ? $name : $name . $postfix; + if (mb_strlen($new_name) > 31) { + $new_name = mb_substr($name, 0, 31 - mb_strlen($postfix)) . $postfix; + } + $NEW_NAME = mb_strtoupper($new_name); + if (!isset($names[$NEW_NAME])) { + $name = $new_name; + break; + } + } + } + + $this->sheets[$this->curSheet] = ['name' => $name, 'hyperlinks' => [], 'mergecells' => [], 'colwidth' => [], 'autofilter' => '']; + + if (isset($rows[0]) && is_array($rows[0])) { + $this->sheets[$this->curSheet]['rows'] = $rows; + } else { + $this->sheets[$this->curSheet]['rows'] = []; + } + return $this; + } + + public function __toString() + { + $fh = fopen('php://memory', 'wb'); + if (!$fh) { + return ''; + } + + if (!$this->_write($fh)) { + fclose($fh); + return ''; + } + $size = ftell($fh); + fseek($fh, 0); + + return (string)fread($fh, $size); + } + + public function saveAs($filename) + { + $fh = fopen($filename, 'wb'); + if (!$fh) { + return false; + } + if (!$this->_write($fh)) { + fclose($fh); + return false; + } + fclose($fh); + + return true; + } + + public function download() + { + return $this->downloadAs(gmdate('YmdHi') . '.xlsx'); + } + + public function downloadAs($filename) + { + $fh = fopen('php://memory', 'wb'); + if (!$fh) { + return false; + } + + if (!$this->_write($fh)) { + fclose($fh); + return false; + } + + $size = ftell($fh); + + header('Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); + header('Content-Length: ' . $size); + + while (ob_get_level()) { + ob_end_clean(); + } + fseek($fh, 0); + fpassthru($fh); + + fclose($fh); + return true; + } + + protected function _write($fh) + { + + + $dirSignatureE = "\x50\x4b\x05\x06"; // end of central dir signature + $zipComments = 'Generated by ' . __CLASS__ . ' PHP class, thanks sergey.shuchkin@gmail.com'; + + if (!$fh) { + return false; + } + + $cdrec = ''; // central directory content + $entries = 0; // number of zipped files + $cnt_sheets = count($this->sheets); + + foreach ($this->template as $cfilename => $template) { + if ($cfilename === 'xl/_rels/workbook.xml.rels') { + $s = ''; + for ($i = 0; $i < $cnt_sheets; $i++) { + $s .= '\n"; + } + $s .= ''; + $template = str_replace('{SHEETS}', $s, $template); + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } elseif ($cfilename === 'xl/workbook.xml') { + $s = ''; + foreach ($this->sheets as $k => $v) { + $s .= ''; + } + $template = str_replace('{SHEETS}', $s, $template); + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } elseif ($cfilename === 'docProps/core.xml') { + $template = str_replace('{DATE}', gmdate('Y-m-d\TH:i:s\Z'), $template); + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } elseif ($cfilename === 'xl/sharedStrings.xml') { + if (!count($this->SI)) { + $this->SI[] = 'No Data'; + } + $si_cnt = count($this->SI); + $si = '' . implode("\r\n", $this->SI) . ''; + $template = str_replace(['{CNT}', '{STRINGS}'], [$si_cnt, $si], $template); + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } elseif ($cfilename === 'xl/worksheets/sheet1.xml') { + foreach ($this->sheets as $k => $v) { + $filename = 'xl/worksheets/sheet' . ($k + 1) . '.xml'; + $xml = $this->_sheetToXML($k, $template); + $this->_writeEntry($fh, $cdrec, $filename, $xml); + $entries++; + } + $xml = null; + } elseif ($cfilename === 'xl/worksheets/_rels/sheet1.xml.rels') { + foreach ($this->sheets as $k => $v) { + if ($this->extLinkId) { + $RH = []; + $filename = 'xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels'; + foreach ($v['hyperlinks'] as $h) { + if ($h['ID']) { + $RH[] = ''; + } + } + $xml = str_replace('{HYPERLINKS}', implode("\r\n", $RH), $template); + $this->_writeEntry($fh, $cdrec, $filename, $xml); + $entries++; + } + } + $xml = null; + + } elseif ($cfilename === '[Content_Types].xml') { + $TYPES = ['']; + foreach ($this->sheets as $k => $v) { + $TYPES[] = ''; + if ($this->extLinkId) { + $TYPES[] = ''; + } + } + $template = str_replace('{TYPES}', implode("\r\n", $TYPES), $template); + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } elseif ($cfilename === 'xl/styles.xml') { + $NF = $XF = $FONTS = $F_KEYS = $FILLS = $FL_KEYS = []; + $BR = ['']; + $BR_KEYS = [0 => 0]; + + foreach ($this->NF as $k => $v) { + $NF[] = ''; + } + + foreach ($this->XF as $xf) { + // 0 - num fmt, 1 - align, 2 - font, 3 - fill, 4 - font color, 5 - bgcolor, 6 - border + // fonts + $F_KEY = $xf[2] . '-' . $xf[4]; + if (isset($F_KEYS[$F_KEY])) { + $F_ID = $F_KEYS[$F_KEY]; + } else { + $F_ID = $F_KEYS[$F_KEY] = count($FONTS); + + $FONTS[] = '' + . ($this->defaultFontSize ? '' : '') + . ($xf[2] & self::F_BOLD ? '' : '') + . ($xf[2] & self::F_ITALIC ? '' : '') + . ($xf[2] & self::F_UNDERLINE ? '' : '') + . ($xf[2] & self::F_STRIKE ? '' : '') + . ($xf[2] & self::F_HYPERLINK ? '' : '') + . ($xf[2] & self::F_COLOR ? '' : '') + . ''; + } + // fills + $FL_KEY = $xf[3] . '-' . $xf[5]; + if (isset($FL_KEYS[$FL_KEY])) { + $FL_ID = $FL_KEYS[$FL_KEY]; + } else { + $FL_ID = $FL_KEYS[$FL_KEY] = count($FILLS); + $FILLS[] = '' : ' />') + . ''; + } + $align = ($xf[1] & self::A_LEFT ? ' horizontal="left"' : '') + . ($xf[1] & self::A_RIGHT ? ' horizontal="right"' : '') + . ($xf[1] & self::A_CENTER ? ' horizontal="center"' : '') + . ($xf[1] & self::A_TOP ? ' vertical="top"' : '') + . ($xf[1] & self::A_MIDDLE ? ' vertical="center"' : '') + . ($xf[1] & self::A_BOTTOM ? ' vertical="bottom"' : '') + . ($xf[1] & self::A_WRAPTEXT ? ' wrapText="1"' : ''); + + // border + $BR_ID = 0; + if ($xf[6] !== '') { + $b = $xf[6]; + if (isset($BR_KEYS[$b])) { + $BR_ID = $BR_KEYS[$b]; + } else { + $BR_ID = count($BR_KEYS); + $BR_KEYS[$b] = $BR_ID; + $border = ''; + $ba = explode(' ', $b); + if (!isset($ba[1])) { + $ba[] = $ba[0]; + $ba[] = $ba[0]; + $ba[] = $ba[0]; + } + if (!isset($ba[4])) { // diagonal + $ba[] = 'none'; + } + $sides = [ 'left' => 3, 'right' => 1, 'top' => 0, 'bottom' => 2, 'diagonal' => 4]; + foreach ($sides as $side => $idx) { + $s = 'thin'; + $c = ''; + + $va = explode('#', $ba[$idx]); + if (isset($va[1])) { + $s = $va[0] === '' ? 'thin' : $va[0]; + $c = $va[1]; + } elseif (in_array($va[0], $this->BR_STYLE, true)) { + $s = $va[0]; + } else { + $c = $va[0]; + } + if (strlen($c) === 6) { + $c = 'FF' . $c; + } + + if ($s && $s !== 'none') { + $border .= '<' . $side . ' style="' . $s . '">' + . '' + . ''; + } else { + $border .= '<' . $side . '/>'; + } + + } + $border .= ''; + $BR[] = $border; + } + } + + + $XF[] = ' 0 ? ' applyNumberFormat="1"' : '') + . ($F_ID > 0 ? ' applyFont="1"' : '') + . ($FL_ID > 0 ? ' applyFill="1"' : '') + . ($BR_ID > 0 ? ' applyBorder="1"' : '') + . ($align ? ' applyAlignment="1">' : '/>'); + + } + + + // wrap collections + array_unshift($NF, ''); + $NF[] = ''; + array_unshift($XF, ''); + $XF[] = ''; + array_unshift($FONTS, ''); + $FONTS[] = ''; + array_unshift($FILLS, ''); + $FILLS[] = ''; + array_unshift($BR, ''); + $BR[] = ''; + + $template = str_replace( + ['{NUMFMTS}', '{FONTS}', '{XF}', '{FILLS}', '{BORDERS}'], + [implode("\r\n", $NF), implode("\r\n", $FONTS), implode("\r\n", $XF), implode("\r\n", $FILLS), implode("\r\n", $BR)], + $template); + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } else { + $this->_writeEntry($fh, $cdrec, $cfilename, $template); + $entries++; + } + } + $before_cd = ftell($fh); + fwrite($fh, $cdrec); + + // end of central dir + fwrite($fh, $dirSignatureE); + fwrite($fh, pack('v', 0)); // number of this disk + fwrite($fh, pack('v', 0)); // number of the disk with the start of the central directory + fwrite($fh, pack('v', $entries)); // total # of entries "on this disk" + fwrite($fh, pack('v', $entries)); // total # of entries overall + fwrite($fh, pack('V', mb_strlen($cdrec, '8bit'))); // size of central dir + fwrite($fh, pack('V', $before_cd)); // offset to start of central dir + fwrite($fh, pack('v', mb_strlen($zipComments, '8bit'))); // .zip file comment length + fwrite($fh, $zipComments); + + return true; + } + + protected function _writeEntry($fh, &$cdrec, $cfilename, $data) + { + $zipSignature = "\x50\x4b\x03\x04"; // local file header signature + $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature + + $e = []; + $e['uncsize'] = mb_strlen($data, '8bit'); + + // if data to compress is too small, just store it + if ($e['uncsize'] < 256) { + $e['comsize'] = $e['uncsize']; + $e['vneeded'] = 10; + $e['cmethod'] = 0; + $zdata = $data; + } else { // otherwise, compress it + $zdata = gzcompress($data); + $zdata = substr(substr($zdata, 0, -4), 2); // fix crc bug (thanks to Eric Mueller) + $e['comsize'] = mb_strlen($zdata, '8bit'); + $e['vneeded'] = 10; + $e['cmethod'] = 8; + } + + $e['bitflag'] = 0; + $e['crc_32'] = crc32($data); + + // Convert date and time to DOS Format, and set then + $lastmod_timeS = str_pad(decbin(date('s') >= 32 ? date('s') - 32 : date('s')), 5, '0', STR_PAD_LEFT); + $lastmod_timeM = str_pad(decbin(date('i')), 6, '0', STR_PAD_LEFT); + $lastmod_timeH = str_pad(decbin(date('H')), 5, '0', STR_PAD_LEFT); + $lastmod_dateD = str_pad(decbin(date('d')), 5, '0', STR_PAD_LEFT); + $lastmod_dateM = str_pad(decbin(date('m')), 4, '0', STR_PAD_LEFT); + $lastmod_dateY = str_pad(decbin(date('Y') - 1980), 7, '0', STR_PAD_LEFT); + + # echo "ModTime: $lastmod_timeS-$lastmod_timeM-$lastmod_timeH (".date("s H H").")\n"; + # echo "ModDate: $lastmod_dateD-$lastmod_dateM-$lastmod_dateY (".date("d m Y").")\n"; + $e['modtime'] = bindec("$lastmod_timeH$lastmod_timeM$lastmod_timeS"); + $e['moddate'] = bindec("$lastmod_dateY$lastmod_dateM$lastmod_dateD"); + + $e['offset'] = ftell($fh); + + fwrite($fh, $zipSignature); + fwrite($fh, pack('s', $e['vneeded'])); // version_needed + fwrite($fh, pack('s', $e['bitflag'])); // general_bit_flag + fwrite($fh, pack('s', $e['cmethod'])); // compression_method + fwrite($fh, pack('s', $e['modtime'])); // lastmod_time + fwrite($fh, pack('s', $e['moddate'])); // lastmod_date + fwrite($fh, pack('V', $e['crc_32'])); // crc-32 + fwrite($fh, pack('I', $e['comsize'])); // compressed_size + fwrite($fh, pack('I', $e['uncsize'])); // uncompressed_size + fwrite($fh, pack('s', mb_strlen($cfilename, '8bit'))); // file_name_length + fwrite($fh, pack('s', 0)); // extra_field_length + fwrite($fh, $cfilename); // file_name + // ignoring extra_field + fwrite($fh, $zdata); + + // Append it to central dir + $e['external_attributes'] = (substr($cfilename, -1) === '/' && !$zdata) ? 16 : 32; // Directory or file name + $e['comments'] = ''; + + $cdrec .= $dirSignature; + $cdrec .= "\x0\x0"; // version made by + $cdrec .= pack('v', $e['vneeded']); // version needed to extract + $cdrec .= "\x0\x0"; // general bit flag + $cdrec .= pack('v', $e['cmethod']); // compression method + $cdrec .= pack('v', $e['modtime']); // lastmod time + $cdrec .= pack('v', $e['moddate']); // lastmod date + $cdrec .= pack('V', $e['crc_32']); // crc32 + $cdrec .= pack('V', $e['comsize']); // compressed filesize + $cdrec .= pack('V', $e['uncsize']); // uncompressed filesize + $cdrec .= pack('v', mb_strlen($cfilename, '8bit')); // file name length + $cdrec .= pack('v', 0); // extra field length + $cdrec .= pack('v', mb_strlen($e['comments'], '8bit')); // file comment length + $cdrec .= pack('v', 0); // disk number start + $cdrec .= pack('v', 0); // internal file attributes + $cdrec .= pack('V', $e['external_attributes']); // internal file attributes + $cdrec .= pack('V', $e['offset']); // relative offset of local header + $cdrec .= $cfilename; + $cdrec .= $e['comments']; + } + + protected function _sheetToXML($idx, $template) + { + // locale floats fr_FR 1.234,56 -> 1234.56 + $_loc = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, 'C'); + $COLS = []; + $ROWS = []; + if (count($this->sheets[$idx]['rows'])) { + $COLS[] = ''; + $CUR_ROW = 0; + $COL = []; + foreach ($this->sheets[$idx]['rows'] as $r) { + $CUR_ROW++; + $row = ''; + $CUR_COL = 0; + $RH = 0; // row height + foreach ($r as $v) { + $CUR_COL++; + if (!isset($COL[$CUR_COL])) { + $COL[$CUR_COL] = 0; + } + $cname = $this->num2name($CUR_COL) . $CUR_ROW; + + if ($v === null || $v === '') { + $row .= ''; + continue; + } + + $ct = $cv = $cf = null; + $N = $A = $F = $FL = $C = $BG = 0; + $BR = ''; + + if (is_string($v)) { + + if ($v[0] === "\0") { // RAW value as string + $v = substr($v, 1); + $vl = mb_strlen($v); + } else { + if (strpos($v, '<') !== false) { // tags? + if (strpos($v, '') !== false) { + $F += self::F_BOLD; + } + if (strpos($v, '') !== false) { + $F += self::F_ITALIC; + } + if (strpos($v, '') !== false) { + $F += self::F_UNDERLINE; + } + if (strpos($v, '') !== false) { + $F += self::F_STRIKE; + } + if (preg_match('/]+)>/', $v, $m)) { + + if (preg_match('/ color="([^"]+)"/', $m[1], $m2)) { + + $F += self::F_COLOR; + $c = ltrim($m2[1], '#'); + $C = strlen($c) === 8 ? $c : ('FF' . $c); + } + if (preg_match('/ bgcolor="([^"]+)"/', $m[1], $m2)) { + $FL += self::FL_COLOR; + $c = ltrim($m2[1], '#'); + $BG = strlen($c) === 8 ? $c : ('FF' . $c); + } + if (preg_match('/ height="([^"]+)"/', $m[1], $m2)) { + $RH = $m2[1]; + } + if (preg_match('/ nf="([^"]+)"/', $m[1], $m2)) { + $c = htmlspecialchars_decode($m2[1], ENT_QUOTES); + $N = $this->getNumFmtId($c); + } + if (preg_match('/ border="([^"]+)"/', $m[1], $m2)) { + $b = htmlspecialchars_decode($m2[1], ENT_QUOTES); + if ($b && $b !== 'none') { + $BR = $b; + } + } + } + if (strpos($v, '') !== false) { + $A += self::A_LEFT; + } + if (strpos($v, '
') !== false) { + $A += self::A_CENTER; + } + if (strpos($v, '') !== false) { + $A += self::A_RIGHT; + } + if (strpos($v, '') !== false) { + $A += self::A_TOP; + } + if (strpos($v, '') !== false) { + $A += self::A_MIDDLE; + } + if (strpos($v, '') !== false) { + $A += self::A_BOTTOM; + } + if (strpos($v, '') !== false) { + $A += self::A_WRAPTEXT; + } + if (preg_match('/(.*?)<\/a>/i', $v, $m)) { + $h = explode('#', $m[1]); + $this->extLinkId++; + $this->sheets[$idx]['hyperlinks'][] = ['ID' => 'rId' . $this->extLinkId, 'R' => $cname, 'H' => $h[0], 'L' => isset($h[1]) ? $h[1] : '']; + $F += self::F_HYPERLINK; // Hyperlink + } + if (preg_match('/(.*?)<\/a>/i', $v, $m)) { + $this->extLinkId++; + $this->sheets[$idx]['hyperlinks'][] = ['ID' => 'rId' . $this->extLinkId, 'R' => $cname, 'H' => $m[1], 'L' => '']; + $F += self::F_HYPERLINK; // mailto hyperlink + } + if (preg_match('/(.*?)<\/a>/i', $v, $m)) { + $this->sheets[$idx]['hyperlinks'][] = ['ID' => null, 'R' => $cname, 'H' => null, 'L' => $m[1]]; + $F += self::F_HYPERLINK; // internal hyperlink + } + if (preg_match('/]*)>/', $v, $m)) { + $cf = strip_tags($v); + $v = 'formula'; + if (preg_match('/ v="([^"]+)"/', $m[1], $m2)) { + $v = $m2[1]; + } + } else { + $v = strip_tags($v); + } + } // tags + $vl = mb_strlen($v); + if ($N) { + $cv = ltrim($v, '+'); + } elseif ($v === '0' || preg_match('/^[-+]?[1-9]\d{0,14}$/', $v)) { // Integer as General + $cv = ltrim($v, '+'); + if ($vl > 10) { + $N = self::N_INT; // [1] 0 + } + } elseif (preg_match('/^[-+]?(0|[1-9]\d*)\.(\d+)$/', $v, $m)) { + $cv = ltrim($v, '+'); + if (strlen($m[2]) < 3) { + $N = self::N_DEC; + } + } elseif (preg_match('/^\$[-+]?[0-9\.]+$/', $v)) { // currency $? + $N = self::N_DOLLAR; + $cv = ltrim($v, '+$'); + } elseif (preg_match('/^[-+]?[0-9\.]+( ₽| €)$/u', $v, $m)) { // currency ₽ €? + if ($m[1] === ' ₽') { + $N = self::N_RUB; + } elseif ($m[1] === ' €') { + $N = self::N_EURO; + } + $cv = trim($v, ' +₽€'); + } elseif (preg_match('/^([-+]?\d+)%$/', $v, $m)) { + $cv = round($m[1] / 100, 2); + $N = self::N_PERCENT_INT; // [9] 0% + } elseif (preg_match('/^([-+]?\d+\.\d+)%$/', $v, $m)) { + $cv = round($m[1] / 100, 4); + $N = self::N_PRECENT_DEC; // [10] 0.00% + } elseif (preg_match('/^(\d\d\d\d)-(\d\d)-(\d\d)$/', $v, $m)) { + $cv = $this->date2excel($m[1], $m[2], $m[3]); + $N = self::N_DATE; // [14] mm-dd-yy + } elseif (preg_match('/^(\d\d)\/(\d\d)\/(\d\d\d\d)$/', $v, $m)) { + $cv = $this->date2excel($m[3], $m[2], $m[1]); + $N = self::N_DATE; // [14] mm-dd-yy + } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $v, $m)) { + $cv = $this->date2excel(0, 0, 0, $m[1], $m[2], $m[3]); + $N = self::N_TIME; // time + } elseif (preg_match('/^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/', $v, $m)) { + $cv = $this->date2excel($m[1], $m[2], $m[3], $m[4], $m[5], $m[6]); + $N = ((int)$m[1] === 0) ? self::N_TIME : self::N_DATETIME; // [22] m/d/yy h:mm + } elseif (preg_match('/^(\d\d)\/(\d\d)\/(\d\d\d\d) (\d\d):(\d\d):(\d\d)$/', $v, $m)) { + $cv = $this->date2excel($m[3], $m[2], $m[1], $m[4], $m[5], $m[6]); + $N = self::N_DATETIME; // [22] m/d/yy h:mm + } elseif (preg_match('/^[0-9+-.]+$/', $v)) { // Long ? + $A += ($A & (self::A_LEFT | self::A_CENTER)) ? 0 : self::A_RIGHT; + } elseif (preg_match('/^https?:\/\/\S+$/i', $v)) { + $h = explode('#', $v); + $this->extLinkId++; + $this->sheets[$idx]['hyperlinks'][] = ['ID' => 'rId' . $this->extLinkId, 'R' => $cname, 'H' => $h[0], 'L' => isset($h[1]) ? $h[1] : '']; + $F += self::F_HYPERLINK; // Hyperlink + } elseif (preg_match("/^[a-zA-Z0-9_\.\-]+@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/", $v)) { + $this->extLinkId++; + $this->sheets[$idx]['hyperlinks'][] = ['ID' => 'rId' . $this->extLinkId, 'R' => $cname, 'H' => 'mailto:' . $v, 'L' => '']; + $F += self::F_HYPERLINK; // Hyperlink + } + if (($N === self::N_DATE || $N === self::N_DATETIME) && $cv < 0) { + $cv = null; + $N = 0; + } + } + if ($cv === null) { + + $v = $this->esc($v); + + if ($cf) { + $ct = 'str'; + $cv = $v; + } elseif (mb_strlen($v) > 160) { + $ct = 'inlineStr'; + $cv = $v; + } else { + $ct = 's'; // shared string + $cv = false; + $skey = '~' . $v; + if (isset($this->SI_KEYS[$skey])) { + $cv = $this->SI_KEYS[$skey]; + } + if ($cv === false) { + $this->SI[] = $v; + $cv = count($this->SI) - 1; + $this->SI_KEYS[$skey] = $cv; + } + } + } + } elseif (is_int($v)) { + $vl = mb_strlen((string)$v); + $cv = $v; + } elseif (is_float($v)) { + $vl = mb_strlen((string)$v); + $cv = $v; + } elseif ($v instanceof \DateTime) { + $vl = 16; + $cv = $this->date2excel($v->format('Y'), $v->format('m'), $v->format('d'), $v->format('H'), $v->format('i'), $v->format('s')); + $N = self::N_DATETIME; // [22] m/d/yy h:mm + } else { + continue; + } + + $COL[$CUR_COL] = max($vl, $COL[$CUR_COL]); + + $cs = 0; + + if (($N + $A + $F + $FL > 0) || $BR !== '') { + + if ($FL === self::FL_COLOR) { + $FL += self::FL_SOLID; + } + if (($F & self::F_HYPERLINK) && !($F & self::F_COLOR)) { + $F += self::F_COLOR; + $C = 'FF0563C1'; + } + + $XF_KEY = $N . '-' . $A . '-' . $F . '-' . $FL . '-' . $C . '-' . $BG . '-' . $BR; +// echo $cname .'='.$XF_KEY.PHP_EOL; + if (isset($this->XF_KEYS[$XF_KEY])) { + $cs = $this->XF_KEYS[$XF_KEY]; + } + if ($cs === 0) { + $cs = count($this->XF); + $this->XF_KEYS[$XF_KEY] = $cs; + $this->XF[] = [$N, $A, $F, $FL, $C, $BG, $BR]; + } + } + + $row .= '' + . ($cf ? '' . $cf . '' : '') + . ($ct === 'inlineStr' ? '' . $cv . '' : '' . $cv . '') . "\r\n"; + } + $ROWS[] = '' . $row . ""; + } + foreach ($COL as $k => $max) { + $w = isset($this->sheets[$idx]['colwidth'][$k]) ? $this->sheets[$idx]['colwidth'][$k] : min($max + 1, 60); + $COLS[] = ''; + } + $COLS[] = ''; + $REF = 'A1:' . $this->num2name(count($COL)) . $CUR_ROW; + } else { + $ROWS[] = '0'; + $REF = 'A1:A1'; + } + + $AUTOFILTER = ''; + if ($this->sheets[$idx]['autofilter']) { + $AUTOFILTER = ''; + } + + $MERGECELLS = []; + if (count($this->sheets[$idx]['mergecells'])) { + $MERGECELLS[] = ''; + $MERGECELLS[] = ''; + foreach ($this->sheets[$idx]['mergecells'] as $m) { + $MERGECELLS[] = ''; + } + $MERGECELLS[] = ''; + } + + $HYPERLINKS = []; + if (count($this->sheets[$idx]['hyperlinks'])) { + $HYPERLINKS[] = ''; + foreach ($this->sheets[$idx]['hyperlinks'] as $h) { + $HYPERLINKS[] = ''; + } + $HYPERLINKS[] = ''; + } + + //restore locale + setlocale(LC_NUMERIC, $_loc); + + return str_replace(['{REF}', '{COLS}', '{ROWS}', '{AUTOFILTER}', '{MERGECELLS}', '{HYPERLINKS}'], + [$REF, + implode("\r\n", $COLS), + implode("\r\n", $ROWS), + $AUTOFILTER, + implode("\r\n", $MERGECELLS), + implode("\r\n", $HYPERLINKS) + ], + $template); + } + + public function num2name($num) + { + $numeric = ($num - 1) % 26; + $letter = chr(65 + $numeric); + $num2 = (int)(($num - 1) / 26); + if ($num2 > 0) { + return $this->num2name($num2) . $letter; + } + return $letter; + } + + public function date2excel($year, $month, $day, $hours = 0, $minutes = 0, $seconds = 0) + { + + $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400; + + if ((int)$year === 0) { + return $excelTime; + } + + // self::CALENDAR_WINDOWS_1900 + $excel1900isLeapYear = True; + if (($year === 1900) && ($month <= 2)) { + $excel1900isLeapYear = False; + } + $myExcelBaseDate = 2415020; + + // Julian base date Adjustment + if ($month > 2) { + $month -= 3; + } else { + $month += 9; + --$year; + } + $century = substr($year, 0, 2); + $decade = substr($year, 2, 2); + // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0) + $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myExcelBaseDate + $excel1900isLeapYear; + + return (float)$excelDate + $excelTime; + } + + public function setDefaultFont($name) + { + $this->defaultFont = $name; + return $this; + } + + public function setDefaultFontSize($size) + { + $this->defaultFontSize = $size; + return $this; + } + + public function autoFilter($range) + { + $this->sheets[$this->curSheet]['autofilter'] = $range; + return $this; + } + + public function mergeCells($range) + { + $this->sheets[$this->curSheet]['mergecells'][] = $range; + return $this; + } + + public function setColWidth($col, $width) + { + $this->sheets[$this->curSheet]['colwidth'][$col] = $width; + return $this; + } + + public function esc($str) + { + // XML UTF-8: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // but we use fast version + return str_replace(['&', '<', '>', "\x00", "\x03", "\x0B"], ['&', '<', '>', '', '', ''], $str); + } + + public function getNumFmtId($code) + { + if (isset($this->NF[$code])) { // id? + return (int)$code; + } + if (isset($this->NF_KEYS[$code])) { + return $this->NF_KEYS[$code]; + } + $id = 197 + count($this->NF); // custom + $this->NF[$id] = $code; + $this->NF_KEYS[$code] = $id; + return $id; + } + + public static function raw($value) + { + return "\0" . (string)$value; + } + +} \ No newline at end of file diff --git a/spreadSheetReader/read_write_xlsx.php b/spreadSheetReader/read_write_xlsx.php new file mode 100644 index 0000000..a8625ed --- /dev/null +++ b/spreadSheetReader/read_write_xlsx.php @@ -0,0 +1,29 @@ +rows()); + } else { + echo SimpleXLSX::parseError(); + } + return $fileContent; +} + + + +function write_xlsxFile($filename, $data) { + $xlsx = SimpleXLSXGen::fromArray($data); + $xlsx->saveAs($filename); +} + +function download_xlsxFile($data) { + $xlsxArray = read_xls_file($_SERVER['DOCUMENT_ROOT'] ."/data/data.xlsx"); + foreach ($data as $row) { + array_push($xlsxArray, $row); + } + $xlsx = SimpleXLSXGen::fromArray($xlsxArray); + $xlsx->downloadAs("data.xlsx"); +} \ No newline at end of file diff --git a/taskExecuter.php b/taskExecuter.php index fb9b262..5211be7 100644 --- a/taskExecuter.php +++ b/taskExecuter.php @@ -1,8 +1,8 @@ "; $response = sendAPICompanySearchRequest($username, $password, $data); $companyArray = json_decode($response, true); - // TODO implement - $companyData = [ - ['John', 30, 'New York'], - ['Alice', 25, 'Los Angeles'], - ['Bob', 35, 'Chicago'], - ]; $companyData = array(); foreach ($companyArray as $company) { $companyData[] = [$company['name'],$company['ehraid'],$company['uid'],$company['chid'],$company['legalSeat'],$company['registryOfCommerceId'],$company['legalForm']['name']['de'],$company['status'],$company['sogcDate'],$company['deletionDate']]; @@ -32,7 +26,6 @@ function doRequest($data, $filename, $username, $password) // If File doesn't exist yet $file = fopen($filename, 'w'); if($file) { - // TODO change headers fputcsv($file, ['name', 'ehraid', 'uid', 'chid', 'legalSeat', 'registryOfCommerceId', 'legalForm', 'status', 'sogcDate', 'deletionDate']); } } else { @@ -54,46 +47,18 @@ function sendEmail($emailAddress, $filename) // Stop executing bevore reaching PHP Max Execution Time function convertcsvToXlsx(string $csvFile, string $xlsxFile) { - // Read the CSV file - $csvData = file_get_contents($csvFile); + $csvData = []; - // Split the CSV data into rows - $csvRows = explode("\n", $csvData); - - // Create a SimpleXMLElement for the workbook - $xmlWorkbook = new SimpleXMLElement(' - - - -
-
-
'); - - // Reference to the Worksheet's Table - $xmlTable = $xmlWorkbook->Worksheet->Table; - - // Loop through CSV rows and create XML elements - foreach ($csvRows as $csvRow) { - // Split the CSV row into cells - $csvCells = str_getcsv($csvRow); - - // Create a new row in the XML table - $xmlRow = $xmlTable->addChild('Row'); - - // Add cells to the row - foreach ($csvCells as $csvCell) { - $xmlCell = $xmlRow->addChild('Cell'); - $xmlData = $xmlCell->addChild('Data', htmlspecialchars($csvCell)); - $xmlData->addAttribute('ss:Type', 'String'); + if (($handle = fopen($csvFile, "r")) !== false) { + while (($data = fgetcsv($handle, 1000, ",")) !== false) { + $csvData[] = $data; } + fclose($handle); + } else { + echo "Failed to open $csvFile for reading."; } - // Save the XML data to a file - $xmlWorkbook->asXML($xlsxFile); + write_xlsxFile($xlsxFile, $csvData); } while($latesEndTime - time() > 60){ @@ -102,8 +67,8 @@ while($latesEndTime - time() > 60){ $taskfiles = scandir($taskDir); $taskfiles = array_diff($taskfiles, array('.', '..')); sort($taskfiles); - // if there are any taks which are older then 1 Minutes. To be sure the file writing process is finished. - if(count($taskfiles) > 0 && intval(explode("-", $taskfiles[0])[0]) + 60 < time()) { + // if there are any taks which are older then 0.5 Minutes. To be sure the file writing process is finished. + if(count($taskfiles) > 0 && intval(explode("-", $taskfiles[0])[0]) + 30 < time()) { $taskString = file_get_contents($taskDir.'/'.$taskfiles[0]); $task = json_decode($taskString, true); @@ -141,5 +106,4 @@ while($latesEndTime - time() > 60){ } } - ?> \ No newline at end of file