1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 4                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2006 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: hightman (hightman[AT]twomice.net) QQ = 16139558             |
16   +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "php_scws.h"
29 #include <scws.h>
30 
31 /// hightman.090716: for PHP5.3+
32 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3) || (PHP_MAJOR_VERSION >= 6)
33 #undef	ZVAL_REFCOUNT
34 #undef	ZVAL_ADDREF
35 #undef	ZVAL_DELREF
36 #define	ZVAL_REFCOUNT	Z_REFCOUNT_P
37 #define	ZVAL_ADDREF		Z_ADDREF_P
38 #define	ZVAL_DELREF		Z_DELREF_P
39 #endif
40 
41 /// hightman.20150614: for PHP7+
42 #if PHP_MAJOR_VERSION >= 7
43 #undef	FREE_ZVAL
44 #undef	ZEND_REGISTER_RESOURCE
45 #undef	RETVAL_RESOURCE
46 #undef	MAKE_STD_ZVAL
47 #undef	RETURN_STRING
48 #undef	add_assoc_string
49 #undef	add_assoc_stringl
50 
51 #define FREE_ZVAL						efree_rel
52 #define ZEND_REGISTER_RESOURCE(x,y,z)	zend_register_resource(y,z)
53 #define RETVAL_RESOURCE					RETVAL_RES
54 #define MAKE_STD_ZVAL(x)				x = (zval *) emalloc(sizeof(zval))
55 #define RETURN_STRING(a,b)				RETVAL_STRING(a); return
56 #define add_assoc_string(a,b,c,d)		add_assoc_string_ex(a,b,strlen(b),c)
57 #define add_assoc_stringl(a,b,c,d,e)	add_assoc_stringl_ex(a,b,strlen(b),(char*)c,d)
58 #define ZEND_LIST_DELETE				zend_list_close
59 
60 typedef size_t	str_size_t;
61 #else
62 #define ZEND_LIST_DELETE				zend_list_delete
63 
64 typedef int str_size_t;
65 #endif
66 
67 /// ZEND_DECLARE_MODULE_GLOBALS(scws)
68 
69 static zend_class_entry *scws_class_entry_ptr;
70 static int le_scws;
71 
72 #define PHP_SCWS_MODULE_VERSION		"0.2.3"
73 #define	PHP_SCWS_DEFAULT_CHARSET	"gbk"
74 #define	PHP_SCWS_OBJECT_TAG			"scws handler"
75 #define	DELREF_SCWS(x)	{	\
76 	if (x != NULL) {	\
77 		ZVAL_DELREF(x);		\
78 		if (ZVAL_REFCOUNT(x) <= 0) {	\
79 			zval_dtor(x);	\
80 			FREE_ZVAL(x);	\
81 		}	\
82 		x = NULL;	\
83 	}	\
84 }
85 
86 #define	CHECK_DR_SCWS()	\
87 if (ps->s->r == NULL || ps->s->d == NULL) {	\
88 	char fpath[128], *folder, *ftoken;	\
89 	int plen;	\
90 	if (((folder = INI_STR("scws.default.fpath")) != NULL) \
91 		&& (plen = strlen(folder)) < 100)	\
92 	{	\
93 		strcpy(fpath, folder);	\
94 		ftoken = fpath + plen;	\
95 		if (plen > 0 && ftoken[-1] != DEFAULT_SLASH)	\
96 			*ftoken++ = DEFAULT_SLASH;	\
97 		if (ps->s->r == NULL) {	\
98 			if (ps->charset[0]	\
99 				&& strcmp(ps->charset, PHP_SCWS_DEFAULT_CHARSET))	\
100 			{	\
101 				zend_sprintf(ftoken, "rules.%s.ini", ps->charset);	\
102 			}	\
103 			else {	\
104 				strcpy(ftoken, "rules.ini");	\
105 			}	\
106 			scws_set_rule(ps->s, fpath);	\
107 		}	\
108 		if (ps->s->d == NULL) {	\
109 			if (ps->charset[0]	\
110 				&& strcmp(ps->charset, PHP_SCWS_DEFAULT_CHARSET))	\
111 			{	\
112 				zend_sprintf(ftoken, "dict.%s.xdb", ps->charset);	\
113 			}	\
114 			else	\
115 			{	\
116 				strcpy(ftoken, "dict.xdb");	\
117 			}	\
118 			scws_set_dict(ps->s, fpath,	SCWS_XDICT_XDB);	\
119 		}	\
120 	}	\
121 }
122 
123 #if PHP_MAJOR_VERSION >= 7
124 #define	SCWS_FETCH_PARAMETERS(ts, ...)	\
125 	do {	\
126 		zval *tmp = getThis();	\
127 		if (tmp) {	\
128 			tmp = zend_hash_str_find(Z_OBJPROP_P(tmp), "handle", sizeof("handle") - 1);	\
129 			if (tmp == NULL) {	\
130 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find the handle property");	\
131 				RETURN_FALSE;	\
132 			}	\
133 			if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ts, ##__VA_ARGS__) == FAILURE) {	\
134 				return;	\
135 			}	\
136 		} else {	\
137 			if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r" ts, &tmp, ##__VA_ARGS__) == FAILURE) {	\
138 				return;	\
139 			}	\
140 		}	\
141 		ps = (struct php_scws *) zend_fetch_resource_ex(tmp, PHP_SCWS_OBJECT_TAG, le_scws);	\
142 	} while(0)
143 #else
144 #define	SCWS_FETCH_PARAMETERS(ts, ...)	\
145 	do {	\
146 		zval **tmp;	\
147 		zval *object = getThis();	\
148 		if (object) {	\
149 			if (zend_hash_find(Z_OBJPROP_P(object), "handle", sizeof("handle"), (void **)&tmp) == FAILURE) {	\
150 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find the handle property");	\
151 				RETURN_FALSE;	\
152 			}	\
153 			if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ts, ##__VA_ARGS__) == FAILURE) {	\
154 				return;	\
155 			}	\
156 		} else {	\
157 			if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r" ts, &object, ##__VA_ARGS__) == FAILURE) {	\
158 				return;	\
159 			}	\
160 			tmp = &object;	\
161 		}	\
162 		ZEND_FETCH_RESOURCE(ps, struct php_scws *, tmp, -1, PHP_SCWS_OBJECT_TAG, le_scws);	\
163 	} while(0)
164 #endif
165 
166 struct php_scws
167 {
168 	scws_t s;
169 	zval *zt;
170 	char charset[8];
171 #if PHP_MAJOR_VERSION >= 7
172 	zend_resource *rsrc_id;
173 #else
174 	int rsrc_id;
175 #endif
176 };
177 
178 zend_function_entry scws_functions[] = {
179 	PHP_FE(scws_open, NULL)
180 	PHP_FE(scws_new, NULL)
181 	PHP_FE(scws_close, NULL)
182 	PHP_FE(scws_set_charset, NULL)
183 	PHP_FE(scws_add_dict, NULL)
184 	PHP_FE(scws_set_dict, NULL)
185 	PHP_FE(scws_set_rule, NULL)
186 	PHP_FE(scws_set_ignore, NULL)
187 	PHP_FE(scws_set_multi, NULL)
188 	PHP_FE(scws_set_duality, NULL)
189 	PHP_FE(scws_send_text, NULL)
190 	PHP_FE(scws_get_result, NULL)
191 	PHP_FE(scws_get_tops, NULL)
192 	PHP_FE(scws_has_word, NULL)
193 	PHP_FE(scws_get_words, NULL)
194 	PHP_FE(scws_version, NULL)
195 	{NULL, NULL, NULL}
196 };
197 
198 static zend_function_entry php_scws_class_functions[] = {
199 	PHP_FALIAS(close,		scws_close,			NULL)
200 	PHP_FALIAS(set_charset,	scws_set_charset,	NULL)
201 	PHP_FALIAS(add_dict,	scws_add_dict,		NULL)
202 	PHP_FALIAS(set_dict,	scws_set_dict,		NULL)
203 	PHP_FALIAS(set_rule,	scws_set_rule,		NULL)
204 	PHP_FALIAS(set_ignore,	scws_set_ignore,	NULL)
205 	PHP_FALIAS(set_multi,	scws_set_multi,		NULL)
206 	PHP_FALIAS(set_duality,	scws_set_duality,	NULL)
207 	PHP_FALIAS(send_text,	scws_send_text,		NULL)
208 	PHP_FALIAS(get_result,	scws_get_result,	NULL)
209 	PHP_FALIAS(get_tops,	scws_get_tops,		NULL)
210 	PHP_FALIAS(has_word,	scws_has_word,		NULL)
211 	PHP_FALIAS(get_words,	scws_get_words,		NULL)
212 	PHP_FALIAS(version,		scws_version,		NULL)
213 	{NULL, NULL, NULL}
214 };
215 
ZEND_RSRC_DTOR_FUNC(php_scws_dtor)216 static ZEND_RSRC_DTOR_FUNC(php_scws_dtor)
217 {
218 #if PHP_MAJOR_VERSION == 7
219 #define rsrc	res
220 #endif
221 	if (rsrc->ptr) {
222 		struct php_scws *ps = (struct php_scws *) rsrc->ptr;
223 		scws_free(ps->s);
224 #if PHP_MAJOR_VERSION < 7
225 		DELREF_SCWS(ps->zt);
226 #endif
227 		efree(ps);
228 		rsrc->ptr = NULL;
229 	}
230 #if PHP_MAJOR_VERSION == 7
231 #undef rsrc
232 #endif
233 }
234 
235 zend_module_entry scws_module_entry = {
236 #if ZEND_MODULE_API_NO >= 20010901
237 	STANDARD_MODULE_HEADER,
238 #endif
239 	"scws",
240 	scws_functions,
241 	PHP_MINIT(scws),
242 	PHP_MSHUTDOWN(scws),
243 	NULL,
244 	PHP_RSHUTDOWN(scws),
245 	PHP_MINFO(scws),
246 #if ZEND_MODULE_API_NO >= 20010901
247 	PHP_SCWS_MODULE_VERSION,
248 #endif
249 	STANDARD_MODULE_PROPERTIES
250 };
251 /* }}} */
252 
253 #ifdef COMPILE_DL_SCWS
254 ZEND_GET_MODULE(scws)
255 #endif
256 
PHP_INI_BEGIN()257 PHP_INI_BEGIN()
258 	PHP_INI_ENTRY("scws.default.charset", PHP_SCWS_DEFAULT_CHARSET, PHP_INI_ALL, NULL)
259 	PHP_INI_ENTRY("scws.default.fpath", NULL, PHP_INI_ALL, NULL)
260 PHP_INI_END()
261 
262 PHP_MINIT_FUNCTION(scws)
263 {
264 	zend_class_entry scws_class_entry;
265 
266 	REGISTER_INI_ENTRIES();
267 
268 	le_scws = zend_register_list_destructors_ex(php_scws_dtor, NULL, PHP_SCWS_OBJECT_TAG, module_number);
269 	INIT_CLASS_ENTRY(scws_class_entry, "SimpleCWS", php_scws_class_functions);
270 	scws_class_entry_ptr = zend_register_internal_class(&scws_class_entry TSRMLS_CC);
271 
272 	REGISTER_LONG_CONSTANT("SCWS_XDICT_XDB",	SCWS_XDICT_XDB, CONST_CS|CONST_PERSISTENT);
273 	REGISTER_LONG_CONSTANT("SCWS_XDICT_MEM",	SCWS_XDICT_MEM, CONST_CS|CONST_PERSISTENT);
274 	REGISTER_LONG_CONSTANT("SCWS_XDICT_TXT",	SCWS_XDICT_TXT, CONST_CS|CONST_PERSISTENT);
275 
276 	REGISTER_LONG_CONSTANT("SCWS_MULTI_NONE",	SCWS_MULTI_NONE, CONST_CS|CONST_PERSISTENT);
277 	REGISTER_LONG_CONSTANT("SCWS_MULTI_SHORT",	(SCWS_MULTI_SHORT>>12), CONST_CS|CONST_PERSISTENT);
278 	REGISTER_LONG_CONSTANT("SCWS_MULTI_DUALITY",(SCWS_MULTI_DUALITY>>12), CONST_CS|CONST_PERSISTENT);
279 	REGISTER_LONG_CONSTANT("SCWS_MULTI_ZMAIN",	(SCWS_MULTI_ZMAIN>>12), CONST_CS|CONST_PERSISTENT);
280 	REGISTER_LONG_CONSTANT("SCWS_MULTI_ZALL",	(SCWS_MULTI_ZALL>>12), CONST_CS|CONST_PERSISTENT);
281 
282 	return SUCCESS;
283 }
284 
PHP_MSHUTDOWN_FUNCTION(scws)285 PHP_MSHUTDOWN_FUNCTION(scws)
286 {
287 	UNREGISTER_INI_ENTRIES();
288 	return SUCCESS;
289 }
290 
PHP_RSHUTDOWN_FUNCTION(scws)291 PHP_RSHUTDOWN_FUNCTION(scws)
292 {
293 	return SUCCESS;
294 }
295 
PHP_MINFO_FUNCTION(scws)296 PHP_MINFO_FUNCTION(scws)
297 {
298 	php_info_print_table_start();
299 	php_info_print_table_header(2, "SCWS support", "Enabled");
300 	php_info_print_table_row(2, "SCWS Description", "Simple Chinese Words Segmentation");
301 	php_info_print_table_row(2, "PECL Module version", PHP_SCWS_MODULE_VERSION);
302 	php_info_print_table_row(2, "SCWS Library", SCWS_VERSION);
303 	php_info_print_table_row(2, "SCWS BugReport", SCWS_BUGREPORT);
304 	php_info_print_table_end();
305 	DISPLAY_INI_ENTRIES();
306 }
307 
_php_create_scws(TSRMLS_D)308 static void *_php_create_scws(TSRMLS_D)
309 {
310 	struct php_scws *ps;
311 	char *ini_cs;
312 	scws_t s;
313 
314 	s = scws_new();
315 	if (s == NULL) {
316 		return NULL;
317 	}
318 
319 	ps = (struct php_scws *)emalloc(sizeof(struct php_scws));
320 	ps->s = s;
321 	ps->zt = NULL;
322 	ps->charset[0] = '\0';
323 	ps->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ps, le_scws);
324 
325 	ini_cs = INI_STR("scws.default.charset");
326 	if (ini_cs != NULL && *ini_cs) {
327 		memset(ps->charset, 0, sizeof(ps->charset));
328 		strncpy(ps->charset, ini_cs, sizeof(ps->charset)-1);
329 		scws_set_charset(s, ps->charset);
330 	}
331 
332 	return ((void *)ps);
333 }
334 
PHP_FUNCTION(scws_open)335 PHP_FUNCTION(scws_open)
336 {
337 	struct php_scws *ps;
338 
339 	ps = (struct php_scws *)_php_create_scws(TSRMLS_C);
340 	if (ps == NULL) {
341 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't create new scws handler");
342 		RETURN_FALSE;
343 	}
344 
345 	RETVAL_RESOURCE(ps->rsrc_id);
346 }
347 
PHP_FUNCTION(scws_new)348 PHP_FUNCTION(scws_new)
349 {
350 	struct php_scws *ps;
351 
352 	ps = (struct php_scws *)_php_create_scws(TSRMLS_C);
353 	if (ps == NULL) {
354 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't create new scws object");
355 		RETURN_FALSE;
356 	}
357 
358 	object_init_ex(return_value, scws_class_entry_ptr);
359 	add_property_resource(return_value, "handle", ps->rsrc_id);
360 }
361 
PHP_FUNCTION(scws_close)362 PHP_FUNCTION(scws_close)
363 {
364 	struct php_scws *ps;
365 
366 	SCWS_FETCH_PARAMETERS("");
367 
368 	ZEND_LIST_DELETE(ps->rsrc_id);
369 }
370 
PHP_FUNCTION(scws_set_charset)371 PHP_FUNCTION(scws_set_charset)
372 {
373 	char *cs;
374 	str_size_t cs_len;
375 	struct php_scws *ps;
376 
377 	SCWS_FETCH_PARAMETERS("s", &cs, &cs_len);
378 
379 	memset(ps->charset, 0, sizeof(ps->charset));
380 	strncpy(ps->charset, cs, sizeof(ps->charset)-1);
381 	scws_set_charset(ps->s, ps->charset);
382 
383 	RETURN_TRUE;
384 }
385 
PHP_FUNCTION(scws_add_dict)386 PHP_FUNCTION(scws_add_dict)
387 {
388 	long xmode = 0;
389 	char *filepath, *fullpath = NULL;
390 	str_size_t filepath_len;
391 	struct php_scws *ps;
392 
393 	SCWS_FETCH_PARAMETERS("s|l", &filepath, &filepath_len, &xmode);
394 
395 	if (!(fullpath = expand_filepath(filepath, NULL TSRMLS_CC))) {
396 		RETURN_FALSE;
397 	}
398 
399 #if PHP_API_VERSION < 20100412
400 	if (PG(safe_mode) && (!php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
401 		efree(fullpath);
402 		RETURN_FALSE;
403 	}
404 #endif
405 
406 	if (php_check_open_basedir(fullpath TSRMLS_CC)) {
407 		efree(fullpath);
408 		RETURN_FALSE;
409 	}
410 
411 	xmode = (int) scws_add_dict(ps->s, fullpath, xmode);
412 	efree(fullpath);
413 
414 	if (xmode != 0) {
415 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to add the dict file");
416 		RETURN_FALSE;
417 	}
418 
419 	RETURN_TRUE;
420 }
421 
PHP_FUNCTION(scws_set_dict)422 PHP_FUNCTION(scws_set_dict)
423 {
424 	long xmode = 0;
425 	char *filepath, *fullpath = NULL;
426 	str_size_t filepath_len;
427 	struct php_scws *ps;
428 
429 	SCWS_FETCH_PARAMETERS("s|l", &filepath, &filepath_len, &xmode);
430 
431 	if (!(fullpath = expand_filepath(filepath, NULL TSRMLS_CC))) {
432 		RETURN_FALSE;
433 	}
434 
435 #if PHP_API_VERSION < 20100412
436 	if (PG(safe_mode) && (!php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
437 		efree(fullpath);
438 		RETURN_FALSE;
439 	}
440 #endif
441 
442 	if (php_check_open_basedir(fullpath TSRMLS_CC)) {
443 		efree(fullpath);
444 		RETURN_FALSE;
445 	}
446 
447 	xmode = (int) scws_set_dict(ps->s, fullpath, xmode);
448 	efree(fullpath);
449 
450 	if (xmode != 0) {
451 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to set the dict file");
452 		RETURN_FALSE;
453 	}
454 
455 	RETURN_TRUE;
456 }
457 
PHP_FUNCTION(scws_set_rule)458 PHP_FUNCTION(scws_set_rule)
459 {
460 	char *filepath, *fullpath = NULL;
461 	str_size_t filepath_len;
462 	struct php_scws *ps;
463 
464 	SCWS_FETCH_PARAMETERS("s", &filepath, &filepath_len);
465 
466 	if (!(fullpath = expand_filepath(filepath, NULL TSRMLS_CC))) {
467 		RETURN_FALSE;
468 	}
469 
470 #if PHP_API_VERSION < 20100412
471 	if (PG(safe_mode) && (!php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
472 		efree(fullpath);
473 		RETURN_FALSE;
474 	}
475 #endif
476 
477 	if (php_check_open_basedir(fullpath TSRMLS_CC)) {
478 		efree(fullpath);
479 		RETURN_FALSE;
480 	}
481 
482 	scws_set_rule(ps->s, fullpath);
483 	efree(fullpath);
484 
485 	if (ps->s->r == NULL) {
486 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to load the ruleset file");
487 		RETURN_FALSE;
488 	}
489 
490 	RETURN_TRUE;
491 }
492 
PHP_FUNCTION(scws_set_ignore)493 PHP_FUNCTION(scws_set_ignore)
494 {
495 	zend_bool boolset = 1;
496 	struct php_scws *ps;
497 
498 	SCWS_FETCH_PARAMETERS("b", &boolset);
499 
500 	scws_set_ignore(ps->s, boolset ? SCWS_YEA : SCWS_NA);
501 	RETURN_TRUE;
502 }
503 
PHP_FUNCTION(scws_set_multi)504 PHP_FUNCTION(scws_set_multi)
505 {
506 	long multi = 0;
507 	struct php_scws *ps;
508 
509 	SCWS_FETCH_PARAMETERS("l", &multi);
510 
511 	if (multi < 0 || (multi & 0x10)) {
512 		RETURN_FALSE;
513 	}
514 
515 	scws_set_multi(ps->s, (multi<<12));
516 	RETURN_TRUE;
517 }
518 
PHP_FUNCTION(scws_set_duality)519 PHP_FUNCTION(scws_set_duality)
520 {
521 	zend_bool boolset = 1;
522 	struct php_scws *ps;
523 
524 	SCWS_FETCH_PARAMETERS("b", &boolset);
525 
526 	scws_set_duality(ps->s, boolset ? SCWS_YEA : SCWS_NA);
527 	RETURN_TRUE;
528 }
529 
PHP_FUNCTION(scws_send_text)530 PHP_FUNCTION(scws_send_text)
531 {
532 	zval *text;
533 	struct php_scws *ps;
534 
535 	SCWS_FETCH_PARAMETERS("z", &text);
536 
537 #if	PHP_MAJOR_VERSION >= 7
538 	convert_to_string_ex(text);
539 #else
540 	convert_to_string_ex(&text);
541 #endif
542 
543 #if PHP_MAJOR_VERSION < 7
544 	DELREF_SCWS(ps->zt);
545 	ZVAL_ADDREF(text);
546 #endif
547 	ps->zt = text;
548 
549 	scws_send_text(ps->s, Z_STRVAL_P(ps->zt), Z_STRLEN_P(ps->zt));
550 
551 	CHECK_DR_SCWS();
552 
553 	RETURN_TRUE;
554 }
555 
PHP_FUNCTION(scws_get_result)556 PHP_FUNCTION(scws_get_result)
557 {
558 	zval *row;
559 	scws_res_t res, cur;
560 	struct php_scws *ps;
561 
562 	SCWS_FETCH_PARAMETERS("");
563 
564 	cur = res = scws_get_result(ps->s);
565 	if (res == NULL) {
566 		RETURN_FALSE;
567 	}
568 
569 	array_init(return_value);
570 	while (cur != NULL) {
571 		MAKE_STD_ZVAL(row);
572 		array_init(row);
573 		add_assoc_stringl(row, "word", (char *) ps->s->txt + cur->off, cur->len, 1);
574 		add_assoc_long(row, "off", cur->off);
575 		add_assoc_long(row, "len", cur->len);
576 		add_assoc_double(row, "idf", (double) cur->idf);
577 		add_assoc_stringl(row, "attr", cur->attr, (cur->attr[1] == '\0' ? 1 : 2), 1);
578 
579 		cur = cur->next;
580 		add_next_index_zval(return_value, row);
581 #if PHP_MAJOR_VERSION >= 7
582 		efree(row);
583 #endif
584 	}
585 	scws_free_result(res);
586 }
587 
588 // [, limit, [, exclude_attrs]]
PHP_FUNCTION(scws_get_tops)589 PHP_FUNCTION(scws_get_tops)
590 {
591 	long limit = 0;
592 	char *attr = NULL;
593 	str_size_t attr_len;
594 	scws_top_t top, cur;
595 	zval *row;
596 	struct php_scws *ps;
597 
598 	SCWS_FETCH_PARAMETERS("|ls", &limit, &attr, &attr_len);
599 
600 	if (limit <= 0) {
601 		limit = 10;
602 	}
603 	cur = top = scws_get_tops(ps->s, limit, attr);
604 	array_init(return_value);
605 	while (cur != NULL) {
606 		MAKE_STD_ZVAL(row);
607 		array_init(row);
608 
609 		add_assoc_string(row, "word", cur->word, 1);
610 		add_assoc_long(row, "times", cur->times);
611 		add_assoc_double(row, "weight", (double) cur->weight);
612 		add_assoc_stringl(row, "attr", cur->attr, (cur->attr[1] == '\0' ? 1 : 2), 1);
613 
614 		cur = cur->next;
615 		add_next_index_zval(return_value, row);
616 #if PHP_MAJOR_VERSION >= 7
617 		efree(row);
618 #endif
619 	}
620 	scws_free_tops(top);
621 }
622 
623 // <attr>
PHP_FUNCTION(scws_has_word)624 PHP_FUNCTION(scws_has_word)
625 {
626 	char *attr;
627 	str_size_t attr_len;
628 	struct php_scws *ps;
629 
630 	SCWS_FETCH_PARAMETERS("s", &attr, &attr_len);
631 
632 	if (scws_has_word(ps->s, attr) == 0) {
633 		RETURN_FALSE;
634 	}
635 
636 	RETURN_TRUE;
637 }
638 
639 // <attr>
PHP_FUNCTION(scws_get_words)640 PHP_FUNCTION(scws_get_words)
641 {
642 	char *attr;
643 	str_size_t attr_len;
644 	scws_top_t top, cur;
645 	zval *row;
646 	struct php_scws *ps;
647 
648 	SCWS_FETCH_PARAMETERS("s", &attr, &attr_len);
649 
650 	array_init(return_value);
651 	cur = top = scws_get_words(ps->s, attr);
652 	while (cur != NULL) {
653 		MAKE_STD_ZVAL(row);
654 		array_init(row);
655 		add_assoc_string(row, "word", cur->word, 1);
656 		add_assoc_long(row, "times", cur->times);
657 		add_assoc_double(row, "weight", (double) cur->weight);
658 		add_assoc_stringl(row, "attr", cur->attr, (cur->attr[1] == '\0' ? 1 : 2), 1);
659 
660 		cur = cur->next;
661 		add_next_index_zval(return_value, row);
662 #if PHP_MAJOR_VERSION >= 7
663 		efree(row);
664 #endif
665 	}
666 	scws_free_tops(top);
667 }
668 
PHP_FUNCTION(scws_version)669 PHP_FUNCTION(scws_version)
670 {
671 	char buf[128];
672 
673 	zend_sprintf(buf, "SCWS (Module version:%s, Library version:%s) - by hightman",
674 		PHP_SCWS_MODULE_VERSION, SCWS_VERSION);
675 	RETURN_STRING(buf, 1);
676 }
677 
678 /// hightman.20150614: for PHP7+
679 #if PHP_MAJOR_VERSION >= 7
680 #undef	RETURN_STRING
681 #undef	add_assoc_string
682 #undef	add_assoc_stringl
683 #endif
684 
685 /*
686  * Local variables:
687  * tab-width: 4
688  * c-basic-offset: 4
689  * End:
690  * vim600: noet sw=4 ts=4 fdm=marker
691  * vim<600: noet sw=4 ts=4
692  */
693