php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
string.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Rasmus Lerdorf <rasmus@php.net> |
14 | Stig Sæther Bakken <ssb@php.net> |
15 | Zeev Suraski <zeev@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19#include <stdio.h>
20#include "php.h"
21#include "php_string.h"
22#include "php_variables.h"
23#include <locale.h>
24#ifdef HAVE_LANGINFO_H
25# include <langinfo.h>
26#endif
27
28#ifdef HAVE_LIBINTL
29# include <libintl.h> /* For LC_MESSAGES */
30#endif
31
32#include "scanf.h"
33#include "zend_API.h"
34#include "zend_execute.h"
35#include "basic_functions.h"
36#include "zend_smart_str.h"
38#ifdef ZTS
39#include "TSRM.h"
40#endif
41
42/* For str_getcsv() support */
43#include "ext/standard/file.h"
44/* For php_next_utf8_char() */
45#include "ext/standard/html.h"
47
48#ifdef __SSE2__
49#include <emmintrin.h>
50#include "Zend/zend_bitset.h"
51#endif
52
53/* this is read-only, so it's ok */
54ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
55
56/* localeconv mutex */
57#ifdef ZTS
58static MUTEX_T locale_mutex = NULL;
59#endif
60
61/* {{{ php_bin2hex */
62static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
63{
65 size_t i, j;
66
67 result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
68
69 for (i = j = 0; i < oldlen; i++) {
70 ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
71 ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
72 }
73 ZSTR_VAL(result)[j] = '\0';
74
75 return result;
76}
77/* }}} */
78
79/* {{{ php_hex2bin */
80static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
81{
82 size_t target_length = oldlen >> 1;
83 zend_string *str = zend_string_alloc(target_length, 0);
84 unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
85 size_t i, j;
86
87 for (i = j = 0; i < target_length; i++) {
88 unsigned char c = old[j++];
89 unsigned char l = c & ~0x20;
90 int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
91 unsigned char d;
92
93 /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
94 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
95 d = (l - 0x10 - 0x27 * is_letter) << 4;
96 } else {
97 zend_string_efree(str);
98 return NULL;
99 }
100 c = old[j++];
101 l = c & ~0x20;
102 is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
103 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
104 d |= l - 0x10 - 0x27 * is_letter;
105 } else {
106 zend_string_efree(str);
107 return NULL;
108 }
109 ret[i] = d;
110 }
111 ret[i] = '\0';
112
113 return str;
114}
115/* }}} */
116
117/* {{{ localeconv_r
118 * glibc's localeconv is not reentrant, so lets make it so ... sorta */
119PHPAPI struct lconv *localeconv_r(struct lconv *out)
120{
121
122#ifdef ZTS
123 tsrm_mutex_lock( locale_mutex );
124#endif
125
126/* cur->locinfo is struct __crt_locale_info which implementation is
127 hidden in vc14. TODO revisit this and check if a workaround available
128 and needed. */
129#if defined(PHP_WIN32) && _MSC_VER < 1900 && defined(ZTS)
130 {
131 /* Even with the enabled per thread locale, localeconv
132 won't check any locale change in the master thread. */
133 _locale_t cur = _get_current_locale();
134 *out = *cur->locinfo->lconv;
135 _free_locale(cur);
136 }
137#else
138 /* localeconv doesn't return an error condition */
139 *out = *localeconv();
140#endif
141
142#ifdef ZTS
143 tsrm_mutex_unlock( locale_mutex );
144#endif
145
146 return out;
147}
148/* }}} */
149
150#ifdef ZTS
151/* {{{ PHP_MINIT_FUNCTION */
153{
154 locale_mutex = tsrm_mutex_alloc();
155 return SUCCESS;
156}
157/* }}} */
158
159/* {{{ PHP_MSHUTDOWN_FUNCTION */
161{
162 tsrm_mutex_free( locale_mutex );
163 locale_mutex = NULL;
164 return SUCCESS;
165}
166/* }}} */
167#endif
168
169/* {{{ Converts the binary representation of data to hex */
171{
174
178
179 result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
180
182}
183/* }}} */
184
185/* {{{ Converts the hex representation of data to binary */
187{
189
193
194 if (ZSTR_LEN(data) % 2 != 0) {
195 php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
197 }
198
199 result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
200
201 if (!result) {
202 php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
204 }
205
207}
208/* }}} */
209
210static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, bool is_strspn) /* {{{ */
211{
212 zend_string *s11, *s22;
213 zend_long start = 0, len = 0;
214 bool len_is_null = 1;
215
217 Z_PARAM_STR(s11)
218 Z_PARAM_STR(s22)
221 Z_PARAM_LONG_OR_NULL(len, len_is_null)
223
224 size_t remain_len = ZSTR_LEN(s11);
225 if (start < 0) {
226 start += remain_len;
227 if (start < 0) {
228 start = 0;
229 }
230 } else if ((size_t) start > remain_len) {
231 start = remain_len;
232 }
233
234 remain_len -= start;
235 if (!len_is_null) {
236 if (len < 0) {
237 len += remain_len;
238 if (len < 0) {
239 len = 0;
240 }
241 } else if ((size_t) len > remain_len) {
242 len = remain_len;
243 }
244 } else {
245 len = remain_len;
246 }
247
248 if (len == 0) {
249 RETURN_LONG(0);
250 }
251
252 if (is_strspn) {
253 RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
254 ZSTR_VAL(s22) /*str2_start*/,
255 ZSTR_VAL(s11) + start + len /*str1_end*/,
256 ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
257 } else {
258 RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
259 ZSTR_VAL(s22) /*str2_start*/,
260 ZSTR_VAL(s11) + start + len /*str1_end*/,
261 ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
262 }
263}
264/* }}} */
265
266/* {{{ Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars) */
268{
269 php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* is_strspn */ true);
270}
271/* }}} */
272
273/* {{{ Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars) */
275{
276 php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* is_strspn */ false);
277}
278/* }}} */
279
280#ifdef HAVE_NL_LANGINFO
281/* {{{ Query language and locale information */
283{
284 zend_long item;
285 char *value;
286
288 Z_PARAM_LONG(item)
290
291 switch(item) { /* {{{ */
292#ifdef ABDAY_1
293 case ABDAY_1:
294 case ABDAY_2:
295 case ABDAY_3:
296 case ABDAY_4:
297 case ABDAY_5:
298 case ABDAY_6:
299 case ABDAY_7:
300#endif
301#ifdef DAY_1
302 case DAY_1:
303 case DAY_2:
304 case DAY_3:
305 case DAY_4:
306 case DAY_5:
307 case DAY_6:
308 case DAY_7:
309#endif
310#ifdef ABMON_1
311 case ABMON_1:
312 case ABMON_2:
313 case ABMON_3:
314 case ABMON_4:
315 case ABMON_5:
316 case ABMON_6:
317 case ABMON_7:
318 case ABMON_8:
319 case ABMON_9:
320 case ABMON_10:
321 case ABMON_11:
322 case ABMON_12:
323#endif
324#ifdef MON_1
325 case MON_1:
326 case MON_2:
327 case MON_3:
328 case MON_4:
329 case MON_5:
330 case MON_6:
331 case MON_7:
332 case MON_8:
333 case MON_9:
334 case MON_10:
335 case MON_11:
336 case MON_12:
337#endif
338#ifdef AM_STR
339 case AM_STR:
340#endif
341#ifdef PM_STR
342 case PM_STR:
343#endif
344#ifdef D_T_FMT
345 case D_T_FMT:
346#endif
347#ifdef D_FMT
348 case D_FMT:
349#endif
350#ifdef T_FMT
351 case T_FMT:
352#endif
353#ifdef T_FMT_AMPM
354 case T_FMT_AMPM:
355#endif
356#ifdef ERA
357 case ERA:
358#endif
359#ifdef ERA_YEAR
360 case ERA_YEAR:
361#endif
362#ifdef ERA_D_T_FMT
363 case ERA_D_T_FMT:
364#endif
365#ifdef ERA_D_FMT
366 case ERA_D_FMT:
367#endif
368#ifdef ERA_T_FMT
369 case ERA_T_FMT:
370#endif
371#ifdef ALT_DIGITS
372 case ALT_DIGITS:
373#endif
374#ifdef INT_CURR_SYMBOL
375 case INT_CURR_SYMBOL:
376#endif
377#ifdef CURRENCY_SYMBOL
378 case CURRENCY_SYMBOL:
379#endif
380#ifdef CRNCYSTR
381 case CRNCYSTR:
382#endif
383#ifdef MON_DECIMAL_POINT
385#endif
386#ifdef MON_THOUSANDS_SEP
388#endif
389#ifdef MON_GROUPING
390 case MON_GROUPING:
391#endif
392#ifdef POSITIVE_SIGN
393 case POSITIVE_SIGN:
394#endif
395#ifdef NEGATIVE_SIGN
396 case NEGATIVE_SIGN:
397#endif
398#ifdef INT_FRAC_DIGITS
399 case INT_FRAC_DIGITS:
400#endif
401#ifdef FRAC_DIGITS
402 case FRAC_DIGITS:
403#endif
404#ifdef P_CS_PRECEDES
405 case P_CS_PRECEDES:
406#endif
407#ifdef P_SEP_BY_SPACE
408 case P_SEP_BY_SPACE:
409#endif
410#ifdef N_CS_PRECEDES
411 case N_CS_PRECEDES:
412#endif
413#ifdef N_SEP_BY_SPACE
414 case N_SEP_BY_SPACE:
415#endif
416#ifdef P_SIGN_POSN
417 case P_SIGN_POSN:
418#endif
419#ifdef N_SIGN_POSN
420 case N_SIGN_POSN:
421#endif
422#ifdef DECIMAL_POINT
423 case DECIMAL_POINT:
424#elif defined(RADIXCHAR)
425 case RADIXCHAR:
426#endif
427#ifdef THOUSANDS_SEP
428 case THOUSANDS_SEP:
429#elif defined(THOUSEP)
430 case THOUSEP:
431#endif
432#ifdef GROUPING
433 case GROUPING:
434#endif
435#ifdef YESEXPR
436 case YESEXPR:
437#endif
438#ifdef NOEXPR
439 case NOEXPR:
440#endif
441#ifdef YESSTR
442 case YESSTR:
443#endif
444#ifdef NOSTR
445 case NOSTR:
446#endif
447#ifdef CODESET
448 case CODESET:
449#endif
450 break;
451 default:
452 php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
454 }
455 /* }}} */
456
457 value = nl_langinfo(item);
458 if (value == NULL) {
460 } else {
462 }
463}
464#endif
465/* }}} */
466
467/* {{{ Compares two strings using the current locale */
469{
470 zend_string *s1, *s2;
471
473 Z_PARAM_STR(s1)
474 Z_PARAM_STR(s2)
476
477 RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
478 (const char *) ZSTR_VAL(s2)));
479}
480/* }}} */
481
482/* {{{ php_charmask
483 * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
484 * it needs to be incrementing.
485 * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
486 */
487static inline zend_result php_charmask(const unsigned char *input, size_t len, char *mask)
488{
489 const unsigned char *end;
490 unsigned char c;
492
493 memset(mask, 0, 256);
494 for (end = input+len; input < end; input++) {
495 c=*input;
496 if ((input+3 < end) && input[1] == '.' && input[2] == '.'
497 && input[3] >= c) {
498 memset(mask+c, 1, input[3] - c + 1);
499 input+=3;
500 } else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
501 /* Error, try to be as helpful as possible:
502 (a range ending/starting with '.' won't be captured here) */
503 if (end-len >= input) { /* there was no 'left' char */
504 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
505 result = FAILURE;
506 continue;
507 }
508 if (input+2 >= end) { /* there is no 'right' char */
509 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
510 result = FAILURE;
511 continue;
512 }
513 if (input[-1] > input[2]) { /* wrong order */
514 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
515 result = FAILURE;
516 continue;
517 }
518 /* FIXME: better error (a..b..c is the only left possibility?) */
519 php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
520 result = FAILURE;
521 continue;
522 } else {
523 mask[c]=1;
524 }
525 }
526 return result;
527}
528/* }}} */
529
530/* {{{ php_trim_int()
531 * mode 1 : trim left
532 * mode 2 : trim right
533 * mode 3 : trim left and right
534 * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
535 */
536static zend_always_inline zend_string *php_trim_int(zend_string *str, const char *what, size_t what_len, int mode)
537{
538 const char *start = ZSTR_VAL(str);
539 const char *end = start + ZSTR_LEN(str);
540 char mask[256];
541
542 if (what) {
543 if (what_len == 1) {
544 char p = *what;
545 if (mode & 1) {
546 while (start != end) {
547 if (*start == p) {
548 start++;
549 } else {
550 break;
551 }
552 }
553 }
554 if (mode & 2) {
555 while (start != end) {
556 if (*(end-1) == p) {
557 end--;
558 } else {
559 break;
560 }
561 }
562 }
563 } else {
564 php_charmask((const unsigned char *) what, what_len, mask);
565
566 if (mode & 1) {
567 while (start != end) {
568 if (mask[(unsigned char)*start]) {
569 start++;
570 } else {
571 break;
572 }
573 }
574 }
575 if (mode & 2) {
576 while (start != end) {
577 if (mask[(unsigned char)*(end-1)]) {
578 end--;
579 } else {
580 break;
581 }
582 }
583 }
584 }
585 } else {
586 if (mode & 1) {
587 while (start != end) {
588 unsigned char c = (unsigned char)*start;
589
590 if (c <= ' ' &&
591 (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
592 start++;
593 } else {
594 break;
595 }
596 }
597 }
598 if (mode & 2) {
599 while (start != end) {
600 unsigned char c = (unsigned char)*(end-1);
601
602 if (c <= ' ' &&
603 (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
604 end--;
605 } else {
606 break;
607 }
608 }
609 }
610 }
611
612 if (ZSTR_LEN(str) == end - start) {
613 return zend_string_copy(str);
614 } else if (end - start == 0) {
615 return ZSTR_EMPTY_ALLOC();
616 } else {
617 return zend_string_init(start, end - start, 0);
618 }
619}
620/* }}} */
621
622/* {{{ php_trim_int()
623 * mode 1 : trim left
624 * mode 2 : trim right
625 * mode 3 : trim left and right
626 * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
627 */
628PHPAPI zend_string *php_trim(zend_string *str, const char *what, size_t what_len, int mode)
629{
630 return php_trim_int(str, what, what_len, mode);
631}
632/* }}} */
633
634/* {{{ php_do_trim
635 * Base for trim(), rtrim() and ltrim() functions.
636 */
637static zend_always_inline void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
638{
639 zend_string *str;
640 zend_string *what = NULL;
641
643 Z_PARAM_STR(str)
645 Z_PARAM_STR(what)
647
648 ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
649}
650/* }}} */
651
652/* {{{ Strips whitespace from the beginning and end of a string */
654{
655 php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
656}
657/* }}} */
658
660{
661 zval str_tmp;
662 zend_string *str;
663
664 Z_FLF_PARAM_STR(1, str, str_tmp);
665
666 ZVAL_STR(return_value, php_trim_int(str, /* what */ NULL, /* what_len */ 0, /* mode */ 3));
667
668flf_clean:
669 Z_FLF_PARAM_FREE_STR(1, str_tmp);
670}
671
673{
674 zval str_tmp, what_tmp;
675 zend_string *str, *what;
676
677 Z_FLF_PARAM_STR(1, str, str_tmp);
678 Z_FLF_PARAM_STR(2, what, what_tmp);
679
680 ZVAL_STR(return_value, php_trim_int(str, ZSTR_VAL(what), ZSTR_LEN(what), /* mode */ 3));
681
682flf_clean:
683 Z_FLF_PARAM_FREE_STR(1, str_tmp);
684 Z_FLF_PARAM_FREE_STR(2, what_tmp);
685}
686
687/* {{{ Removes trailing whitespace */
692/* }}} */
693
694/* {{{ Strips whitespace from the beginning of a string */
699/* }}} */
700
701/* {{{ Wraps buffer to selected number of characters using string break char */
703{
705 char *breakchar = "\n";
706 size_t newtextlen, chk, breakchar_len = 1;
707 size_t alloced;
708 zend_long current = 0, laststart = 0, lastspace = 0;
709 zend_long linelength = 75;
710 bool docut = 0;
711 zend_string *newtext;
712
716 Z_PARAM_LONG(linelength)
717 Z_PARAM_STRING(breakchar, breakchar_len)
718 Z_PARAM_BOOL(docut)
720
721 if (ZSTR_LEN(text) == 0) {
723 }
724
725 if (breakchar_len == 0) {
728 }
729
730 if (linelength == 0 && docut) {
731 zend_argument_value_error(4, "cannot be true when argument #2 ($width) is 0");
733 }
734
735 /* Special case for a single-character break as it needs no
736 additional storage space */
737 if (breakchar_len == 1 && !docut) {
738 newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
739
740 laststart = lastspace = 0;
741 for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
742 if (ZSTR_VAL(text)[current] == breakchar[0]) {
743 laststart = lastspace = current + 1;
744 } else if (ZSTR_VAL(text)[current] == ' ') {
745 if (current - laststart >= linelength) {
746 ZSTR_VAL(newtext)[current] = breakchar[0];
747 laststart = current + 1;
748 }
749 lastspace = current;
750 } else if (current - laststart >= linelength && laststart != lastspace) {
751 ZSTR_VAL(newtext)[lastspace] = breakchar[0];
752 laststart = lastspace + 1;
753 }
754 }
755
756 RETURN_NEW_STR(newtext);
757 } else {
758 /* Multiple character line break or forced cut */
759 if (linelength > 0) {
760 chk = (size_t)(ZSTR_LEN(text)/linelength + 1);
761 newtext = zend_string_safe_alloc(chk, breakchar_len, ZSTR_LEN(text), 0);
762 alloced = ZSTR_LEN(text) + chk * breakchar_len + 1;
763 } else {
764 chk = ZSTR_LEN(text);
765 alloced = ZSTR_LEN(text) * (breakchar_len + 1) + 1;
766 newtext = zend_string_safe_alloc(ZSTR_LEN(text), breakchar_len + 1, 0, 0);
767 }
768
769 /* now keep track of the actual new text length */
770 newtextlen = 0;
771
772 laststart = lastspace = 0;
773 for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
774 if (chk == 0) {
775 alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
776 newtext = zend_string_extend(newtext, alloced, 0);
777 chk = (size_t) ((ZSTR_LEN(text) - current)/linelength) + 1;
778 }
779 /* when we hit an existing break, copy to new buffer, and
780 * fix up laststart and lastspace */
781 if (ZSTR_VAL(text)[current] == breakchar[0]
782 && current + breakchar_len < ZSTR_LEN(text)
783 && !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
784 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart + breakchar_len);
785 newtextlen += current - laststart + breakchar_len;
786 current += breakchar_len - 1;
787 laststart = lastspace = current + 1;
788 chk--;
789 }
790 /* if it is a space, check if it is at the line boundary,
791 * copy and insert a break, or just keep track of it */
792 else if (ZSTR_VAL(text)[current] == ' ') {
793 if (current - laststart >= linelength) {
794 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
795 newtextlen += current - laststart;
796 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
797 newtextlen += breakchar_len;
798 laststart = current + 1;
799 chk--;
800 }
801 lastspace = current;
802 }
803 /* if we are cutting, and we've accumulated enough
804 * characters, and we haven't see a space for this line,
805 * copy and insert a break. */
806 else if (current - laststart >= linelength
807 && docut && laststart >= lastspace) {
808 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
809 newtextlen += current - laststart;
810 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
811 newtextlen += breakchar_len;
812 laststart = lastspace = current;
813 chk--;
814 }
815 /* if the current word puts us over the linelength, copy
816 * back up until the last space, insert a break, and move
817 * up the laststart */
818 else if (current - laststart >= linelength
819 && laststart < lastspace) {
820 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, lastspace - laststart);
821 newtextlen += lastspace - laststart;
822 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
823 newtextlen += breakchar_len;
824 laststart = lastspace = lastspace + 1;
825 chk--;
826 }
827 }
828
829 /* copy over any stragglers */
830 if (laststart != current) {
831 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
832 newtextlen += current - laststart;
833 }
834
835 ZSTR_VAL(newtext)[newtextlen] = '\0';
836 /* free unused memory */
837 newtext = zend_string_truncate(newtext, newtextlen, 0);
838
839 RETURN_NEW_STR(newtext);
840 }
841}
842/* }}} */
843
844/* {{{ php_explode */
846{
847 const char *p1 = ZSTR_VAL(str);
848 const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
849 const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
850 zval tmp;
851
852 if (p2 == NULL) {
853 ZVAL_STR_COPY(&tmp, str);
855 } else {
858 do {
860 ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, p2 - p1));
862 p1 = p2 + ZSTR_LEN(delim);
863 p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
864 } while (p2 != NULL && --limit > 1);
865
866 if (p1 <= endp) {
868 ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, endp - p1));
870 }
872 }
873}
874/* }}} */
875
876/* {{{ php_explode_negative_limit */
878{
879#define EXPLODE_ALLOC_STEP 64
880 const char *p1 = ZSTR_VAL(str);
881 const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
882 const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
883 zval tmp;
884
885 if (p2 == NULL) {
886 /*
887 do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
888 by doing nothing we return empty array
889 */
890 } else {
891 size_t allocated = EXPLODE_ALLOC_STEP, found = 0;
892 zend_long i, to_return;
893 const char **positions = emalloc(allocated * sizeof(char *));
894
895 positions[found++] = p1;
896 do {
897 if (found >= allocated) {
898 allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
899 positions = erealloc(ZEND_VOIDP(positions), allocated*sizeof(char *));
900 }
901 positions[found++] = p1 = p2 + ZSTR_LEN(delim);
902 p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
903 } while (p2 != NULL);
904
905 to_return = limit + found;
906 /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
907 for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
908 ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
910 }
911 efree((void *)positions);
912 }
913#undef EXPLODE_ALLOC_STEP
914}
915/* }}} */
916
917/* {{{ Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned. */
919{
920 zend_string *str, *delim;
921 zend_long limit = ZEND_LONG_MAX; /* No limit */
922 zval tmp;
923
925 Z_PARAM_STR(delim)
926 Z_PARAM_STR(str)
928 Z_PARAM_LONG(limit)
930
931 if (ZSTR_LEN(delim) == 0) {
934 }
935
937
938 if (ZSTR_LEN(str) == 0) {
939 if (limit >= 0) {
940 ZVAL_EMPTY_STRING(&tmp);
942 }
943 return;
944 }
945
946 if (limit > 1) {
947 php_explode(delim, str, return_value, limit);
948 } else if (limit < 0) {
949 php_explode_negative_limit(delim, str, return_value, limit);
950 } else {
951 ZVAL_STR_COPY(&tmp, str);
953 }
954}
955/* }}} */
956
957/* {{{ php_implode */
959{
960 zval *tmp;
961 uint32_t numelems;
962 zend_string *str;
963 char *cptr;
964 size_t len = 0;
965 struct {
966 zend_string *str;
967 zend_long lval;
968 } *strings, *ptr;
969 ALLOCA_FLAG(use_heap)
970
971 numelems = zend_hash_num_elements(pieces);
972
973 if (numelems == 0) {
975 } else if (numelems == 1) {
976 /* loop to search the first not undefined element... */
977 ZEND_HASH_FOREACH_VAL(pieces, tmp) {
978 RETURN_STR(zval_get_string(tmp));
980 }
981
982 ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap);
983
985
986 ZEND_HASH_FOREACH_VAL(pieces, tmp) {
987 if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) {
988 ptr->str = Z_STR_P(tmp);
989 len += ZSTR_LEN(ptr->str);
990 ptr->lval = 0;
992 ptr++;
993 } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) {
994 zend_long val = Z_LVAL_P(tmp);
995
996 ptr->str = NULL;
997 ptr->lval = val;
998 ptr++;
999 if (val <= 0) {
1000 len++;
1001 }
1002 while (val) {
1003 val /= 10;
1004 len++;
1005 }
1006 } else {
1007 ptr->str = zval_get_string_func(tmp);
1008 len += ZSTR_LEN(ptr->str);
1009 ptr->lval = 1;
1011 ptr++;
1012 }
1014
1015 /* numelems cannot be 0, we checked above */
1016 str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
1017 GC_ADD_FLAGS(str, flags);
1018 cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
1019 *cptr = 0;
1020
1021 while (1) {
1022 ptr--;
1023 if (EXPECTED(ptr->str)) {
1024 cptr -= ZSTR_LEN(ptr->str);
1025 memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
1026 if (ptr->lval) {
1027 zend_string_release_ex(ptr->str, 0);
1028 }
1029 } else {
1030 char *oldPtr = cptr;
1031 char oldVal = *cptr;
1032 cptr = zend_print_long_to_buf(cptr, ptr->lval);
1033 *oldPtr = oldVal;
1034 }
1035
1036 if (ptr == strings) {
1037 break;
1038 }
1039
1040 cptr -= ZSTR_LEN(glue);
1041 memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
1042 }
1043
1044 free_alloca(strings, use_heap);
1045 RETURN_NEW_STR(str);
1046}
1047/* }}} */
1048
1049/* {{{ Joins array elements placing glue string between items and return one string */
1051{
1052 zend_string *arg1_str = NULL;
1053 HashTable *arg1_array = NULL;
1054 zend_array *pieces = NULL;
1055
1057 Z_PARAM_ARRAY_HT_OR_STR(arg1_array, arg1_str)
1061
1062 if (pieces == NULL) {
1063 if (arg1_array == NULL) {
1065 "%s(): If argument #1 ($separator) is of type string, "
1066 "argument #2 ($array) must be of type array, null given",
1068 );
1069 RETURN_THROWS();
1070 }
1071
1072 arg1_str = ZSTR_EMPTY_ALLOC();
1073 pieces = arg1_array;
1074 } else {
1075 if (arg1_str == NULL) {
1076 zend_argument_type_error(1, "must be of type string, array given");
1077 RETURN_THROWS();
1078 }
1079 }
1080
1081 php_implode(arg1_str, pieces, return_value);
1082}
1083/* }}} */
1084
1086{
1087 zval *pieces;
1088
1089 /* Manual parsing for more accurate error message. */
1090 if (!zend_parse_arg_array(arg1, &pieces, /* null_check */ false, /* or_object */ false)) { \
1091 zend_type_error(
1092 "%s(): If argument #1 ($separator) is of type string, "
1093 "argument #2 ($array) must be of type array, null given",
1095 );
1096 goto flf_clean; \
1097 }
1098
1100
1101 php_implode(str, Z_ARR_P(pieces), return_value);
1102
1103flf_clean:;
1104}
1105
1107{
1108 zval str_tmp;
1109 zend_string *str;
1110 zval *pieces;
1111
1112 Z_FLF_PARAM_STR(1, str, str_tmp);
1113 Z_FLF_PARAM_ARRAY_OR_NULL(2, pieces);
1114
1115 if (!pieces) {
1117 "%s(): If argument #1 ($separator) is of type string, "
1118 "argument #2 ($array) must be of type array, null given",
1120 );
1121 goto flf_clean;
1122 }
1123
1124 php_implode(str, Z_ARR_P(pieces), return_value);
1125
1126flf_clean:;
1127 Z_FLF_PARAM_FREE_STR(1, str_tmp);
1128}
1129
1130#define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1131
1132/* {{{ Tokenize a string */
1134{
1135 zend_string *str, *tok = NULL;
1136 char *token;
1137 char *token_end;
1138 char *p;
1139 char *pe;
1140 size_t skipped = 0;
1141
1143 Z_PARAM_STR(str)
1147
1148 if (!tok) {
1149 tok = str;
1150 } else {
1151 if (BG(strtok_string)) {
1152 zend_string_release(BG(strtok_string));
1153 }
1154 BG(strtok_string) = zend_string_copy(str);
1155 BG(strtok_last) = ZSTR_VAL(str);
1156 BG(strtok_len) = ZSTR_LEN(str);
1157 }
1158
1159 if (!BG(strtok_string)) {
1160 /* String to tokenize not set. */
1161 php_error_docref(NULL, E_WARNING, "Both arguments must be provided when starting tokenization");
1163 }
1164
1165 p = BG(strtok_last); /* Where we start to search */
1166 pe = ZSTR_VAL(BG(strtok_string)) + BG(strtok_len);
1167 if (p >= pe) {
1168 /* Reached the end of the string. */
1170 }
1171
1172 token = ZSTR_VAL(tok);
1173 token_end = token + ZSTR_LEN(tok);
1174
1175 while (token < token_end) {
1176 STRTOK_TABLE(token++) = 1;
1177 }
1178
1179 /* Skip leading delimiters */
1180 while (STRTOK_TABLE(p)) {
1181 if (++p >= pe) {
1182 /* no other chars left */
1183 goto return_false;
1184 }
1185 skipped++;
1186 }
1187
1188 /* We know at this place that *p is no delimiter, so skip it */
1189 while (++p < pe) {
1190 if (STRTOK_TABLE(p)) {
1191 goto return_token;
1192 }
1193 }
1194
1195 if (p - BG(strtok_last)) {
1196return_token:
1197 RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1198 BG(strtok_last) = p + 1;
1199 } else {
1200return_false:
1202 zend_string_release(BG(strtok_string));
1203 BG(strtok_string) = NULL;
1204 }
1205
1206 /* Restore table -- usually faster then memset'ing the table on every invocation */
1207 token = ZSTR_VAL(tok);
1208 while (token < token_end) {
1209 STRTOK_TABLE(token++) = 0;
1210 }
1211}
1212/* }}} */
1213
1214/* {{{ Makes a string uppercase */
1216{
1218
1222
1223 RETURN_STR(zend_string_toupper(arg));
1224}
1225/* }}} */
1226
1227/* {{{ Makes a string lowercase */
1229{
1230 zend_string *str;
1231
1233 Z_PARAM_STR(str)
1235
1236 RETURN_STR(zend_string_tolower(str));
1237}
1238/* }}} */
1239
1241{
1242 zend_string *str;
1243
1245 Z_PARAM_STR(str)
1247
1248 if (ZSTR_LEN(str) == 0) {
1250 RETURN_THROWS();
1251 }
1253 zend_argument_value_error(1, "must be composed only of alphanumeric ASCII characters");
1254 RETURN_THROWS();
1255 }
1256
1257 zend_string *incremented = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), /* persistent */ false);
1258 size_t position = ZSTR_LEN(str)-1;
1259 bool carry = false;
1260
1261 do {
1262 char c = ZSTR_VAL(incremented)[position];
1263 /* We know c is in ['a', 'z'], ['A', 'Z'], or ['0', '9'] range from zend_string_only_has_ascii_alphanumeric() */
1264 if (EXPECTED( c != 'z' && c != 'Z' && c != '9' )) {
1265 carry = false;
1266 ZSTR_VAL(incremented)[position]++;
1267 } else { /* if 'z', 'Z', or '9' */
1268 carry = true;
1269 if (c == '9') {
1270 ZSTR_VAL(incremented)[position] = '0';
1271 } else {
1272 ZSTR_VAL(incremented)[position] -= 25;
1273 }
1274 }
1275 } while (carry && position-- > 0);
1276
1277 if (UNEXPECTED(carry)) {
1278 zend_string *tmp = zend_string_alloc(ZSTR_LEN(incremented)+1, 0);
1279 memcpy(ZSTR_VAL(tmp) + 1, ZSTR_VAL(incremented), ZSTR_LEN(incremented));
1280 ZSTR_VAL(tmp)[ZSTR_LEN(incremented)+1] = '\0';
1281 switch (ZSTR_VAL(incremented)[0]) {
1282 case '0':
1283 ZSTR_VAL(tmp)[0] = '1';
1284 break;
1285 default:
1286 ZSTR_VAL(tmp)[0] = ZSTR_VAL(incremented)[0];
1287 break;
1288 }
1289 zend_string_release_ex(incremented, /* persistent */ false);
1290 RETURN_STR(tmp);
1291 }
1292 RETURN_STR(incremented);
1293}
1294
1295
1297{
1298 zend_string *str;
1299
1301 Z_PARAM_STR(str)
1303
1304 if (ZSTR_LEN(str) == 0) {
1306 RETURN_THROWS();
1307 }
1309 zend_argument_value_error(1, "must be composed only of alphanumeric ASCII characters");
1310 RETURN_THROWS();
1311 }
1312 if (ZSTR_LEN(str) >= 1 && ZSTR_VAL(str)[0] == '0') {
1313 zend_argument_value_error(1, "\"%s\" is out of decrement range", ZSTR_VAL(str));
1314 RETURN_THROWS();
1315 }
1316
1317 zend_string *decremented = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), /* persistent */ false);
1318 size_t position = ZSTR_LEN(str)-1;
1319 bool carry = false;
1320
1321 do {
1322 char c = ZSTR_VAL(decremented)[position];
1323 /* We know c is in ['a', 'z'], ['A', 'Z'], or ['0', '9'] range from zend_string_only_has_ascii_alphanumeric() */
1324 if (EXPECTED( c != 'a' && c != 'A' && c != '0' )) {
1325 carry = false;
1326 ZSTR_VAL(decremented)[position]--;
1327 } else { /* if 'a', 'A', or '0' */
1328 carry = true;
1329 if (c == '0') {
1330 ZSTR_VAL(decremented)[position] = '9';
1331 } else {
1332 ZSTR_VAL(decremented)[position] += 25;
1333 }
1334 }
1335 } while (carry && position-- > 0);
1336
1337 if (UNEXPECTED(carry || (ZSTR_VAL(decremented)[0] == '0' && ZSTR_LEN(decremented) > 1))) {
1338 if (ZSTR_LEN(decremented) == 1) {
1339 zend_string_release_ex(decremented, /* persistent */ false);
1340 zend_argument_value_error(1, "\"%s\" is out of decrement range", ZSTR_VAL(str));
1341 RETURN_THROWS();
1342 }
1343 zend_string *tmp = zend_string_alloc(ZSTR_LEN(decremented) - 1, 0);
1344 memcpy(ZSTR_VAL(tmp), ZSTR_VAL(decremented) + 1, ZSTR_LEN(decremented) - 1);
1345 ZSTR_VAL(tmp)[ZSTR_LEN(decremented) - 1] = '\0';
1346 zend_string_release_ex(decremented, /* persistent */ false);
1347 RETURN_STR(tmp);
1348 }
1349 RETURN_STR(decremented);
1350}
1351
1352#if defined(PHP_WIN32)
1353static bool _is_basename_start(const char *start, const char *pos)
1354{
1355 if (pos - start >= 1
1356 && *(pos-1) != '/'
1357 && *(pos-1) != '\\') {
1358 if (pos - start == 1) {
1359 return 1;
1360 } else if (*(pos-2) == '/' || *(pos-2) == '\\') {
1361 return 1;
1362 } else if (*(pos-2) == ':'
1363 && _is_basename_start(start, pos - 2)) {
1364 return 1;
1365 }
1366 }
1367 return 0;
1368}
1369#endif
1370
1371/* {{{ php_basename */
1372PHPAPI zend_string *php_basename(const char *s, size_t len, const char *suffix, size_t suffix_len)
1373{
1374 const char *basename_start;
1375 const char *basename_end;
1376
1377 if (CG(ascii_compatible_locale)) {
1378 basename_end = s + len - 1;
1379
1380 /* Strip trailing slashes */
1381 while (basename_end >= s
1382#ifdef PHP_WIN32
1383 && (*basename_end == '/'
1384 || *basename_end == '\\'
1385 || (*basename_end == ':'
1386 && _is_basename_start(s, basename_end)))) {
1387#else
1388 && *basename_end == '/') {
1389#endif
1390 basename_end--;
1391 }
1392 if (basename_end < s) {
1393 return ZSTR_EMPTY_ALLOC();
1394 }
1395
1396 /* Extract filename */
1397 basename_start = basename_end;
1398 basename_end++;
1399 while (basename_start > s
1400#ifdef PHP_WIN32
1401 && *(basename_start-1) != '/'
1402 && *(basename_start-1) != '\\') {
1403
1404 if (*(basename_start-1) == ':' &&
1405 _is_basename_start(s, basename_start - 1)) {
1406 break;
1407 }
1408#else
1409 && *(basename_start-1) != '/') {
1410#endif
1411 basename_start--;
1412 }
1413 } else {
1414 /* State 0 is directly after a directory separator (or at the start of the string).
1415 * State 1 is everything else. */
1416 int state = 0;
1417
1418 basename_start = s;
1419 basename_end = s;
1420 while (len > 0) {
1421 int inc_len = (*s == '\0' ? 1 : php_mblen(s, len));
1422
1423 switch (inc_len) {
1424 case 0:
1425 goto quit_loop;
1426 case 1:
1427#ifdef PHP_WIN32
1428 if (*s == '/' || *s == '\\') {
1429#else
1430 if (*s == '/') {
1431#endif
1432 if (state == 1) {
1433 state = 0;
1434 basename_end = s;
1435 }
1436#ifdef PHP_WIN32
1437 /* Catch relative paths in c:file.txt style. They're not to confuse
1438 with the NTFS streams. This part ensures also, that no drive
1439 letter traversing happens. */
1440 } else if ((*s == ':' && (s - basename_start == 1))) {
1441 if (state == 0) {
1442 basename_start = s;
1443 state = 1;
1444 } else {
1445 basename_end = s;
1446 state = 0;
1447 }
1448#endif
1449 } else {
1450 if (state == 0) {
1451 basename_start = s;
1452 state = 1;
1453 }
1454 }
1455 break;
1456 default:
1457 if (inc_len < 0) {
1458 /* If character is invalid, treat it like other non-significant characters. */
1459 inc_len = 1;
1460 php_mb_reset();
1461 }
1462 if (state == 0) {
1463 basename_start = s;
1464 state = 1;
1465 }
1466 break;
1467 }
1468 s += inc_len;
1469 len -= inc_len;
1470 }
1471
1472quit_loop:
1473 if (state == 1) {
1474 basename_end = s;
1475 }
1476 }
1477
1478 if (suffix != NULL && suffix_len < (size_t)(basename_end - basename_start) &&
1479 memcmp(basename_end - suffix_len, suffix, suffix_len) == 0) {
1480 basename_end -= suffix_len;
1481 }
1482
1483 return zend_string_init(basename_start, basename_end - basename_start, 0);
1484}
1485/* }}} */
1486
1487/* {{{ Returns the filename component of the path */
1489{
1490 char *string, *suffix = NULL;
1491 size_t string_len, suffix_len = 0;
1492
1494 Z_PARAM_STRING(string, string_len)
1496 Z_PARAM_STRING(suffix, suffix_len)
1498
1499 RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1500}
1501/* }}} */
1502
1503/* {{{ php_dirname
1504 Returns directory name component of path */
1505PHPAPI size_t php_dirname(char *path, size_t len)
1506{
1507 return zend_dirname(path, len);
1508}
1509/* }}} */
1510
1511static inline void _zend_dirname(zval *return_value, zend_string *str, zend_long levels)
1512{
1514
1515 ret = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
1516
1517 if (levels == 1) {
1518 /* Default case */
1519#ifdef PHP_WIN32
1521#else
1523#endif
1524 } else if (levels < 1) {
1525 zend_argument_value_error(2, "must be greater than or equal to 1");
1526 zend_string_efree(ret);
1527 RETURN_THROWS();
1528 } else {
1529 /* Some levels up */
1530 size_t str_len;
1531 do {
1532#ifdef PHP_WIN32
1534#else
1535 ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1536#endif
1537 } while (ZSTR_LEN(ret) < str_len && --levels);
1538 }
1539
1541}
1542
1543/* {{{ Returns the directory name component of the path */
1545{
1546 zend_string *str;
1547 zend_long levels = 1;
1548
1550 Z_PARAM_STR(str)
1552 Z_PARAM_LONG(levels)
1554
1555 _zend_dirname(return_value, str, levels);
1556}
1557/* }}} */
1558
1560{
1561 zval str_tmp;
1562 zend_string *str;
1563
1564 Z_FLF_PARAM_STR(1, str, str_tmp);
1565
1566 _zend_dirname(return_value, str, 1);
1567
1568flf_clean:
1569 Z_FLF_PARAM_FREE_STR(1, str_tmp);
1570}
1571
1573{
1574 zval str_tmp;
1575 zend_string *str;
1576 zend_long levels;
1577
1578 Z_FLF_PARAM_STR(1, str, str_tmp);
1579 Z_FLF_PARAM_LONG(2, levels);
1580
1581 _zend_dirname(return_value, str, levels);
1582
1583flf_clean:
1584 Z_FLF_PARAM_FREE_STR(1, str_tmp);
1585}
1586
1587/* {{{ Returns information about a certain string */
1589{
1590 zval tmp;
1591 char *path, *dirname;
1592 size_t path_len;
1593 bool have_basename;
1595 zend_string *ret = NULL;
1596
1598 Z_PARAM_STRING(path, path_len)
1600 Z_PARAM_LONG(opt)
1602
1603 have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
1604
1605 array_init(&tmp);
1606
1608 dirname = estrndup(path, path_len);
1609 php_dirname(dirname, path_len);
1610 if (*dirname) {
1611 add_assoc_string(&tmp, "dirname", dirname);
1612 }
1613 efree(dirname);
1614 }
1615
1616 if (have_basename) {
1617 ret = php_basename(path, path_len, NULL, 0);
1618 add_assoc_str(&tmp, "basename", zend_string_copy(ret));
1619 }
1620
1622 const char *p;
1623 ptrdiff_t idx;
1624
1625 if (!have_basename) {
1626 ret = php_basename(path, path_len, NULL, 0);
1627 }
1628
1629 p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1630
1631 if (p) {
1632 idx = p - ZSTR_VAL(ret);
1633 add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
1634 }
1635 }
1636
1638 const char *p;
1639 ptrdiff_t idx;
1640
1641 /* Have we already looked up the basename? */
1642 if (!have_basename && !ret) {
1643 ret = php_basename(path, path_len, NULL, 0);
1644 }
1645
1646 p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1647
1648 idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
1649 add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
1650 }
1651
1652 if (ret) {
1654 }
1655
1656 if (opt == PHP_PATHINFO_ALL) {
1657 RETURN_COPY_VALUE(&tmp);
1658 } else {
1659 zval *element;
1660 if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
1661 RETVAL_COPY_DEREF(element);
1662 } else {
1664 }
1665 zval_ptr_dtor(&tmp);
1666 }
1667}
1668/* }}} */
1669
1670/* {{{ php_stristr
1671 case insensitive strstr */
1672PHPAPI char *php_stristr(const char *s, const char *t, size_t s_len, size_t t_len)
1673{
1674 return (char*)php_memnistr(s, t, t_len, s + s_len);
1675}
1676/* }}} */
1677
1678static size_t php_strspn_strcspn_common(const char *haystack, const char *characters, const char *haystack_end, const char *characters_end, bool must_match)
1679{
1680 /* Fast path for short strings.
1681 * The table lookup cannot be faster in this case because we not only have to compare, but also build the table.
1682 * We only compare in this case.
1683 * Empirically tested that the table lookup approach is only beneficial if characters is longer than 1 character. */
1684 if (characters_end - characters == 1) {
1685 const char *ptr = haystack;
1686 while (ptr < haystack_end && (*ptr == *characters) == must_match) {
1687 ptr++;
1688 }
1689 return ptr - haystack;
1690 }
1691
1692 /* Every character in characters will set a boolean in this lookup table.
1693 * We'll use the lookup table as a fast lookup for the characters in characters while looping over haystack. */
1694 bool table[256];
1695 /* Use multiple small memsets to inline the memset with intrinsics, trick learned from glibc. */
1696 memset(table, 0, 64);
1697 memset(table + 64, 0, 64);
1698 memset(table + 128, 0, 64);
1699 memset(table + 192, 0, 64);
1700
1701 while (characters < characters_end) {
1702 table[(unsigned char) *characters] = true;
1703 characters++;
1704 }
1705
1706 const char *ptr = haystack;
1707 while (ptr < haystack_end && table[(unsigned char) *ptr] == must_match) {
1708 ptr++;
1709 }
1710
1711 return ptr - haystack;
1712}
1713
1714/* {{{ php_strspn */
1715PHPAPI size_t php_strspn(const char *haystack, const char *characters, const char *haystack_end, const char *characters_end)
1716{
1717 return php_strspn_strcspn_common(haystack, characters, haystack_end, characters_end, true);
1718}
1719/* }}} */
1720
1721/* {{{ php_strcspn */
1722PHPAPI size_t php_strcspn(const char *haystack, const char *characters, const char *haystack_end, const char *characters_end)
1723{
1724 return php_strspn_strcspn_common(haystack, characters, haystack_end, characters_end, false);
1725}
1726/* }}} */
1727
1728/* {{{ Finds first occurrence of a string within another, case insensitive */
1730{
1731 zend_string *haystack, *needle;
1732 const char *found = NULL;
1733 size_t found_offset;
1734 bool part = 0;
1735
1737 Z_PARAM_STR(haystack)
1738 Z_PARAM_STR(needle)
1740 Z_PARAM_BOOL(part)
1742
1743 found = php_stristr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(haystack), ZSTR_LEN(needle));
1744
1745 if (UNEXPECTED(!found)) {
1747 }
1748 found_offset = found - ZSTR_VAL(haystack);
1749 if (part) {
1750 RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1751 }
1752 RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1753}
1754/* }}} */
1755
1756static inline void _zend_strstr(zval *return_value, zend_string *haystack, zend_string *needle, bool part)
1757{
1758 const char *found = NULL;
1759 zend_long found_offset;
1760
1761 found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1762
1763 if (UNEXPECTED(!found)) {
1765 }
1766 found_offset = found - ZSTR_VAL(haystack);
1767 if (part) {
1768 RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1769 }
1770 RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1771}
1772
1773/* {{{ Finds first occurrence of a string within another */
1775{
1776 zend_string *haystack, *needle;
1777 bool part = 0;
1778
1780 Z_PARAM_STR(haystack)
1781 Z_PARAM_STR(needle)
1783 Z_PARAM_BOOL(part)
1785
1786 _zend_strstr(return_value, haystack, needle, part);
1787}
1788/* }}} */
1789
1791{
1792 zval haystack_tmp, needle_tmp;
1793 zend_string *haystack, *needle;
1794
1795 Z_FLF_PARAM_STR(1, haystack, haystack_tmp);
1796 Z_FLF_PARAM_STR(2, needle, needle_tmp);
1797
1798 _zend_strstr(return_value, haystack, needle, /* part */ false);
1799
1800flf_clean:
1801 Z_FLF_PARAM_FREE_STR(1, haystack_tmp);
1802 Z_FLF_PARAM_FREE_STR(2, needle_tmp);
1803}
1804
1806{
1807 zval haystack_tmp, needle_tmp;
1808 zend_string *haystack, *needle;
1809 bool part;
1810
1811 Z_FLF_PARAM_STR(1, haystack, haystack_tmp);
1812 Z_FLF_PARAM_STR(2, needle, needle_tmp);
1813 Z_FLF_PARAM_BOOL(3, part);
1814
1815 _zend_strstr(return_value, haystack, needle, part);
1816
1817flf_clean:
1818 Z_FLF_PARAM_FREE_STR(1, haystack_tmp);
1819 Z_FLF_PARAM_FREE_STR(2, needle_tmp);
1820}
1821
1822/* {{{ Checks if a string contains another */
1824{
1825 zend_string *haystack, *needle;
1826
1828 Z_PARAM_STR(haystack)
1829 Z_PARAM_STR(needle)
1831
1832 RETURN_BOOL(php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack)));
1833}
1834/* }}} */
1835
1837{
1838 zval haystack_tmp, needle_tmp;
1839 zend_string *haystack, *needle;
1840
1841 Z_FLF_PARAM_STR(1, haystack, haystack_tmp);
1842 Z_FLF_PARAM_STR(2, needle, needle_tmp);
1843
1844 RETVAL_BOOL(php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack)));
1845
1846flf_clean:
1847 Z_FLF_PARAM_FREE_STR(1, haystack_tmp);
1848 Z_FLF_PARAM_FREE_STR(2, needle_tmp);
1849}
1850
1851/* {{{ Checks if haystack starts with needle */
1853{
1854 zend_string *haystack, *needle;
1855
1857 Z_PARAM_STR(haystack)
1858 Z_PARAM_STR(needle)
1860
1861 RETURN_BOOL(zend_string_starts_with(haystack, needle));
1862}
1863/* }}} */
1864
1866{
1867 zval haystack_tmp, needle_tmp;
1868 zend_string *haystack, *needle;
1869
1870 Z_FLF_PARAM_STR(1, haystack, haystack_tmp);
1871 Z_FLF_PARAM_STR(2, needle, needle_tmp);
1872
1873 RETVAL_BOOL(zend_string_starts_with(haystack, needle));
1874
1875flf_clean:
1876 Z_FLF_PARAM_FREE_STR(1, haystack_tmp);
1877 Z_FLF_PARAM_FREE_STR(2, needle_tmp);
1878}
1879
1880/* {{{ Checks if haystack ends with needle */
1882{
1883 zend_string *haystack, *needle;
1884
1886 Z_PARAM_STR(haystack)
1887 Z_PARAM_STR(needle)
1889
1890 if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
1892 }
1893
1894 RETURN_BOOL(memcmp(
1895 ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - ZSTR_LEN(needle),
1896 ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0);
1897}
1898/* }}} */
1899
1900static inline void _zend_strpos(zval *return_value, zend_string *haystack, zend_string *needle, zend_long offset)
1901{
1902 const char *found = NULL;
1903
1904 if (offset < 0) {
1905 offset += (zend_long)ZSTR_LEN(haystack);
1906 }
1907 if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1908 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1909 RETURN_THROWS();
1910 }
1911
1912 found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1913 ZSTR_VAL(needle), ZSTR_LEN(needle),
1914 ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1915
1916 if (UNEXPECTED(!found)) {
1918 }
1919 RETURN_LONG(found - ZSTR_VAL(haystack));
1920}
1921
1922/* {{{ Finds position of first occurrence of a string within another */
1924{
1925 zend_string *haystack, *needle;
1926 zend_long offset = 0;
1927
1929 Z_PARAM_STR(haystack)
1930 Z_PARAM_STR(needle)
1934
1935 _zend_strpos(return_value, haystack, needle, offset);
1936}
1937/* }}} */
1938
1940{
1941 zval haystack_tmp, needle_tmp;
1942 zend_string *haystack, *needle;
1943
1944 Z_FLF_PARAM_STR(1, haystack, haystack_tmp);
1945 Z_FLF_PARAM_STR(2, needle, needle_tmp);
1946
1947 _zend_strpos(return_value, haystack, needle, 0);
1948
1949flf_clean:
1950 Z_FLF_PARAM_FREE_STR(1, haystack_tmp);
1951 Z_FLF_PARAM_FREE_STR(2, needle_tmp);
1952}
1953
1955{
1956 zval haystack_tmp, needle_tmp;
1957 zend_string *haystack, *needle;
1959
1960 Z_FLF_PARAM_STR(1, haystack, haystack_tmp);
1961 Z_FLF_PARAM_STR(2, needle, needle_tmp);
1963
1964 _zend_strpos(return_value, haystack, needle, offset);
1965
1966flf_clean:
1967 Z_FLF_PARAM_FREE_STR(1, haystack_tmp);
1968 Z_FLF_PARAM_FREE_STR(2, needle_tmp);
1969}
1970
1971/* {{{ Finds position of first occurrence of a string within another, case insensitive */
1973{
1974 const char *found = NULL;
1975 zend_string *haystack, *needle;
1976 zend_long offset = 0;
1977
1979 Z_PARAM_STR(haystack)
1980 Z_PARAM_STR(needle)
1984
1985 if (offset < 0) {
1986 offset += (zend_long)ZSTR_LEN(haystack);
1987 }
1988 if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1989 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1990 RETURN_THROWS();
1991 }
1992
1993 found = (char*)php_memnistr(ZSTR_VAL(haystack) + offset,
1994 ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1995
1996 if (UNEXPECTED(!found)) {
1998 }
1999 RETURN_LONG(found - ZSTR_VAL(haystack));
2000}
2001/* }}} */
2002
2003/* {{{ Finds position of last occurrence of a string within another string */
2005{
2006 zend_string *needle;
2007 zend_string *haystack;
2008 zend_long offset = 0;
2009 const char *p, *e, *found;
2010
2012 Z_PARAM_STR(haystack)
2013 Z_PARAM_STR(needle)
2017
2018 if (offset >= 0) {
2019 if ((size_t)offset > ZSTR_LEN(haystack)) {
2020 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2021 RETURN_THROWS();
2022 }
2023 p = ZSTR_VAL(haystack) + (size_t)offset;
2024 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2025 } else {
2026 if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2027 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2028 RETURN_THROWS();
2029 }
2030
2031 p = ZSTR_VAL(haystack);
2032 if ((size_t)-offset < ZSTR_LEN(needle)) {
2033 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2034 } else {
2035 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2036 }
2037 }
2038
2039 found = zend_memnrstr(p, ZSTR_VAL(needle), ZSTR_LEN(needle), e);
2040
2041 if (UNEXPECTED(!found)) {
2043 }
2044 RETURN_LONG(found - ZSTR_VAL(haystack));
2045}
2046/* }}} */
2047
2048/* {{{ Finds position of last occurrence of a string within another string */
2050{
2051 zend_string *needle;
2052 zend_string *haystack;
2053 zend_long offset = 0;
2054 const char *p, *e, *found;
2055 zend_string *needle_dup, *haystack_dup;
2056
2058 Z_PARAM_STR(haystack)
2059 Z_PARAM_STR(needle)
2063
2064 if (ZSTR_LEN(needle) == 1) {
2065 /* Single character search can shortcut memcmps
2066 Can also avoid tolower emallocs */
2067 char lowered;
2068 if (offset >= 0) {
2069 if ((size_t)offset > ZSTR_LEN(haystack)) {
2070 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2071 RETURN_THROWS();
2072 }
2073 p = ZSTR_VAL(haystack) + (size_t)offset;
2074 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - 1;
2075 } else {
2076 p = ZSTR_VAL(haystack);
2077 if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2078 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2079 RETURN_THROWS();
2080 }
2081 e = ZSTR_VAL(haystack) + (ZSTR_LEN(haystack) + (size_t)offset);
2082 }
2083 lowered = zend_tolower_ascii(*ZSTR_VAL(needle));
2084 while (e >= p) {
2085 if (zend_tolower_ascii(*e) == lowered) {
2086 RETURN_LONG(e - p + (offset > 0 ? offset : 0));
2087 }
2088 e--;
2089 }
2091 }
2092
2093 haystack_dup = zend_string_tolower(haystack);
2094 if (offset >= 0) {
2095 if ((size_t)offset > ZSTR_LEN(haystack)) {
2096 zend_string_release_ex(haystack_dup, 0);
2097 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2098 RETURN_THROWS();
2099 }
2100 p = ZSTR_VAL(haystack_dup) + offset;
2101 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2102 } else {
2103 if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2104 zend_string_release_ex(haystack_dup, 0);
2105 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2106 RETURN_THROWS();
2107 }
2108
2109 p = ZSTR_VAL(haystack_dup);
2110 if ((size_t)-offset < ZSTR_LEN(needle)) {
2111 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2112 } else {
2113 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2114 }
2115 }
2116
2117 needle_dup = zend_string_tolower(needle);
2118 if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) {
2119 RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2120 zend_string_release_ex(needle_dup, 0);
2121 zend_string_release_ex(haystack_dup, 0);
2122 } else {
2123 zend_string_release_ex(needle_dup, 0);
2124 zend_string_release_ex(haystack_dup, 0);
2126 }
2127}
2128/* }}} */
2129
2130/* {{{ Finds the last occurrence of a character in a string within another */
2132{
2133 zend_string *haystack, *needle;
2134 const char *found = NULL;
2135 zend_long found_offset;
2136 bool part = 0;
2137
2139 Z_PARAM_STR(haystack)
2140 Z_PARAM_STR(needle)
2142 Z_PARAM_BOOL(part)
2144
2145 found = zend_memrchr(ZSTR_VAL(haystack), *ZSTR_VAL(needle), ZSTR_LEN(haystack));
2146 if (UNEXPECTED(!found)) {
2148 }
2149 found_offset = found - ZSTR_VAL(haystack);
2150 if (part) {
2151 RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
2152 }
2153 RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
2154}
2155/* }}} */
2156
2157/* {{{ php_chunk_split */
2158static zend_string *php_chunk_split(const char *src, size_t srclen, const char *end, size_t endlen, size_t chunklen)
2159{
2160 char *q;
2161 const char *p;
2162 size_t chunks;
2163 size_t restlen;
2164 zend_string *dest;
2165
2166 chunks = srclen / chunklen;
2167 restlen = srclen - chunks * chunklen; /* srclen % chunklen */
2168 if (restlen) {
2169 /* We want chunks to be rounded up rather than rounded down.
2170 * Increment can't overflow because chunks <= SIZE_MAX/2 at this point. */
2171 chunks++;
2172 }
2173
2174 dest = zend_string_safe_alloc(chunks, endlen, srclen, 0);
2175
2176 for (p = src, q = ZSTR_VAL(dest); p < (src + srclen - chunklen + 1); ) {
2177 q = zend_mempcpy(q, p, chunklen);
2178 q = zend_mempcpy(q, end, endlen);
2179 p += chunklen;
2180 }
2181
2182 if (restlen) {
2183 q = zend_mempcpy(q, p, restlen);
2184 q = zend_mempcpy(q, end, endlen);
2185 }
2186
2187 *q = '\0';
2188 ZEND_ASSERT(q - ZSTR_VAL(dest) == ZSTR_LEN(dest));
2189
2190 return dest;
2191}
2192/* }}} */
2193
2194/* {{{ Returns split line */
2196{
2197 zend_string *str;
2198 char *end = "\r\n";
2199 size_t endlen = 2;
2200 zend_long chunklen = 76;
2202
2204 Z_PARAM_STR(str)
2206 Z_PARAM_LONG(chunklen)
2207 Z_PARAM_STRING(end, endlen)
2209
2210 if (chunklen <= 0) {
2211 zend_argument_value_error(2, "must be greater than 0");
2212 RETURN_THROWS();
2213 }
2214
2215 if ((size_t)chunklen > ZSTR_LEN(str)) {
2216 /* to maintain BC, we must return original string + ending */
2217 result = zend_string_safe_alloc(ZSTR_LEN(str), 1, endlen, 0);
2218 memcpy(ZSTR_VAL(result), ZSTR_VAL(str), ZSTR_LEN(str));
2219 memcpy(ZSTR_VAL(result) + ZSTR_LEN(str), end, endlen);
2220 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2222 }
2223
2224 if (!ZSTR_LEN(str)) {
2226 }
2227
2228 result = php_chunk_split(ZSTR_VAL(str), ZSTR_LEN(str), end, endlen, (size_t)chunklen);
2229
2231}
2232/* }}} */
2233
2234static inline void _zend_substr(zval *return_value, zend_string *str, zend_long f, bool len_is_null, zend_long l)
2235{
2236 if (f < 0) {
2237 /* if "from" position is negative, count start position from the end
2238 * of the string
2239 */
2240 if (-(size_t)f > ZSTR_LEN(str)) {
2241 f = 0;
2242 } else {
2243 f = (zend_long)ZSTR_LEN(str) + f;
2244 }
2245 } else if ((size_t)f > ZSTR_LEN(str)) {
2247 }
2248
2249 if (!len_is_null) {
2250 if (l < 0) {
2251 /* if "length" position is negative, set it to the length
2252 * needed to stop that many chars from the end of the string
2253 */
2254 if (-(size_t)l > ZSTR_LEN(str) - (size_t)f) {
2255 l = 0;
2256 } else {
2257 l = (zend_long)ZSTR_LEN(str) - f + l;
2258 }
2259 } else if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
2260 l = (zend_long)ZSTR_LEN(str) - f;
2261 }
2262 } else {
2263 l = (zend_long)ZSTR_LEN(str) - f;
2264 }
2265
2266 if (l == ZSTR_LEN(str)) {
2267 RETURN_STR_COPY(str);
2268 } else {
2269 RETURN_STRINGL_FAST(ZSTR_VAL(str) + f, l);
2270 }
2271}
2272
2273/* {{{ Returns part of a string */
2275{
2276 zend_string *str;
2277 zend_long l = 0, f;
2278 bool len_is_null = 1;
2279
2281 Z_PARAM_STR(str)
2282 Z_PARAM_LONG(f)
2284 Z_PARAM_LONG_OR_NULL(l, len_is_null)
2286
2287 _zend_substr(return_value, str, f, len_is_null, l);
2288}
2289/* }}} */
2290
2292{
2293 zval str_tmp;
2294 zend_string *str;
2295 zend_long f;
2296
2297 Z_FLF_PARAM_STR(1, str, str_tmp);
2298 Z_FLF_PARAM_LONG(2, f);
2299
2300 _zend_substr(return_value, str, f, /* len_is_null */ true, 0);
2301
2302flf_clean:
2303 Z_FLF_PARAM_FREE_STR(1, str_tmp);
2304}
2305
2307{
2308 zval str_tmp;
2309 zend_string *str;
2310 zend_long f, l;
2311 bool len_is_null;
2312
2313 Z_FLF_PARAM_STR(1, str, str_tmp);
2314 Z_FLF_PARAM_LONG(2, f);
2315 Z_FLF_PARAM_LONG_OR_NULL(3, len_is_null, l);
2316
2317 _zend_substr(return_value, str, f, len_is_null, l);
2318
2319flf_clean:
2320 Z_FLF_PARAM_FREE_STR(1, str_tmp);
2321}
2322
2323/* {{{ Replaces part of a string with another string */
2325{
2326 zend_string *str, *repl_str;
2327 HashTable *str_ht, *repl_ht;
2328 HashTable *from_ht;
2329 zend_long from_long;
2330 HashTable *len_ht = NULL;
2331 zend_long len_long;
2332 bool len_is_null = 1;
2333 zend_long l = 0;
2334 zend_long f;
2336 HashPosition from_idx, repl_idx, len_idx;
2337 zval *tmp_str = NULL, *tmp_repl, *tmp_from = NULL, *tmp_len= NULL;
2338
2340 Z_PARAM_ARRAY_HT_OR_STR(str_ht, str)
2341 Z_PARAM_ARRAY_HT_OR_STR(repl_ht, repl_str)
2342 Z_PARAM_ARRAY_HT_OR_LONG(from_ht, from_long)
2344 Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(len_ht, len_long, len_is_null)
2346
2347 if (len_is_null) {
2348 if (str) {
2349 l = ZSTR_LEN(str);
2350 }
2351 } else if (!len_ht) {
2352 l = len_long;
2353 }
2354
2355 if (str) {
2356 if (from_ht) {
2357 zend_argument_type_error(3, "cannot be an array when working on a single string");
2358 RETURN_THROWS();
2359 }
2360 if (len_ht) {
2361 zend_argument_type_error(4, "cannot be an array when working on a single string");
2362 RETURN_THROWS();
2363 }
2364
2365 f = from_long;
2366
2367 /* if "from" position is negative, count start position from the end
2368 * of the string
2369 */
2370 if (f < 0) {
2371 f = (zend_long)ZSTR_LEN(str) + f;
2372 if (f < 0) {
2373 f = 0;
2374 }
2375 } else if ((size_t)f > ZSTR_LEN(str)) {
2376 f = ZSTR_LEN(str);
2377 }
2378 /* if "length" position is negative, set it to the length
2379 * needed to stop that many chars from the end of the string
2380 */
2381 if (l < 0) {
2382 l = ((zend_long)ZSTR_LEN(str) - f) + l;
2383 if (l < 0) {
2384 l = 0;
2385 }
2386 }
2387
2388 if ((size_t)l > ZSTR_LEN(str)) {
2389 l = ZSTR_LEN(str);
2390 }
2391
2392 if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2393 l = ZSTR_LEN(str) - f;
2394 }
2395
2396 zend_string *tmp_repl_str = NULL;
2397 if (repl_ht) {
2398 repl_idx = 0;
2399 if (HT_IS_PACKED(repl_ht)) {
2400 while (repl_idx < repl_ht->nNumUsed) {
2401 tmp_repl = &repl_ht->arPacked[repl_idx];
2402 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2403 break;
2404 }
2405 repl_idx++;
2406 }
2407 } else {
2408 while (repl_idx < repl_ht->nNumUsed) {
2409 tmp_repl = &repl_ht->arData[repl_idx].val;
2410 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2411 break;
2412 }
2413 repl_idx++;
2414 }
2415 }
2416 if (repl_idx < repl_ht->nNumUsed) {
2417 repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2418 } else {
2419 repl_str = STR_EMPTY_ALLOC();
2420 }
2421 }
2422
2423 result = zend_string_safe_alloc(1, ZSTR_LEN(str) - l + ZSTR_LEN(repl_str), 0, 0);
2424
2425 memcpy(ZSTR_VAL(result), ZSTR_VAL(str), f);
2426 if (ZSTR_LEN(repl_str)) {
2427 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2428 }
2429 memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(str) + f + l, ZSTR_LEN(str) - f - l);
2430 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2431 zend_tmp_string_release(tmp_repl_str);
2433 } else { /* str is array of strings */
2434 zend_string *str_index = NULL;
2435 size_t result_len;
2436 zend_ulong num_index;
2437
2438 /* TODO
2439 if (!len_is_null && from_ht) {
2440 if (zend_hash_num_elements(from_ht) != zend_hash_num_elements(len_ht)) {
2441 php_error_docref(NULL, E_WARNING, "'start' and 'length' should have the same number of elements");
2442 RETURN_STR_COPY(str);
2443 }
2444 }
2445 */
2446
2448
2449 from_idx = len_idx = repl_idx = 0;
2450
2451 ZEND_HASH_FOREACH_KEY_VAL(str_ht, num_index, str_index, tmp_str) {
2452 zend_string *tmp_orig_str;
2453 zend_string *orig_str = zval_get_tmp_string(tmp_str, &tmp_orig_str);
2454
2455 if (from_ht) {
2456 if (HT_IS_PACKED(from_ht)) {
2457 while (from_idx < from_ht->nNumUsed) {
2458 tmp_from = &from_ht->arPacked[from_idx];
2459 if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2460 break;
2461 }
2462 from_idx++;
2463 }
2464 } else {
2465 while (from_idx < from_ht->nNumUsed) {
2466 tmp_from = &from_ht->arData[from_idx].val;
2467 if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2468 break;
2469 }
2470 from_idx++;
2471 }
2472 }
2473 if (from_idx < from_ht->nNumUsed) {
2474 f = zval_get_long(tmp_from);
2475
2476 if (f < 0) {
2477 f = (zend_long)ZSTR_LEN(orig_str) + f;
2478 if (f < 0) {
2479 f = 0;
2480 }
2481 } else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2482 f = ZSTR_LEN(orig_str);
2483 }
2484 from_idx++;
2485 } else {
2486 f = 0;
2487 }
2488 } else {
2489 f = from_long;
2490 if (f < 0) {
2491 f = (zend_long)ZSTR_LEN(orig_str) + f;
2492 if (f < 0) {
2493 f = 0;
2494 }
2495 } else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2496 f = ZSTR_LEN(orig_str);
2497 }
2498 }
2499
2500 if (len_ht) {
2501 if (HT_IS_PACKED(len_ht)) {
2502 while (len_idx < len_ht->nNumUsed) {
2503 tmp_len = &len_ht->arPacked[len_idx];
2504 if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2505 break;
2506 }
2507 len_idx++;
2508 }
2509 } else {
2510 while (len_idx < len_ht->nNumUsed) {
2511 tmp_len = &len_ht->arData[len_idx].val;
2512 if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2513 break;
2514 }
2515 len_idx++;
2516 }
2517 }
2518 if (len_idx < len_ht->nNumUsed) {
2519 l = zval_get_long(tmp_len);
2520 len_idx++;
2521 } else {
2522 l = ZSTR_LEN(orig_str);
2523 }
2524 } else if (!len_is_null) {
2525 l = len_long;
2526 } else {
2527 l = ZSTR_LEN(orig_str);
2528 }
2529
2530 if (l < 0) {
2531 l = (ZSTR_LEN(orig_str) - f) + l;
2532 if (l < 0) {
2533 l = 0;
2534 }
2535 }
2536
2537 ZEND_ASSERT(0 <= f && f <= ZEND_LONG_MAX);
2538 ZEND_ASSERT(0 <= l && l <= ZEND_LONG_MAX);
2539 if (((size_t) f + l) > ZSTR_LEN(orig_str)) {
2540 l = ZSTR_LEN(orig_str) - f;
2541 }
2542
2543 result_len = ZSTR_LEN(orig_str) - l;
2544
2545 if (repl_ht) {
2546 if (HT_IS_PACKED(repl_ht)) {
2547 while (repl_idx < repl_ht->nNumUsed) {
2548 tmp_repl = &repl_ht->arPacked[repl_idx];
2549 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2550 break;
2551 }
2552 repl_idx++;
2553 }
2554 } else {
2555 while (repl_idx < repl_ht->nNumUsed) {
2556 tmp_repl = &repl_ht->arData[repl_idx].val;
2557 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2558 break;
2559 }
2560 repl_idx++;
2561 }
2562 }
2563 if (repl_idx < repl_ht->nNumUsed) {
2564 zend_string *tmp_repl_str;
2565 zend_string *repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2566
2567 result_len += ZSTR_LEN(repl_str);
2568 repl_idx++;
2569 result = zend_string_safe_alloc(1, result_len, 0, 0);
2570
2571 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2572 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2573 memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2574 zend_tmp_string_release(tmp_repl_str);
2575 } else {
2576 result = zend_string_safe_alloc(1, result_len, 0, 0);
2577
2578 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2579 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2580 }
2581 } else {
2582 result_len += ZSTR_LEN(repl_str);
2583
2584 result = zend_string_safe_alloc(1, result_len, 0, 0);
2585
2586 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2587 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2588 memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2589 }
2590
2591 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2592
2593 if (str_index) {
2594 zval tmp;
2595
2596 ZVAL_NEW_STR(&tmp, result);
2597 zend_symtable_update(Z_ARRVAL_P(return_value), str_index, &tmp);
2598 } else {
2599 add_index_str(return_value, num_index, result);
2600 }
2601
2602 zend_tmp_string_release(tmp_orig_str);
2604 } /* if */
2605}
2606/* }}} */
2607
2608/* {{{ Quotes meta characters */
2610{
2611 zend_string *old;
2612 const char *old_end, *p;
2613 char *q;
2614 char c;
2615 zend_string *str;
2616
2618 Z_PARAM_STR(old)
2620
2621 old_end = ZSTR_VAL(old) + ZSTR_LEN(old);
2622
2623 if (ZSTR_LEN(old) == 0) {
2625 }
2626
2627 str = zend_string_safe_alloc(2, ZSTR_LEN(old), 0, 0);
2628
2629 for (p = ZSTR_VAL(old), q = ZSTR_VAL(str); p != old_end; p++) {
2630 c = *p;
2631 switch (c) {
2632 case '.':
2633 case '\\':
2634 case '+':
2635 case '*':
2636 case '?':
2637 case '[':
2638 case '^':
2639 case ']':
2640 case '$':
2641 case '(':
2642 case ')':
2643 *q++ = '\\';
2645 default:
2646 *q++ = c;
2647 }
2648 }
2649
2650 *q = '\0';
2651
2652 RETURN_NEW_STR(zend_string_truncate(str, q - ZSTR_VAL(str), 0));
2653}
2654/* }}} */
2655
2656/* {{{ Returns ASCII value of character
2657 Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */
2659{
2660 zend_string *str;
2661
2663 Z_PARAM_STR(str)
2665
2666 RETURN_LONG((unsigned char) ZSTR_VAL(str)[0]);
2667}
2668/* }}} */
2669
2670/* {{{ Converts ASCII code to a character
2671 Warning: This function is special-cased by zend_compile.c and so is bypassed for constant integer argument */
2673{
2674 zend_long c;
2675
2677 Z_PARAM_LONG(c)
2679
2680 c &= 0xff;
2681 RETURN_CHAR(c);
2682}
2683/* }}} */
2684
2685/* {{{ php_ucfirst
2686 Uppercase the first character of the word in a native string */
2687static zend_string* php_ucfirst(zend_string *str)
2688{
2689 const unsigned char ch = ZSTR_VAL(str)[0];
2690 unsigned char r = zend_toupper_ascii(ch);
2691 if (r == ch) {
2692 return zend_string_copy(str);
2693 } else {
2694 zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2695 ZSTR_VAL(s)[0] = r;
2696 return s;
2697 }
2698}
2699/* }}} */
2700
2701/* {{{ Makes a string's first character uppercase */
2703{
2704 zend_string *str;
2705
2707 Z_PARAM_STR(str)
2709
2710 if (!ZSTR_LEN(str)) {
2712 }
2713
2714 RETURN_STR(php_ucfirst(str));
2715}
2716/* }}} */
2717
2718/* {{{
2719 Lowercase the first character of the word in a native string */
2720static zend_string* php_lcfirst(zend_string *str)
2721{
2722 unsigned char r = zend_tolower_ascii(ZSTR_VAL(str)[0]);
2723 if (r == ZSTR_VAL(str)[0]) {
2724 return zend_string_copy(str);
2725 } else {
2726 zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2727 ZSTR_VAL(s)[0] = r;
2728 return s;
2729 }
2730}
2731/* }}} */
2732
2733/* {{{ Make a string's first character lowercase */
2735{
2736 zend_string *str;
2737
2739 Z_PARAM_STR(str)
2741
2742 if (!ZSTR_LEN(str)) {
2744 }
2745
2746 RETURN_STR(php_lcfirst(str));
2747}
2748/* }}} */
2749
2750/* {{{ Uppercase the first character of every word in a string */
2752{
2753 zend_string *str;
2754 char *delims = " \t\r\n\f\v";
2755 char *r;
2756 const char *r_end;
2757 size_t delims_len = 6;
2758 char mask[256];
2759
2761 Z_PARAM_STR(str)
2763 Z_PARAM_STRING(delims, delims_len)
2765
2766 if (!ZSTR_LEN(str)) {
2768 }
2769
2770 php_charmask((const unsigned char *) delims, delims_len, mask);
2771
2774
2775 *r = zend_toupper_ascii((unsigned char) *r);
2776 for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2777 if (mask[(unsigned char)*r++]) {
2778 *r = zend_toupper_ascii((unsigned char) *r);
2779 }
2780 }
2781}
2782/* }}} */
2783
2784/* {{{ php_strtr */
2785PHPAPI char *php_strtr(char *str, size_t len, const char *str_from, const char *str_to, size_t trlen)
2786{
2787 size_t i;
2788
2789 if (UNEXPECTED(trlen < 1)) {
2790 return str;
2791 } else if (trlen == 1) {
2792 char ch_from = *str_from;
2793 char ch_to = *str_to;
2794
2795 for (i = 0; i < len; i++) {
2796 if (str[i] == ch_from) {
2797 str[i] = ch_to;
2798 }
2799 }
2800 } else {
2801 unsigned char xlat[256];
2802
2803 memset(xlat, 0, sizeof(xlat));
2804
2805 for (i = 0; i < trlen; i++) {
2806 xlat[(size_t)(unsigned char) str_from[i]] = str_to[i] - str_from[i];
2807 }
2808
2809 for (i = 0; i < len; i++) {
2810 str[i] += xlat[(size_t)(unsigned char) str[i]];
2811 }
2812 }
2813
2814 return str;
2815}
2816/* }}} */
2817
2818/* {{{ php_strtr_ex */
2819static zend_string *php_strtr_ex(zend_string *str, const char *str_from, const char *str_to, size_t trlen)
2820{
2821 zend_string *new_str = NULL;
2822 size_t i;
2823
2824 if (UNEXPECTED(trlen < 1)) {
2825 return zend_string_copy(str);
2826 } else if (trlen == 1) {
2827 char ch_from = *str_from;
2828 char ch_to = *str_to;
2829 char *output;
2830 char *input = ZSTR_VAL(str);
2831 size_t len = ZSTR_LEN(str);
2832
2833#ifdef __SSE2__
2834 if (ZSTR_LEN(str) >= sizeof(__m128i)) {
2835 __m128i search = _mm_set1_epi8(ch_from);
2836 __m128i delta = _mm_set1_epi8(ch_to - ch_from);
2837
2838 do {
2839 __m128i src = _mm_loadu_si128((__m128i*)(input));
2840 __m128i mask = _mm_cmpeq_epi8(src, search);
2841 if (_mm_movemask_epi8(mask)) {
2842 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2843 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), input - ZSTR_VAL(str));
2844 output = ZSTR_VAL(new_str) + (input - ZSTR_VAL(str));
2845 _mm_storeu_si128((__m128i *)(output),
2846 _mm_add_epi8(src,
2847 _mm_and_si128(mask, delta)));
2848 input += sizeof(__m128i);
2849 output += sizeof(__m128i);
2850 len -= sizeof(__m128i);
2851 for (; len >= sizeof(__m128i); input += sizeof(__m128i), output += sizeof(__m128i), len -= sizeof(__m128i)) {
2852 src = _mm_loadu_si128((__m128i*)(input));
2853 mask = _mm_cmpeq_epi8(src, search);
2854 _mm_storeu_si128((__m128i *)(output),
2855 _mm_add_epi8(src,
2856 _mm_and_si128(mask, delta)));
2857 }
2858 for (; len > 0; input++, output++, len--) {
2859 *output = (*input == ch_from) ? ch_to : *input;
2860 }
2861 *output = 0;
2862 return new_str;
2863 }
2864 input += sizeof(__m128i);
2865 len -= sizeof(__m128i);
2866 } while (len >= sizeof(__m128i));
2867 }
2868#endif
2869 for (; len > 0; input++, len--) {
2870 if (*input == ch_from) {
2871 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2872 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), input - ZSTR_VAL(str));
2873 output = ZSTR_VAL(new_str) + (input - ZSTR_VAL(str));
2874 *output = ch_to;
2875 input++;
2876 output++;
2877 len--;
2878 for (; len > 0; input++, output++, len--) {
2879 *output = (*input == ch_from) ? ch_to : *input;
2880 }
2881 *output = 0;
2882 return new_str;
2883 }
2884 }
2885 } else {
2886 unsigned char xlat[256];
2887
2888 memset(xlat, 0, sizeof(xlat));;
2889
2890 for (i = 0; i < trlen; i++) {
2891 xlat[(size_t)(unsigned char) str_from[i]] = str_to[i] - str_from[i];
2892 }
2893
2894 for (i = 0; i < ZSTR_LEN(str); i++) {
2895 if (xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) {
2896 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2897 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2898 do {
2899 ZSTR_VAL(new_str)[i] = ZSTR_VAL(str)[i] + xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2900 i++;
2901 } while (i < ZSTR_LEN(str));
2902 ZSTR_VAL(new_str)[i] = 0;
2903 return new_str;
2904 }
2905 }
2906 }
2907
2908 return zend_string_copy(str);
2909}
2910/* }}} */
2911
2912static void php_strtr_array_ex(zval *return_value, zend_string *input, HashTable *pats)
2913{
2914 const char *str = ZSTR_VAL(input);
2915 size_t slen = ZSTR_LEN(input);
2916 zend_ulong num_key;
2917 zend_string *str_key;
2918 size_t len, pos, old_pos;
2919 bool has_num_keys = false;
2920 size_t minlen = 128*1024;
2921 size_t maxlen = 0;
2922 HashTable str_hash;
2923 zval *entry;
2924 const char *key;
2925 smart_str result = {0};
2926 zend_ulong bitset[256/sizeof(zend_ulong)];
2927 zend_ulong *num_bitset;
2928
2929 /* we will collect all possible key lengths */
2930 num_bitset = ecalloc((slen + sizeof(zend_ulong)) / sizeof(zend_ulong), sizeof(zend_ulong));
2931 memset(bitset, 0, sizeof(bitset));
2932
2933 /* check if original array has numeric keys */
2934 ZEND_HASH_FOREACH_STR_KEY(pats, str_key) {
2935 if (UNEXPECTED(!str_key)) {
2936 has_num_keys = true;
2937 } else {
2938 len = ZSTR_LEN(str_key);
2939 if (UNEXPECTED(len == 0)) {
2940 php_error_docref(NULL, E_WARNING, "Ignoring replacement of empty string");
2941 continue;
2942 } else if (UNEXPECTED(len > slen)) {
2943 /* skip long patterns */
2944 continue;
2945 }
2946 if (len > maxlen) {
2947 maxlen = len;
2948 }
2949 if (len < minlen) {
2950 minlen = len;
2951 }
2952 /* remember possible key length */
2953 num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
2954 bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong));
2955 }
2957
2958 if (UNEXPECTED(has_num_keys)) {
2959 zend_string *key_used;
2960 /* we have to rebuild HashTable with numeric keys */
2961 zend_hash_init(&str_hash, zend_hash_num_elements(pats), NULL, NULL, 0);
2962 ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
2963 if (UNEXPECTED(!str_key)) {
2964 key_used = zend_long_to_str(num_key);
2965 len = ZSTR_LEN(key_used);
2966 if (UNEXPECTED(len > slen)) {
2967 /* skip long patterns */
2968 zend_string_release(key_used);
2969 continue;
2970 }
2971 if (len > maxlen) {
2972 maxlen = len;
2973 }
2974 if (len < minlen) {
2975 minlen = len;
2976 }
2977 /* remember possible key length */
2978 num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
2979 bitset[((unsigned char)ZSTR_VAL(key_used)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(key_used)[0]) % sizeof(zend_ulong));
2980 } else {
2981 key_used = str_key;
2982 len = ZSTR_LEN(key_used);
2983 if (UNEXPECTED(len > slen)) {
2984 /* skip long patterns */
2985 continue;
2986 }
2987 }
2988 zend_hash_add(&str_hash, key_used, entry);
2989 if (UNEXPECTED(!str_key)) {
2990 zend_string_release_ex(key_used, 0);
2991 }
2993 pats = &str_hash;
2994 }
2995
2996 if (UNEXPECTED(minlen > maxlen)) {
2997 /* return the original string */
2998 if (pats == &str_hash) {
2999 zend_hash_destroy(&str_hash);
3000 }
3001 efree(num_bitset);
3002 RETURN_STR_COPY(input);
3003 }
3004
3005 old_pos = pos = 0;
3006 while (pos <= slen - minlen) {
3007 key = str + pos;
3008 if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) {
3009 len = maxlen;
3010 if (len > slen - pos) {
3011 len = slen - pos;
3012 }
3013 while (len >= minlen) {
3014 if ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1) << (len % sizeof(zend_ulong))))) {
3015 entry = zend_hash_str_find(pats, key, len);
3016 if (entry != NULL) {
3017 zend_string *tmp;
3018 zend_string *s = zval_get_tmp_string(entry, &tmp);
3019 smart_str_appendl(&result, str + old_pos, pos - old_pos);
3020 smart_str_append(&result, s);
3021 old_pos = pos + len;
3022 pos = old_pos - 1;
3023 zend_tmp_string_release(tmp);
3024 break;
3025 }
3026 }
3027 len--;
3028 }
3029 }
3030 pos++;
3031 }
3032
3033 if (result.s) {
3034 smart_str_appendl(&result, str + old_pos, slen - old_pos);
3035 RETVAL_STR(smart_str_extract(&result));
3036 } else {
3037 smart_str_free(&result);
3038 RETVAL_STR_COPY(input);
3039 }
3040
3041 if (pats == &str_hash) {
3042 zend_hash_destroy(&str_hash);
3043 }
3044 efree(num_bitset);
3045}
3046
3047/* {{{ count_chars */
3048static zend_always_inline zend_long count_chars(const char *p, zend_long length, char ch)
3049{
3050 zend_long count = 0;
3051 const char *endp;
3052
3053#ifdef __SSE2__
3054 if (length >= sizeof(__m128i)) {
3055 __m128i search = _mm_set1_epi8(ch);
3056
3057 do {
3058 __m128i src = _mm_loadu_si128((__m128i*)(p));
3059 uint32_t mask = _mm_movemask_epi8(_mm_cmpeq_epi8(src, search));
3060 // TODO: It would be great to use POPCNT, but it's available only with SSE4.1
3061#if 1
3062 while (mask != 0) {
3063 count++;
3064 mask = mask & (mask - 1);
3065 }
3066#else
3067 if (mask) {
3068 mask = mask - ((mask >> 1) & 0x5555);
3069 mask = (mask & 0x3333) + ((mask >> 2) & 0x3333);
3070 mask = (mask + (mask >> 4)) & 0x0F0F;
3071 mask = (mask + (mask >> 8)) & 0x00ff;
3072 count += mask;
3073 }
3074#endif
3075 p += sizeof(__m128i);
3076 length -= sizeof(__m128i);
3077 } while (length >= sizeof(__m128i));
3078 }
3079 endp = p + length;
3080 while (p != endp) {
3081 count += (*p == ch);
3082 p++;
3083 }
3084#else
3085 endp = p + length;
3086 while ((p = memchr(p, ch, endp-p))) {
3087 count++;
3088 p++;
3089 }
3090#endif
3091 return count;
3092}
3093/* }}} */
3094
3095/* {{{ php_char_to_str_ex */
3096static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, size_t to_len, bool case_sensitivity, zend_long *replace_count)
3097{
3099 size_t char_count;
3100 int lc_from = 0;
3101 const char *source, *source_end;
3102 char *target;
3103
3104 if (case_sensitivity) {
3105 char_count = count_chars(ZSTR_VAL(str), ZSTR_LEN(str), from);
3106 } else {
3107 char_count = 0;
3108 lc_from = zend_tolower_ascii(from);
3109 source_end = ZSTR_VAL(str) + ZSTR_LEN(str);
3110 for (source = ZSTR_VAL(str); source < source_end; source++) {
3111 if (zend_tolower_ascii(*source) == lc_from) {
3112 char_count++;
3113 }
3114 }
3115 }
3116
3117 if (char_count == 0) {
3118 return zend_string_copy(str);
3119 }
3120
3121 if (replace_count) {
3122 *replace_count += char_count;
3123 }
3124
3125 if (to_len > 0) {
3126 result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3127 } else {
3128 result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3129 }
3130 target = ZSTR_VAL(result);
3131
3132 if (case_sensitivity) {
3133 char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3134
3135 while ((p = memchr(p, from, (e - p)))) {
3136 target = zend_mempcpy(target, s, (p - s));
3137 target = zend_mempcpy(target, to, to_len);
3138 p++;
3139 s = p;
3140 if (--char_count == 0) break;
3141 }
3142 if (s < e) {
3143 target = zend_mempcpy(target, s, e - s);
3144 }
3145 } else {
3146 source_end = ZSTR_VAL(str) + ZSTR_LEN(str);
3147 for (source = ZSTR_VAL(str); source < source_end; source++) {
3148 if (zend_tolower_ascii(*source) == lc_from) {
3149 target = zend_mempcpy(target, to, to_len);
3150 } else {
3151 *target = *source;
3152 target++;
3153 }
3154 }
3155 }
3156 *target = 0;
3157 return result;
3158}
3159/* }}} */
3160
3161/* {{{ php_str_to_str_ex */
3162static zend_string *php_str_to_str_ex(zend_string *haystack,
3163 const char *needle, size_t needle_len, const char *str, size_t str_len, zend_long *replace_count)
3164{
3165
3166 if (needle_len < ZSTR_LEN(haystack)) {
3167 zend_string *new_str;
3168 const char *end;
3169 const char *p, *r;
3170 char *e;
3171
3172 if (needle_len == str_len) {
3173 new_str = NULL;
3174 end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3175 for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3176 if (!new_str) {
3177 new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3178 }
3179 memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3180 (*replace_count)++;
3181 }
3182 if (!new_str) {
3183 goto nothing_todo;
3184 }
3185 return new_str;
3186 } else {
3187 size_t count = 0;
3188 const char *o = ZSTR_VAL(haystack);
3189 const char *n = needle;
3190 const char *endp = o + ZSTR_LEN(haystack);
3191
3192 while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3193 o += needle_len;
3194 count++;
3195 }
3196 if (count == 0) {
3197 /* Needle doesn't occur, shortcircuit the actual replacement. */
3198 goto nothing_todo;
3199 }
3200 if (str_len > needle_len) {
3201 new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3202 } else {
3203 new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3204 }
3205
3206 e = ZSTR_VAL(new_str);
3207 end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3208 for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3209 e = zend_mempcpy(e, p, r - p);
3210 e = zend_mempcpy(e, str, str_len);
3211 (*replace_count)++;
3212 }
3213
3214 if (p < end) {
3215 e = zend_mempcpy(e, p, end - p);
3216 }
3217
3218 *e = '\0';
3219 return new_str;
3220 }
3221 } else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3222nothing_todo:
3223 return zend_string_copy(haystack);
3224 } else {
3225 (*replace_count)++;
3226 return zend_string_init_fast(str, str_len);
3227 }
3228}
3229/* }}} */
3230
3231/* {{{ php_str_to_str_i_ex */
3232static zend_string *php_str_to_str_i_ex(zend_string *haystack, const char *lc_haystack,
3233 zend_string *needle, const char *str, size_t str_len, zend_long *replace_count)
3234{
3235 zend_string *new_str = NULL;
3236 zend_string *lc_needle;
3237
3238 if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3239 const char *end;
3240 const char *p, *r;
3241 char *e;
3242
3243 if (ZSTR_LEN(needle) == str_len) {
3244 lc_needle = zend_string_tolower(needle);
3245 end = lc_haystack + ZSTR_LEN(haystack);
3246 for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3247 if (!new_str) {
3248 new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3249 }
3250 memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3251 (*replace_count)++;
3252 }
3253 zend_string_release_ex(lc_needle, 0);
3254
3255 if (!new_str) {
3256 goto nothing_todo;
3257 }
3258 return new_str;
3259 } else {
3260 size_t count = 0;
3261 const char *o = lc_haystack;
3262 const char *n;
3263 const char *endp = o + ZSTR_LEN(haystack);
3264
3265 lc_needle = zend_string_tolower(needle);
3266 n = ZSTR_VAL(lc_needle);
3267
3268 while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3269 o += ZSTR_LEN(lc_needle);
3270 count++;
3271 }
3272 if (count == 0) {
3273 /* Needle doesn't occur, shortcircuit the actual replacement. */
3274 zend_string_release_ex(lc_needle, 0);
3275 goto nothing_todo;
3276 }
3277
3278 if (str_len > ZSTR_LEN(lc_needle)) {
3279 new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3280 } else {
3281 new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3282 }
3283
3284 e = ZSTR_VAL(new_str);
3285 end = lc_haystack + ZSTR_LEN(haystack);
3286
3287 for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3288 e = zend_mempcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3289 e = zend_mempcpy(e, str, str_len);
3290 (*replace_count)++;
3291 }
3292
3293 if (p < end) {
3294 e = zend_mempcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3295 }
3296 *e = '\0';
3297
3298 zend_string_release_ex(lc_needle, 0);
3299
3300 return new_str;
3301 }
3302 } else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3303nothing_todo:
3304 return zend_string_copy(haystack);
3305 } else {
3306 lc_needle = zend_string_tolower(needle);
3307
3308 if (memcmp(lc_haystack, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle))) {
3309 zend_string_release_ex(lc_needle, 0);
3310 goto nothing_todo;
3311 }
3312 zend_string_release_ex(lc_needle, 0);
3313
3314 new_str = zend_string_init(str, str_len, 0);
3315
3316 (*replace_count)++;
3317 return new_str;
3318 }
3319}
3320/* }}} */
3321
3322/* {{{ php_str_to_str */
3323PHPAPI zend_string *php_str_to_str(const char *haystack, size_t length, const char *needle, size_t needle_len, const char *str, size_t str_len)
3324{
3325 zend_string *new_str;
3326
3327 if (needle_len < length) {
3328 const char *end;
3329 const char *s, *p;
3330 char *e, *r;
3331
3332 if (needle_len == str_len) {
3333 new_str = zend_string_init(haystack, length, 0);
3334 end = ZSTR_VAL(new_str) + length;
3335 for (p = ZSTR_VAL(new_str); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3336 memcpy(r, str, str_len);
3337 }
3338 return new_str;
3339 } else {
3340 if (str_len < needle_len) {
3341 new_str = zend_string_alloc(length, 0);
3342 } else {
3343 size_t count = 0;
3344 const char *o = haystack;
3345 const char *n = needle;
3346 const char *endp = o + length;
3347
3348 while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3349 o += needle_len;
3350 count++;
3351 }
3352 if (count == 0) {
3353 /* Needle doesn't occur, shortcircuit the actual replacement. */
3354 new_str = zend_string_init(haystack, length, 0);
3355 return new_str;
3356 } else {
3357 if (str_len > needle_len) {
3358 new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3359 } else {
3360 new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3361 }
3362 }
3363 }
3364
3365 s = e = ZSTR_VAL(new_str);
3366 end = haystack + length;
3367 for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3368 e = zend_mempcpy(e, p, r - p);
3369 e = zend_mempcpy(e, str, str_len);
3370 }
3371
3372 if (p < end) {
3373 e = zend_mempcpy(e, p, end - p);
3374 }
3375
3376 *e = '\0';
3377 new_str = zend_string_truncate(new_str, e - s, 0);
3378 return new_str;
3379 }
3380 } else if (needle_len > length || memcmp(haystack, needle, length)) {
3381 new_str = zend_string_init(haystack, length, 0);
3382 return new_str;
3383 } else {
3384 new_str = zend_string_init(str, str_len, 0);
3385
3386 return new_str;
3387 }
3388}
3389/* }}} */
3390
3391static void php_strtr_array(zval *return_value, zend_string *str, HashTable *from_ht)
3392{
3393 if (zend_hash_num_elements(from_ht) < 1) {
3394 RETURN_STR_COPY(str);
3395 } else if (zend_hash_num_elements(from_ht) == 1) {
3396 zend_long num_key;
3397 zend_string *str_key, *tmp_str, *replace, *tmp_replace;
3398 zval *entry;
3399
3400 ZEND_HASH_FOREACH_KEY_VAL(from_ht, num_key, str_key, entry) {
3401 tmp_str = NULL;
3402 if (UNEXPECTED(!str_key)) {
3403 str_key = tmp_str = zend_long_to_str(num_key);
3404 }
3405 replace = zval_get_tmp_string(entry, &tmp_replace);
3406 if (ZSTR_LEN(str_key) < 1) {
3407 php_error_docref(NULL, E_WARNING, "Ignoring replacement of empty string");
3408 RETVAL_STR_COPY(str);
3409 } else if (ZSTR_LEN(str_key) == 1) {
3410 RETVAL_STR(php_char_to_str_ex(str,
3411 ZSTR_VAL(str_key)[0],
3412 ZSTR_VAL(replace),
3413 ZSTR_LEN(replace),
3414 /* case_sensitive */ true,
3415 NULL));
3416 } else {
3417 zend_long dummy = 0;
3418 RETVAL_STR(php_str_to_str_ex(str,
3419 ZSTR_VAL(str_key), ZSTR_LEN(str_key),
3420 ZSTR_VAL(replace), ZSTR_LEN(replace), &dummy));
3421 }
3422 zend_tmp_string_release(tmp_str);
3423 zend_tmp_string_release(tmp_replace);
3424 return;
3426 } else {
3427 php_strtr_array_ex(return_value, str, from_ht);
3428 }
3429}
3430
3431/* {{{ Translates characters in str using given translation tables */
3433{
3434 zend_string *str, *from_str = NULL;
3435 HashTable *from_ht = NULL;
3436 char *to = NULL;
3437 size_t to_len = 0;
3438
3439 if (ZEND_NUM_ARGS() <= 2) {
3441 Z_PARAM_STR(str)
3442 Z_PARAM_ARRAY_HT(from_ht)
3444 } else {
3446 Z_PARAM_STR(str)
3447 Z_PARAM_STR(from_str)
3448 Z_PARAM_STRING(to, to_len)
3450 }
3451
3452 /* shortcut for empty string */
3453 if (ZSTR_LEN(str) == 0) {
3455 }
3456
3457 if (!to) {
3458 php_strtr_array(return_value, str, from_ht);
3459 } else {
3460 RETURN_STR(php_strtr_ex(str,
3461 ZSTR_VAL(from_str),
3462 to,
3463 MIN(ZSTR_LEN(from_str), to_len)));
3464 }
3465}
3466/* }}} */
3467
3469{
3470 zval str_tmp;
3471 zend_string *str;
3472 zval *from;
3473
3474 Z_FLF_PARAM_STR(1, str, str_tmp);
3475 Z_FLF_PARAM_ARRAY(2, from);
3476
3477 if (ZSTR_LEN(str) == 0) {
3479 goto flf_clean;
3480 }
3481
3482 php_strtr_array(return_value, str, Z_ARR_P(from));
3483
3484flf_clean:
3485 Z_FLF_PARAM_FREE_STR(1, str_tmp);
3486}
3487
3489{
3490 zval str_tmp, from_tmp, to_tmp;
3491 zend_string *str, *from, *to;
3492
3493 Z_FLF_PARAM_STR(1, str, str_tmp);
3494 Z_FLF_PARAM_STR(2, from, from_tmp);
3495 Z_FLF_PARAM_STR(3, to, to_tmp);
3496
3497 if (ZSTR_LEN(str) == 0) {
3499 goto flf_clean;
3500 }
3501
3502 RETVAL_STR(php_strtr_ex(str, ZSTR_VAL(from), ZSTR_VAL(to), MIN(ZSTR_LEN(from), ZSTR_LEN(to))));
3503
3504flf_clean:
3505 Z_FLF_PARAM_FREE_STR(1, str_tmp);
3506 Z_FLF_PARAM_FREE_STR(2, from_tmp);
3507 Z_FLF_PARAM_FREE_STR(3, to_tmp);
3508}
3509
3510/* {{{ Reverse a string */
3511#ifdef ZEND_INTRIN_SSSE3_NATIVE
3512#include <tmmintrin.h>
3513#elif defined(__aarch64__) || defined(_M_ARM64)
3514#include <arm_neon.h>
3515#endif
3517{
3518 zend_string *str;
3519 const char *s, *e;
3520 char *p;
3521 zend_string *n;
3522
3524 Z_PARAM_STR(str)
3526
3527 n = zend_string_alloc(ZSTR_LEN(str), 0);
3528 p = ZSTR_VAL(n);
3529
3530 s = ZSTR_VAL(str);
3531 e = s + ZSTR_LEN(str);
3532 --e;
3533#ifdef ZEND_INTRIN_SSSE3_NATIVE
3534 if (e - s > 15) {
3535 const __m128i map = _mm_set_epi8(
3536 0, 1, 2, 3,
3537 4, 5, 6, 7,
3538 8, 9, 10, 11,
3539 12, 13, 14, 15);
3540 do {
3541 const __m128i str = _mm_loadu_si128((__m128i *)(e - 15));
3542 _mm_storeu_si128((__m128i *)p, _mm_shuffle_epi8(str, map));
3543 p += 16;
3544 e -= 16;
3545 } while (e - s > 15);
3546 }
3547#elif defined(__aarch64__)
3548 if (e - s > 15) {
3549 do {
3550 const uint8x16_t str = vld1q_u8((uint8_t *)(e - 15));
3551 /* Synthesize rev128 with a rev64 + ext. */
3552 const uint8x16_t rev = vrev64q_u8(str);
3553 const uint8x16_t ext = (uint8x16_t)
3554 vextq_u64((uint64x2_t)rev, (uint64x2_t)rev, 1);
3555 vst1q_u8((uint8_t *)p, ext);
3556 p += 16;
3557 e -= 16;
3558 } while (e - s > 15);
3559 }
3560#elif defined(_M_ARM64)
3561 if (e - s > 15) {
3562 do {
3563 const __n128 str = vld1q_u8((uint8_t *)(e - 15));
3564 /* Synthesize rev128 with a rev64 + ext. */
3565 /* strange force cast limit on windows: you cannot convert anything */
3566 const __n128 rev = vrev64q_u8(str);
3567 const __n128 ext = vextq_u64(rev, rev, 1);
3568 vst1q_u8((uint8_t *)p, ext);
3569 p += 16;
3570 e -= 16;
3571 } while (e - s > 15);
3572 }
3573#endif
3574 while (e >= s) {
3575 *p++ = *e--;
3576 }
3577
3578 *p = '\0';
3579
3581}
3582/* }}} */
3583
3584/* {{{ php_similar_str */
3585static void php_similar_str(const char *txt1, size_t len1, const char *txt2, size_t len2, size_t *pos1, size_t *pos2, size_t *max, size_t *count)
3586{
3587 const char *p, *q;
3588 const char *end1 = (char *) txt1 + len1;
3589 const char *end2 = (char *) txt2 + len2;
3590 size_t l;
3591
3592 *max = 0;
3593 *count = 0;
3594 for (p = (char *) txt1; p < end1; p++) {
3595 for (q = (char *) txt2; q < end2; q++) {
3596 for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3597 if (l > *max) {
3598 *max = l;
3599 *count += 1;
3600 *pos1 = p - txt1;
3601 *pos2 = q - txt2;
3602 }
3603 }
3604 }
3605}
3606/* }}} */
3607
3608/* {{{ php_similar_char */
3609static size_t php_similar_char(const char *txt1, size_t len1, const char *txt2, size_t len2)
3610{
3611 size_t sum;
3612 size_t pos1 = 0, pos2 = 0, max, count;
3613
3614 php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max, &count);
3615 if ((sum = max)) {
3616 if (pos1 && pos2 && count > 1) {
3617 sum += php_similar_char(txt1, pos1,
3618 txt2, pos2);
3619 }
3620 if ((pos1 + max < len1) && (pos2 + max < len2)) {
3621 sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3622 txt2 + pos2 + max, len2 - pos2 - max);
3623 }
3624 }
3625
3626 return sum;
3627}
3628/* }}} */
3629
3630/* {{{ Calculates the similarity between two strings */
3632{
3633 zend_string *t1, *t2;
3634 zval *percent = NULL;
3635 bool compute_percentage = ZEND_NUM_ARGS() >= 3;
3636 size_t sim;
3637
3642 Z_PARAM_ZVAL(percent)
3644
3645 if (ZSTR_LEN(t1) + ZSTR_LEN(t2) == 0) {
3646 if (compute_percentage) {
3647 ZEND_TRY_ASSIGN_REF_DOUBLE(percent, 0);
3648 }
3649
3650 RETURN_LONG(0);
3651 }
3652
3653 sim = php_similar_char(ZSTR_VAL(t1), ZSTR_LEN(t1), ZSTR_VAL(t2), ZSTR_LEN(t2));
3654
3655 if (compute_percentage) {
3656 ZEND_TRY_ASSIGN_REF_DOUBLE(percent, sim * 200.0 / (ZSTR_LEN(t1) + ZSTR_LEN(t2)));
3657 }
3658
3659 RETURN_LONG(sim);
3660}
3661/* }}} */
3662
3663/* {{{ Escapes all chars mentioned in charlist with backslash. It creates octal representations if asked to backslash characters with 8th bit set or with ASCII<32 (except '\n', '\r', '\t' etc...) */
3665{
3666 zend_string *str, *what;
3667
3669 Z_PARAM_STR(str)
3670 Z_PARAM_STR(what)
3672
3673 if (ZSTR_LEN(str) == 0) {
3675 }
3676
3677 if (ZSTR_LEN(what) == 0) {
3678 RETURN_STR_COPY(str);
3679 }
3680
3682}
3683/* }}} */
3684
3685/* {{{ Escapes single quote, double quotes and backslash characters in a string with backslashes */
3687{
3688 zend_string *str;
3689
3691 Z_PARAM_STR(str)
3693
3694 if (ZSTR_LEN(str) == 0) {
3696 }
3697
3699}
3700/* }}} */
3701
3702/* {{{ Strips backslashes from a string. Uses C-style conventions */
3714/* }}} */
3715
3716/* {{{ Strips backslashes from a string */
3728/* }}} */
3729
3730/* {{{ php_stripcslashes */
3732{
3733 const char *source, *end;
3734 char *target;
3735 size_t nlen = ZSTR_LEN(str), i;
3736 char numtmp[4];
3737
3738 for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(str); source < end; source++) {
3739 if (*source == '\\' && source + 1 < end) {
3740 source++;
3741 switch (*source) {
3742 case 'n': *target++='\n'; nlen--; break;
3743 case 'r': *target++='\r'; nlen--; break;
3744 case 'a': *target++='\a'; nlen--; break;
3745 case 't': *target++='\t'; nlen--; break;
3746 case 'v': *target++='\v'; nlen--; break;
3747 case 'b': *target++='\b'; nlen--; break;
3748 case 'f': *target++='\f'; nlen--; break;
3749 case '\\': *target++='\\'; nlen--; break;
3750 case 'x':
3751 if (source+1 < end && isxdigit((int)(*(source+1)))) {
3752 numtmp[0] = *++source;
3753 if (source+1 < end && isxdigit((int)(*(source+1)))) {
3754 numtmp[1] = *++source;
3755 numtmp[2] = '\0';
3756 nlen-=3;
3757 } else {
3758 numtmp[1] = '\0';
3759 nlen-=2;
3760 }
3761 *target++=(char)strtol(numtmp, NULL, 16);
3762 break;
3763 }
3765 default:
3766 i=0;
3767 while (source < end && *source >= '0' && *source <= '7' && i<3) {
3768 numtmp[i++] = *source++;
3769 }
3770 if (i) {
3771 numtmp[i]='\0';
3772 *target++=(char)strtol(numtmp, NULL, 8);
3773 nlen-=i;
3774 source--;
3775 } else {
3776 *target++=*source;
3777 nlen--;
3778 }
3779 }
3780 } else {
3781 *target++=*source;
3782 }
3783 }
3784
3785 if (nlen != 0) {
3786 *target='\0';
3787 }
3788
3789 ZSTR_LEN(str) = nlen;
3790}
3791/* }}} */
3792
3793/* {{{ php_addcslashes_str */
3794PHPAPI zend_string *php_addcslashes_str(const char *str, size_t len, const char *what, size_t wlength)
3795{
3796 char flags[256];
3797 char *target;
3798 const char *source, *end;
3799 char c;
3800 size_t newlen;
3801 zend_string *new_str = zend_string_safe_alloc(4, len, 0, 0);
3802
3803 php_charmask((const unsigned char *) what, wlength, flags);
3804
3805 for (source = str, end = source + len, target = ZSTR_VAL(new_str); source < end; source++) {
3806 c = *source;
3807 if (flags[(unsigned char)c]) {
3808 if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3809 *target++ = '\\';
3810 switch (c) {
3811 case '\n': *target++ = 'n'; break;
3812 case '\t': *target++ = 't'; break;
3813 case '\r': *target++ = 'r'; break;
3814 case '\a': *target++ = 'a'; break;
3815 case '\v': *target++ = 'v'; break;
3816 case '\b': *target++ = 'b'; break;
3817 case '\f': *target++ = 'f'; break;
3818 default: target += snprintf(target, 4, "%03o", (unsigned char) c);
3819 }
3820 continue;
3821 }
3822 *target++ = '\\';
3823 }
3824 *target++ = c;
3825 }
3826 *target = 0;
3827 newlen = target - ZSTR_VAL(new_str);
3828 if (newlen < len * 4) {
3829 new_str = zend_string_truncate(new_str, newlen, 0);
3830 }
3831 return new_str;
3832}
3833/* }}} */
3834
3835/* {{{ php_addcslashes */
3836PHPAPI zend_string *php_addcslashes(zend_string *str, const char *what, size_t wlength)
3837{
3838 return php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), what, wlength);
3839}
3840/* }}} */
3841
3842/* {{{ php_addslashes */
3843
3844#ifdef ZEND_INTRIN_SSE4_2_NATIVE
3845# include <nmmintrin.h>
3846# include "Zend/zend_bitset.h"
3847#elif defined(ZEND_INTRIN_SSE4_2_RESOLVER)
3848# include <nmmintrin.h>
3849# include "Zend/zend_bitset.h"
3850# include "Zend/zend_cpuinfo.h"
3851
3852ZEND_INTRIN_SSE4_2_FUNC_DECL(zend_string *php_addslashes_sse42(zend_string *str));
3853zend_string *php_addslashes_default(zend_string *str);
3854
3855# ifdef ZEND_INTRIN_SSE4_2_FUNC_PROTO
3856PHPAPI zend_string *php_addslashes(zend_string *str) __attribute__((ifunc("resolve_addslashes")));
3857
3858typedef zend_string *(*php_addslashes_func_t)(zend_string *);
3859
3861ZEND_ATTRIBUTE_UNUSED /* clang mistakenly warns about this */
3862static php_addslashes_func_t resolve_addslashes(void) {
3863 if (zend_cpu_supports_sse42()) {
3864 return php_addslashes_sse42;
3865 }
3866 return php_addslashes_default;
3867}
3868# else /* ZEND_INTRIN_SSE4_2_FUNC_PTR */
3869
3870static zend_string *(*php_addslashes_ptr)(zend_string *str) = NULL;
3871
3873 return php_addslashes_ptr(str);
3874}
3875
3876/* {{{ PHP_MINIT_FUNCTION */
3877PHP_MINIT_FUNCTION(string_intrin)
3878{
3879 if (zend_cpu_supports_sse42()) {
3880 php_addslashes_ptr = php_addslashes_sse42;
3881 } else {
3882 php_addslashes_ptr = php_addslashes_default;
3883 }
3884 return SUCCESS;
3885}
3886/* }}} */
3887# endif
3888#endif
3889
3890#if defined(ZEND_INTRIN_SSE4_2_NATIVE) || defined(ZEND_INTRIN_SSE4_2_RESOLVER)
3891# ifdef ZEND_INTRIN_SSE4_2_NATIVE
3893# elif defined(ZEND_INTRIN_SSE4_2_RESOLVER)
3894zend_string *php_addslashes_sse42(zend_string *str)
3895# endif
3896{
3897 ZEND_SET_ALIGNED(16, static const char slashchars[16]) = "\'\"\\\0";
3898 __m128i w128, s128;
3899 uint32_t res = 0;
3900 /* maximum string length, worst case situation */
3901 char *target;
3902 const char *source, *end;
3903 size_t offset;
3904 zend_string *new_str;
3905
3906 if (!str) {
3907 return ZSTR_EMPTY_ALLOC();
3908 }
3909
3910 source = ZSTR_VAL(str);
3911 end = source + ZSTR_LEN(str);
3912
3913 if (ZSTR_LEN(str) > 15) {
3914 w128 = _mm_load_si128((__m128i *)slashchars);
3915 do {
3916 s128 = _mm_loadu_si128((__m128i *)source);
3917 res = _mm_cvtsi128_si32(_mm_cmpestrm(w128, 4, s128, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK));
3918 if (res) {
3919 goto do_escape;
3920 }
3921 source += 16;
3922 } while ((end - source) > 15);
3923 }
3924
3925 while (source < end) {
3926 switch (*source) {
3927 case '\0':
3928 case '\'':
3929 case '\"':
3930 case '\\':
3931 goto do_escape;
3932 default:
3933 source++;
3934 break;
3935 }
3936 }
3937
3938 return zend_string_copy(str);
3939
3940do_escape:
3941 offset = source - (char *)ZSTR_VAL(str);
3942 new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0);
3943 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset);
3944 target = ZSTR_VAL(new_str) + offset;
3945
3946 if (res) {
3947 int pos = 0;
3948 do {
3949 int i, n = zend_ulong_ntz(res);
3950 for (i = 0; i < n; i++) {
3951 *target++ = source[pos + i];
3952 }
3953 pos += n;
3954 *target++ = '\\';
3955 if (source[pos] == '\0') {
3956 *target++ = '0';
3957 } else {
3958 *target++ = source[pos];
3959 }
3960 pos++;
3961 res = res >> (n + 1);
3962 } while (res);
3963
3964 for (; pos < 16; pos++) {
3965 *target++ = source[pos];
3966 }
3967 source += 16;
3968 } else if (end - source > 15) {
3969 w128 = _mm_load_si128((__m128i *)slashchars);
3970 }
3971
3972 for (; end - source > 15; source += 16) {
3973 int pos = 0;
3974 s128 = _mm_loadu_si128((__m128i *)source);
3975 res = _mm_cvtsi128_si32(_mm_cmpestrm(w128, 4, s128, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK));
3976 if (res) {
3977 do {
3978 int i, n = zend_ulong_ntz(res);
3979 for (i = 0; i < n; i++) {
3980 *target++ = source[pos + i];
3981 }
3982 pos += n;
3983 *target++ = '\\';
3984 if (source[pos] == '\0') {
3985 *target++ = '0';
3986 } else {
3987 *target++ = source[pos];
3988 }
3989 pos++;
3990 res = res >> (n + 1);
3991 } while (res);
3992 for (; pos < 16; pos++) {
3993 *target++ = source[pos];
3994 }
3995 } else {
3996 _mm_storeu_si128((__m128i*)target, s128);
3997 target += 16;
3998 }
3999 }
4000
4001 while (source < end) {
4002 switch (*source) {
4003 case '\0':
4004 *target++ = '\\';
4005 *target++ = '0';
4006 break;
4007 case '\'':
4008 case '\"':
4009 case '\\':
4010 *target++ = '\\';
4012 default:
4013 *target++ = *source;
4014 break;
4015 }
4016 source++;
4017 }
4018
4019 *target = '\0';
4020
4021 if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) {
4022 new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0);
4023 } else {
4024 ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str);
4025 }
4026
4027 return new_str;
4028}
4029/* }}} */
4030#endif
4031
4032#if defined(__aarch64__) || defined(_M_ARM64)
4033typedef union {
4034 uint8_t mem[16];
4035 uint64_t dw[2];
4036} quad_word;
4037
4038static zend_always_inline quad_word aarch64_contains_slash_chars(uint8x16_t x) {
4039 uint8x16_t s0 = vceqq_u8(x, vdupq_n_u8('\0'));
4040 uint8x16_t s1 = vceqq_u8(x, vdupq_n_u8('\''));
4041 uint8x16_t s2 = vceqq_u8(x, vdupq_n_u8('\"'));
4042 uint8x16_t s3 = vceqq_u8(x, vdupq_n_u8('\\'));
4043 uint8x16_t s01 = vorrq_u8(s0, s1);
4044 uint8x16_t s23 = vorrq_u8(s2, s3);
4045 uint8x16_t s0123 = vorrq_u8(s01, s23);
4046 quad_word qw;
4047 vst1q_u8(qw.mem, s0123);
4048 return qw;
4049}
4050
4051static zend_always_inline char *aarch64_add_slashes(quad_word res, const char *source, char *target)
4052{
4053 for (int i = 0; i < 16; i++) {
4054 char s = source[i];
4055 if (res.mem[i] == 0)
4056 *target++ = s;
4057 else {
4058 *target++ = '\\';
4059 if (s == '\0')
4060 *target++ = '0';
4061 else
4062 *target++ = s;
4063 }
4064 }
4065 return target;
4066}
4067#endif /* defined(__aarch64__) || defined(_M_ARM64) */
4068
4069#ifndef ZEND_INTRIN_SSE4_2_NATIVE
4070# ifdef ZEND_INTRIN_SSE4_2_RESOLVER
4071zend_string *php_addslashes_default(zend_string *str) /* {{{ */
4072# else
4074# endif
4075{
4076 /* maximum string length, worst case situation */
4077 char *target;
4078 const char *source, *end;
4079 size_t offset;
4080 zend_string *new_str;
4081
4082 if (!str) {
4083 return ZSTR_EMPTY_ALLOC();
4084 }
4085
4086 source = ZSTR_VAL(str);
4087 end = source + ZSTR_LEN(str);
4088
4089# if defined(__aarch64__) || defined(_M_ARM64)
4090 quad_word res = {0};
4091 if (ZSTR_LEN(str) > 15) {
4092 do {
4093 res = aarch64_contains_slash_chars(vld1q_u8((uint8_t *)source));
4094 if (res.dw[0] | res.dw[1])
4095 goto do_escape;
4096 source += 16;
4097 } while ((end - source) > 15);
4098 }
4099 /* Finish the last 15 bytes or less with the scalar loop. */
4100# endif /* defined(__aarch64__) || defined(_M_ARM64) */
4101
4102 while (source < end) {
4103 switch (*source) {
4104 case '\0':
4105 case '\'':
4106 case '\"':
4107 case '\\':
4108 goto do_escape;
4109 default:
4110 source++;
4111 break;
4112 }
4113 }
4114
4115 return zend_string_copy(str);
4116
4117do_escape:
4118 offset = source - (char *)ZSTR_VAL(str);
4119 new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0);
4120 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset);
4121 target = ZSTR_VAL(new_str) + offset;
4122
4123# if defined(__aarch64__) || defined(_M_ARM64)
4124 if (res.dw[0] | res.dw[1]) {
4125 target = aarch64_add_slashes(res, source, target);
4126 source += 16;
4127 }
4128 for (; end - source > 15; source += 16) {
4129 uint8x16_t x = vld1q_u8((uint8_t *)source);
4130 res = aarch64_contains_slash_chars(x);
4131 if (res.dw[0] | res.dw[1]) {
4132 target = aarch64_add_slashes(res, source, target);
4133 } else {
4134 vst1q_u8((uint8_t*)target, x);
4135 target += 16;
4136 }
4137 }
4138 /* Finish the last 15 bytes or less with the scalar loop. */
4139# endif /* defined(__aarch64__) || defined(_M_ARM64) */
4140
4141 while (source < end) {
4142 switch (*source) {
4143 case '\0':
4144 *target++ = '\\';
4145 *target++ = '0';
4146 break;
4147 case '\'':
4148 case '\"':
4149 case '\\':
4150 *target++ = '\\';
4152 default:
4153 *target++ = *source;
4154 break;
4155 }
4156 source++;
4157 }
4158
4159 *target = '\0';
4160
4161 if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) {
4162 new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0);
4163 } else {
4164 ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str);
4165 }
4166
4167 return new_str;
4168}
4169#endif
4170/* }}} */
4171/* }}} */
4172
4173/* {{{ php_stripslashes
4174 *
4175 * be careful, this edits the string in-place */
4176static zend_always_inline char *php_stripslashes_impl(const char *str, char *out, size_t len)
4177{
4178#if defined(__aarch64__) || defined(_M_ARM64)
4179 while (len > 15) {
4180 uint8x16_t x = vld1q_u8((uint8_t *)str);
4181 quad_word q;
4182 vst1q_u8(q.mem, vceqq_u8(x, vdupq_n_u8('\\')));
4183 if (q.dw[0] | q.dw[1]) {
4184 unsigned int i = 0;
4185 while (i < 16) {
4186 if (q.mem[i] == 0) {
4187 *out++ = str[i];
4188 i++;
4189 continue;
4190 }
4191
4192 i++; /* skip the slash */
4193 if (i < len) {
4194 char s = str[i];
4195 if (s == '0')
4196 *out++ = '\0';
4197 else
4198 *out++ = s; /* preserve the next character */
4199 i++;
4200 }
4201 }
4202 str += i;
4203 len -= i;
4204 } else {
4205 vst1q_u8((uint8_t*)out, x);
4206 out += 16;
4207 str += 16;
4208 len -= 16;
4209 }
4210 }
4211 /* Finish the last 15 bytes or less with the scalar loop. */
4212#endif /* defined(__aarch64__) || defined(_M_ARM64) */
4213 while (len > 0) {
4214 if (*str == '\\') {
4215 str++; /* skip the slash */
4216 len--;
4217 if (len > 0) {
4218 if (*str == '0') {
4219 *out++='\0';
4220 str++;
4221 } else {
4222 *out++ = *str++; /* preserve the next character */
4223 }
4224 len--;
4225 }
4226 } else {
4227 *out++ = *str++;
4228 len--;
4229 }
4230 }
4231
4232 return out;
4233}
4234
4235#ifdef __SSE2__
4237{
4238 const char *s = ZSTR_VAL(str);
4239 char *t = ZSTR_VAL(str);
4240 size_t l = ZSTR_LEN(str);
4241
4242 if (l > 15) {
4243 const __m128i slash = _mm_set1_epi8('\\');
4244
4245 do {
4246 __m128i in = _mm_loadu_si128((__m128i *)s);
4247 __m128i any_slash = _mm_cmpeq_epi8(in, slash);
4248 uint32_t res = _mm_movemask_epi8(any_slash);
4249
4250 if (res) {
4251 int i, n = zend_ulong_ntz(res);
4252 const char *e = s + 15;
4253 l -= n;
4254 for (i = 0; i < n; i++) {
4255 *t++ = *s++;
4256 }
4257 for (; s < e; s++) {
4258 if (*s == '\\') {
4259 s++;
4260 l--;
4261 if (*s == '0') {
4262 *t = '\0';
4263 } else {
4264 *t = *s;
4265 }
4266 } else {
4267 *t = *s;
4268 }
4269 t++;
4270 l--;
4271 }
4272 } else {
4273 _mm_storeu_si128((__m128i *)t, in);
4274 s += 16;
4275 t += 16;
4276 l -= 16;
4277 }
4278 } while (l > 15);
4279 }
4280
4281 t = php_stripslashes_impl(s, t, l);
4282 if (t != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
4283 ZSTR_LEN(str) = t - ZSTR_VAL(str);
4284 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
4285 }
4286}
4287#else
4289{
4290 const char *t = php_stripslashes_impl(ZSTR_VAL(str), ZSTR_VAL(str), ZSTR_LEN(str));
4291 if (t != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
4292 ZSTR_LEN(str) = t - ZSTR_VAL(str);
4293 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
4294 }
4295}
4296#endif
4297/* }}} */
4298
4299#define _HEB_BLOCK_TYPE_ENG 1
4300#define _HEB_BLOCK_TYPE_HEB 2
4301#define isheb(c) (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
4302#define _isblank(c) (((((unsigned char) c) == ' ' || ((unsigned char) c) == '\t')) ? 1 : 0)
4303#define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
4304
4305/* {{{ php_str_replace_in_subject */
4306static zend_long php_str_replace_in_subject(
4307 zend_string *search_str, HashTable *search_ht, zend_string *replace_str, HashTable *replace_ht,
4308 zend_string *subject_str, zval *result, bool case_sensitivity
4309) {
4310 zval *search_entry;
4311 zend_string *tmp_result;
4312 char *replace_value = NULL;
4313 size_t replace_len = 0;
4314 zend_long replace_count = 0;
4315 zend_string *lc_subject_str = NULL;
4316 uint32_t replace_idx;
4317
4318 if (ZSTR_LEN(subject_str) == 0) {
4320 return 0;
4321 }
4322
4323 /* If search is an array */
4324 if (search_ht) {
4325 /* Duplicate subject string for repeated replacement */
4326 zend_string_addref(subject_str);
4327
4328 if (replace_ht) {
4329 replace_idx = 0;
4330 } else {
4331 /* Set replacement value to the passed one */
4332 replace_value = ZSTR_VAL(replace_str);
4333 replace_len = ZSTR_LEN(replace_str);
4334 }
4335
4336 /* For each entry in the search array, get the entry */
4337 ZEND_HASH_FOREACH_VAL(search_ht, search_entry) {
4338 /* Make sure we're dealing with strings. */
4339 zend_string *tmp_search_str;
4340 zend_string *search_str = zval_get_tmp_string(search_entry, &tmp_search_str);
4341 zend_string *replace_entry_str, *tmp_replace_entry_str = NULL;
4342
4343 /* If replace is an array. */
4344 if (replace_ht) {
4345 /* Get current entry */
4346 zval *replace_entry = NULL;
4347 if (HT_IS_PACKED(replace_ht)) {
4348 while (replace_idx < replace_ht->nNumUsed) {
4349 replace_entry = &replace_ht->arPacked[replace_idx];
4350 if (Z_TYPE_P(replace_entry) != IS_UNDEF) {
4351 break;
4352 }
4353 replace_idx++;
4354 }
4355 } else {
4356 while (replace_idx < replace_ht->nNumUsed) {
4357 replace_entry = &replace_ht->arData[replace_idx].val;
4358 if (Z_TYPE_P(replace_entry) != IS_UNDEF) {
4359 break;
4360 }
4361 replace_idx++;
4362 }
4363 }
4364 if (replace_idx < replace_ht->nNumUsed) {
4365 /* Make sure we're dealing with strings. */
4366 replace_entry_str = zval_get_tmp_string(replace_entry, &tmp_replace_entry_str);
4367
4368 /* Set replacement value to the one we got from array */
4369 replace_value = ZSTR_VAL(replace_entry_str);
4370 replace_len = ZSTR_LEN(replace_entry_str);
4371
4372 replace_idx++;
4373 } else {
4374 /* We've run out of replacement strings, so use an empty one. */
4375 replace_value = "";
4376 replace_len = 0;
4377 }
4378 }
4379
4380 if (ZSTR_LEN(search_str) == 1) {
4381 zend_long old_replace_count = replace_count;
4382
4383 tmp_result = php_char_to_str_ex(subject_str,
4384 ZSTR_VAL(search_str)[0],
4385 replace_value,
4386 replace_len,
4387 case_sensitivity,
4388 &replace_count);
4389 if (lc_subject_str && replace_count != old_replace_count) {
4390 zend_string_release_ex(lc_subject_str, 0);
4391 lc_subject_str = NULL;
4392 }
4393 } else if (ZSTR_LEN(search_str) > 1) {
4394 if (case_sensitivity) {
4395 tmp_result = php_str_to_str_ex(subject_str,
4396 ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4397 replace_value, replace_len, &replace_count);
4398 } else {
4399 zend_long old_replace_count = replace_count;
4400
4401 if (!lc_subject_str) {
4402 lc_subject_str = zend_string_tolower(subject_str);
4403 }
4404 tmp_result = php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str),
4405 search_str, replace_value, replace_len, &replace_count);
4406 if (replace_count != old_replace_count) {
4407 zend_string_release_ex(lc_subject_str, 0);
4408 lc_subject_str = NULL;
4409 }
4410 }
4411 } else {
4412 zend_tmp_string_release(tmp_search_str);
4413 zend_tmp_string_release(tmp_replace_entry_str);
4414 continue;
4415 }
4416
4417 zend_tmp_string_release(tmp_search_str);
4418 zend_tmp_string_release(tmp_replace_entry_str);
4419
4420 if (subject_str == tmp_result) {
4421 zend_string_delref(subject_str);
4422 } else {
4423 zend_string_release_ex(subject_str, 0);
4424 subject_str = tmp_result;
4425 if (ZSTR_LEN(subject_str) == 0) {
4426 zend_string_release_ex(subject_str, 0);
4428 if (lc_subject_str) {
4429 zend_string_release_ex(lc_subject_str, 0);
4430 }
4431 return replace_count;
4432 }
4433 }
4435 ZVAL_STR(result, subject_str);
4436 if (lc_subject_str) {
4437 zend_string_release_ex(lc_subject_str, 0);
4438 }
4439 } else {
4440 ZEND_ASSERT(search_str);
4441 if (ZSTR_LEN(search_str) == 1) {
4443 php_char_to_str_ex(subject_str,
4444 ZSTR_VAL(search_str)[0],
4445 ZSTR_VAL(replace_str),
4446 ZSTR_LEN(replace_str),
4447 case_sensitivity,
4448 &replace_count));
4449 } else if (ZSTR_LEN(search_str) > 1) {
4450 if (case_sensitivity) {
4451 ZVAL_STR(result, php_str_to_str_ex(subject_str,
4452 ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4453 ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4454 } else {
4455 lc_subject_str = zend_string_tolower(subject_str);
4456 ZVAL_STR(result, php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str),
4457 search_str, ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4458 zend_string_release_ex(lc_subject_str, 0);
4459 }
4460 } else {
4461 ZVAL_STR_COPY(result, subject_str);
4462 }
4463 }
4464 return replace_count;
4465}
4466/* }}} */
4467
4468static void _php_str_replace_common(
4470 HashTable *search_ht, zend_string *search_str,
4471 HashTable *replace_ht, zend_string *replace_str,
4472 HashTable *subject_ht, zend_string *subject_str,
4473 zval *zcount,
4474 bool case_sensitivity
4475) {
4476 zval *subject_entry;
4477 zval result;
4478 zend_string *string_key;
4479 zend_ulong num_key;
4480 zend_long count = 0;
4481
4482 /* Make sure we're dealing with strings and do the replacement. */
4483 if (search_str && replace_ht) {
4484 zend_argument_type_error(2, "must be of type string when argument #1 ($search) is a string");
4485 RETURN_THROWS();
4486 }
4487
4488 /* if subject is an array */
4489 if (subject_ht) {
4491
4492 /* For each subject entry, convert it to string, then perform replacement
4493 and add the result to the return_value array. */
4494 ZEND_HASH_FOREACH_KEY_VAL(subject_ht, num_key, string_key, subject_entry) {
4495 zend_string *tmp_subject_str;
4496 ZVAL_DEREF(subject_entry);
4497 subject_str = zval_get_tmp_string(subject_entry, &tmp_subject_str);
4498 count += php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, &result, case_sensitivity);
4499 zend_tmp_string_release(tmp_subject_str);
4500
4501 /* Add to return array */
4502 if (string_key) {
4504 } else {
4506 }
4508 } else { /* if subject is not an array */
4509 count = php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, return_value, case_sensitivity);
4510 }
4511 if (zcount) {
4513 }
4514}
4515
4516/* {{{ php_str_replace_common */
4517static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool case_sensitivity)
4518{
4519 zend_string *search_str;
4520 HashTable *search_ht;
4521 zend_string *replace_str;
4522 HashTable *replace_ht;
4523 zend_string *subject_str;
4524 HashTable *subject_ht;
4525 zval *zcount = NULL;
4526
4528 Z_PARAM_ARRAY_HT_OR_STR(search_ht, search_str)
4529 Z_PARAM_ARRAY_HT_OR_STR(replace_ht, replace_str)
4530 Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
4532 Z_PARAM_ZVAL(zcount)
4534
4535 _php_str_replace_common(return_value, search_ht, search_str, replace_ht, replace_str, subject_ht, subject_str, zcount, case_sensitivity);
4536}
4537/* }}} */
4538
4539/* {{{ Replaces all occurrences of search in haystack with replace */
4541{
4542 php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4543}
4544/* }}} */
4545
4547{
4548 zend_string *search_str, *replace_str, *subject_str;
4549 HashTable *search_ht, *replace_ht, *subject_ht;
4550 zval search_tmp, replace_tmp, subject_tmp;
4551
4552 Z_FLF_PARAM_ARRAY_HT_OR_STR(1, search_ht, search_str, search_tmp);
4553 Z_FLF_PARAM_ARRAY_HT_OR_STR(2, replace_ht, replace_str, replace_tmp);
4554 Z_FLF_PARAM_ARRAY_HT_OR_STR(3, subject_ht, subject_str, subject_tmp);
4555
4556 _php_str_replace_common(return_value, search_ht, search_str, replace_ht, replace_str, subject_ht, subject_str, /* zcount */ NULL, /* case_sensitivity */ true);
4557
4558flf_clean:;
4559 Z_FLF_PARAM_FREE_STR(1, search_tmp);
4560 Z_FLF_PARAM_FREE_STR(2, replace_tmp);
4561 Z_FLF_PARAM_FREE_STR(3, subject_tmp);
4562}
4563
4564/* {{{ Replaces all occurrences of search in haystack with replace / case-insensitive */
4566{
4567 php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4568}
4569/* }}} */
4570
4571/* {{{ Converts logical Hebrew text to visual text */
4573{
4574 char *str, *heb_str, *target;
4575 const char *tmp;
4576 size_t block_start, block_end, block_type, i;
4577 zend_long max_chars=0, char_count;
4578 size_t begin, end, orig_begin;
4579 size_t str_len;
4580 zend_string *broken_str;
4581
4583 Z_PARAM_STRING(str, str_len)
4585 Z_PARAM_LONG(max_chars)
4587
4588 if (str_len == 0) {
4590 }
4591
4592 tmp = str;
4593 block_start=block_end=0;
4594
4595 heb_str = (char *) emalloc(str_len+1);
4596 target = heb_str+str_len;
4597 *target = 0;
4598 target--;
4599
4600 if (isheb(*tmp)) {
4601 block_type = _HEB_BLOCK_TYPE_HEB;
4602 } else {
4603 block_type = _HEB_BLOCK_TYPE_ENG;
4604 }
4605
4606 do {
4607 if (block_type == _HEB_BLOCK_TYPE_HEB) {
4608 while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end<str_len-1) {
4609 tmp++;
4610 block_end++;
4611 }
4612 for (i = block_start+1; i<= block_end+1; i++) {
4613 *target = str[i-1];
4614 switch (*target) {
4615 case '(':
4616 *target = ')';
4617 break;
4618 case ')':
4619 *target = '(';
4620 break;
4621 case '[':
4622 *target = ']';
4623 break;
4624 case ']':
4625 *target = '[';
4626 break;
4627 case '{':
4628 *target = '}';
4629 break;
4630 case '}':
4631 *target = '{';
4632 break;
4633 case '<':
4634 *target = '>';
4635 break;
4636 case '>':
4637 *target = '<';
4638 break;
4639 case '\\':
4640 *target = '/';
4641 break;
4642 case '/':
4643 *target = '\\';
4644 break;
4645 default:
4646 break;
4647 }
4648 target--;
4649 }
4650 block_type = _HEB_BLOCK_TYPE_ENG;
4651 } else {
4652 while (!isheb(*(tmp+1)) && (int)*(tmp+1)!='\n' && block_end < str_len-1) {
4653 tmp++;
4654 block_end++;
4655 }
4656 while ((_isblank((int)*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) {
4657 tmp--;
4658 block_end--;
4659 }
4660 for (i = block_end+1; i >= block_start+1; i--) {
4661 *target = str[i-1];
4662 target--;
4663 }
4664 block_type = _HEB_BLOCK_TYPE_HEB;
4665 }
4666 block_start=block_end+1;
4667 } while (block_end < str_len-1);
4668
4669
4670 broken_str = zend_string_alloc(str_len, 0);
4671 begin = end = str_len-1;
4672 target = ZSTR_VAL(broken_str);
4673
4674 while (1) {
4675 char_count=0;
4676 while ((!max_chars || (max_chars > 0 && char_count < max_chars)) && begin > 0) {
4677 char_count++;
4678 begin--;
4679 if (_isnewline(heb_str[begin])) {
4680 while (begin > 0 && _isnewline(heb_str[begin-1])) {
4681 begin--;
4682 char_count++;
4683 }
4684 break;
4685 }
4686 }
4687 if (max_chars >= 0 && char_count == max_chars) { /* try to avoid breaking words */
4688 size_t new_char_count=char_count, new_begin=begin;
4689
4690 while (new_char_count > 0) {
4691 if (_isblank(heb_str[new_begin]) || _isnewline(heb_str[new_begin])) {
4692 break;
4693 }
4694 new_begin++;
4695 new_char_count--;
4696 }
4697 if (new_char_count > 0) {
4698 begin=new_begin;
4699 }
4700 }
4701 orig_begin=begin;
4702
4703 if (_isblank(heb_str[begin])) {
4704 heb_str[begin]='\n';
4705 }
4706 while (begin <= end && _isnewline(heb_str[begin])) { /* skip leading newlines */
4707 begin++;
4708 }
4709 for (i = begin; i <= end; i++) { /* copy content */
4710 *target = heb_str[i];
4711 target++;
4712 }
4713 for (i = orig_begin; i <= end && _isnewline(heb_str[i]); i++) {
4714 *target = heb_str[i];
4715 target++;
4716 }
4717 begin=orig_begin;
4718
4719 if (begin == 0) {
4720 *target = 0;
4721 break;
4722 }
4723 begin--;
4724 end=begin;
4725 }
4726 efree(heb_str);
4727
4728 RETURN_NEW_STR(broken_str);
4729}
4730/* }}} */
4731
4732/* {{{ Converts newlines to HTML line breaks */
4734{
4735 /* in brief this inserts <br /> or <br> before matched regexp \n\r?|\r\n? */
4736 const char *tmp, *end;
4737 zend_string *str;
4738 char *target;
4739 size_t repl_cnt = 0;
4740 bool is_xhtml = 1;
4742
4744 Z_PARAM_STR(str)
4746 Z_PARAM_BOOL(is_xhtml)
4748
4749 tmp = ZSTR_VAL(str);
4750 end = ZSTR_VAL(str) + ZSTR_LEN(str);
4751
4752 /* it is really faster to scan twice and allocate mem once instead of scanning once
4753 and constantly reallocing */
4754 while (tmp < end) {
4755 if (*tmp == '\r') {
4756 if (*(tmp+1) == '\n') {
4757 tmp++;
4758 }
4759 repl_cnt++;
4760 } else if (*tmp == '\n') {
4761 if (*(tmp+1) == '\r') {
4762 tmp++;
4763 }
4764 repl_cnt++;
4765 }
4766
4767 tmp++;
4768 }
4769
4770 if (repl_cnt == 0) {
4771 RETURN_STR_COPY(str);
4772 }
4773
4774 {
4775 size_t repl_len = is_xhtml ? (sizeof("<br />") - 1) : (sizeof("<br>") - 1);
4776
4777 result = zend_string_safe_alloc(repl_cnt, repl_len, ZSTR_LEN(str), 0);
4778 target = ZSTR_VAL(result);
4779 }
4780
4781 tmp = ZSTR_VAL(str);
4782 while (tmp < end) {
4783 switch (*tmp) {
4784 case '\r':
4785 case '\n':
4786 *target++ = '<';
4787 *target++ = 'b';
4788 *target++ = 'r';
4789
4790 if (is_xhtml) {
4791 *target++ = ' ';
4792 *target++ = '/';
4793 }
4794
4795 *target++ = '>';
4796
4797 if ((*tmp == '\r' && *(tmp+1) == '\n') || (*tmp == '\n' && *(tmp+1) == '\r')) {
4798 *target++ = *tmp++;
4799 }
4801 default:
4802 *target++ = *tmp;
4803 }
4804
4805 tmp++;
4806 }
4807
4808 *target = '\0';
4809
4811}
4812/* }}} */
4813
4814/* {{{ Strips HTML and PHP tags from a string */
4816{
4818 zend_string *str;
4819 zend_string *allow_str = NULL;
4820 HashTable *allow_ht = NULL;
4821 const char *allowed_tags=NULL;
4822 size_t allowed_tags_len=0;
4823 smart_str tags_ss = {0};
4824
4826 Z_PARAM_STR(str)
4828 Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(allow_ht, allow_str)
4830
4831 if (allow_ht) {
4832 zval *tmp;
4833 zend_string *tag;
4834
4835 ZEND_HASH_FOREACH_VAL(allow_ht, tmp) {
4836 tag = zval_get_string(tmp);
4837 smart_str_appendc(&tags_ss, '<');
4838 smart_str_append(&tags_ss, tag);
4839 smart_str_appendc(&tags_ss, '>');
4840 zend_string_release(tag);
4842 if (tags_ss.s) {
4843 smart_str_0(&tags_ss);
4844 allowed_tags = ZSTR_VAL(tags_ss.s);
4845 allowed_tags_len = ZSTR_LEN(tags_ss.s);
4846 }
4847 } else if (allow_str) {
4848 allowed_tags = ZSTR_VAL(allow_str);
4849 allowed_tags_len = ZSTR_LEN(allow_str);
4850 }
4851
4852 buf = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
4853 ZSTR_LEN(buf) = php_strip_tags_ex(ZSTR_VAL(buf), ZSTR_LEN(str), allowed_tags, allowed_tags_len, 0);
4854 smart_str_free(&tags_ss);
4856}
4857/* }}} */
4858
4859static zend_string *try_setlocale_str(zend_long cat, zend_string *loc) {
4860 const char *retval;
4861
4862 if (zend_string_equals_literal(loc, "0")) {
4863 loc = NULL;
4864 } else {
4865 if (ZSTR_LEN(loc) >= 255) {
4866 php_error_docref(NULL, E_WARNING, "Specified locale name is too long");
4867 return NULL;
4868 }
4869 }
4870
4871# ifndef PHP_WIN32
4872 retval = setlocale(cat, loc ? ZSTR_VAL(loc) : NULL);
4873# else
4874 if (loc) {
4875 /* BC: don't try /^[a-z]{2}_[A-Z]{2}($|\..*)/ except for /^u[ks]_U[KS]$/ */
4876 char *locp = ZSTR_VAL(loc);
4877 if (ZSTR_LEN(loc) >= 5 && locp[2] == '_'
4878 && locp[0] >= 'a' && locp[0] <= 'z' && locp[1] >= 'a' && locp[1] <= 'z'
4879 && locp[3] >= 'A' && locp[3] <= 'Z' && locp[4] >= 'A' && locp[4] <= 'Z'
4880 && (locp[5] == '\0' || locp[5] == '.')
4881 && !(locp[0] == 'u' && (locp[1] == 'k' || locp[1] == 's')
4882 && locp[3] == 'U' && (locp[4] == 'K' || locp[4] == 'S')
4883 && locp[5] == '\0')
4884 ) {
4885 retval = NULL;
4886 } else {
4887 retval = setlocale(cat, ZSTR_VAL(loc));
4888 }
4889 } else {
4890 retval = setlocale(cat, NULL);
4891 }
4892# endif
4893 if (!retval) {
4894 return NULL;
4895 }
4896
4897 if (loc) {
4898 /* Remember if locale was changed */
4899 size_t len = strlen(retval);
4900
4901 BG(locale_changed) = 1;
4902 if (cat == LC_CTYPE || cat == LC_ALL) {
4904 if (BG(ctype_string)) {
4905 zend_string_release_ex(BG(ctype_string), 0);
4906 }
4907 if (len == 1 && *retval == 'C') {
4908 /* C locale is represented as NULL. */
4909 BG(ctype_string) = NULL;
4910 return ZSTR_CHAR('C');
4911 } else if (zend_string_equals_cstr(loc, retval, len)) {
4912 BG(ctype_string) = zend_string_copy(loc);
4913 return zend_string_copy(BG(ctype_string));
4914 } else {
4915 BG(ctype_string) = zend_string_init(retval, len, 0);
4916 return zend_string_copy(BG(ctype_string));
4917 }
4918 } else if (zend_string_equals_cstr(loc, retval, len)) {
4919 return zend_string_copy(loc);
4920 }
4921 }
4922 return zend_string_init(retval, strlen(retval), 0);
4923}
4924
4925static zend_string *try_setlocale_zval(zend_long cat, zval *loc_zv) {
4926 zend_string *tmp_loc_str;
4927 zend_string *loc_str = zval_try_get_tmp_string(loc_zv, &tmp_loc_str);
4928 if (UNEXPECTED(loc_str == NULL)) {
4929 return NULL;
4930 }
4931 zend_string *result = try_setlocale_str(cat, loc_str);
4932 zend_tmp_string_release(tmp_loc_str);
4933 return result;
4934}
4935
4936/* {{{ Set locale information */
4938{
4939 zend_long cat;
4940 zval *args = NULL;
4941 int num_args;
4942
4944 Z_PARAM_LONG(cat)
4947
4948 for (uint32_t i = 0; i < num_args; i++) {
4949 if (Z_TYPE(args[i]) == IS_ARRAY) {
4950 zval *elem;
4952 zend_string *result = try_setlocale_zval(cat, elem);
4953 if (EG(exception)) {
4954 RETURN_THROWS();
4955 }
4956 if (result) {
4958 }
4960 } else {
4961 zend_string *result = try_setlocale_zval(cat, &args[i]);
4962 if (EG(exception)) {
4963 RETURN_THROWS();
4964 }
4965 if (result) {
4967 }
4968 }
4969 }
4970
4972}
4973/* }}} */
4974
4975/* {{{ Parses GET/POST/COOKIE data and sets global variables */
4977{
4978 char *arg;
4979 zval *arrayArg = NULL;
4980 char *res = NULL;
4981 size_t arglen;
4982
4984 Z_PARAM_STRING(arg, arglen)
4985 Z_PARAM_ZVAL(arrayArg)
4987
4988 arrayArg = zend_try_array_init(arrayArg);
4989 if (!arrayArg) {
4990 RETURN_THROWS();
4991 }
4992
4993 res = estrndup(arg, arglen);
4994 sapi_module.treat_data(PARSE_STRING, res, arrayArg);
4995}
4996/* }}} */
4997
4998#define PHP_TAG_BUF_SIZE 1023
4999
5000/* {{{ php_tag_find
5001 *
5002 * Check if tag is in a set of tags
5003 *
5004 * states:
5005 *
5006 * 0 start tag
5007 * 1 first non-whitespace char seen
5008 */
5009static bool php_tag_find(char *tag, size_t len, const char *set) {
5010 char c, *n;
5011 const char *t;
5012 int state = 0;
5013 bool done = 0;
5014 char *norm;
5015
5016 if (len == 0) {
5017 return 0;
5018 }
5019
5020 norm = emalloc(len+1);
5021
5022 n = norm;
5023 t = tag;
5024 c = zend_tolower_ascii(*t);
5025 /*
5026 normalize the tag removing leading and trailing whitespace
5027 and turn any <a whatever...> into just <a> and any </tag>
5028 into <tag>
5029 */
5030 while (!done) {
5031 switch (c) {
5032 case '<':
5033 *(n++) = c;
5034 break;
5035 case '>':
5036 done =1;
5037 break;
5038 default:
5039 if (!isspace((int)c)) {
5040 if (state == 0) {
5041 state=1;
5042 }
5043 if (c != '/' || (*(t-1) != '<' && *(t+1) != '>')) {
5044 *(n++) = c;
5045 }
5046 } else {
5047 if (state == 1)
5048 done=1;
5049 }
5050 break;
5051 }
5052 c = zend_tolower_ascii(*(++t));
5053 }
5054 *(n++) = '>';
5055 *n = '\0';
5056 if (strstr(set, norm)) {
5057 done=1;
5058 } else {
5059 done=0;
5060 }
5061 efree(norm);
5062 return done;
5063}
5064/* }}} */
5065
5066PHPAPI size_t php_strip_tags(char *rbuf, size_t len, const char *allow, size_t allow_len) /* {{{ */
5067{
5068 return php_strip_tags_ex(rbuf, len, allow, allow_len, 0);
5069}
5070/* }}} */
5071
5072/* {{{ php_strip_tags
5073
5074 A simple little state-machine to strip out html and php tags
5075
5076 State 0 is the output state, State 1 means we are inside a
5077 normal html tag and state 2 means we are inside a php tag.
5078
5079 The state variable is passed in to allow a function like fgetss
5080 to maintain state across calls to the function.
5081
5082 lc holds the last significant character read and br is a bracket
5083 counter.
5084
5085 When an allow string is passed in we keep track of the string
5086 in state 1 and when the tag is closed check it against the
5087 allow string to see if we should allow it.
5088
5089 swm: Added ability to strip <?xml tags without assuming it PHP
5090 code.
5091*/
5092PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, const char *allow, size_t allow_len, bool allow_tag_spaces)
5093{
5094 char *tbuf, *tp, *rp, c, lc;
5095 const char *buf, *p, *end;
5096 int br, depth=0, in_q = 0;
5097 uint8_t state = 0;
5098 size_t pos;
5099 char *allow_free = NULL;
5100 char is_xml = 0;
5101
5102 buf = estrndup(rbuf, len);
5103 end = buf + len;
5104 lc = '\0';
5105 p = buf;
5106 rp = rbuf;
5107 br = 0;
5108 if (allow) {
5109 allow_free = zend_str_tolower_dup_ex(allow, allow_len);
5110 allow = allow_free ? allow_free : allow;
5111 tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
5112 tp = tbuf;
5113 } else {
5114 tbuf = tp = NULL;
5115 }
5116
5117state_0:
5118 if (p >= end) {
5119 goto finish;
5120 }
5121 c = *p;
5122 switch (c) {
5123 case '\0':
5124 break;
5125 case '<':
5126 if (in_q) {
5127 break;
5128 }
5129 if (isspace(*(p + 1)) && !allow_tag_spaces) {
5130 *(rp++) = c;
5131 break;
5132 }
5133 lc = '<';
5134 state = 1;
5135 if (allow) {
5136 if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
5137 pos = tp - tbuf;
5138 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
5139 tp = tbuf + pos;
5140 }
5141 *(tp++) = '<';
5142 }
5143 p++;
5144 goto state_1;
5145 case '>':
5146 if (depth) {
5147 depth--;
5148 break;
5149 }
5150
5151 if (in_q) {
5152 break;
5153 }
5154
5155 *(rp++) = c;
5156 break;
5157 default:
5158 *(rp++) = c;
5159 break;
5160 }
5161 p++;
5162 goto state_0;
5163
5164state_1:
5165 if (p >= end) {
5166 goto finish;
5167 }
5168 c = *p;
5169 switch (c) {
5170 case '\0':
5171 break;
5172 case '<':
5173 if (in_q) {
5174 break;
5175 }
5176 if (isspace(*(p + 1)) && !allow_tag_spaces) {
5177 goto reg_char_1;
5178 }
5179 depth++;
5180 break;
5181 case '>':
5182 if (depth) {
5183 depth--;
5184 break;
5185 }
5186 if (in_q) {
5187 break;
5188 }
5189
5190 lc = '>';
5191 if (is_xml && p >= buf + 1 && *(p -1) == '-') {
5192 break;
5193 }
5194 in_q = state = is_xml = 0;
5195 if (allow) {
5196 if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
5197 pos = tp - tbuf;
5198 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
5199 tp = tbuf + pos;
5200 }
5201 *(tp++) = '>';
5202 *tp='\0';
5203 if (php_tag_find(tbuf, tp-tbuf, allow)) {
5204 rp = zend_mempcpy(rp, tbuf, tp - tbuf);
5205 }
5206 tp = tbuf;
5207 }
5208 p++;
5209 goto state_0;
5210 case '"':
5211 case '\'':
5212 if (p != buf && (!in_q || *p == in_q)) {
5213 if (in_q) {
5214 in_q = 0;
5215 } else {
5216 in_q = *p;
5217 }
5218 }
5219 goto reg_char_1;
5220 case '!':
5221 /* JavaScript & Other HTML scripting languages */
5222 if (p >= buf + 1 && *(p-1) == '<') {
5223 state = 3;
5224 lc = c;
5225 p++;
5226 goto state_3;
5227 } else {
5228 goto reg_char_1;
5229 }
5230 break;
5231 case '?':
5232 if (p >= buf + 1 && *(p-1) == '<') {
5233 br=0;
5234 state = 2;
5235 p++;
5236 goto state_2;
5237 } else {
5238 goto reg_char_1;
5239 }
5240 break;
5241 default:
5242reg_char_1:
5243 if (allow) {
5244 if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
5245 pos = tp - tbuf;
5246 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
5247 tp = tbuf + pos;
5248 }
5249 *(tp++) = c;
5250 }
5251 break;
5252 }
5253 p++;
5254 goto state_1;
5255
5256state_2:
5257 if (p >= end) {
5258 goto finish;
5259 }
5260 c = *p;
5261 switch (c) {
5262 case '(':
5263 if (lc != '"' && lc != '\'') {
5264 lc = '(';
5265 br++;
5266 }
5267 break;
5268 case ')':
5269 if (lc != '"' && lc != '\'') {
5270 lc = ')';
5271 br--;
5272 }
5273 break;
5274 case '>':
5275 if (depth) {
5276 depth--;
5277 break;
5278 }
5279 if (in_q) {
5280 break;
5281 }
5282
5283 if (!br && p >= buf + 1 && lc != '\"' && *(p-1) == '?') {
5284 in_q = state = 0;
5285 tp = tbuf;
5286 p++;
5287 goto state_0;
5288 }
5289 break;
5290 case '"':
5291 case '\'':
5292 if (p >= buf + 1 && *(p-1) != '\\') {
5293 if (lc == c) {
5294 lc = '\0';
5295 } else if (lc != '\\') {
5296 lc = c;
5297 }
5298 if (p != buf && (!in_q || *p == in_q)) {
5299 if (in_q) {
5300 in_q = 0;
5301 } else {
5302 in_q = *p;
5303 }
5304 }
5305 }
5306 break;
5307 case 'l':
5308 case 'L':
5309 /* swm: If we encounter '<?xml' then we shouldn't be in
5310 * state == 2 (PHP). Switch back to HTML.
5311 */
5312 if (state == 2 && p > buf+4
5313 && (*(p-1) == 'm' || *(p-1) == 'M')
5314 && (*(p-2) == 'x' || *(p-2) == 'X')
5315 && *(p-3) == '?'
5316 && *(p-4) == '<') {
5317 state = 1; is_xml=1;
5318 p++;
5319 goto state_1;
5320 }
5321 break;
5322 default:
5323 break;
5324 }
5325 p++;
5326 goto state_2;
5327
5328state_3:
5329 if (p >= end) {
5330 goto finish;
5331 }
5332 c = *p;
5333 switch (c) {
5334 case '>':
5335 if (depth) {
5336 depth--;
5337 break;
5338 }
5339 if (in_q) {
5340 break;
5341 }
5342 in_q = state = 0;
5343 tp = tbuf;
5344 p++;
5345 goto state_0;
5346 case '"':
5347 case '\'':
5348 if (p != buf && *(p-1) != '\\' && (!in_q || *p == in_q)) {
5349 if (in_q) {
5350 in_q = 0;
5351 } else {
5352 in_q = *p;
5353 }
5354 }
5355 break;
5356 case '-':
5357 if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
5358 state = 4;
5359 p++;
5360 goto state_4;
5361 }
5362 break;
5363 case 'E':
5364 case 'e':
5365 /* !DOCTYPE exception */
5366 if (p > buf+6
5367 && (*(p-1) == 'p' || *(p-1) == 'P')
5368 && (*(p-2) == 'y' || *(p-2) == 'Y')
5369 && (*(p-3) == 't' || *(p-3) == 'T')
5370 && (*(p-4) == 'c' || *(p-4) == 'C')
5371 && (*(p-5) == 'o' || *(p-5) == 'O')
5372 && (*(p-6) == 'd' || *(p-6) == 'D')) {
5373 state = 1;
5374 p++;
5375 goto state_1;
5376 }
5377 break;
5378 default:
5379 break;
5380 }
5381 p++;
5382 goto state_3;
5383
5384state_4:
5385 while (p < end) {
5386 c = *p;
5387 if (c == '>' && !in_q) {
5388 if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
5389 in_q = state = 0;
5390 tp = tbuf;
5391 p++;
5392 goto state_0;
5393 }
5394 }
5395 p++;
5396 }
5397
5398finish:
5399 if (rp < rbuf + len) {
5400 *rp = '\0';
5401 }
5402 efree((void *)buf);
5403 if (tbuf) {
5404 efree(tbuf);
5405 }
5406 if (allow_free) {
5407 efree(allow_free);
5408 }
5409
5410 return (size_t)(rp - rbuf);
5411}
5412/* }}} */
5413
5414/* {{{ Parse a CSV string into an array */
5416{
5417 zend_string *str;
5418 char delimiter = ',', enclosure = '"';
5419 char *delimiter_str = NULL, *enclosure_str = NULL;
5420 size_t delimiter_str_len = 0, enclosure_str_len = 0;
5421 zend_string *escape_str = NULL;
5422
5424 Z_PARAM_STR(str)
5426 Z_PARAM_STRING(delimiter_str, delimiter_str_len)
5427 Z_PARAM_STRING(enclosure_str, enclosure_str_len)
5428 Z_PARAM_STR(escape_str)
5430
5431 if (delimiter_str != NULL) {
5432 /* Make sure that there is at least one character in string */
5433 if (delimiter_str_len != 1) {
5434 zend_argument_value_error(2, "must be a single character");
5435 RETURN_THROWS();
5436 }
5437 /* use first character from string */
5438 delimiter = delimiter_str[0];
5439 }
5440 if (enclosure_str != NULL) {
5441 if (enclosure_str_len != 1) {
5442 zend_argument_value_error(3, "must be a single character");
5443 RETURN_THROWS();
5444 }
5445 /* use first character from string */
5446 enclosure = enclosure_str[0];
5447 }
5448
5449 int escape_char = php_csv_handle_escape_argument(escape_str, 4);
5450 if (escape_char == PHP_CSV_ESCAPE_ERROR) {
5451 RETURN_THROWS();
5452 }
5453
5454 HashTable *values = php_fgetcsv(NULL, delimiter, enclosure, escape_char, ZSTR_LEN(str), ZSTR_VAL(str));
5455 if (values == NULL) {
5456 values = php_bc_fgetcsv_empty_line();
5457 }
5458 RETURN_ARR(values);
5459}
5460/* }}} */
5461
5462/* {{{ Returns the input string repeat mult times */
5464{
5465 zend_string *input_str; /* Input string */
5466 zend_long mult; /* Multiplier */
5467 zend_string *result; /* Resulting string */
5468 size_t result_len; /* Length of the resulting string */
5469
5471 Z_PARAM_STR(input_str)
5472 Z_PARAM_LONG(mult)
5474
5475 if (mult < 0) {
5476 zend_argument_value_error(2, "must be greater than or equal to 0");
5477 RETURN_THROWS();
5478 }
5479
5480 /* Don't waste our time if it's empty */
5481 /* ... or if the multiplier is zero */
5482 if (ZSTR_LEN(input_str) == 0 || mult == 0)
5484
5485 /* Initialize the result string */
5486 result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
5487 result_len = ZSTR_LEN(input_str) * mult;
5489
5490 /* Heavy optimization for situations where input string is 1 byte long */
5491 if (ZSTR_LEN(input_str) == 1) {
5492 memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
5493 } else {
5494 const char *s, *ee;
5495 char *e;
5496 ptrdiff_t l=0;
5497 memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
5498 s = ZSTR_VAL(result);
5499 e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
5500 ee = ZSTR_VAL(result) + result_len;
5501
5502 while (e<ee) {
5503 l = (e-s) < (ee-e) ? (e-s) : (ee-e);
5504 memmove(e, s, l);
5505 e += l;
5506 }
5507 }
5508
5509 ZSTR_VAL(result)[result_len] = '\0';
5510
5512}
5513/* }}} */
5514
5515/* {{{ Returns info about what characters are used in input */
5517{
5518 zend_string *input;
5519 int chars[256];
5520 zend_long mymode=0;
5521 const unsigned char *buf;
5522 int inx;
5523 char retstr[256];
5524 size_t retlen=0;
5525 size_t tmp = 0;
5526
5528 Z_PARAM_STR(input)
5530 Z_PARAM_LONG(mymode)
5532
5533 if (mymode < 0 || mymode > 4) {
5534 zend_argument_value_error(2, "must be between 0 and 4 (inclusive)");
5535 RETURN_THROWS();
5536 }
5537
5538 buf = (const unsigned char *) ZSTR_VAL(input);
5539 memset((void*) chars, 0, sizeof(chars));
5540
5541 while (tmp < ZSTR_LEN(input)) {
5542 chars[*buf]++;
5543 buf++;
5544 tmp++;
5545 }
5546
5547 if (mymode < 3) {
5549 }
5550
5551 for (inx = 0; inx < 256; inx++) {
5552 switch (mymode) {
5553 case 0:
5554 add_index_long(return_value, inx, chars[inx]);
5555 break;
5556 case 1:
5557 if (chars[inx] != 0) {
5558 add_index_long(return_value, inx, chars[inx]);
5559 }
5560 break;
5561 case 2:
5562 if (chars[inx] == 0) {
5563 add_index_long(return_value, inx, chars[inx]);
5564 }
5565 break;
5566 case 3:
5567 if (chars[inx] != 0) {
5568 retstr[retlen++] = inx;
5569 }
5570 break;
5571 case 4:
5572 if (chars[inx] == 0) {
5573 retstr[retlen++] = inx;
5574 }
5575 break;
5576 }
5577 }
5578
5579 if (mymode == 3 || mymode == 4) {
5580 RETURN_STRINGL(retstr, retlen);
5581 }
5582}
5583/* }}} */
5584
5585/* {{{ php_strnatcmp */
5586static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, bool is_case_insensitive)
5587{
5588 zend_string *s1, *s2;
5589
5591 Z_PARAM_STR(s1)
5592 Z_PARAM_STR(s2)
5594
5596 ZSTR_VAL(s2), ZSTR_LEN(s2),
5597 is_case_insensitive));
5598}
5599/* }}} */
5600
5601/* {{{ Returns the result of string comparison using 'natural' algorithm */
5603{
5604 php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5605}
5606/* }}} */
5607
5608/* {{{ Returns the result of case-insensitive string comparison using 'natural' algorithm */
5613/* }}} */
5614
5615/* {{{ Returns numeric formatting information based on the current locale */
5617{
5618 zval grouping, mon_grouping;
5619 size_t len, i;
5620
5622
5624 array_init(&grouping);
5625 array_init(&mon_grouping);
5626
5627 {
5628 struct lconv currlocdata;
5629
5630 localeconv_r( &currlocdata );
5631
5632 /* Grab the grouping data out of the array */
5633 len = strlen(currlocdata.grouping);
5634
5635 for (i = 0; i < len; i++) {
5636 add_index_long(&grouping, i, currlocdata.grouping[i]);
5637 }
5638
5639 /* Grab the monetary grouping data out of the array */
5640 len = strlen(currlocdata.mon_grouping);
5641
5642 for (i = 0; i < len; i++) {
5643 add_index_long(&mon_grouping, i, currlocdata.mon_grouping[i]);
5644 }
5645
5646 add_assoc_string(return_value, "decimal_point", currlocdata.decimal_point);
5647 add_assoc_string(return_value, "thousands_sep", currlocdata.thousands_sep);
5648 add_assoc_string(return_value, "int_curr_symbol", currlocdata.int_curr_symbol);
5649 add_assoc_string(return_value, "currency_symbol", currlocdata.currency_symbol);
5650 add_assoc_string(return_value, "mon_decimal_point", currlocdata.mon_decimal_point);
5651 add_assoc_string(return_value, "mon_thousands_sep", currlocdata.mon_thousands_sep);
5652 add_assoc_string(return_value, "positive_sign", currlocdata.positive_sign);
5653 add_assoc_string(return_value, "negative_sign", currlocdata.negative_sign);
5654 add_assoc_long( return_value, "int_frac_digits", currlocdata.int_frac_digits);
5655 add_assoc_long( return_value, "frac_digits", currlocdata.frac_digits);
5656 add_assoc_long( return_value, "p_cs_precedes", currlocdata.p_cs_precedes);
5657 add_assoc_long( return_value, "p_sep_by_space", currlocdata.p_sep_by_space);
5658 add_assoc_long( return_value, "n_cs_precedes", currlocdata.n_cs_precedes);
5659 add_assoc_long( return_value, "n_sep_by_space", currlocdata.n_sep_by_space);
5660 add_assoc_long( return_value, "p_sign_posn", currlocdata.p_sign_posn);
5661 add_assoc_long( return_value, "n_sign_posn", currlocdata.n_sign_posn);
5662 }
5663
5664 zend_hash_str_update(Z_ARRVAL_P(return_value), "grouping", sizeof("grouping")-1, &grouping);
5665 zend_hash_str_update(Z_ARRVAL_P(return_value), "mon_grouping", sizeof("mon_grouping")-1, &mon_grouping);
5666}
5667/* }}} */
5668
5669/* {{{ Returns the number of times a substring occurs in the string */
5671{
5672 char *haystack, *needle;
5673 zend_long offset = 0, length = 0;
5674 bool length_is_null = 1;
5676 size_t haystack_len, needle_len;
5677 const char *p, *endp;
5678
5680 Z_PARAM_STRING(haystack, haystack_len)
5681 Z_PARAM_STRING(needle, needle_len)
5684 Z_PARAM_LONG_OR_NULL(length, length_is_null)
5686
5687 if (needle_len == 0) {
5689 RETURN_THROWS();
5690 }
5691
5692 p = haystack;
5693
5694 if (offset) {
5695 if (offset < 0) {
5696 offset += (zend_long)haystack_len;
5697 }
5698 if ((offset < 0) || ((size_t)offset > haystack_len)) {
5699 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
5700 RETURN_THROWS();
5701 }
5702 p += offset;
5703 haystack_len -= offset;
5704 }
5705
5706 if (!length_is_null) {
5707 if (length < 0) {
5708 length += haystack_len;
5709 }
5710 if (length < 0 || ((size_t)length > haystack_len)) {
5711 zend_argument_value_error(4, "must be contained in argument #1 ($haystack)");
5712 RETURN_THROWS();
5713 }
5714 } else {
5715 length = haystack_len;
5716 }
5717
5718 if (needle_len == 1) {
5719 count = count_chars(p, length, needle[0]);
5720 } else {
5721 count = 0;
5722 endp = p + length;
5723 while ((p = (char*)php_memnstr(p, needle, needle_len, endp))) {
5724 p += needle_len;
5725 count++;
5726 }
5727 }
5728
5730}
5731/* }}} */
5732
5733/* {{{ Returns input string padded on the left or right to specified length with pad_string */
5735{
5736 /* Input arguments */
5737 zend_string *input; /* Input string */
5738 zend_long pad_length; /* Length to pad to */
5739
5740 /* Helper variables */
5741 size_t num_pad_chars; /* Number of padding characters (total - input size) */
5742 char *pad_str = " "; /* Pointer to padding string */
5743 size_t pad_str_len = 1;
5744 zend_long pad_type_val = PHP_STR_PAD_RIGHT; /* The padding type value */
5745 size_t i, left_pad=0, right_pad=0;
5746 zend_string *result = NULL; /* Resulting string */
5747
5749 Z_PARAM_STR(input)
5750 Z_PARAM_LONG(pad_length)
5752 Z_PARAM_STRING(pad_str, pad_str_len)
5753 Z_PARAM_LONG(pad_type_val)
5755
5756 /* If resulting string turns out to be shorter than input string,
5757 we simply copy the input and return. */
5758 if (pad_length < 0 || (size_t)pad_length <= ZSTR_LEN(input)) {
5759 RETURN_STR_COPY(input);
5760 }
5761
5762 if (pad_str_len == 0) {
5764 RETURN_THROWS();
5765 }
5766
5767 if (pad_type_val < PHP_STR_PAD_LEFT || pad_type_val > PHP_STR_PAD_BOTH) {
5768 zend_argument_value_error(4, "must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
5769 RETURN_THROWS();
5770 }
5771
5772 num_pad_chars = pad_length - ZSTR_LEN(input);
5773 result = zend_string_safe_alloc(1, ZSTR_LEN(input), num_pad_chars, 0);
5774 ZSTR_LEN(result) = 0;
5775
5776 /* We need to figure out the left/right padding lengths. */
5777 switch (pad_type_val) {
5778 case PHP_STR_PAD_RIGHT:
5779 left_pad = 0;
5780 right_pad = num_pad_chars;
5781 break;
5782
5783 case PHP_STR_PAD_LEFT:
5784 left_pad = num_pad_chars;
5785 right_pad = 0;
5786 break;
5787
5788 case PHP_STR_PAD_BOTH:
5789 left_pad = num_pad_chars / 2;
5790 right_pad = num_pad_chars - left_pad;
5791 break;
5792 }
5793
5794 /* First we pad on the left. */
5795 for (i = 0; i < left_pad; i++)
5796 ZSTR_VAL(result)[ZSTR_LEN(result)++] = pad_str[i % pad_str_len];
5797
5798 /* Then we copy the input string. */
5799 memcpy(ZSTR_VAL(result) + ZSTR_LEN(result), ZSTR_VAL(input), ZSTR_LEN(input));
5800 ZSTR_LEN(result) += ZSTR_LEN(input);
5801
5802 /* Finally, we pad on the right. */
5803 for (i = 0; i < right_pad; i++)
5804 ZSTR_VAL(result)[ZSTR_LEN(result)++] = pad_str[i % pad_str_len];
5805
5806 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
5807
5809}
5810/* }}} */
5811
5812/* {{{ Implements an ANSI C compatible sscanf */
5814{
5815 zval *args = NULL;
5816 char *str, *format;
5817 size_t str_len, format_len;
5818 int result, num_args = 0;
5819
5821 Z_PARAM_STRING(str, str_len)
5822 Z_PARAM_STRING(format, format_len)
5825
5827
5830 }
5831}
5832/* }}} */
5833
5834/* static zend_string *php_str_rot13(zend_string *str) {{{ */
5835static zend_string *php_str_rot13(zend_string *str)
5836{
5838 const char *p, *e;
5839 char *target;
5840
5841 if (UNEXPECTED(ZSTR_LEN(str) == 0)) {
5842 return ZSTR_EMPTY_ALLOC();
5843 }
5844
5845 ret = zend_string_alloc(ZSTR_LEN(str), 0);
5846
5847 p = ZSTR_VAL(str);
5848 e = p + ZSTR_LEN(str);
5849 target = ZSTR_VAL(ret);
5850
5851#ifdef __SSE2__
5852 if (e - p > 15) {
5853 const __m128i a_minus_1 = _mm_set1_epi8('a' - 1);
5854 const __m128i m_plus_1 = _mm_set1_epi8('m' + 1);
5855 const __m128i n_minus_1 = _mm_set1_epi8('n' - 1);
5856 const __m128i z_plus_1 = _mm_set1_epi8('z' + 1);
5857 const __m128i A_minus_1 = _mm_set1_epi8('A' - 1);
5858 const __m128i M_plus_1 = _mm_set1_epi8('M' + 1);
5859 const __m128i N_minus_1 = _mm_set1_epi8('N' - 1);
5860 const __m128i Z_plus_1 = _mm_set1_epi8('Z' + 1);
5861 const __m128i add = _mm_set1_epi8(13);
5862 const __m128i sub = _mm_set1_epi8(-13);
5863
5864 do {
5865 __m128i in, gt, lt, cmp, delta;
5866
5867 delta = _mm_setzero_si128();
5868 in = _mm_loadu_si128((__m128i *)p);
5869
5870 gt = _mm_cmpgt_epi8(in, a_minus_1);
5871 lt = _mm_cmplt_epi8(in, m_plus_1);
5872 cmp = _mm_and_si128(lt, gt);
5873 if (_mm_movemask_epi8(cmp)) {
5874 cmp = _mm_and_si128(cmp, add);
5875 delta = _mm_or_si128(delta, cmp);
5876 }
5877
5878 gt = _mm_cmpgt_epi8(in, n_minus_1);
5879 lt = _mm_cmplt_epi8(in, z_plus_1);
5880 cmp = _mm_and_si128(lt, gt);
5881 if (_mm_movemask_epi8(cmp)) {
5882 cmp = _mm_and_si128(cmp, sub);
5883 delta = _mm_or_si128(delta, cmp);
5884 }
5885
5886 gt = _mm_cmpgt_epi8(in, A_minus_1);
5887 lt = _mm_cmplt_epi8(in, M_plus_1);
5888 cmp = _mm_and_si128(lt, gt);
5889 if (_mm_movemask_epi8(cmp)) {
5890 cmp = _mm_and_si128(cmp, add);
5891 delta = _mm_or_si128(delta, cmp);
5892 }
5893
5894 gt = _mm_cmpgt_epi8(in, N_minus_1);
5895 lt = _mm_cmplt_epi8(in, Z_plus_1);
5896 cmp = _mm_and_si128(lt, gt);
5897 if (_mm_movemask_epi8(cmp)) {
5898 cmp = _mm_and_si128(cmp, sub);
5899 delta = _mm_or_si128(delta, cmp);
5900 }
5901
5902 in = _mm_add_epi8(in, delta);
5903 _mm_storeu_si128((__m128i *)target, in);
5904
5905 p += 16;
5906 target += 16;
5907 } while (e - p > 15);
5908 }
5909#endif
5910
5911 while (p < e) {
5912 if (*p >= 'a' && *p <= 'z') {
5913 *target++ = 'a' + (((*p++ - 'a') + 13) % 26);
5914 } else if (*p >= 'A' && *p <= 'Z') {
5915 *target++ = 'A' + (((*p++ - 'A') + 13) % 26);
5916 } else {
5917 *target++ = *p++;
5918 }
5919 }
5920
5921 *target = '\0';
5922
5923 return ret;
5924}
5925/* }}} */
5926
5927/* {{{ Perform the rot13 transform on a string */
5929{
5931
5935
5936 RETURN_STR(php_str_rot13(arg));
5937}
5938/* }}} */
5939
5940/* {{{ php_binary_string_shuffle */
5942{
5943 const php_random_algo *algo = engine.algo;
5944 void *state = engine.state;
5945
5946 int64_t n_elems, rnd_idx, n_left;
5947 char temp;
5948
5949 /* The implementation is stolen from array_data_shuffle */
5950 /* Thus the characteristics of the randomization are the same */
5951 n_elems = len;
5952
5953 if (n_elems <= 1) {
5954 return true;
5955 }
5956
5957 n_left = n_elems;
5958
5959 while (--n_left) {
5960 rnd_idx = algo->range(state, 0, n_left);
5961 if (EG(exception)) {
5962 return false;
5963 }
5964 if (rnd_idx != n_left) {
5965 temp = str[n_left];
5966 str[n_left] = str[rnd_idx];
5967 str[rnd_idx] = temp;
5968 }
5969 }
5970
5971 return true;
5972}
5973/* }}} */
5974
5975/* {{{ Shuffles string. One permutation of all possible is created */
5977{
5979
5983
5985 if (Z_STRLEN_P(return_value) > 1) {
5987 php_random_default_engine(),
5990 );
5991 }
5992}
5993/* }}} */
5994
5995/* {{{ Counts the number of words inside a string. If format of 1 is specified,
5996 then the function will return an array containing all the words
5997 found inside the string. If format of 2 is specified, then the function
5998 will return an associated array where the position of the word is the key
5999 and the word itself is the value.
6000 For the purpose of this function, 'word' is defined as a locale dependent
6001 string containing alphabetic characters, which also may contain, but not start
6002 with "'" and "-" characters.
6003*/
6005{
6006 zend_string *str;
6007 char *char_list = NULL, ch[256];
6008 const char *p, *e, *s;
6009 size_t char_list_len = 0, word_count = 0;
6010 zend_long type = 0;
6011
6013 Z_PARAM_STR(str)
6016 Z_PARAM_STRING_OR_NULL(char_list, char_list_len)
6018
6019 switch(type) {
6020 case 1:
6021 case 2:
6023 if (!ZSTR_LEN(str)) {
6024 return;
6025 }
6026 break;
6027 case 0:
6028 if (!ZSTR_LEN(str)) {
6029 RETURN_LONG(0);
6030 }
6031 /* nothing to be done */
6032 break;
6033 default:
6034 zend_argument_value_error(2, "must be a valid format value");
6035 RETURN_THROWS();
6036 }
6037
6038 if (char_list) {
6039 php_charmask((const unsigned char *) char_list, char_list_len, ch);
6040 }
6041
6042 p = ZSTR_VAL(str);
6043 e = ZSTR_VAL(str) + ZSTR_LEN(str);
6044
6045 /* first character cannot be ' or -, unless explicitly allowed by the user */
6046 if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
6047 p++;
6048 }
6049 /* last character cannot be -, unless explicitly allowed by the user */
6050 if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
6051 e--;
6052 }
6053
6054 while (p < e) {
6055 s = p;
6056 while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
6057 p++;
6058 }
6059 if (p > s) {
6060 switch (type)
6061 {
6062 case 1:
6064 break;
6065 case 2:
6066 add_index_stringl(return_value, (s - ZSTR_VAL(str)), s, p - s);
6067 break;
6068 default:
6069 word_count++;
6070 break;
6071 }
6072 }
6073 p++;
6074 }
6075
6076 if (!type) {
6077 RETURN_LONG(word_count);
6078 }
6079}
6080
6081/* }}} */
6082
6083/* {{{ Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
6085{
6086 zend_string *str;
6087 zend_long split_length = 1;
6088 const char *p;
6089 size_t n_reg_segments;
6090
6092 Z_PARAM_STR(str)
6094 Z_PARAM_LONG(split_length)
6096
6097 if (split_length <= 0) {
6098 zend_argument_value_error(2, "must be greater than 0");
6099 RETURN_THROWS();
6100 }
6101
6102 if ((size_t)split_length >= ZSTR_LEN(str)) {
6103 if (0 == ZSTR_LEN(str)) {
6105 }
6106
6109 return;
6110 }
6111
6112 array_init_size(return_value, (uint32_t)(((ZSTR_LEN(str) - 1) / split_length) + 1));
6113
6114 n_reg_segments = ZSTR_LEN(str) / split_length;
6115 p = ZSTR_VAL(str);
6116
6117 while (n_reg_segments-- > 0) {
6118 add_next_index_stringl(return_value, p, split_length);
6119 p += split_length;
6120 }
6121
6122 if (p != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
6124 }
6125}
6126/* }}} */
6127
6128/* {{{ Search a string for any of a set of characters */
6130{
6131 zend_string *haystack, *char_list;
6132
6134 Z_PARAM_STR(haystack)
6135 Z_PARAM_STR(char_list)
6137
6138 if (!ZSTR_LEN(char_list)) {
6139 zend_argument_value_error(2, "must be a non-empty string");
6140 RETURN_THROWS();
6141 }
6142
6143 size_t shift = php_strcspn(
6144 ZSTR_VAL(haystack),
6145 ZSTR_VAL(char_list),
6146 ZSTR_VAL(haystack) + ZSTR_LEN(haystack),
6147 ZSTR_VAL(char_list) + ZSTR_LEN(char_list)
6148 );
6149 if (shift < ZSTR_LEN(haystack)) {
6150 RETURN_STRINGL(ZSTR_VAL(haystack) + shift, ZSTR_LEN(haystack) - shift);
6151 }
6152
6154}
6155/* }}} */
6156
6157/* {{{ Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
6159{
6160 zend_string *s1, *s2;
6161 zend_long offset, len=0;
6162 bool len_is_default=1;
6163 bool cs=0;
6164 size_t cmp_len;
6165
6167 Z_PARAM_STR(s1)
6168 Z_PARAM_STR(s2)
6171 Z_PARAM_LONG_OR_NULL(len, len_is_default)
6172 Z_PARAM_BOOL(cs)
6174
6175 if (!len_is_default && len <= 0) {
6176 if (len == 0) {
6177 RETURN_LONG(0L);
6178 } else {
6179 zend_argument_value_error(4, "must be greater than or equal to 0");
6180 RETURN_THROWS();
6181 }
6182 }
6183
6184 if (offset < 0) {
6185 offset = ZSTR_LEN(s1) + offset;
6186 offset = (offset < 0) ? 0 : offset;
6187 }
6188
6189 if ((size_t)offset > ZSTR_LEN(s1)) {
6190 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
6191 RETURN_THROWS();
6192 }
6193
6194 cmp_len = len ? (size_t)len : MAX(ZSTR_LEN(s2), (ZSTR_LEN(s1) - offset));
6195
6196 if (!cs) {
6197 RETURN_LONG(zend_binary_strncmp(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
6198 } else {
6200 }
6201}
6202/* }}} */
6203
6204/* {{{ */
6205static zend_string *php_utf8_encode(const char *s, size_t len)
6206{
6207 size_t pos = len;
6208 zend_string *str;
6209 unsigned char c;
6210
6211 str = zend_string_safe_alloc(len, 2, 0, 0);
6212 ZSTR_LEN(str) = 0;
6213 while (pos > 0) {
6214 /* The lower 256 codepoints of Unicode are identical to Latin-1,
6215 * so we don't need to do any mapping here. */
6216 c = (unsigned char)(*s);
6217 if (c < 0x80) {
6218 ZSTR_VAL(str)[ZSTR_LEN(str)++] = (char) c;
6219 /* We only account for the single-byte and two-byte cases because
6220 * we're only dealing with the first 256 Unicode codepoints. */
6221 } else {
6222 ZSTR_VAL(str)[ZSTR_LEN(str)++] = (0xc0 | (c >> 6));
6223 ZSTR_VAL(str)[ZSTR_LEN(str)++] = (0x80 | (c & 0x3f));
6224 }
6225 pos--;
6226 s++;
6227 }
6228 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
6229 str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6230 return str;
6231}
6232/* }}} */
6233
6234/* {{{ */
6235static zend_string *php_utf8_decode(const char *s, size_t len)
6236{
6237 size_t pos = 0;
6238 unsigned int c;
6239 zend_string *str;
6240
6241 str = zend_string_alloc(len, 0);
6242 ZSTR_LEN(str) = 0;
6243 while (pos < len) {
6245 c = php_next_utf8_char((const unsigned char*)s, (size_t) len, &pos, &status);
6246
6247 /* The lower 256 codepoints of Unicode are identical to Latin-1,
6248 * so we don't need to do any mapping here beyond replacing non-Latin-1
6249 * characters. */
6250 if (status == FAILURE || c > 0xFFU) {
6251 c = '?';
6252 }
6253
6254 ZSTR_VAL(str)[ZSTR_LEN(str)++] = c;
6255 }
6256 ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
6257 if (ZSTR_LEN(str) < len) {
6258 str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6259 }
6260
6261 return str;
6262}
6263/* }}} */
6264
6265/* {{{ Encodes an ISO-8859-1 string to UTF-8 */
6267{
6268 char *arg;
6269 size_t arg_len;
6270
6272 Z_PARAM_STRING(arg, arg_len)
6274
6275 RETURN_STR(php_utf8_encode(arg, arg_len));
6276}
6277/* }}} */
6278
6279/* {{{ Converts a UTF-8 encoded string to ISO-8859-1 */
6281{
6282 char *arg;
6283 size_t arg_len;
6284
6286 Z_PARAM_STRING(arg, arg_len)
6288
6289 RETURN_STR(php_utf8_decode(arg, arg_len));
6290}
6291/* }}} */
SAPI_API sapi_module_struct sapi_module
Definition SAPI.c:65
size_t len
Definition apprentice.c:174
file_private const char ext[]
bool exception
Definition assert.c:30
#define BG(v)
nl_langinfo(int $item)
const P_SIGN_POSN
const ABMON_3
stristr(string $haystack, string $needle, bool $before_needle=false)
stripos(string $haystack, string $needle, int $offset=0)
str_split(string $string, int $length=1)
const ABDAY_4
const THOUSANDS_SEP
const ABMON_7
const P_SEP_BY_SPACE
const NEGATIVE_SIGN
const D_T_FMT
quotemeta(string $string)
str_increment(string $string)
bin2hex(string $string)
str_repeat(string $string, int $times)
implode(string|array $separator, ?array $array=null)
lcfirst(string $string)
str_getcsv(string $string, string $separator=",", string $enclosure="\"", string $escape="\\")
sscanf(string $string, string $format, mixed &... $vars)
const MON_DECIMAL_POINT
str_rot13(string $string)
const N_CS_PRECEDES
utf8_encode(string $string)
rtrim(string $string, string $characters=" \n\r\t\v\0")
const ALT_DIGITS
str_contains(string $haystack, string $needle)
const ERA_YEAR
trim(string $string, string $characters=" \n\r\t\v\0")
const ABMON_8
substr_count(string $haystack, string $needle, int $offset=0, ?int $length=null)
const LC_CTYPE
ord(string $character)
str_ireplace(array|string $search, array|string $replace, string|array $subject, &$count=null)
strip_tags(string $string, array|string|null $allowed_tags=null)
str_decrement(string $string)
const RADIXCHAR
setlocale(int $category, $locales,... $rest)
wordwrap(string $string, int $width=75, string $break="\n", bool $cut_long_words=false)
const ERA_D_FMT
strtoupper(string $string)
explode(string $separator, string $string, int $limit=PHP_INT_MAX)
ucwords(string $string, string $separators=" \t\r\n\f\v")
str_ends_with(string $haystack, string $needle)
const POSITIVE_SIGN
substr_replace(array|string $string, array|string $replace, array|int $offset, array|int|null $length=null)
const ERA_T_FMT
strtr(string $string, string|array $from, ?string $to=null)
const ABMON_5
const ABMON_10
strpos(string $haystack, string $needle, int $offset=0)
strrev(string $string)
const ABMON_9
strtok(string $string, ?string $token=null)
str_replace(array|string $search, array|string $replace, string|array $subject, &$count=null)
strcoll(string $string1, string $string2)
ucfirst(string $string)
strrchr(string $haystack, string $needle, bool $before_needle=false)
const N_SEP_BY_SPACE
pathinfo(string $path, int $flags=PATHINFO_ALL)
str_word_count(string $string, int $format=0, ?string $characters=null)
const INT_FRAC_DIGITS
utf8_decode(string $string)
const ABDAY_6
const ABDAY_5
addslashes(string $string)
const INT_CURR_SYMBOL
stripcslashes(string $string)
dirname(string $path, int $levels=1)
strrpos(string $haystack, string $needle, int $offset=0)
const MON_GROUPING
str_starts_with(string $haystack, string $needle)
const ABDAY_7
const T_FMT_AMPM
nl2br(string $string, bool $use_xhtml=true)
hebrev(string $string, int $max_chars_per_line=0)
substr_compare(string $haystack, string $needle, int $offset, ?int $length=null, bool $case_insensitive=false)
strspn(string $string, string $characters, int $offset=0, ?int $length=null)
const ABMON_11
const ABMON_2
strtolower(string $string)
const ABDAY_3
const DECIMAL_POINT
str_pad(string $string, int $length, string $pad_string=" ", int $pad_type=STR_PAD_RIGHT)
ltrim(string $string, string $characters=" \n\r\t\v\0")
const CRNCYSTR
count_chars(string $string, int $mode=0)
strpbrk(string $string, string $characters)
const N_SIGN_POSN
count(Countable|array $value, int $mode=COUNT_NORMAL)
addcslashes(string $string, string $characters)
const ABMON_12
str_shuffle(string $string)
const THOUSEP
const ABDAY_2
strstr(string $haystack, string $needle, bool $before_needle=false)
hex2bin(string $string)
chr(int $codepoint)
stripslashes(string $string)
const MON_THOUSANDS_SEP
basename(string $path, string $suffix="")
strcspn(string $string, string $characters, int $offset=0, ?int $length=null)
const FRAC_DIGITS
const CURRENCY_SYMBOL
const GROUPING
const ABMON_4
parse_str(string $string, &$result)
similar_text(string $string1, string $string2, &$percent=null)
const YESEXPR
const ABDAY_1
const CODESET
const ABMON_1
const ERA_D_T_FMT
substr(string $string, int $offset, ?int $length=null)
const P_CS_PRECEDES
const ABMON_6
strripos(string $haystack, string $needle, int $offset=0)
chunk_split(string $string, int $length=76, string $separator="\r\n")
char s[4]
Definition cdf.c:77
zend_long ptrdiff_t
DNS_STATUS status
Definition dns_win32.c:49
int begin
Definition eaw_table.h:20
#define max(a, b)
Definition exif.c:60
zend_ffi_type * type
Definition ffi.c:3812
zend_long ch
Definition ffi.c:4580
zend_long n
Definition ffi.c:4979
zend_string * res
Definition ffi.c:4692
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
zval * arg
Definition ffi.c:3975
memset(ptr, 0, type->size)
zval * val
Definition ffi.c:4262
buf start
Definition ffi.c:4687
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
PHPAPI HashTable * php_bc_fgetcsv_empty_line(void)
Definition file.c:1886
PHPAPI HashTable * php_fgetcsv(php_stream *stream, char delimiter, char enclosure, int escape_char, size_t buf_len, char *buf)
Definition file.c:1895
PHPAPI int php_csv_handle_escape_argument(const zend_string *escape_str, uint32_t arg_num)
Definition file.c:1656
#define __attribute__(a)
Definition file.h:131
zend_long offset
char * mode
zend_long maxlen
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
PHPAPI unsigned int php_next_utf8_char(const unsigned char *str, size_t str_len, size_t *cursor, zend_result *status)
Definition html.c:351
again j
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
Definition ioutil.c:521
switch(IR_OPT_TYPE(opt))
Definition ir_fold.h:1194
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define gt(L)
Definition minilua.c:415
#define memmove(a, b, c)
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MSHUTDOWN_FUNCTION
Definition php.h:401
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define php_memnistr
Definition php.h:344
#define php_memnstr
Definition php.h:343
#define PHPAPI
Definition php.h:71
#define add(i, ts)
unsigned const char * end
Definition php_ffi.h:51
unsigned const char * pos
Definition php_ffi.h:52
unsigned const char * text
Definition php_ffi.h:53
#define t1
#define t2
struct _php_random_algo_with_state php_random_algo_with_state
struct _php_random_algo php_random_algo
unsigned char key[REFLECTION_KEY_LEN]
#define PHP_PATHINFO_FILENAME
Definition php_string.h:76
#define PHP_STR_PAD_RIGHT
Definition php_string.h:71
#define PHP_STR_PAD_BOTH
Definition php_string.h:72
#define strnatcasecmp(a, b)
Definition php_string.h:33
#define php_mb_reset()
Definition php_string.h:67
#define PHP_PATHINFO_ALL
Definition php_string.h:77
#define strnatcmp(a, b)
Definition php_string.h:31
PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, bool is_case_insensitive)
Definition strnatcmp.c:88
#define PHP_PATHINFO_BASENAME
Definition php_string.h:74
#define PHP_STR_PAD_LEFT
Definition php_string.h:70
#define php_mblen(ptr, len)
Definition php_string.h:66
#define PHP_PATHINFO_DIRNAME
Definition php_string.h:73
#define PHP_PATHINFO_EXTENSION
Definition php_string.h:75
#define PARSE_STRING
zend_constant * data
PHPAPI int php_sscanf_internal(char *string, char *format, int argCount, zval *args, int varStart, zval *return_value)
Definition scanf.c:574
#define SCAN_ERROR_WRONG_PARAM_COUNT
Definition scanf.h:31
zval * current
Definition session.c:1024
p
Definition session.c:1105
#define PHP_CSV_ESCAPE_ERROR
Definition file.h:48
PHPAPI char * php_stristr(const char *s, const char *t, size_t s_len, size_t t_len)
Definition string.c:1672
#define PHP_TAG_BUF_SIZE
Definition string.c:4998
PHPAPI void php_stripcslashes(zend_string *str)
Definition string.c:3731
PHPAPI size_t php_strcspn(const char *haystack, const char *characters, const char *haystack_end, const char *characters_end)
Definition string.c:1722
#define _isnewline(c)
Definition string.c:4303
PHPAPI char * php_strtr(char *str, size_t len, const char *str_from, const char *str_to, size_t trlen)
Definition string.c:2785
PHPAPI zend_string * php_basename(const char *s, size_t len, const char *suffix, size_t suffix_len)
Definition string.c:1372
#define _isblank(c)
Definition string.c:4302
#define STRTOK_TABLE(p)
Definition string.c:1130
#define _HEB_BLOCK_TYPE_HEB
Definition string.c:4300
PHPAPI bool php_binary_string_shuffle(php_random_algo_with_state engine, char *str, zend_long len)
Definition string.c:5941
PHPAPI struct lconv * localeconv_r(struct lconv *out)
Definition string.c:119
PHPAPI void php_implode(const zend_string *glue, HashTable *pieces, zval *return_value)
Definition string.c:958
#define EXPLODE_ALLOC_STEP
PHPAPI zend_string * php_str_to_str(const char *haystack, size_t length, const char *needle, size_t needle_len, const char *str, size_t str_len)
Definition string.c:3323
PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, const char *allow, size_t allow_len, bool allow_tag_spaces)
Definition string.c:5092
PHPAPI size_t php_strip_tags(char *rbuf, size_t len, const char *allow, size_t allow_len)
Definition string.c:5066
PHPAPI zend_string * php_addcslashes(zend_string *str, const char *what, size_t wlength)
Definition string.c:3836
PHPAPI zend_string * php_addslashes(zend_string *str)
Definition string.c:4073
#define _HEB_BLOCK_TYPE_ENG
Definition string.c:4299
PHPAPI zend_string * php_addcslashes_str(const char *str, size_t len, const char *what, size_t wlength)
Definition string.c:3794
#define isheb(c)
Definition string.c:4301
PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
Definition string.c:845
PHPAPI size_t php_strspn(const char *haystack, const char *characters, const char *haystack_end, const char *characters_end)
Definition string.c:1715
PHPAPI void php_stripslashes(zend_string *str)
Definition string.c:4288
PHPAPI void php_explode_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
Definition string.c:877
PHPAPI size_t php_dirname(char *path, size_t len)
Definition string.c:1505
PHPAPI zend_string * php_trim(zend_string *str, const char *what, size_t what_len, int mode)
Definition string.c:628
zval val
Definition zend_types.h:381
zend_long(* range)(void *state, zend_long min, zend_long max)
Definition php_random.h:91
Bucket * arData
Definition zend_types.h:403
zval * arPacked
Definition zend_types.h:404
zend_string * s
ZEND_API ZEND_COLD void zend_type_error(const char *format,...)
Definition zend.c:1824
#define INTERNAL_FUNCTION_PARAMETERS
Definition zend.h:49
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
Definition zend.h:50
ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t length)
Definition zend_API.c:2195
ZEND_API void add_index_stringl(zval *arg, zend_ulong index, const char *str, size_t length)
Definition zend_API.c:2096
ZEND_API void add_index_str(zval *arg, zend_ulong index, zend_string *str)
Definition zend_API.c:2078
ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num)
Definition zend_API.c:443
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:423
ZEND_API void add_index_long(zval *arg, zend_ulong index, zend_long n)
Definition zend_API.c:2033
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
#define RETURN_CHAR(c)
Definition zend_API.h:1048
#define Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(dest_ht, dest_str)
Definition zend_API.h:2154
#define RETVAL_COPY_DEREF(zv)
Definition zend_API.h:1030
#define RETURN_STRING(s)
Definition zend_API.h:1043
#define RETURN_STRINGL(s, l)
Definition zend_API.h:1044
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define Z_PARAM_ARRAY_HT_OR_NULL(dest)
Definition zend_API.h:1855
#define Z_PARAM_STR_OR_NULL(dest)
Definition zend_API.h:2089
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define RETVAL_STR_COPY(s)
Definition zend_API.h:1016
#define array_init_size(arg, size)
Definition zend_API.h:538
#define RETVAL_NEW_STR(s)
Definition zend_API.h:1015
#define RETURN_ARR(r)
Definition zend_API.h:1050
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define Z_PARAM_STRING(dest, dest_len)
Definition zend_API.h:2071
#define Z_PARAM_STR(dest)
Definition zend_API.h:2086
#define Z_PARAM_STRING_OR_NULL(dest, dest_len)
Definition zend_API.h:2074
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define RETVAL_EMPTY_STRING()
Definition zend_API.h:1021
#define ZEND_TRY_ASSIGN_REF_LONG(zv, lval)
Definition zend_API.h:1205
#define Z_PARAM_ARRAY_HT_OR_STR(dest_ht, dest_str)
Definition zend_API.h:2151
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define Z_PARAM_VARIADIC(spec, dest, dest_num)
Definition zend_API.h:2124
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define RETURN_NEW_STR(s)
Definition zend_API.h:1041
#define Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(dest_ht, dest_long, is_null)
Definition zend_API.h:1869
#define ZEND_TRY_ASSIGN_REF_DOUBLE(zv, dval)
Definition zend_API.h:1227
#define RETURN_THROWS()
Definition zend_API.h:1060
#define Z_PARAM_ARRAY_HT(dest)
Definition zend_API.h:1852
#define RETURN_STR(s)
Definition zend_API.h:1039
#define RETVAL_BOOL(b)
Definition zend_API.h:1009
#define Z_PARAM_LONG_OR_NULL(dest, is_null)
Definition zend_API.h:1899
#define RETURN_COPY_VALUE(zv)
Definition zend_API.h:1055
#define RETURN_STRINGL_FAST(s, l)
Definition zend_API.h:1046
#define RETVAL_LONG(l)
Definition zend_API.h:1011
#define Z_PARAM_BOOL(dest)
Definition zend_API.h:1726
#define RETURN_EMPTY_ARRAY()
Definition zend_API.h:1051
#define RETURN_EMPTY_STRING()
Definition zend_API.h:1047
#define Z_PARAM_ZVAL(dest)
Definition zend_API.h:2100
#define RETVAL_STR(s)
Definition zend_API.h:1013
#define ZVAL_STRINGL(z, s, l)
Definition zend_API.h:952
#define WRONG_PARAM_COUNT
Definition zend_API.h:529
#define RETVAL_FALSE
Definition zend_API.h:1032
#define Z_PARAM_ARRAY_HT_OR_LONG(dest_ht, dest_long)
Definition zend_API.h:1866
#define RETVAL_STRINGL(s, l)
Definition zend_API.h:1018
#define RETURN_STR_COPY(s)
Definition zend_API.h:1042
#define array_init(arg)
Definition zend_API.h:537
#define ZVAL_EMPTY_STRING(z)
Definition zend_API.h:961
#define estrndup(s, length)
Definition zend_alloc.h:165
#define ecalloc(nmemb, size)
Definition zend_alloc.h:158
#define efree(ptr)
Definition zend_alloc.h:155
#define erealloc(ptr, size)
Definition zend_alloc.h:159
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
strncmp(string $string1, string $string2, int $length)
uint32_t num_args
zend_string_release_ex(func->internal_function.function_name, 0)
zval * args
ZEND_API size_t zend_dirname(char *path, size_t len)
#define snprintf
#define ZEND_NO_SANITIZE_ADDRESS
#define E_WARNING
Definition zend_errors.h:24
ZEND_API const char * get_active_function_name(void)
#define Z_FLF_PARAM_ARRAY(arg_num, dest)
#define Z_FLF_PARAM_LONG_OR_NULL(arg_num, is_null, dest)
#define Z_FLF_PARAM_ARRAY_OR_NULL(arg_num, dest)
#define Z_FLF_PARAM_BOOL(arg_num, dest)
#define Z_FLF_PARAM_STR(arg_num, dest, tmp)
#define ZEND_FRAMELESS_FUNCTION(name, arity)
#define Z_FLF_PARAM_ARRAY_HT_OR_STR(arg_num, dest_ht, dest_str, str_tmp)
#define Z_FLF_PARAM_FREE_STR(arg_num, tmp)
#define Z_FLF_PARAM_LONG(arg_num, dest)
#define CG(v)
#define EG(v)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht)
Definition zend_hash.c:330
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
Definition zend_hash.c:2689
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
Definition zend_hash.c:1229
ZEND_API zval *ZEND_FASTCALL zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1214
ZEND_API zval *ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:1007
ZEND_API zval *ZEND_FASTCALL zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData)
Definition zend_hash.c:1031
ZEND_API zval *ZEND_FASTCALL zend_hash_add(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:992
#define ZEND_HASH_FILL_GROW()
Definition zend_hash.h:1534
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
#define ZEND_HASH_FILL_PACKED(ht)
Definition zend_hash.h:1528
#define ZEND_HASH_FILL_END()
Definition zend_hash.h:1582
#define ZEND_HASH_FILL_SET_STR(_val)
Definition zend_hash.h:1556
#define HT_IS_PACKED(ht)
Definition zend_hash.h:59
#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val)
Definition zend_hash.h:1181
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
#define ZEND_HASH_FILL_NEXT()
Definition zend_hash.h:1565
#define ZEND_HASH_FOREACH_STR_KEY(ht, _key)
Definition zend_hash.h:1138
#define ZEND_HASH_FOREACH_VAL(ht, _val)
Definition zend_hash.h:1102
int32_t zend_long
Definition zend_long.h:42
uint32_t zend_ulong
Definition zend_long.h:43
#define ZEND_LONG_FMT
Definition zend_long.h:87
#define Z_UL(i)
Definition zend_long.h:49
#define ZEND_LONG_MAX
Definition zend_long.h:45
struct _zend_string zend_string
ZEND_API zend_string *ZEND_FASTCALL zend_long_to_str(zend_long num)
ZEND_API int ZEND_FASTCALL zend_binary_strncmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length)
ZEND_API bool zend_string_only_has_ascii_alphanumeric(const zend_string *str)
ZEND_API char *ZEND_FASTCALL zend_str_tolower_dup_ex(const char *source, size_t length)
ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp_l(const char *s1, size_t len1, const char *s2, size_t len2, size_t length)
ZEND_API void zend_update_current_locale(void)
ZEND_API zend_string *ZEND_FASTCALL zval_get_string_func(zval *op)
#define zend_toupper_ascii(c)
#define zend_tolower_ascii(c)
#define ALLOCA_FLAG(name)
#define ZEND_ATTRIBUTE_UNUSED
#define MIN(a, b)
#define ZEND_FALLTHROUGH
#define EXPECTED(condition)
#define do_alloca(p, use_heap)
#define zend_always_inline
#define ZEND_ASSERT(c)
#define ZEND_SET_ALIGNED(alignment, decl)
#define free_alloca(p, use_heap)
#define ZEND_INTRIN_SSE4_2_FUNC_DECL(func)
#define ZEND_VOIDP(ptr)
#define UNEXPECTED(condition)
#define MAX(a, b)
struct _zend_array zend_array
#define ZSTR_COPY_CONCAT_PROPERTIES(out, in)
Definition zend_string.h:96
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define zend_string_equals_literal(str, literal)
#define ZSTR_EMPTY_ALLOC()
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define STR_EMPTY_ALLOC()
Definition zend_string.h:76
#define ZSTR_CHAR(c)
#define ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(s)
Definition zend_string.h:92
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_STR(z, s)
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define IS_UNDEF
Definition zend_types.h:600
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_DEREF(z)
#define IS_STRING
Definition zend_types.h:606
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_ARRAY
Definition zend_types.h:607
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
@ FAILURE
Definition zend_types.h:61
#define IS_LONG
Definition zend_types.h:604
#define ZVAL_NEW_STR(z, s)
uint32_t HashPosition
Definition zend_types.h:548
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_ARRVAL(zval)
Definition zend_types.h:986
#define Z_ARR_P(zval_p)
Definition zend_types.h:984
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
#define GC_ADD_FLAGS(p, flags)
Definition zend_types.h:759
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
zval * return_value
zval * arg1
bool result
zval * ret
value
out($f, $s)