<?php
/* =================================================================================
 * License: GPL-2.0 license
 * Author: 众产®  https://ciy.cn/code
 * Version: 0.1.3
====================================================================================*/
/*
 * excel.php Excel导入导出扩展库
 * 
 * ciy_ImportCSV        导入Excel CSV格式解析
 * ciy_runExcelCSV      导出到Excel CSV格式
 * ciy_runExcelxml      导出到Excel xml格式，样式可控
 * ciy_runExcelxlsx     导出到Excel xlsx格式（不建议实现）
 * 
 */
namespace ciy;
class excel {
    public static function ciy_ImportCSV($file) {
        $fh = file_get_contents($file);
        $dot = ',';
        if (ord($fh[0]) == 255) {
            $line = mb_convert_encoding(substr($fh, 2), 'UTF-8', 'UTF-16LE');
            $dot = "\t";
        } else
            $line = mb_convert_encoding($fh, 'UTF-8', 'GBK');
        $csvlines = array();
        $csvfields = array();
        $csvdata = '';
        $bcontent = false;
        $linecnt = strlen($line) - 1;
        if ($line[$linecnt] != "\n") {
            $line .= "\n\n";
            $linecnt += 2;
        } else {
            $line .= "\n";
            $linecnt++;
        }
        for ($i = 0; $i < $linecnt; $i++) {
            if ($line[$i] == "\"") {
                if ($line[$i + 1] == "\"")
                    $csvdata .= $line[$i];
                else
                    $bcontent = !$bcontent;
            } else if ($bcontent) {
                $csvdata .= $line[$i];
            } else if ($line[$i] == $dot) {
                $csvfields[] = $csvdata;
                $csvdata = '';
            } else if ($line[$i] == "\n") {
                $csvfields[] = str_replace("\r", '', $csvdata);
                $csvdata = '';
                $csvlines[] = $csvfields;
                $csvfields = array();
            } else
                $csvdata .= $line[$i];
        }
        return $csvlines;
    }

    /**
     * CSV函数调用组件，用于数据导出到Excel操作
     * ?func=函数名
     * 将调用csv_函数名(){}
     * 函数返回array数组。第一行包含.csv，则为CSV文件名。
     */
    public static function ciy_runExcelCSV($csql) {
        if (!isset($_GET['excel']) || $_GET['excel'] != 'csv')
            return;
        $funcname = 'excel_' . get('func');
        if (!function_exists($funcname)) {
            clog('Excel CSV导出失败：' . $funcname . '函数未定义');
            exit;
        }
        $retarr = call_user_func($funcname, $csql);
        if (!is_array($retarr)) {
            clog('Excel CSV导出失败：' . $retarr);
            exit;
        }
        $filename = $retarr[0];
        if (empty($filename))
            $filename = date('Y-m-d_H-i-s') . rand(1000, 9999);
        $filename .= '.csv';
        $fields = $retarr[1];
        $datas = $retarr[2];
        header("Cache-Control: public");
        header("Pragma: public");
        header("Content-type: text/csv");
        header("Content-Disposition: attachment; filename=" . $filename);
        echo (chr(255) . chr(254));
        $bline = false;
        foreach ($fields as $field) {
            if (is_array($field))
                $d = $field['name'];
            else
                $d = $field;
            if ($bline)
                echo mb_convert_encoding("\t", "UTF-16LE", "UTF-8");
            echo mb_convert_encoding('"' . $d . '"', "UTF-16LE", "UTF-8");
            $bline = true;
        }
        echo mb_convert_encoding("\r\n", "UTF-16LE", "UTF-8");
        foreach ($datas as $data) {
            $bline = false;
            foreach ($data as $d) {
                if ($bline)
                    echo mb_convert_encoding("\t", "UTF-16LE", "UTF-8");
                echo mb_convert_encoding('"' . $d . '"', "UTF-16LE", "UTF-8");
                $bline = true;
            }
            echo mb_convert_encoding("\r\n", "UTF-16LE", "UTF-8");
        }
        exit;
    }
    public static function ciy_runExcelxml($csql) {
        if (!isset($_GET['excel']) || $_GET['excel'] != 'xml')
            return;
        $funcname = 'excel_' . get('func');
        if (!function_exists($funcname)) {
            clog('Excel xml导出失败：' . $funcname . '函数未定义');
            exit;
        }
        $retarr = call_user_func($funcname, $csql);
        if (!is_array($retarr)) {
            clog('Excel xml导出失败:' . $retarr);
            exit;
        }
        $filename = $retarr[0];
        if (empty($filename))
            $filename = date('Y-m-d_H-i-s') . rand(1000, 9999);
        $filename .= '.xls';
        $fields = $retarr[1];
        $datas = $retarr[2];
        $styles = @$retarr[3];
        if (!is_array($styles))
            $styles = array();
        $exts = @$retarr[4];
        if (!is_array($exts))
            $exts = array();
        $sheetname = 'sheetCIY';
        if (isset($exts['sheetname']))
            $sheetname = $exts['sheetname'];
        $DefaultColumnWidth = 60; //默认宽度
        $DefaultRowHeight = 16; //默认高度
        $dat = '<?xml version="1.0"?><?mso-application progid="Excel.Sheet"?><Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author>CIY</Author>
<Version>15.00</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office"><AllowPNG/></OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"><WindowTopX>0</WindowTopX><WindowTopY>0</WindowTopY>  <ProtectStructure>False</ProtectStructure><ProtectWindows>False</ProtectWindows></ExcelWorkbook>
<Styles>';
        foreach ($styles as $id => $style)
            $dat .= '<Style ss:ID="' . $id . '">' . $style . '</Style>';
        $dat .= '</Styles><Worksheet ss:Name="' . $sheetname . '"><Table ss:ExpandedColumnCount="' . (count($fields) + 10) . '" ss:ExpandedRowCount="' . (count($datas) + 20) . '" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="' . $DefaultColumnWidth . '" ss:DefaultRowHeight="' . $DefaultRowHeight . '">';
        foreach ($fields as $field) {
            if (!is_array($field) || !isset($field['width']))
                $dat .= '<Column ss:Width="' . $DefaultColumnWidth . '"/>';
            else
                $dat .= '<Column ss:Width="' . $field['width'] . '"/>';
        }
        $dat .= @$exts['rowstop']; //自定义表格头
        if (isset($exts['titleheight']))
            $dat .= '<Row ss:Height="' . $exts['titleheight'] . '">';
        else
            $dat .= '<Row>';
        $cellpre = '<Cell';
        if (isset($styles['ts']))
            $cellpre .= '<Cell ss:StyleID="ts"';
        foreach ($fields as $field) {
            if (is_array($field)) {
                $dat .= $cellpre . '><Data ss:Type="String">' . @$field['name'] . '</Data></Cell>';
            } else
                $dat .= $cellpre . '><Data ss:Type="String">' . $field . '</Data></Cell>';
        }
        $dat .= '</Row>';
        foreach ($datas as $data) {
            $dat .= '<Row>';
            foreach ($data as $ind => $d) {
                $dat .= '<Cell';
                $type = 'String';
                if (is_array($fields[$ind])) {
                    if (isset($fields[$ind]['style']))
                        $dat .= ' ss:StyleID="' . $fields[$ind]['style'] . '"';
                    if (isset($fields[$ind]['type']))
                        $type = $fields[$ind]['type'];
                }
                $dat .= '><Data ss:Type="' . $type . '">' . $d . '</Data></Cell>';
            }
            $dat .= '</Row>';
        }
        $dat .= @$exts['rowsfooter']; //自定义表格尾
        $dat .= '</Table>
  <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
   <PageSetup>';
        $dat .= @$exts['pagesetup'];
        $dat .= '<Header x:Margin="0.3"/>
    <Footer x:Margin="0.3"/>
    <PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
   </PageSetup>
   <Print>
    <ValidPrinterInfo/>
    <PaperSizeIndex>1</PaperSizeIndex>
    <HorizontalResolution>600</HorizontalResolution>
    <VerticalResolution>0</VerticalResolution>
   </Print>
   <Selected/>
   <ProtectObjects>False</ProtectObjects>
   <ProtectScenarios>False</ProtectScenarios>
  </WorksheetOptions>
 </Worksheet>
</Workbook>';

        header("Cache-Control: public");
        header("Pragma: public");
        header("Content-type: text/xml");
        header("Content-Disposition: attachment; filename=" . $filename);
        echo $dat;
        exit;
    }
    public static function general_excel_xml($param) {
        $fields = $param['field'];
        $datas = $param['data'];
        $styles = @$param['style'];
        if (!is_array($styles)) {
            $styles = array();
            $styles['l'] = '<Alignment ss:Horizontal="Left"/><Font ss:Color="#000000"/><Borders>
        <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
    </Borders>';
            $styles['c'] = '<Alignment ss:Horizontal="Center"/><Font ss:Color="#000000"/><Borders>
        <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
    </Borders>';
            $styles['r'] = '<Alignment ss:Horizontal="Right"/><Font ss:Color="#000000"/><Borders>
        <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
    </Borders>';
            $styles['dd'] = '<Alignment ss:Horizontal="Center"/><Font ss:Color="#000000"/><Borders>
        <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
    </Borders><NumberFormat ss:Format="yyyy\-mm\-dd"/>';
            $styles['dt'] = '<Alignment ss:Horizontal="Center"/><Font ss:Color="#000000"/><Borders>
        <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
        <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
    </Borders><NumberFormat ss:Format="yyyy\-mm\-dd\ hh:mm"/>';
            $styles['ts'] = '<Alignment ss:Horizontal="Center"/><Interior ss:Color="#bdd7ee" ss:Pattern="Solid"/><Borders>
            <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
            <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
            <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
            <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
        </Borders>';
            $styles['cap'] = '<Alignment ss:Horizontal="Left" ss:Vertical="Center"/>
        <Font ss:Size="16" ss:Color="#1f4e78" ss:Bold="1"/>
        <Interior ss:Color="#ffffff" ss:Pattern="Solid"/>';
        }
        $sheetname = 'sheetCIY';
        if (isset($param['sheetname']))
            $sheetname = $param['sheetname'];
        $DefaultColumnWidth = 60; //默认宽度
        $DefaultRowHeight = 20; //默认高度
        $dat = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><?mso-application progid="Excel.Sheet"?><Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Author>CIY</Author>
<Version>15.00</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office"><AllowPNG/></OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"><WindowTopX>0</WindowTopX><WindowTopY>0</WindowTopY>  <ProtectStructure>False</ProtectStructure><ProtectWindows>False</ProtectWindows></ExcelWorkbook>
<Styles>';
        foreach ($styles as $id => $style)
            $dat .= '<Style ss:ID="' . $id . '">' . $style . '</Style>';
        $dat .= '</Styles><Worksheet ss:Name="' . $sheetname . '"><Table ss:ExpandedColumnCount="' . (count($fields) + 10) . '" ss:ExpandedRowCount="' . (count($datas) + 20) . '" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="' . $DefaultColumnWidth . '" ss:DefaultRowHeight="' . $DefaultRowHeight . '">';
        foreach ($fields as $field) {
            if (isset($field['width']))
                $dat .= '<Column ss:Width="' . $field['width'] . '"/>';
            else
                $dat .= '<Column ss:Width="' . $DefaultColumnWidth . '"/>';
        }
        if (isset($param['toptitle'])) { //头部标题
            $dat .= '<Row ss:Height="30"><Cell ss:StyleID="cap"><Data ss:Type="String"></Data></Cell><Cell ss:StyleID="cap"><Data ss:Type="String">' . $param['toptitle'] . '</Data></Cell></Row>';
            //$dat .= '<Row ss:Height="30"><Cell ss:MergeAcross="' . (count($fields) - 1) . '" ss:StyleID="cap"><Data ss:Type="String">' . $param['toptitle'] . '</Data></Cell></Row>';
        }
        if (isset($param['rowstop'])) //自定义表格头
            $dat .= $param['rowstop'];
        if (isset($param['titleheight']))
            $dat .= '<Row ss:Height="' . $param['titleheight'] . '">';
        else
            $dat .= '<Row>';
        $cellpre = '<Cell';
        if (isset($styles['ts']))
            $cellpre .= ' ss:StyleID="ts"';
        foreach ($fields as $field) {
            $dat .= $cellpre . '><Data ss:Type="String">' . $field['name'] . '</Data></Cell>';
        }
        $dat .= '</Row>';
        foreach ($datas as $data) {
            $dat .= '<Row>';
            foreach ($data as $ind => $d) {
                $dat .= '<Cell';
                $type = 'String';
                if (is_array($fields[$ind])) {
                    if (isset($fields[$ind]['style']))
                        $dat .= ' ss:StyleID="' . $fields[$ind]['style'] . '"';
                    if (isset($fields[$ind]['type'])) {
                        $type = $fields[$ind]['type'];
                        if (!is_numeric($d) && $type == 'Number')
                            $type = 'String';
                    } else {
                        if (strlen($d) < 11 && is_numeric($d) && (!empty($dd) && $d[0] == '0'))
                            $type = 'Number';
                        else
                            $type = 'String';
                    }
                }
                $dat .= '><Data ss:Type="' . $type . '">' . $d . '</Data></Cell>';
            }
            $dat .= '</Row>';
        }
        if (isset($param['total'])) {
            $dat .= '<Row>';
            foreach ($param['total'] as $total) {
                $dat .= '<Cell';
                if (isset($total['merge']))
                    $dat .= ' ss:MergeAcross="' . $total['merge'] . '"';
                if (isset($total['style']))
                    $dat .= ' ss:StyleID="' . $total['style'] . '"';
                if ($total['name'][0] == '=') {
                    $dat .= ' ss:Formula="' . $total['name'] . '"><Data ss:Type="Number"></Data></Cell>';
                } else {
                    $dat .= '><Data ss:Type="String">' . $total['name'] . '</Data></Cell>';
                }
            }
            $dat .= '</Row>';

            // $param['total'][] = array('name'=>'合计','merge'=>2,'style'=>'l');
            // $param['total'][] = array('name'=>'=SUM(R[-2]C:R[-1]C)','style'=>'r');
        }
        if (isset($param['rowsfooter']))
            $dat .= $param['rowsfooter']; //自定义表格尾
        $dat .= '</Table>
  <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
   <PageSetup>';
        if (isset($param['pagesetup']))
            $dat .= $param['pagesetup'];
        else {
            if (isset($param['landscape']))
                $dat .= '<Layout x:Orientation="Landscape"/>'; //横向打印
            $dat .= '<Header x:Margin="0.3"/><Footer x:Margin="0.3"/>
        <PageMargins x:Bottom="0.3" x:Left="0.3" x:Right="0.3" x:Top="0.3"/>';
        }
        $dat .= '</PageSetup>';
        if (isset($param['fixtopage']))
            $dat .= '<FitToPage/>';
        $dat .= '<Print>
    <ValidPrinterInfo/>
    <PaperSizeIndex>1</PaperSizeIndex>
    <HorizontalResolution>600</HorizontalResolution>
    <VerticalResolution>0</VerticalResolution>
   </Print>
   <Selected/>
   <ProtectObjects>False</ProtectObjects>
   <ProtectScenarios>False</ProtectScenarios>
  </WorksheetOptions>
 </Worksheet>
</Workbook>';
        return $dat;
    }
    //直接导出xls/xlsx格式。
    //分析具体格式，将xlsx修改为zip解压后，研究sheet1.xml/styles.xml。
    //使用PHPExcel后端导出（服务器压力较大，可以自定义样式或导入样式）
    //建议使用js-xlsx前端导出（服务器压力小，前端JS控制数据样式）
    public static function ciy_runExcelxlsx($csql) {
        if (!isset($_GET['excel']) || $_GET['excel'] != 'xlsx')
            return;
    }
}
