浏览代码

Merge pull request #167 from viest/dev

Feat: nextCellCallback read
viest 5 年之前
父节点
当前提交
fe5d9a6ce5

+ 20 - 9
.travis.yml

@@ -7,13 +7,21 @@ compiler:
 os:
   - linux
 
-php:
-  - 7.0
-  - 7.1
-  - 7.2
-  - 7.3
-  - 7.4snapshot
-  - nightly
+matrix:
+  include:
+    - php: 7.0
+      env: USE_VALGRIND=1
+    - php: 7.1
+      env: USE_VALGRIND=1
+    - php: 7.2
+      env: USE_VALGRIND=1
+    - php: 7.3
+    - php: 7.4snapshot
+    - php: nightly
+
+addons:
+  apt:
+    packages: valgrind
 
 notifications:
   email: [email protected]
@@ -21,12 +29,15 @@ notifications:
 before_script:
   - git submodule update --init
   - sudo apt-get install zlib1g-dev -y
-  - phpize && ./configure && make clean && make
+  - phpize && ./configure --enable-reader && make clean && make
 
 branches:
   only:
     - master
     - dev
-    - dev-read
+
 script:
     - ./travis/run-test.sh
+    - make clean && phpize --clean
+    - phpize && ./configure --enable-reader && make clean && make
+    - if [ -n "$USE_VALGRIND" ]; then REPORT_EXIT_STATUS=1 php -n run-tests.php -m -n -d extension_dir=./modules/ -d extension=xlswriter.so -P --show-diff --set-timeout 120; fi

+ 19 - 1
ide-helper/helper.php

@@ -16,6 +16,10 @@ class Excel
     const TYPE_DOUBLE = 0x04;
     const TYPE_TIMESTAMP = 0x08;
 
+    const SKIP_NONE = 0x00;
+    const SKIP_EMPTY_ROW = 0x01;
+    const SKIP_EMPTY_CELLS = 0x02;
+
     /**
      * Excel constructor.
      *
@@ -328,12 +332,13 @@ class Excel
      * default open first sheet
      *
      * @param string|NULL $sheetName
+     * @param int         skipFlag
      *
      * @return Excel
      *
      * @author viest
      */
-    public function openSheet(string $sheetName = NULL): self
+    public function openSheet(string $sheetName = NULL, int $skipFlag = 0x00): self
     {
         return $this;
     }
@@ -361,6 +366,19 @@ class Excel
     {
         return [];
     }
+
+    /**
+     * Next Cell In Callback
+     *
+     * @param callable    $callback  function(int $row, int $cell, string $data)
+     * @param string|NULL $sheetName sheet name
+     *
+     * @author viest
+     */
+    public function nextCellCallback(callable $callback, string $sheetName = NULL): void
+    {
+        //
+    }
 }
 
 /**

+ 4 - 0
include/excel.h

@@ -23,6 +23,10 @@
 #define V_XLS_CONST_READ_TYPE_STRING   "TYPE_STRING"
 #define V_XLS_CONST_READ_TYPE_DATETIME "TYPE_TIMESTAMP"
 
+#define V_XLS_CONST_READ_SKIP_NONE        "SKIP_NONE"
+#define V_XLS_CONST_READ_SKIP_EMPTY_ROW   "SKIP_EMPTY_ROW"
+#define V_XLS_CONST_READ_SKIP_EMPTY_CELLS "SKIP_EMPTY_CELLS"
+
 #define READ_TYPE_EMPTY    0x00
 #define READ_TYPE_STRING   0x01
 #define READ_TYPE_INT      0x02

+ 3 - 2
include/read.h

@@ -16,10 +16,11 @@
 #define READ_SKIP_ROW 0
 #define READ_ROW 0x01
 
-int sheet_read_row(xlsxioreadersheet sheet);
+int sheet_read_row(xlsxioreadersheet sheet_t);
 xlsxioreader file_open(const char *directory, const char *file_name);
 void load_sheet_all_data(xlsxioreadersheet sheet_t, zval *zv_result_t);
-xlsxioreadersheet sheet_open(xlsxioreader file, const zend_string *zs_sheet_name_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);
+unsigned int load_sheet_current_row_data_callback(zend_string *zs_sheet_name_t, xlsxioreader file_t, void *callback_data);
 
 #endif //PHP_READ_INCLUDE_H

+ 14 - 0
include/xlswriter.h

@@ -42,6 +42,11 @@ typedef struct {
     xlsxioreader      file_t;
     xlsxioreadersheet sheet_t;
 } xls_resource_read_t;
+
+typedef struct {
+    zend_fcall_info       *fci;
+    zend_fcall_info_cache *fci_cache;
+} xls_read_callback_data;
 #endif
 
 #ifndef ENABLE_READER
@@ -117,6 +122,15 @@ static inline chart_object *php_vtiful_chart_fetch_object(zend_object *obj) {
         }                                                                                                            \
     } while(0);
 
+#define FCALL_TWO_ARGS(bucket)                   \
+    ZVAL_COPY_VALUE(&args[0], &bucket->val); \
+        if (bucket->key) {                       \
+            ZVAL_STR(&args[1], bucket->key);     \
+        } else {                                 \
+            ZVAL_LONG(&args[1], bucket->h);      \
+        }                                        \
+        zend_call_function(&fci, &fci_cache);
+
 #define ROW(range) \
     lxw_name_to_row(range)
 

+ 49 - 6
kernel/excel.c

@@ -162,6 +162,11 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(xls_open_sheet_arginfo, 0, 0, 1)
                 ZEND_ARG_INFO(0, zs_sheet_name)
 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)
+ZEND_END_ARG_INFO()
 /* }}} */
 
 /** {{{ \Vtiful\Kernel\xls::__construct(array $config)
@@ -728,11 +733,13 @@ PHP_METHOD(vtiful_xls, openFile)
  */
 PHP_METHOD(vtiful_xls, openSheet)
 {
+    zend_long zl_flag = XLSXIOREAD_SKIP_NONE;
     zend_string *zs_sheet_name = NULL;
 
-    ZEND_PARSE_PARAMETERS_START(0, 1)
+    ZEND_PARSE_PARAMETERS_START(0, 2)
             Z_PARAM_OPTIONAL
             Z_PARAM_STR(zs_sheet_name)
+            Z_PARAM_LONG(zl_flag)
     ZEND_PARSE_PARAMETERS_END();
 
     ZVAL_COPY(return_value, getThis());
@@ -743,7 +750,7 @@ PHP_METHOD(vtiful_xls, openSheet)
         RETURN_NULL();
     }
 
-    obj->read_ptr.sheet_t = sheet_open(obj->read_ptr.file_t, zs_sheet_name);
+    obj->read_ptr.sheet_t = sheet_open(obj->read_ptr.file_t, zs_sheet_name, zl_flag);
 }
 /* }}} */
 
@@ -782,6 +789,35 @@ PHP_METHOD(vtiful_xls, nextRow)
 }
 /* }}} */
 
+/** {{{ \Vtiful\Kernel\xls::nextCellCallback()
+ */
+PHP_METHOD(vtiful_xls, nextCellCallback)
+{
+    zend_string *zs_sheet_name = NULL;
+    zend_fcall_info       fci       = empty_fcall_info;
+    zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
+
+    ZEND_PARSE_PARAMETERS_START(1, 2)
+            Z_PARAM_FUNC(fci, fci_cache)
+            Z_PARAM_OPTIONAL
+            Z_PARAM_STR(zs_sheet_name)
+    ZEND_PARSE_PARAMETERS_END();
+
+    xls_object *obj = Z_XLS_P(getThis());
+
+    if (!obj->read_ptr.file_t) {
+        RETURN_FALSE;
+    }
+
+    xls_read_callback_data callback_data;
+
+    callback_data.fci = &fci;
+    callback_data.fci_cache = &fci_cache;
+
+    load_sheet_current_row_data_callback(zs_sheet_name, obj->read_ptr.file_t, &callback_data);
+}
+/* }}} */
+
 #endif
 
 /** {{{ xls_methods
@@ -808,10 +844,11 @@ zend_function_entry xls_methods[] = {
         PHP_ME(vtiful_xls, setRow,        xls_set_row_arginfo,        ZEND_ACC_PUBLIC)
 
 #ifdef ENABLE_READER
-        PHP_ME(vtiful_xls, openFile,      xls_open_file_arginfo,      ZEND_ACC_PUBLIC)
-        PHP_ME(vtiful_xls, openSheet,     xls_open_sheet_arginfo,     ZEND_ACC_PUBLIC)
-        PHP_ME(vtiful_xls, getSheetData,  NULL,                       ZEND_ACC_PUBLIC)
-        PHP_ME(vtiful_xls, nextRow,       NULL,                       ZEND_ACC_PUBLIC)
+        PHP_ME(vtiful_xls, openFile,         xls_open_file_arginfo,          ZEND_ACC_PUBLIC)
+        PHP_ME(vtiful_xls, openSheet,        xls_open_sheet_arginfo,         ZEND_ACC_PUBLIC)
+        PHP_ME(vtiful_xls, getSheetData,     NULL,                           ZEND_ACC_PUBLIC)
+        PHP_ME(vtiful_xls, nextRow,          NULL,                           ZEND_ACC_PUBLIC)
+        PHP_ME(vtiful_xls, nextCellCallback, xls_next_cell_callback_arginfo, ZEND_ACC_PUBLIC)
 #endif
 
         PHP_FE_END
@@ -834,6 +871,12 @@ VTIFUL_STARTUP_FUNCTION(excel) {
     REGISTER_CLASS_PROPERTY_NULL(vtiful_xls_ce, V_XLS_COF, ZEND_ACC_PRIVATE);
     REGISTER_CLASS_PROPERTY_NULL(vtiful_xls_ce, V_XLS_FIL, ZEND_ACC_PRIVATE);
 
+#ifdef ENABLE_READER
+    REGISTER_CLASS_CONST_LONG(vtiful_xls_ce, V_XLS_CONST_READ_SKIP_NONE,        XLSXIOREAD_SKIP_NONE);
+    REGISTER_CLASS_CONST_LONG(vtiful_xls_ce, V_XLS_CONST_READ_SKIP_EMPTY_ROW,   XLSXIOREAD_SKIP_EMPTY_ROWS);
+    REGISTER_CLASS_CONST_LONG(vtiful_xls_ce, V_XLS_CONST_READ_SKIP_EMPTY_CELLS, XLSXIOREAD_SKIP_EMPTY_CELLS);
+#endif
+
     REGISTER_CLASS_CONST_LONG(vtiful_xls_ce, V_XLS_CONST_READ_TYPE_INT,      READ_TYPE_INT);
     REGISTER_CLASS_CONST_LONG(vtiful_xls_ce, V_XLS_CONST_READ_TYPE_DOUBLE,   READ_TYPE_DOUBLE);
     REGISTER_CLASS_CONST_LONG(vtiful_xls_ce, V_XLS_CONST_READ_TYPE_STRING,   READ_TYPE_STRING);

+ 76 - 5
kernel/read.c

@@ -31,20 +31,20 @@ xlsxioreader file_open(const char *directory, const char *file_name) {
 /* }}} */
 
 /* {{{ */
-xlsxioreadersheet sheet_open(xlsxioreader file, const zend_string *zs_sheet_name_t)
+xlsxioreadersheet sheet_open(xlsxioreader file_t, const zend_string *zs_sheet_name_t, const zend_long zl_flag)
 {
     if (zs_sheet_name_t == NULL) {
-        return xlsxioread_sheet_open(file, NULL, XLSXIOREAD_SKIP_EMPTY_ROWS);
+        return xlsxioread_sheet_open(file_t, NULL, zl_flag);
     }
 
-    return xlsxioread_sheet_open(file, ZSTR_VAL(zs_sheet_name_t), XLSXIOREAD_SKIP_EMPTY_ROWS);
+    return xlsxioread_sheet_open(file_t, ZSTR_VAL(zs_sheet_name_t), zl_flag);
 }
 /* }}} */
 
 /* {{{ */
-int sheet_read_row(xlsxioreadersheet sheet)
+int sheet_read_row(xlsxioreadersheet sheet_t)
 {
-    return xlsxioread_sheet_next_row(sheet);
+    return xlsxioread_sheet_next_row(sheet_t);
 }
 /* }}} */
 
@@ -111,6 +111,77 @@ unsigned int load_sheet_current_row_data(xlsxioreadersheet sheet_t, zval *zv_res
 }
 /* }}} */
 
+/* {{{ */
+int sheet_row_callback (size_t row, size_t max_col, void* callback_data)
+{
+    if (callback_data == NULL) {
+        return FAILURE;
+    }
+
+    xls_read_callback_data *_callback_data = (xls_read_callback_data *)callback_data;
+
+    zval args[3], retval;
+
+    _callback_data->fci->retval      = &retval;
+    _callback_data->fci->params      = args;
+    _callback_data->fci->param_count = 3;
+
+    ZVAL_LONG(&args[0], row);
+    ZVAL_LONG(&args[1], max_col);
+    ZVAL_STRING(&args[2], "XLSX_ROW_END");
+
+    zend_call_function(_callback_data->fci, _callback_data->fci_cache);
+
+    zval_ptr_dtor(&args[2]);
+    zval_ptr_dtor(&retval);
+
+    return SUCCESS;
+}
+/* }}} */
+
+/* {{{ */
+int sheet_cell_callback (size_t row, size_t col, const char *value, void *callback_data)
+{
+    if (callback_data == NULL) {
+        return FAILURE;
+    }
+
+    xls_read_callback_data *_callback_data = (xls_read_callback_data *)callback_data;
+
+    if (_callback_data->fci == NULL || _callback_data->fci_cache == NULL) {
+        return FAILURE;
+    }
+
+    zval args[3], retval;
+
+    _callback_data->fci->retval      = &retval;
+    _callback_data->fci->params      = args;
+    _callback_data->fci->param_count = 3;
+
+    ZVAL_LONG(&args[0], row);
+    ZVAL_LONG(&args[1], col);
+    ZVAL_STRING(&args[2], value);
+
+    zend_call_function(_callback_data->fci, _callback_data->fci_cache);
+
+    zval_ptr_dtor(&args[2]);
+    zval_ptr_dtor(&retval);
+
+    return SUCCESS;
+}
+/* }}} */
+
+/* {{{ */
+unsigned int load_sheet_current_row_data_callback(zend_string *zs_sheet_name_t, xlsxioreader file_t, void *callback_data)
+{
+    if (zs_sheet_name_t == NULL) {
+        return xlsxioread_process(file_t, NULL, XLSXIOREAD_SKIP_NONE, sheet_cell_callback, sheet_row_callback, callback_data);
+    }
+
+    return xlsxioread_process(file_t, ZSTR_VAL(zs_sheet_name_t), XLSXIOREAD_SKIP_NONE, sheet_cell_callback, sheet_row_callback, callback_data);
+}
+/* }}} */
+
 /* {{{ */
 void load_sheet_all_data(xlsxioreadersheet sheet_t, zval *zv_result_t)
 {

+ 89 - 0
tests/open_xlsx_get_data_skip_empty.phpt

@@ -0,0 +1,89 @@
+--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')
+    ->header(['', 'Cost'])
+    ->data([
+        [],
+        ['viest', '']
+    ])
+    ->output();
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_CELLS)
+    ->getSheetData();
+
+var_dump($data);
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_ROW)
+    ->getSheetData();
+
+var_dump($data);
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_CELLS|\Vtiful\Kernel\Excel::SKIP_EMPTY_ROW)
+    ->getSheetData();
+
+var_dump($data);
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+?>
+--EXPECT--
+array(3) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(4) "Cost"
+  }
+  [1]=>
+  array(2) {
+    [0]=>
+    string(0) ""
+    [1]=>
+    string(0) ""
+  }
+  [2]=>
+  array(1) {
+    [0]=>
+    string(5) "viest"
+  }
+}
+array(2) {
+  [0]=>
+  array(2) {
+    [0]=>
+    string(0) ""
+    [1]=>
+    string(4) "Cost"
+  }
+  [1]=>
+  array(2) {
+    [0]=>
+    string(5) "viest"
+    [1]=>
+    string(0) ""
+  }
+}
+array(2) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(4) "Cost"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(5) "viest"
+  }
+}

+ 33 - 0
tests/open_xlsx_next_cell_callback.phpt

@@ -0,0 +1,33 @@
+--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')
+    ->header(['Item', 'Cost'])
+    ->data([
+        ['Item_1', 'Cost_1'],
+    ])
+    ->output();
+
+$excel->openFile('tutorial.xlsx')->nextCellCallback(function ($row, $cell, $data) {
+    echo 'cell:' . $cell . ', row:' . $row . ', value:' . $data . PHP_EOL;
+});
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+?>
+--EXPECT--
+cell:1, row:1, value:Item
+cell:2, row:1, value:Cost
+cell:2, row:1, value:XLSX_ROW_END
+cell:1, row:2, value:Item_1
+cell:2, row:2, value:Cost_1
+cell:2, row:2, value:XLSX_ROW_END

+ 88 - 0
tests/open_xlsx_next_row_skip_empty.phpt

@@ -0,0 +1,88 @@
+--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')
+    ->header(['', 'Cost'])
+    ->data([
+        [],
+        ['viest', ''],
+    ])
+    ->output();
+
+echo 'skip cells' . PHP_EOL;
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_CELLS);
+
+while ($data = $excel->nextRow()) {
+    var_dump($data);
+}
+
+echo 'skip row' . PHP_EOL;
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_ROW);
+
+while ($data = $excel->nextRow()) {
+    var_dump($data);
+}
+
+echo 'skip cells & row' . PHP_EOL;
+
+$data = $excel->openFile('tutorial.xlsx')
+    ->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_CELLS | \Vtiful\Kernel\Excel::SKIP_EMPTY_ROW);
+
+while ($data = $excel->nextRow()) {
+    var_dump($data);
+}
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+?>
+--EXPECT--
+skip cells
+array(1) {
+  [0]=>
+  string(4) "Cost"
+}
+array(2) {
+  [0]=>
+  string(0) ""
+  [1]=>
+  string(0) ""
+}
+array(1) {
+  [0]=>
+  string(5) "viest"
+}
+skip row
+array(2) {
+  [0]=>
+  string(0) ""
+  [1]=>
+  string(4) "Cost"
+}
+array(2) {
+  [0]=>
+  string(5) "viest"
+  [1]=>
+  string(0) ""
+}
+skip cells & row
+array(1) {
+  [0]=>
+  string(4) "Cost"
+}
+array(1) {
+  [0]=>
+  string(5) "viest"
+}

+ 33 - 0
tests/open_xlsx_sheet_flag.phpt

@@ -0,0 +1,33 @@
+--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')
+    ->header(['Item', 'Cost'])
+    ->output();
+
+$data = $excel->openFile('tutorial.xlsx')->openSheet('Sheet1', \Vtiful\Kernel\Excel::SKIP_EMPTY_CELLS);
+
+var_dump($data);
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/tutorial.xlsx');
+?>
+--EXPECT--
+object(Vtiful\Kernel\Excel)#1 (2) {
+  ["config":"Vtiful\Kernel\Excel":private]=>
+  array(1) {
+    ["path"]=>
+    string(7) "./tests"
+  }
+  ["fileName":"Vtiful\Kernel\Excel":private]=>
+  string(21) "./tests/tutorial.xlsx"
+}