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 : /* }}} */
|