浏览代码

Merge pull request #242 from viest/dev

Feat: reader, writer
viest 5 年之前
父节点
当前提交
f96c24394c

+ 3 - 0
.gitignore

@@ -2,6 +2,9 @@
 *.lo
 *.la
 .libs
+debug
+debug.php
+php
 acinclude.m4
 aclocal.m4
 autom4te.cache

+ 4 - 5
include/excel.h

@@ -14,11 +14,10 @@
 #define VTIFUL_XLS_H
 
 #define V_XLS_HANDLE "handle"
-#define V_XLS_FIL           "fileName"
-#define V_XLS_COF           "config"
-#define V_XLS_PAT           "path"
-#define V_XLS_TYPE          "read_row_type"
-#define V_XLS_TYPE_SKIP_ROW "read_row_type_skip"
+#define V_XLS_FIL    "fileName"
+#define V_XLS_COF    "config"
+#define V_XLS_PAT    "path"
+#define V_XLS_TYPE   "read_row_type"
 
 #define V_XLS_CONST_READ_TYPE_INT      "TYPE_INT"
 #define V_XLS_CONST_READ_TYPE_DOUBLE   "TYPE_DOUBLE"

+ 1 - 0
include/read.h

@@ -22,6 +22,7 @@ void data_to_null(zval *zv_result_t);
 int sheet_read_row(xlsxioreadersheet sheet_t);
 void sheet_list(xlsxioreader file_t, zval *zv_result_t);
 xlsxioreader file_open(const char *directory, const char *file_name);
+void skip_rows(xlsxioreadersheet sheet_t, zval *zv_type_t, zend_long zl_skip_row);
 void load_sheet_all_data(xlsxioreadersheet sheet_t, zval *zv_type_t, zval *zv_result_t);
 xlsxioreadersheet sheet_open(xlsxioreader file_t, const zend_string *zs_sheet_name_t, const zend_long zl_flag);
 unsigned int load_sheet_current_row_data(xlsxioreadersheet sheet_t, zval *zv_result_t, zval *zv_type, unsigned int flag);

+ 3 - 0
include/xlswriter.h

@@ -19,6 +19,7 @@
 
 #include <php.h>
 
+#include "zend_smart_str.h"
 #include "zend_exceptions.h"
 #include "zend.h"
 #include "zend_API.h"
@@ -205,11 +206,13 @@ void set_row(zend_string *range, double height, xls_resource_write_t *res, lxw_f
 void set_column(zend_string *range, double width, xls_resource_write_t *res, lxw_format *format);
 void merge_cells(zend_string *range, zend_string *value, xls_resource_write_t *res, lxw_format *format);
 void url_writer(zend_long row, zend_long columns, xls_resource_write_t *res, zend_string *url, lxw_format *format);
+void call_object_method(zval *object, const char *function_name, uint32_t param_count, zval *params, zval *ret_val);
 void chart_writer(zend_long row, zend_long columns, xls_resource_chart_t *chart_resource, xls_resource_write_t *res);
 void worksheet_set_rows(lxw_row_t start, lxw_row_t end, double height, xls_resource_write_t *res, lxw_format *format);
 void image_writer(zval *value, zend_long row, zend_long columns, double width, double height, xls_resource_write_t *res);
 void formula_writer(zend_string *value, zend_long row, zend_long columns, xls_resource_write_t *res, lxw_format *format);
 void type_writer(zval *value, zend_long row, zend_long columns, xls_resource_write_t *res, zend_string *format, lxw_format *format_handle);
+void datetime_writer(lxw_datetime *datetime, zend_long row, zend_long columns, zend_string *format, xls_resource_write_t *res, lxw_format *format_handle);
 
 lxw_error workbook_file(xls_resource_write_t *self);
 

+ 21 - 0
kernel/common.c

@@ -48,4 +48,25 @@ zend_string* str_pick_up(zend_string *left, char *right)
 
     return full;
 }
+/* }}} */
+
+/* {{{ */
+void call_object_method(zval *object, const char *function_name, uint32_t param_count, zval *params, zval *ret_val)
+{
+    uint32_t index;
+    zval z_f_name;
+
+    ZVAL_STRINGL(&z_f_name, function_name, strlen(function_name));
+    call_user_function_ex(NULL, object, &z_f_name, ret_val, param_count, params, 0, NULL);
+
+    if (Z_ISUNDEF_P(ret_val)) {
+        ZVAL_NULL(ret_val);
+    }
+
+    for (index = 0; index < param_count; index++) {
+        zval_ptr_dtor(&params[index]);
+    }
+
+    zval_ptr_dtor(&z_f_name);
+}
 /* }}} */

+ 40 - 6
kernel/excel.c

@@ -11,6 +11,7 @@
 */
 
 #include "xlswriter.h"
+#include "ext/date/php_date.h"
 
 zend_class_entry *vtiful_xls_ce;
 
@@ -210,6 +211,10 @@ ZEND_BEGIN_ARG_INFO_EX(xls_set_type_arginfo, 0, 0, 1)
                 ZEND_ARG_INFO(0, zv_type_t)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(xls_set_skip_arginfo, 0, 0, 1)
+                ZEND_ARG_INFO(0, zv_skip_t)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(xls_next_cell_callback_arginfo, 0, 0, 2)
                 ZEND_ARG_INFO(0, fci)
                 ZEND_ARG_INFO(0, sheet_name)
@@ -557,21 +562,27 @@ PHP_METHOD(vtiful_xls, insertDate)
         format = zend_string_init(ZEND_STRL("yyyy-mm-dd hh:mm:ss"), 0);
     }
 
-    zval _zv_double_time;
-    ZVAL_DOUBLE(&_zv_double_time, ((double)data->value.lval / 86400 + 25569));
+    int yearLocal   = php_idate('Y', data->value.lval, 0);
+    int monthLocal  = php_idate('m', data->value.lval, 0);
+    int dayLocal    = php_idate('d', data->value.lval, 0);
+    int hourLocal   = php_idate('H', data->value.lval, 0);
+    int minuteLocal = php_idate('i', data->value.lval, 0);
+    int secondLocal = php_idate('s', data->value.lval, 0);
+
+    lxw_datetime datetime = {
+            yearLocal, monthLocal, dayLocal, hourLocal, minuteLocal, secondLocal
+    };
 
     if (format_handle) {
-        type_writer(&_zv_double_time, row, column, &obj->write_ptr, format, zval_get_format(format_handle));
+        datetime_writer(&datetime, row, column, format, &obj->write_ptr, zval_get_format(format_handle));
     } else {
-        type_writer(&_zv_double_time, row, column, &obj->write_ptr, format, obj->format_ptr.format);
+        datetime_writer(&datetime, row, column, format, &obj->write_ptr, obj->format_ptr.format);
     }
 
     // Release default format
     if (ZEND_NUM_ARGS() == 3) {
         zend_string_release(format);
     }
-
-    zval_ptr_dtor(&_zv_double_time);
 }
 /* }}} */
 
@@ -1023,6 +1034,28 @@ PHP_METHOD(vtiful_xls, setType)
 }
 /* }}} */
 
+/** {{{ \Vtiful\Kernel\Excel::setSkipRows(int $skip)
+ */
+PHP_METHOD(vtiful_xls, setSkipRows)
+{
+    zend_long zl_skip = 0;
+
+    ZEND_PARSE_PARAMETERS_START(1, 1)
+            Z_PARAM_LONG(zl_skip)
+    ZEND_PARSE_PARAMETERS_END();
+
+    ZVAL_COPY(return_value, getThis());
+
+    xls_object *obj = Z_XLS_P(getThis());
+
+    if (!obj->read_ptr.sheet_t) {
+        RETURN_FALSE;
+    }
+
+    skip_rows(obj->read_ptr.sheet_t, NULL, zl_skip);
+}
+/* }}} */
+
 /** {{{ \Vtiful\Kernel\Excel::putCSV()
  */
 PHP_METHOD(vtiful_xls, putCSV)
@@ -1214,6 +1247,7 @@ zend_function_entry xls_methods[] = {
         PHP_ME(vtiful_xls, putCSVCallback,   xls_put_csv_callback_arginfo,   ZEND_ACC_PUBLIC)
         PHP_ME(vtiful_xls, sheetList,        xls_sheet_list_arginfo,         ZEND_ACC_PUBLIC)
         PHP_ME(vtiful_xls, setType,          xls_set_type_arginfo,           ZEND_ACC_PUBLIC)
+        PHP_ME(vtiful_xls, setSkipRows,      xls_set_skip_arginfo,           ZEND_ACC_PUBLIC)
         PHP_ME(vtiful_xls, getSheetData,     xls_get_sheet_data_arginfo,     ZEND_ACC_PUBLIC)
         PHP_ME(vtiful_xls, nextRow,          xls_next_row_arginfo,           ZEND_ACC_PUBLIC)
         PHP_ME(vtiful_xls, nextCellCallback, xls_next_cell_callback_arginfo, ZEND_ACC_PUBLIC)

+ 75 - 17
kernel/read.c

@@ -11,6 +11,7 @@
 */
 
 #include "xlswriter.h"
+#include "ext/date/php_date.h"
 
 /* {{{ */
 xlsxioreader file_open(const char *directory, const char *file_name) {
@@ -88,11 +89,15 @@ void data_to_null(zval *zv_result_t)
 /* {{{ */
 void data_to_custom_type(const char *string_value, const size_t string_value_length, const zend_ulong type, zval *zv_result_t, const zend_ulong zv_hashtable_index)
 {
-    if (type & READ_TYPE_DATETIME) {
-        if (!is_number(string_value)) {
-            goto STRING;
-        }
+    if (type == 0) {
+        goto STRING;
+    }
 
+    if (!is_number(string_value)) {
+        goto STRING;
+    }
+
+    if (type & READ_TYPE_DATETIME) {
         if (string_value_length == 0) {
             data_to_null(zv_result_t);
 
@@ -100,25 +105,61 @@ void data_to_custom_type(const char *string_value, const size_t string_value_len
         }
 
         double value = strtod(string_value, NULL);
-
-        if (value != 0) {
-            value = (value - 25569) * 86400;
+        double days, partDay, hours, minutes, seconds;
+
+        days    = floor(value);
+        partDay = value - days;
+        hours   = floor(partDay * 24);
+        partDay = partDay * 24 - hours;
+        minutes = floor(partDay * 60);
+        partDay = partDay * 60 - minutes;
+        seconds = round(partDay * 60);
+
+        zval datetime;
+        php_date_instantiate(php_date_get_date_ce(), &datetime);
+        php_date_initialize(Z_PHPDATE_P(&datetime), ZEND_STRL("1899-12-30"), NULL, NULL, 1);
+
+        zval _modify_args[1], _modify_result;
+        smart_str _modify_arg_string = {0};
+        if (days >= 0) {
+            smart_str_appendl(&_modify_arg_string, "+", 1);
         }
+        smart_str_append_long(&_modify_arg_string, days);
+        smart_str_appendl(&_modify_arg_string, " days", 5);
+        ZVAL_STR(&_modify_args[0], _modify_arg_string.s);
+        call_object_method(&datetime, "modify", 1, _modify_args, &_modify_result);
+        zval_ptr_dtor(&datetime);
+
+        zval _set_time_args[3], _set_time_result;
+        ZVAL_LONG(&_set_time_args[0], (zend_long)hours);
+        ZVAL_LONG(&_set_time_args[1], (zend_long)minutes);
+        ZVAL_LONG(&_set_time_args[2], (zend_long)seconds);
+        call_object_method(&_modify_result, "setTime", 3, _set_time_args, &_set_time_result);
+        zval_ptr_dtor(&_modify_result);
+
+        zval _format_args[1], _format_result;
+        ZVAL_STRING(&_format_args[0], "U");
+        call_object_method(&_set_time_result, "format", 1, _format_args, &_format_result);
+        zval_ptr_dtor(&_set_time_result);
+
+        zend_long timestamp = ZEND_STRTOL(Z_STRVAL(_format_result), NULL ,10);
+        zval_ptr_dtor(&_format_result);
+
+        // GMT
+        // if (value != 0) {
+        //     timestamp = (value - 25569) * 86400;
+        // }
 
         if (Z_TYPE_P(zv_result_t) == IS_ARRAY) {
-            add_index_long(zv_result_t, zv_hashtable_index, (zend_long)(value + 0.5));
+            add_index_long(zv_result_t, zv_hashtable_index, timestamp);
         } else {
-            ZVAL_LONG(zv_result_t, (zend_long)(value + 0.5));
+            ZVAL_LONG(zv_result_t, timestamp);
         }
 
         return;
     }
 
     if (type & READ_TYPE_DOUBLE) {
-        if (!is_number(string_value)) {
-            goto STRING;
-        }
-
         if (string_value_length == 0) {
             data_to_null(zv_result_t);
 
@@ -135,10 +176,6 @@ void data_to_custom_type(const char *string_value, const size_t string_value_len
     }
 
     if (type & READ_TYPE_INT) {
-        if (!is_number(string_value)) {
-            goto STRING;
-        }
-
         if (string_value_length == 0) {
             data_to_null(zv_result_t);
 
@@ -383,3 +420,24 @@ void load_sheet_all_data (xlsxioreadersheet sheet_t, zval *zv_type_t, zval *zv_r
     }
 }
 /* }}} */
+
+void skip_rows(xlsxioreadersheet sheet_t, zval *zv_type_t, zend_long zl_skip_row)
+{
+    while (sheet_read_row(sheet_t))
+    {
+        zval _zv_tmp_row;
+        ZVAL_NULL(&_zv_tmp_row);
+
+        if (xlsxioread_sheet_last_row_index(sheet_t) < zl_skip_row) {
+            sheet_read_row(sheet_t);
+        }
+
+        load_sheet_current_row_data(sheet_t, &_zv_tmp_row, zv_type_t, READ_SKIP_ROW);
+
+        zval_ptr_dtor(&_zv_tmp_row);
+
+        if (xlsxioread_sheet_last_row_index(sheet_t) >= zl_skip_row) {
+            break;
+        }
+    }
+}

+ 19 - 0
kernel/write.c

@@ -184,11 +184,30 @@ void formula_writer(zend_string *value, zend_long row, zend_long columns, xls_re
     worksheet_write_formula(res->worksheet, (lxw_row_t)row, (lxw_col_t)columns, ZSTR_VAL(value), format);
 }
 
+/*
+ * Write the chart to the file
+ */
 void chart_writer(zend_long row, zend_long columns, xls_resource_chart_t *chart_resource, xls_resource_write_t *res)
 {
     worksheet_insert_chart(res->worksheet, (lxw_row_t)row, (lxw_col_t)columns, chart_resource->chart);
 }
 
+/*
+ * Write the datetime to the file
+ */
+void datetime_writer(lxw_datetime *datetime, zend_long row, zend_long columns, zend_string *format, xls_resource_write_t *res, lxw_format *format_handle)
+{
+    lxw_format *value_format = NULL;
+
+    if (format_handle != NULL) {
+        format_copy(value_format, format_handle);
+    }
+
+    value_format = workbook_add_format(res->workbook);
+    format_set_num_format(value_format, ZSTR_VAL(format));
+    worksheet_write_datetime(res->worksheet, (lxw_row_t)row, (lxw_col_t)columns, datetime, value_format);
+}
+
 /*
  * Add the autofilter.
  */

+ 4 - 0
package.xml

@@ -211,12 +211,14 @@
    <file md5sum="f1addf56af65d4cccc0e1a2e92bd1f46" name="tests/open_xlsx_file.phpt" role="test" />
    <file md5sum="88290bb78df0b94df8901d81d930337e" name="tests/open_xlsx_get_data.phpt" role="test" />
    <file md5sum="30c1a9fbb09eed1908a84c5b617d292f" name="tests/open_xlsx_get_data_skip_empty.phpt" role="test" />
+   <file name="tests/open_xlsx_get_data_skip_rows.phpt" role="test" />
    <file md5sum="6ea988e0cc58f2bba7f2a91e133f00cc" name="tests/open_xlsx_get_data_with_set_type.phpt" role="test" />
    <file md5sum="4d68e2f3dafa35feef8f4e54786fb6d7" name="tests/open_xlsx_get_sheet_not_found_data.phpt" role="test" />
    <file md5sum="f8a2c32aad6f4828fcac7b4c6ccf7a74" name="tests/open_xlsx_next_cell_callback.phpt" role="test" />
    <file md5sum="38a275929a200a29ca37e4c15cd083ce" name="tests/open_xlsx_next_cell_callback_with_data_type.phpt" role="test" />
    <file md5sum="154e924e44ba43aec9abc6d6b6d389b1" name="tests/open_xlsx_next_row.phpt" role="test" />
    <file md5sum="bb159ef1e1f3312a90cec655c12227b2" name="tests/open_xlsx_next_row_skip_empty.phpt" role="test" />
+   <file name="tests/open_xlsx_next_row_skip_rows.phpt" role="test" />
    <file md5sum="95fca9449059d583fc0b54cf7c571c8f" name="tests/open_xlsx_next_row_with_data_type_date.phpt" role="test" />
    <file md5sum="d39c67c041982264d563450737396e00" name="tests/open_xlsx_next_row_with_data_type_date_array_index.phpt" role="test" />
    <file md5sum="68628ad9158f5860917860eef232cbf3" name="tests/open_xlsx_next_row_with_data_type_string.phpt" role="test" />
@@ -231,6 +233,8 @@
    <file name="tests/xlsx_to_csv_callback.phpt" role="test" />
    <file name="tests/xlsx_to_csv_callback_custom_delimiter.phpt" role="test" />
    <file name="tests/xlsx_to_csv_custom_delimiter.phpt" role="test" />
+   <file name="tests/xlsx_to_csv_skip_rows.phpt" role="test" />
+   <file name="tests/xlsx_to_csv_skip_rows_callback.phpt" role="test" />
    <file md5sum="a9af7f4ca385ba41b008b50ac67f8e96" name="tests/zoom.phpt" role="test" />
    <file md5sum="f4a2d1a28ad1bf782502d698de0b1907" name="tests/include/skipif.inc" role="test" />
    <file md5sum="bb4256831dfd81f951bd6f4afbe1719f" name="CREDITS" role="doc" />

+ 45 - 0
tests/open_xlsx_get_data_skip_rows.phpt

@@ -0,0 +1,45 @@
+--TEST--
+Check for vtiful presence
+--SKIPIF--
+<?php
+require __DIR__ . '/include/skipif.inc';
+skip_disable_reader();
+?>
+--FILE--
+<?php
+$config   = ['path' => './tests'];
+$excel    = new \Vtiful\Kernel\Excel($config);
+$filePath = $excel->fileName('tutorial.xlsx', 'TestSheet1')
+    ->header(['Item', 'Cost'])
+    ->data([
+        ['Item_1', 'Cost_1', 10, 10.9999995],
+        ['Item_2', 'Cost_2', 10, 10.9999995],
+        ['Item_3', 'Cost_3', 10, 10.9999995],
+    ])
+    ->output();
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet()
+    ->setSkipRows(3)
+    ->getSheetData();
+
+var_dump($data);
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  array(4) {
+    [0]=>
+    string(6) "Item_3"
+    [1]=>
+    string(6) "Cost_3"
+    [2]=>
+    int(10)
+    [3]=>
+    float(10.9999995)
+  }
+}

+ 43 - 0
tests/open_xlsx_next_row_skip_rows.phpt

@@ -0,0 +1,43 @@
+--TEST--
+Check for vtiful presence
+--SKIPIF--
+<?php
+require __DIR__ . '/include/skipif.inc';
+skip_disable_reader();
+?>
+--FILE--
+<?php
+$config   = ['path' => './tests'];
+$excel    = new \Vtiful\Kernel\Excel($config);
+$filePath = $excel->fileName('tutorial.xlsx', 'TestSheet1')
+    ->header(['Item', 'Cost'])
+    ->data([
+        ['Item_1', 'Cost_1', 10, 10.9999995],
+        ['Item_2', 'Cost_2', 10, 10.9999995],
+        ['Item_3', 'Cost_3', 10, 10.9999995],
+    ])
+    ->output();
+
+$excel->openFile('tutorial.xlsx')
+    ->openSheet()
+    ->setSkipRows(3);
+
+while (is_array($data = $excel->nextRow())) {
+    var_dump($data);
+}
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+?>
+--EXPECT--
+array(4) {
+  [0]=>
+  string(6) "Item_3"
+  [1]=>
+  string(6) "Cost_3"
+  [2]=>
+  int(10)
+  [3]=>
+  float(10.9999995)
+}

+ 59 - 0
tests/xlsx_to_csv_skip_rows.phpt

@@ -0,0 +1,59 @@
+--TEST--
+Check for vtiful presence
+--SKIPIF--
+<?php
+require __DIR__ . '/include/skipif.inc';
+skip_disable_reader();
+?>
+--FILE--
+<?php
+$config   = ['path' => './tests'];
+$excel    = new \Vtiful\Kernel\Excel($config);
+$filePath = $excel->fileName('tutorial.xlsx', 'TestSheet1')
+    ->header(['Item', 'Cost'])
+    ->data([
+        ['Item_1', 'Cost_1', 10, 10.9999995],
+        ['Item_2', 'Cost_2', 10, 10.9999995],
+        ['Item_3', 'Cost_3', 10, 10.9999995],
+    ])
+    ->output();
+
+$fp = fopen('./tests/file.csv', 'w');
+
+$csvResult = $excel->openFile('tutorial.xlsx')
+    ->openSheet()
+    ->setSkipRows(1)
+    ->putCSV($fp);
+
+fclose($fp);
+
+var_dump($csvResult);
+var_dump(file_get_contents('./tests/file.csv'));
+
+$fp = fopen('./tests/file.csv', 'w');
+
+$csvResult = $excel->openFile('tutorial.xlsx')
+    ->openSheet()
+    ->setSkipRows(2)
+    ->putCSV($fp);
+
+fclose($fp);
+
+var_dump($csvResult);
+var_dump(file_get_contents('./tests/file.csv'));
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+@unlink(__DIR__ . '/file.csv');
+?>
+--EXPECT--
+bool(true)
+string(84) "Item_1,Cost_1,10,10.9999995
+Item_2,Cost_2,10,10.9999995
+Item_3,Cost_3,10,10.9999995
+"
+bool(true)
+string(56) "Item_2,Cost_2,10,10.9999995
+Item_3,Cost_3,10,10.9999995
+"

+ 43 - 0
tests/xlsx_to_csv_skip_rows_callback.phpt

@@ -0,0 +1,43 @@
+--TEST--
+Check for vtiful presence
+--SKIPIF--
+<?php
+require __DIR__ . '/include/skipif.inc';
+skip_disable_reader();
+?>
+--FILE--
+<?php
+$config   = ['path' => './tests'];
+$excel    = new \Vtiful\Kernel\Excel($config);
+$filePath = $excel->fileName('tutorial.xlsx', 'TestSheet1')
+    ->header(['Item', 'Cost'])
+    ->data([
+        ['Item_1', 'Cost_1', 10, 10.9999995],
+        ['Item_2', 'Cost_2', 10, 10.9999995],
+        ['Item_3', 'Cost_3', 10, 10.9999995],
+    ])
+    ->output();
+
+$fp = fopen('./tests/file.csv', 'w');
+
+$csvResult = $excel->openFile('tutorial.xlsx')
+    ->openSheet()
+    ->setSkipRows(3)
+    ->putCSVCallback(function($row){
+        return $row;
+    }, $fp);
+
+fclose($fp);
+
+var_dump($csvResult);
+var_dump(file_get_contents('./tests/file.csv'));
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+@unlink(__DIR__ . '/file.csv');
+?>
+--EXPECT--
+bool(true)
+string(28) "Item_3,Cost_3,10,10.9999995
+"