LCOV - code coverage report
Current view: top level - src - php_wikidiff2.cpp (source / functions) Hit Total Coverage
Test: mediawiki/php/wikidiff2 test coverage report Lines: 63 164 38.4 %
Date: 2023-07-04 10:20:16 Functions: 10 14 71.4 %

          Line data    Source code
       1             : /* $Id$ */
       2             : 
       3             : #ifdef HAVE_CONFIG_H
       4             : #include "config.h"
       5             : #endif
       6             : 
       7             : #include "php.h"
       8             : #include "php_ini.h"
       9             : #include "ext/standard/info.h"
      10             : #include "zend_API.h"
      11             : #include "php_wikidiff2.h"
      12             : #include "lib/Wikidiff2.h"
      13             : #include "lib/TableFormatter.h"
      14             : #include "lib/InlineFormatter.h"
      15             : #include "lib/InlineJSONFormatter.h"
      16             : 
      17             : #include <memory>
      18             : #include <list>
      19             : 
      20             : #define WIKIDIFF2_VERSION_STRING "1.14.1"
      21             : 
      22             : #if PHP_VERSION_ID >= 80000
      23             : #   include "wikidiff2_arginfo.h"
      24             : #else
      25             : #   define arginfo_wikidiff2_do_diff NULL
      26             : #   define arginfo_wikidiff2_inline_diff NULL
      27             : #   define arginfo_wikidiff2_inline_json_diff NULL
      28             : #   define arginfo_wikidiff2_version NULL
      29             : #   define arginfo_wikidiff2_multi_format_diff NULL
      30             : #endif
      31             : 
      32             : #define COMPAT_RETURN_STRINGL(s, l) { RETURN_STRINGL(s, l); return; }
      33             : 
      34             : #if PHP_VERSION_ID < 70000
      35             : #    error "PHP version 7 or later is required."
      36             : #endif
      37             : 
      38             : using wikidiff2::Wikidiff2;
      39             : using wikidiff2::Formatter;
      40             : using wikidiff2::TableFormatter;
      41             : using wikidiff2::InlineFormatter;
      42             : using wikidiff2::InlineJSONFormatter;
      43             : 
      44             : static int le_wikidiff2;
      45             : 
      46             : zend_function_entry wikidiff2_functions[] = {
      47             :     PHP_FE(wikidiff2_do_diff, arginfo_wikidiff2_do_diff)
      48             :     PHP_FE(wikidiff2_inline_diff, arginfo_wikidiff2_inline_diff)
      49             :     PHP_FE(wikidiff2_inline_json_diff, arginfo_wikidiff2_inline_json_diff)
      50             :     PHP_FE(wikidiff2_version, arginfo_wikidiff2_version)
      51             :     PHP_FE(wikidiff2_multi_format_diff, arginfo_wikidiff2_multi_format_diff)
      52             :     {NULL, NULL, NULL}
      53             : };
      54             : 
      55             : zend_module_entry wikidiff2_module_entry = {
      56             :     STANDARD_MODULE_HEADER,
      57             :     "wikidiff2",
      58             :     wikidiff2_functions,
      59             :     PHP_MINIT(wikidiff2),
      60             :     PHP_MSHUTDOWN(wikidiff2),
      61             :     PHP_RINIT(wikidiff2),
      62             :     PHP_RSHUTDOWN(wikidiff2),
      63             :     PHP_MINFO(wikidiff2),
      64             :     WIKIDIFF2_VERSION_STRING,
      65             :     STANDARD_MODULE_PROPERTIES
      66             : };
      67             : 
      68             : /* {{{ INI Settings */
      69             : PHP_INI_BEGIN()
      70             :     PHP_INI_ENTRY("wikidiff2.change_threshold",  "0.2", PHP_INI_ALL, NULL)
      71             :     PHP_INI_ENTRY("wikidiff2.moved_line_threshold",  "0.4", PHP_INI_ALL, NULL)
      72             :     PHP_INI_ENTRY("wikidiff2.moved_paragraph_detection_cutoff",  "100", PHP_INI_ALL, NULL)
      73             :     PHP_INI_ENTRY("wikidiff2.max_word_level_diff_complexity", "40000000", PHP_INI_ALL, NULL)
      74             :     PHP_INI_ENTRY("wikidiff2.max_split_size", "1", PHP_INI_ALL, NULL)
      75             :     PHP_INI_ENTRY("wikidiff2.initial_split_threshold",  "0.1", PHP_INI_ALL, NULL)
      76             :     PHP_INI_ENTRY("wikidiff2.final_split_threshold",  "0.6", PHP_INI_ALL, NULL)
      77             : PHP_INI_END()
      78             : /* }}} */
      79             : 
      80             : #ifdef COMPILE_DL_WIKIDIFF2
      81          33 : ZEND_GET_MODULE(wikidiff2)
      82             : #endif
      83             : 
      84          33 : PHP_MINIT_FUNCTION(wikidiff2)
      85             : {
      86          33 :     REGISTER_INI_ENTRIES();
      87          33 :     return SUCCESS;
      88             : }
      89             : 
      90          33 : PHP_MSHUTDOWN_FUNCTION(wikidiff2)
      91             : {
      92          33 :     UNREGISTER_INI_ENTRIES();
      93          33 :     return SUCCESS;
      94             : }
      95             : 
      96          33 : PHP_RINIT_FUNCTION(wikidiff2)
      97             : {
      98          33 :     return SUCCESS;
      99             : }
     100             : 
     101          33 : PHP_RSHUTDOWN_FUNCTION(wikidiff2)
     102             : {
     103          33 :     return SUCCESS;
     104             : }
     105             : 
     106           0 : PHP_MINFO_FUNCTION(wikidiff2)
     107             : {
     108           0 :     php_info_print_table_start();
     109           0 :     php_info_print_table_header(2, "wikidiff2 support", "enabled");
     110           0 :     php_info_print_table_row(2, "wikidiff2 version", WIKIDIFF2_VERSION_STRING);
     111           0 :     php_info_print_table_end();
     112           0 : }
     113             : 
     114             : /**
     115             :  * Get Wikidiff2 config based on the php.ini settings and supplied context line value.
     116             :  */
     117          47 : static Wikidiff2::Config wikidiff2_get_config(int numContextLines)
     118             : {
     119             :     Wikidiff2::Config config;
     120          47 :     config.numContextLines = numContextLines;
     121          47 :     config.changeThreshold = INI_FLT("wikidiff2.change_threshold");
     122          47 :     config.movedLineThreshold = INI_FLT("wikidiff2.moved_line_threshold");
     123          47 :     config.maxMovedLines = INI_INT("wikidiff2.moved_paragraph_detection_cutoff");
     124          47 :     config.maxWordLevelDiffComplexity = INI_INT("wikidiff2.max_word_level_diff_complexity");
     125          47 :     config.maxSplitSize = INI_INT("wikidiff2.max_split_size");
     126          47 :     config.initialSplitThreshold = INI_FLT("wikidiff2.initial_split_threshold");
     127          47 :     config.finalSplitThreshold = INI_FLT("wikidiff2.final_split_threshold");
     128          47 :     return config;
     129             : }
     130             : 
     131          47 : static void wikidiff2_do_diff_impl(zval *return_value,
     132             :         const Wikidiff2::Config & config, Formatter & formatter,
     133             :         char *text1, size_t text1_len,
     134             :         char *text2, size_t text2_len)
     135             : {
     136          94 :     Wikidiff2 wikidiff2(config);
     137          47 :     wikidiff2.addFormatter(formatter);
     138          94 :     Wikidiff2::String text1String(text1, text1_len);
     139          94 :     Wikidiff2::String text2String(text2, text2_len);
     140          47 :     wikidiff2.execute(text1String, text2String);
     141          47 :     Wikidiff2::String ret = formatter.getResult().str();
     142          94 :     ZVAL_STRINGL(return_value, const_cast<char*>(ret.data()), ret.size());
     143          47 : }
     144             : 
     145           0 : static void wikidiff2_handle_exception(std::exception & e)
     146             : {
     147           0 :     php_error_docref(NULL, E_WARNING, "%s", e.what());
     148           0 : }
     149             : 
     150             : /* {{{ proto string wikidiff2_do_diff(string text1, string text2, int numContextLines)
     151             :  *
     152             :  * Warning: the input text must be valid UTF-8! Do not pass user input directly
     153             :  * to this function.
     154             :  */
     155          16 : PHP_FUNCTION(wikidiff2_do_diff)
     156             : {
     157          16 :     char *text1 = NULL;
     158          16 :     char *text2 = NULL;
     159             :     size_t text1_len;
     160             :     size_t text2_len;
     161             :     zend_long numContextLines;
     162             : 
     163          16 :     ZEND_PARSE_PARAMETERS_START(3, 3)
     164          32 :         Z_PARAM_STRING(text1, text1_len)
     165          32 :         Z_PARAM_STRING(text2, text2_len)
     166          32 :         Z_PARAM_LONG(numContextLines)
     167          16 :     ZEND_PARSE_PARAMETERS_END();
     168             : 
     169             :     try {
     170          32 :         TableFormatter formatter;
     171          16 :         auto config = wikidiff2_get_config(numContextLines);
     172          16 :         wikidiff2_do_diff_impl(
     173             :                 return_value,
     174             :                 config,
     175             :                 formatter,
     176             :                 text1, text1_len,
     177             :                 text2, text2_len
     178             :         );
     179           0 :     } catch (std::bad_alloc &e) {
     180           0 :         php_error_docref(NULL, E_WARNING, "out of memory");
     181           0 :     } catch (std::exception &e) {
     182           0 :         wikidiff2_handle_exception(e);
     183           0 :     } catch (...) {
     184           0 :         php_error_docref(NULL, E_WARNING, "unknown exception");
     185             :     }
     186             : }
     187             : 
     188             : /* {{{ proto string wikidiff2_inline_diff(string text1, string text2, int numContextLines)
     189             :  *
     190             :  * Warning: the input text must be valid UTF-8! Do not pass user input directly
     191             :  * to this function.
     192             :  */
     193          20 : PHP_FUNCTION(wikidiff2_inline_diff)
     194             : {
     195          20 :     char *text1 = NULL;
     196          20 :     char *text2 = NULL;
     197             :     size_t text1_len;
     198             :     size_t text2_len;
     199             :     zend_long numContextLines;
     200             : 
     201          20 :     ZEND_PARSE_PARAMETERS_START(3, 3)
     202          40 :         Z_PARAM_STRING(text1, text1_len)
     203          40 :         Z_PARAM_STRING(text2, text2_len)
     204          40 :         Z_PARAM_LONG(numContextLines)
     205          20 :     ZEND_PARSE_PARAMETERS_END();
     206             : 
     207             :     try {
     208          20 :         InlineFormatter formatter;
     209          20 :         wikidiff2_do_diff_impl(
     210             :                 return_value,
     211          40 :                 wikidiff2_get_config(numContextLines),
     212             :                 formatter,
     213             :                 text1, text1_len,
     214             :                 text2, text2_len
     215             :         );
     216           0 :     } catch (std::bad_alloc &e) {
     217           0 :         php_error_docref(NULL, E_WARNING, "out of memory");
     218           0 :     } catch (std::exception &e) {
     219           0 :         wikidiff2_handle_exception(e);
     220           0 :     } catch (...) {
     221           0 :         php_error_docref(NULL, E_WARNING, "unknown exception");
     222             :     }
     223             : }
     224             : 
     225             : /* {{{ proto string wikidiff2_inline_json_diff(string text1, string text2, int numContextLines)
     226             :  *
     227             :  * Warning: the input text must be valid UTF-8! Do not pass user input directly
     228             :  * to this function.
     229             :  */
     230          11 : PHP_FUNCTION(wikidiff2_inline_json_diff)
     231             : {
     232          11 :     char *text1 = NULL;
     233          11 :     char *text2 = NULL;
     234             :     size_t text1_len;
     235             :     size_t text2_len;
     236             :     zend_long numContextLines;
     237             : 
     238          11 :     ZEND_PARSE_PARAMETERS_START(3, 3)
     239          22 :         Z_PARAM_STRING(text1, text1_len)
     240          22 :         Z_PARAM_STRING(text2, text2_len)
     241          22 :         Z_PARAM_LONG(numContextLines)
     242          11 :     ZEND_PARSE_PARAMETERS_END();
     243             : 
     244             :     try {
     245          11 :         InlineJSONFormatter formatter;
     246          11 :         wikidiff2_do_diff_impl(
     247             :                 return_value,
     248          22 :                 wikidiff2_get_config(numContextLines),
     249             :                 formatter,
     250             :                 text1, text1_len,
     251             :                 text2, text2_len
     252             :         );
     253           0 :     } catch (std::bad_alloc &e) {
     254           0 :         php_error_docref(NULL, E_WARNING, "out of memory");
     255           0 :     } catch (std::exception &e) {
     256           0 :         wikidiff2_handle_exception(e);
     257           0 :     } catch (...) {
     258           0 :         php_error_docref(NULL, E_WARNING, "unknown exception");
     259             :     }
     260             : }
     261             : 
     262             : /* {{{ proto string wikidiff2_version()
     263             :  */
     264           0 : PHP_FUNCTION(wikidiff2_version)
     265             : {
     266           0 :     COMPAT_RETURN_STRINGL( const_cast<char*>(WIKIDIFF2_VERSION_STRING), strlen(WIKIDIFF2_VERSION_STRING));
     267             : }
     268             : 
     269           0 : PHP_FUNCTION(wikidiff2_multi_format_diff)
     270             : {
     271             :     typedef std::shared_ptr<Formatter> FormatterPtr;
     272             :     typedef std::list<FormatterPtr, WD2_ALLOCATOR<FormatterPtr>> FormatterList;
     273             : 
     274           0 :     char *text1 = NULL;
     275           0 :     char *text2 = NULL;
     276             :     size_t text1_len;
     277             :     size_t text2_len;
     278           0 :     zend_array *ht_options = NULL;
     279             :     zval *zp_option;
     280             : 
     281           0 :     FormatterList formatters;
     282             : 
     283           0 :     ZEND_PARSE_PARAMETERS_START(2, 3)
     284           0 :         Z_PARAM_STRING(text1, text1_len)
     285           0 :         Z_PARAM_STRING(text2, text2_len)
     286           0 :         Z_PARAM_OPTIONAL
     287           0 :         Z_PARAM_ARRAY_HT(ht_options)
     288           0 :     ZEND_PARSE_PARAMETERS_END();
     289             : 
     290           0 :     Wikidiff2::Config config = wikidiff2_get_config(2);
     291             : 
     292           0 :     if (ht_options) {
     293             :         zend_long l_tmp;
     294             : 
     295           0 :         zp_option = zend_hash_str_find(ht_options, "numContextLines", sizeof("numContextLines")-1);
     296           0 :         if (zp_option) config.numContextLines = zval_get_long(zp_option);
     297             : 
     298           0 :         zp_option = zend_hash_str_find(ht_options, "changeThreshold", sizeof("changeThreshold")-1);
     299           0 :         if (zp_option) config.changeThreshold = zval_get_double(zp_option);
     300             : 
     301           0 :         zp_option = zend_hash_str_find(ht_options, "movedLineThreshold", sizeof("movedLineThreshold")-1);
     302           0 :         if (zp_option) config.movedLineThreshold = zval_get_double(zp_option);
     303             : 
     304           0 :         zp_option = zend_hash_str_find(ht_options, "maxMovedLines", sizeof("maxMovedLines")-1);
     305           0 :         if (zp_option) config.maxMovedLines = zval_get_long(zp_option);
     306             : 
     307           0 :         zp_option = zend_hash_str_find(ht_options, "maxWordLevelDiffComplexity", sizeof("maxWordLevelDiffComplexity")-1);
     308           0 :         if (zp_option) config.maxWordLevelDiffComplexity = zval_get_long(zp_option);
     309             : 
     310           0 :         zp_option = zend_hash_str_find(ht_options, "maxSplitSize", sizeof("maxSplitSize")-1);
     311           0 :         if (zp_option) config.maxSplitSize = zval_get_long(zp_option);
     312             : 
     313           0 :         zp_option = zend_hash_str_find(ht_options, "initialSplitThreshold", sizeof("initialSplitThreshold")-1);
     314           0 :         if (zp_option) config.initialSplitThreshold = zval_get_double(zp_option);
     315             : 
     316           0 :         zp_option = zend_hash_str_find(ht_options, "finalSplitThreshold", sizeof("finalSplitThreshold")-1);
     317           0 :         if (zp_option) config.finalSplitThreshold = zval_get_double(zp_option);
     318             :     }
     319             : 
     320           0 :     Wikidiff2 wikidiff2(config);
     321             : 
     322           0 :     if (ht_options) {
     323           0 :         zp_option = zend_hash_str_find(ht_options, "formats", sizeof("formats")-1);
     324           0 :         if (zp_option) {
     325           0 :             if (Z_TYPE_P(zp_option) == IS_ARRAY) {
     326           0 :                 zend_array *ht_tmp = Z_ARRVAL_P(zp_option);
     327             :                 zval *zp_formatter;
     328           0 :                 ZEND_HASH_FOREACH_VAL(ht_tmp, zp_formatter) {
     329           0 :                     if (Z_TYPE_P(zp_formatter) != IS_STRING) {
     330           0 :                         php_error_docref(NULL, E_WARNING, "invalid formatter, should be string");
     331           0 :                         continue;
     332             :                     }
     333           0 :                     zend_string *s = Z_STR_P(zp_formatter);
     334           0 :                     if (zend_string_equals_literal(s, "table")) {
     335           0 :                         formatters.push_back(std::allocate_shared<TableFormatter>(
     336           0 :                             WD2_ALLOCATOR<TableFormatter>()));
     337           0 :                     } else if (zend_string_equals_literal(s, "inline")) {
     338           0 :                         formatters.push_back(std::allocate_shared<InlineFormatter>(
     339           0 :                             WD2_ALLOCATOR<InlineFormatter>()));
     340           0 :                     } else if (zend_string_equals_literal(s, "inlineJSON")) {
     341           0 :                         formatters.push_back(std::allocate_shared<InlineJSONFormatter>(
     342           0 :                             WD2_ALLOCATOR<InlineJSONFormatter>()));
     343             :                     } else {
     344           0 :                         php_error_docref(NULL, E_WARNING, "unknown formatter \"%s\"", ZSTR_VAL(s));
     345             :                     }
     346             :                 }
     347             :                 ZEND_HASH_FOREACH_END();
     348             :             } else {
     349           0 :                 php_error_docref(NULL, E_WARNING, "invalid formats option");
     350             :             }
     351             :         }
     352             :     }
     353             : 
     354           0 :     if (formatters.empty()) {
     355           0 :         formatters.push_back(std::allocate_shared<TableFormatter>(
     356           0 :             WD2_ALLOCATOR<TableFormatter>()));
     357             :     }
     358             : 
     359           0 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     360           0 :         wikidiff2.addFormatter(**f);
     361             :     }
     362             : 
     363             :     try {
     364           0 :         Wikidiff2::String text1String(text1, text1_len);
     365           0 :         Wikidiff2::String text2String(text2, text2_len);
     366           0 :         wikidiff2.execute(text1String, text2String);
     367           0 :     } catch (std::bad_alloc &e) {
     368           0 :         php_error_docref(NULL, E_WARNING, "out of memory");
     369           0 :     } catch (std::exception &e) {
     370           0 :         wikidiff2_handle_exception(e);
     371           0 :     } catch (...) {
     372           0 :         php_error_docref(NULL, E_WARNING, "unknown exception");
     373             :     }
     374             : 
     375           0 :     HashTable * ht_ret = zend_new_array(formatters.size());
     376           0 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     377           0 :         const char * name = (*f)->getName();
     378             :         zval z_result;
     379           0 :         Wikidiff2::String result = (*f)->getResult().str();
     380           0 :         ZVAL_STRINGL(&z_result, const_cast<char*>(result.data()), result.size());
     381           0 :         zend_hash_str_update(ht_ret, name, strlen(name), &z_result);
     382             :     }
     383             : 
     384           0 :     RETVAL_ARR(ht_ret);
     385             : }
     386             : /* }}} */

Generated by: LCOV version 1.13