xref: /php-src/ext/standard/string.c (revision c3dda473)
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_rand.h"
22 #include "php_string.h"
23 #include "php_variables.h"
24 #include <locale.h>
25 #ifdef HAVE_LANGINFO_H
26 # include <langinfo.h>
27 #endif
28 
29 #ifdef HAVE_LIBINTL
30 # include <libintl.h> /* For LC_MESSAGES */
31 #endif
32 
33 #include "scanf.h"
34 #include "zend_API.h"
35 #include "zend_execute.h"
36 #include "php_globals.h"
37 #include "basic_functions.h"
38 #include "zend_smart_str.h"
39 #include <Zend/zend_exceptions.h>
40 #ifdef ZTS
41 #include "TSRM.h"
42 #endif
43 
44 /* For str_getcsv() support */
45 #include "ext/standard/file.h"
46 /* For php_next_utf8_char() */
47 #include "ext/standard/html.h"
48 
49 #define STR_PAD_LEFT			0
50 #define STR_PAD_RIGHT			1
51 #define STR_PAD_BOTH			2
52 #define PHP_PATHINFO_DIRNAME 	1
53 #define PHP_PATHINFO_BASENAME 	2
54 #define PHP_PATHINFO_EXTENSION 	4
55 #define PHP_PATHINFO_FILENAME 	8
56 #define PHP_PATHINFO_ALL	(PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
57 
58 #define STR_STRSPN				0
59 #define STR_STRCSPN				1
60 
61 /* {{{ register_string_constants */
62 void register_string_constants(INIT_FUNC_ARGS)
63 {
64 	REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
65 	REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
66 	REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
67 	REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
68 	REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
69 	REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
70 	REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
71 	REGISTER_LONG_CONSTANT("PATHINFO_ALL", PHP_PATHINFO_ALL, CONST_CS | CONST_PERSISTENT);
72 
73 	/* If last members of struct lconv equal CHAR_MAX, no grouping is done */
74 	REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
75 	REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
76 	REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
77 	REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
78 	REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
79 	REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
80 	REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
81 # ifdef LC_MESSAGES
82 	REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
83 # endif
84 
85 }
86 /* }}} */
87 
88 int php_tag_find(char *tag, size_t len, const char *set);
89 
90 /* this is read-only, so it's ok */
91 ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
92 
93 /* localeconv mutex */
94 #ifdef ZTS
95 static MUTEX_T locale_mutex = NULL;
96 #endif
97 
98 /* {{{ php_bin2hex */
99 static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
100 {
101 	zend_string *result;
102 	size_t i, j;
103 
104 	result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
105 
106 	for (i = j = 0; i < oldlen; i++) {
107 		ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
108 		ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
109 	}
110 	ZSTR_VAL(result)[j] = '\0';
111 
112 	return result;
113 }
114 /* }}} */
115 
116 /* {{{ php_hex2bin */
117 static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
118 {
119 	size_t target_length = oldlen >> 1;
120 	zend_string *str = zend_string_alloc(target_length, 0);
121 	unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
122 	size_t i, j;
123 
124 	for (i = j = 0; i < target_length; i++) {
125 		unsigned char c = old[j++];
126 		unsigned char l = c & ~0x20;
127 		int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
128 		unsigned char d;
129 
130 		/* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
131 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
132 			d = (l - 0x10 - 0x27 * is_letter) << 4;
133 		} else {
134 			zend_string_efree(str);
135 			return NULL;
136 		}
137 		c = old[j++];
138 		l = c & ~0x20;
139 		is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
140 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
141 			d |= l - 0x10 - 0x27 * is_letter;
142 		} else {
143 			zend_string_efree(str);
144 			return NULL;
145 		}
146 		ret[i] = d;
147 	}
148 	ret[i] = '\0';
149 
150 	return str;
151 }
152 /* }}} */
153 
154 /* {{{ localeconv_r
155  * glibc's localeconv is not reentrant, so lets make it so ... sorta */
156 PHPAPI struct lconv *localeconv_r(struct lconv *out)
157 {
158 
159 #ifdef ZTS
160 	tsrm_mutex_lock( locale_mutex );
161 #endif
162 
163 /*  cur->locinfo is struct __crt_locale_info which implementation is
164 	hidden in vc14. TODO revisit this and check if a workaround available
165 	and needed. */
166 #if defined(PHP_WIN32) && _MSC_VER < 1900 && defined(ZTS)
167 	{
168 		/* Even with the enabled per thread locale, localeconv
169 			won't check any locale change in the master thread. */
170 		_locale_t cur = _get_current_locale();
171 		*out = *cur->locinfo->lconv;
172 		_free_locale(cur);
173 	}
174 #else
175 	/* localeconv doesn't return an error condition */
176 	*out = *localeconv();
177 #endif
178 
179 #ifdef ZTS
180 	tsrm_mutex_unlock( locale_mutex );
181 #endif
182 
183 	return out;
184 }
185 /* }}} */
186 
187 #ifdef ZTS
188 /* {{{ PHP_MINIT_FUNCTION */
189 PHP_MINIT_FUNCTION(localeconv)
190 {
191 	locale_mutex = tsrm_mutex_alloc();
192 	return SUCCESS;
193 }
194 /* }}} */
195 
196 /* {{{ PHP_MSHUTDOWN_FUNCTION */
197 PHP_MSHUTDOWN_FUNCTION(localeconv)
198 {
199 	tsrm_mutex_free( locale_mutex );
200 	locale_mutex = NULL;
201 	return SUCCESS;
202 }
203 /* }}} */
204 #endif
205 
206 /* {{{ Converts the binary representation of data to hex */
207 PHP_FUNCTION(bin2hex)
208 {
209 	zend_string *result;
210 	zend_string *data;
211 
212 	ZEND_PARSE_PARAMETERS_START(1, 1)
213 		Z_PARAM_STR(data)
214 	ZEND_PARSE_PARAMETERS_END();
215 
216 	result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
217 
218 	RETURN_STR(result);
219 }
220 /* }}} */
221 
222 /* {{{ Converts the hex representation of data to binary */
223 PHP_FUNCTION(hex2bin)
224 {
225 	zend_string *result, *data;
226 
227 	ZEND_PARSE_PARAMETERS_START(1, 1)
228 		Z_PARAM_STR(data)
229 	ZEND_PARSE_PARAMETERS_END();
230 
231 	if (ZSTR_LEN(data) % 2 != 0) {
232 		php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
233 		RETURN_FALSE;
234 	}
235 
236 	result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
237 
238 	if (!result) {
239 		php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
240 		RETURN_FALSE;
241 	}
242 
243 	RETVAL_STR(result);
244 }
245 /* }}} */
246 
247 static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
248 {
249 	zend_string *s11, *s22;
250 	zend_long start = 0, len = 0;
251 	bool len_is_null = 1;
252 
253 	ZEND_PARSE_PARAMETERS_START(2, 4)
254 		Z_PARAM_STR(s11)
255 		Z_PARAM_STR(s22)
256 		Z_PARAM_OPTIONAL
257 		Z_PARAM_LONG(start)
258 		Z_PARAM_LONG_OR_NULL(len, len_is_null)
259 	ZEND_PARSE_PARAMETERS_END();
260 
261 	size_t remain_len = ZSTR_LEN(s11);
262 	if (start < 0) {
263 		start += remain_len;
264 		if (start < 0) {
265 			start = 0;
266 		}
267 	} else if ((size_t) start > remain_len) {
268 		start = remain_len;
269 	}
270 
271 	remain_len -= start;
272 	if (!len_is_null) {
273 		if (len < 0) {
274 			len += remain_len;
275 			if (len < 0) {
276 				len = 0;
277 			}
278 		} else if ((size_t) len > remain_len) {
279 			len = remain_len;
280 		}
281 	} else {
282 		len = remain_len;
283 	}
284 
285 	if (len == 0) {
286 		RETURN_LONG(0);
287 	}
288 
289 	if (behavior == STR_STRSPN) {
290 		RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
291 						ZSTR_VAL(s22) /*str2_start*/,
292 						ZSTR_VAL(s11) + start + len /*str1_end*/,
293 						ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
294 	} else {
295 		ZEND_ASSERT(behavior == STR_STRCSPN);
296 		RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
297 						ZSTR_VAL(s22) /*str2_start*/,
298 						ZSTR_VAL(s11) + start + len /*str1_end*/,
299 						ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
300 	}
301 }
302 /* }}} */
303 
304 /* {{{ 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) */
305 PHP_FUNCTION(strspn)
306 {
307 	php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
308 }
309 /* }}} */
310 
311 /* {{{ 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) */
312 PHP_FUNCTION(strcspn)
313 {
314 	php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
315 }
316 /* }}} */
317 
318 /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
319 #if HAVE_NL_LANGINFO
320 PHP_MINIT_FUNCTION(nl_langinfo)
321 {
322 #define REGISTER_NL_LANGINFO_CONSTANT(x)	REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
323 #ifdef ABDAY_1
324 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
325 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
326 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
327 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
328 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
329 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
330 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
331 #endif
332 #ifdef DAY_1
333 	REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
334 	REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
335 	REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
336 	REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
337 	REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
338 	REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
339 	REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
340 #endif
341 #ifdef ABMON_1
342 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
343 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
344 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
345 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
346 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
347 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
348 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
349 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
350 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
351 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
352 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
353 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
354 #endif
355 #ifdef MON_1
356 	REGISTER_NL_LANGINFO_CONSTANT(MON_1);
357 	REGISTER_NL_LANGINFO_CONSTANT(MON_2);
358 	REGISTER_NL_LANGINFO_CONSTANT(MON_3);
359 	REGISTER_NL_LANGINFO_CONSTANT(MON_4);
360 	REGISTER_NL_LANGINFO_CONSTANT(MON_5);
361 	REGISTER_NL_LANGINFO_CONSTANT(MON_6);
362 	REGISTER_NL_LANGINFO_CONSTANT(MON_7);
363 	REGISTER_NL_LANGINFO_CONSTANT(MON_8);
364 	REGISTER_NL_LANGINFO_CONSTANT(MON_9);
365 	REGISTER_NL_LANGINFO_CONSTANT(MON_10);
366 	REGISTER_NL_LANGINFO_CONSTANT(MON_11);
367 	REGISTER_NL_LANGINFO_CONSTANT(MON_12);
368 #endif
369 #ifdef AM_STR
370 	REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
371 #endif
372 #ifdef PM_STR
373 	REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
374 #endif
375 #ifdef D_T_FMT
376 	REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
377 #endif
378 #ifdef D_FMT
379 	REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
380 #endif
381 #ifdef T_FMT
382 	REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
383 #endif
384 #ifdef T_FMT_AMPM
385 	REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
386 #endif
387 #ifdef ERA
388 	REGISTER_NL_LANGINFO_CONSTANT(ERA);
389 #endif
390 #ifdef ERA_YEAR
391 	REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
392 #endif
393 #ifdef ERA_D_T_FMT
394 	REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
395 #endif
396 #ifdef ERA_D_FMT
397 	REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
398 #endif
399 #ifdef ERA_T_FMT
400 	REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
401 #endif
402 #ifdef ALT_DIGITS
403 	REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
404 #endif
405 #ifdef INT_CURR_SYMBOL
406 	REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
407 #endif
408 #ifdef CURRENCY_SYMBOL
409 	REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
410 #endif
411 #ifdef CRNCYSTR
412 	REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
413 #endif
414 #ifdef MON_DECIMAL_POINT
415 	REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
416 #endif
417 #ifdef MON_THOUSANDS_SEP
418 	REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
419 #endif
420 #ifdef MON_GROUPING
421 	REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
422 #endif
423 #ifdef POSITIVE_SIGN
424 	REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
425 #endif
426 #ifdef NEGATIVE_SIGN
427 	REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
428 #endif
429 #ifdef INT_FRAC_DIGITS
430 	REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
431 #endif
432 #ifdef FRAC_DIGITS
433 	REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
434 #endif
435 #ifdef P_CS_PRECEDES
436 	REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
437 #endif
438 #ifdef P_SEP_BY_SPACE
439 	REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
440 #endif
441 #ifdef N_CS_PRECEDES
442 	REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
443 #endif
444 #ifdef N_SEP_BY_SPACE
445 	REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
446 #endif
447 #ifdef P_SIGN_POSN
448 	REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
449 #endif
450 #ifdef N_SIGN_POSN
451 	REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
452 #endif
453 #ifdef DECIMAL_POINT
454 	REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
455 #endif
456 #ifdef RADIXCHAR
457 	REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
458 #endif
459 #ifdef THOUSANDS_SEP
460 	REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
461 #endif
462 #ifdef THOUSEP
463 	REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
464 #endif
465 #ifdef GROUPING
466 	REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
467 #endif
468 #ifdef YESEXPR
469 	REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
470 #endif
471 #ifdef NOEXPR
472 	REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
473 #endif
474 #ifdef YESSTR
475 	REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
476 #endif
477 #ifdef NOSTR
478 	REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
479 #endif
480 #ifdef CODESET
481 	REGISTER_NL_LANGINFO_CONSTANT(CODESET);
482 #endif
483 #undef REGISTER_NL_LANGINFO_CONSTANT
484 	return SUCCESS;
485 }
486 /* }}} */
487 
488 /* {{{ Query language and locale information */
489 PHP_FUNCTION(nl_langinfo)
490 {
491 	zend_long item;
492 	char *value;
493 
494 	ZEND_PARSE_PARAMETERS_START(1, 1)
495 		Z_PARAM_LONG(item)
496 	ZEND_PARSE_PARAMETERS_END();
497 
498 	switch(item) { /* {{{ */
499 #ifdef ABDAY_1
500 		case ABDAY_1:
501 		case ABDAY_2:
502 		case ABDAY_3:
503 		case ABDAY_4:
504 		case ABDAY_5:
505 		case ABDAY_6:
506 		case ABDAY_7:
507 #endif
508 #ifdef DAY_1
509 		case DAY_1:
510 		case DAY_2:
511 		case DAY_3:
512 		case DAY_4:
513 		case DAY_5:
514 		case DAY_6:
515 		case DAY_7:
516 #endif
517 #ifdef ABMON_1
518 		case ABMON_1:
519 		case ABMON_2:
520 		case ABMON_3:
521 		case ABMON_4:
522 		case ABMON_5:
523 		case ABMON_6:
524 		case ABMON_7:
525 		case ABMON_8:
526 		case ABMON_9:
527 		case ABMON_10:
528 		case ABMON_11:
529 		case ABMON_12:
530 #endif
531 #ifdef MON_1
532 		case MON_1:
533 		case MON_2:
534 		case MON_3:
535 		case MON_4:
536 		case MON_5:
537 		case MON_6:
538 		case MON_7:
539 		case MON_8:
540 		case MON_9:
541 		case MON_10:
542 		case MON_11:
543 		case MON_12:
544 #endif
545 #ifdef AM_STR
546 		case AM_STR:
547 #endif
548 #ifdef PM_STR
549 		case PM_STR:
550 #endif
551 #ifdef D_T_FMT
552 		case D_T_FMT:
553 #endif
554 #ifdef D_FMT
555 		case D_FMT:
556 #endif
557 #ifdef T_FMT
558 		case T_FMT:
559 #endif
560 #ifdef T_FMT_AMPM
561 		case T_FMT_AMPM:
562 #endif
563 #ifdef ERA
564 		case ERA:
565 #endif
566 #ifdef ERA_YEAR
567 		case ERA_YEAR:
568 #endif
569 #ifdef ERA_D_T_FMT
570 		case ERA_D_T_FMT:
571 #endif
572 #ifdef ERA_D_FMT
573 		case ERA_D_FMT:
574 #endif
575 #ifdef ERA_T_FMT
576 		case ERA_T_FMT:
577 #endif
578 #ifdef ALT_DIGITS
579 		case ALT_DIGITS:
580 #endif
581 #ifdef INT_CURR_SYMBOL
582 		case INT_CURR_SYMBOL:
583 #endif
584 #ifdef CURRENCY_SYMBOL
585 		case CURRENCY_SYMBOL:
586 #endif
587 #ifdef CRNCYSTR
588 		case CRNCYSTR:
589 #endif
590 #ifdef MON_DECIMAL_POINT
591 		case MON_DECIMAL_POINT:
592 #endif
593 #ifdef MON_THOUSANDS_SEP
594 		case MON_THOUSANDS_SEP:
595 #endif
596 #ifdef MON_GROUPING
597 		case MON_GROUPING:
598 #endif
599 #ifdef POSITIVE_SIGN
600 		case POSITIVE_SIGN:
601 #endif
602 #ifdef NEGATIVE_SIGN
603 		case NEGATIVE_SIGN:
604 #endif
605 #ifdef INT_FRAC_DIGITS
606 		case INT_FRAC_DIGITS:
607 #endif
608 #ifdef FRAC_DIGITS
609 		case FRAC_DIGITS:
610 #endif
611 #ifdef P_CS_PRECEDES
612 		case P_CS_PRECEDES:
613 #endif
614 #ifdef P_SEP_BY_SPACE
615 		case P_SEP_BY_SPACE:
616 #endif
617 #ifdef N_CS_PRECEDES
618 		case N_CS_PRECEDES:
619 #endif
620 #ifdef N_SEP_BY_SPACE
621 		case N_SEP_BY_SPACE:
622 #endif
623 #ifdef P_SIGN_POSN
624 		case P_SIGN_POSN:
625 #endif
626 #ifdef N_SIGN_POSN
627 		case N_SIGN_POSN:
628 #endif
629 #ifdef DECIMAL_POINT
630 		case DECIMAL_POINT:
631 #elif defined(RADIXCHAR)
632 		case RADIXCHAR:
633 #endif
634 #ifdef THOUSANDS_SEP
635 		case THOUSANDS_SEP:
636 #elif defined(THOUSEP)
637 		case THOUSEP:
638 #endif
639 #ifdef GROUPING
640 		case GROUPING:
641 #endif
642 #ifdef YESEXPR
643 		case YESEXPR:
644 #endif
645 #ifdef NOEXPR
646 		case NOEXPR:
647 #endif
648 #ifdef YESSTR
649 		case YESSTR:
650 #endif
651 #ifdef NOSTR
652 		case NOSTR:
653 #endif
654 #ifdef CODESET
655 		case CODESET:
656 #endif
657 			break;
658 		default:
659 			php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
660 			RETURN_FALSE;
661 	}
662 	/* }}} */
663 
664 	value = nl_langinfo(item);
665 	if (value == NULL) {
666 		RETURN_FALSE;
667 	} else {
668 		RETURN_STRING(value);
669 	}
670 }
671 #endif
672 /* }}} */
673 
674 /* {{{ Compares two strings using the current locale */
675 PHP_FUNCTION(strcoll)
676 {
677 	zend_string *s1, *s2;
678 
679 	ZEND_PARSE_PARAMETERS_START(2, 2)
680 		Z_PARAM_STR(s1)
681 		Z_PARAM_STR(s2)
682 	ZEND_PARSE_PARAMETERS_END();
683 
684 	RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
685 	                    (const char *) ZSTR_VAL(s2)));
686 }
687 /* }}} */
688 
689 /* {{{ php_charmask
690  * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
691  * it needs to be incrementing.
692  * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
693  */
694 static inline int php_charmask(const unsigned char *input, size_t len, char *mask)
695 {
696 	const unsigned char *end;
697 	unsigned char c;
698 	int result = SUCCESS;
699 
700 	memset(mask, 0, 256);
701 	for (end = input+len; input < end; input++) {
702 		c=*input;
703 		if ((input+3 < end) && input[1] == '.' && input[2] == '.'
704 				&& input[3] >= c) {
705 			memset(mask+c, 1, input[3] - c + 1);
706 			input+=3;
707 		} else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
708 			/* Error, try to be as helpful as possible:
709 			   (a range ending/starting with '.' won't be captured here) */
710 			if (end-len >= input) { /* there was no 'left' char */
711 				php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
712 				result = FAILURE;
713 				continue;
714 			}
715 			if (input+2 >= end) { /* there is no 'right' char */
716 				php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
717 				result = FAILURE;
718 				continue;
719 			}
720 			if (input[-1] > input[2]) { /* wrong order */
721 				php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
722 				result = FAILURE;
723 				continue;
724 			}
725 			/* FIXME: better error (a..b..c is the only left possibility?) */
726 			php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
727 			result = FAILURE;
728 			continue;
729 		} else {
730 			mask[c]=1;
731 		}
732 	}
733 	return result;
734 }
735 /* }}} */
736 
737 /* {{{ php_trim_int()
738  * mode 1 : trim left
739  * mode 2 : trim right
740  * mode 3 : trim left and right
741  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
742  */
743 static zend_always_inline zend_string *php_trim_int(zend_string *str, const char *what, size_t what_len, int mode)
744 {
745 	const char *start = ZSTR_VAL(str);
746 	const char *end = start + ZSTR_LEN(str);
747 	char mask[256];
748 
749 	if (what) {
750 		if (what_len == 1) {
751 			char p = *what;
752 			if (mode & 1) {
753 				while (start != end) {
754 					if (*start == p) {
755 						start++;
756 					} else {
757 						break;
758 					}
759 				}
760 			}
761 			if (mode & 2) {
762 				while (start != end) {
763 					if (*(end-1) == p) {
764 						end--;
765 					} else {
766 						break;
767 					}
768 				}
769 			}
770 		} else {
771 			php_charmask((const unsigned char *) what, what_len, mask);
772 
773 			if (mode & 1) {
774 				while (start != end) {
775 					if (mask[(unsigned char)*start]) {
776 						start++;
777 					} else {
778 						break;
779 					}
780 				}
781 			}
782 			if (mode & 2) {
783 				while (start != end) {
784 					if (mask[(unsigned char)*(end-1)]) {
785 						end--;
786 					} else {
787 						break;
788 					}
789 				}
790 			}
791 		}
792 	} else {
793 		if (mode & 1) {
794 			while (start != end) {
795 				unsigned char c = (unsigned char)*start;
796 
797 				if (c <= ' ' &&
798 				    (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
799 					start++;
800 				} else {
801 					break;
802 				}
803 			}
804 		}
805 		if (mode & 2) {
806 			while (start != end) {
807 				unsigned char c = (unsigned char)*(end-1);
808 
809 				if (c <= ' ' &&
810 				    (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
811 					end--;
812 				} else {
813 					break;
814 				}
815 			}
816 		}
817 	}
818 
819 	if (ZSTR_LEN(str) == end - start) {
820 		return zend_string_copy(str);
821 	} else if (end - start == 0) {
822 		return ZSTR_EMPTY_ALLOC();
823 	} else {
824 		return zend_string_init(start, end - start, 0);
825 	}
826 }
827 /* }}} */
828 
829 /* {{{ php_trim_int()
830  * mode 1 : trim left
831  * mode 2 : trim right
832  * mode 3 : trim left and right
833  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
834  */
835 PHPAPI zend_string *php_trim(zend_string *str, const char *what, size_t what_len, int mode)
836 {
837 	return php_trim_int(str, what, what_len, mode);
838 }
839 /* }}} */
840 
841 /* {{{ php_do_trim
842  * Base for trim(), rtrim() and ltrim() functions.
843  */
844 static zend_always_inline void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
845 {
846 	zend_string *str;
847 	zend_string *what = NULL;
848 
849 	ZEND_PARSE_PARAMETERS_START(1, 2)
850 		Z_PARAM_STR(str)
851 		Z_PARAM_OPTIONAL
852 		Z_PARAM_STR(what)
853 	ZEND_PARSE_PARAMETERS_END();
854 
855 	ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
856 }
857 /* }}} */
858 
859 /* {{{ Strips whitespace from the beginning and end of a string */
860 PHP_FUNCTION(trim)
861 {
862 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
863 }
864 /* }}} */
865 
866 /* {{{ Removes trailing whitespace */
867 PHP_FUNCTION(rtrim)
868 {
869 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
870 }
871 /* }}} */
872 
873 /* {{{ Strips whitespace from the beginning of a string */
874 PHP_FUNCTION(ltrim)
875 {
876 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
877 }
878 /* }}} */
879 
880 /* {{{ Wraps buffer to selected number of characters using string break char */
881 PHP_FUNCTION(wordwrap)
882 {
883 	zend_string *text;
884 	char *breakchar = "\n";
885 	size_t newtextlen, chk, breakchar_len = 1;
886 	size_t alloced;
887 	zend_long current = 0, laststart = 0, lastspace = 0;
888 	zend_long linelength = 75;
889 	bool docut = 0;
890 	zend_string *newtext;
891 
892 	ZEND_PARSE_PARAMETERS_START(1, 4)
893 		Z_PARAM_STR(text)
894 		Z_PARAM_OPTIONAL
895 		Z_PARAM_LONG(linelength)
896 		Z_PARAM_STRING(breakchar, breakchar_len)
897 		Z_PARAM_BOOL(docut)
898 	ZEND_PARSE_PARAMETERS_END();
899 
900 	if (ZSTR_LEN(text) == 0) {
901 		RETURN_EMPTY_STRING();
902 	}
903 
904 	if (breakchar_len == 0) {
905 		zend_argument_value_error(3, "cannot be empty");
906 		RETURN_THROWS();
907 	}
908 
909 	if (linelength == 0 && docut) {
910 		zend_argument_value_error(4, "cannot be true when argument #2 ($width) is 0");
911 		RETURN_THROWS();
912 	}
913 
914 	/* Special case for a single-character break as it needs no
915 	   additional storage space */
916 	if (breakchar_len == 1 && !docut) {
917 		newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
918 
919 		laststart = lastspace = 0;
920 		for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
921 			if (ZSTR_VAL(text)[current] == breakchar[0]) {
922 				laststart = lastspace = current + 1;
923 			} else if (ZSTR_VAL(text)[current] == ' ') {
924 				if (current - laststart >= linelength) {
925 					ZSTR_VAL(newtext)[current] = breakchar[0];
926 					laststart = current + 1;
927 				}
928 				lastspace = current;
929 			} else if (current - laststart >= linelength && laststart != lastspace) {
930 				ZSTR_VAL(newtext)[lastspace] = breakchar[0];
931 				laststart = lastspace + 1;
932 			}
933 		}
934 
935 		RETURN_NEW_STR(newtext);
936 	} else {
937 		/* Multiple character line break or forced cut */
938 		if (linelength > 0) {
939 			chk = (size_t)(ZSTR_LEN(text)/linelength + 1);
940 			newtext = zend_string_safe_alloc(chk, breakchar_len, ZSTR_LEN(text), 0);
941 			alloced = ZSTR_LEN(text) + chk * breakchar_len + 1;
942 		} else {
943 			chk = ZSTR_LEN(text);
944 			alloced = ZSTR_LEN(text) * (breakchar_len + 1) + 1;
945 			newtext = zend_string_safe_alloc(ZSTR_LEN(text), breakchar_len + 1, 0, 0);
946 		}
947 
948 		/* now keep track of the actual new text length */
949 		newtextlen = 0;
950 
951 		laststart = lastspace = 0;
952 		for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
953 			if (chk == 0) {
954 				alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
955 				newtext = zend_string_extend(newtext, alloced, 0);
956 				chk = (size_t) ((ZSTR_LEN(text) - current)/linelength) + 1;
957 			}
958 			/* when we hit an existing break, copy to new buffer, and
959 			 * fix up laststart and lastspace */
960 			if (ZSTR_VAL(text)[current] == breakchar[0]
961 				&& current + breakchar_len < ZSTR_LEN(text)
962 				&& !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
963 				memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart + breakchar_len);
964 				newtextlen += current - laststart + breakchar_len;
965 				current += breakchar_len - 1;
966 				laststart = lastspace = current + 1;
967 				chk--;
968 			}
969 			/* if it is a space, check if it is at the line boundary,
970 			 * copy and insert a break, or just keep track of it */
971 			else if (ZSTR_VAL(text)[current] == ' ') {
972 				if (current - laststart >= linelength) {
973 					memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
974 					newtextlen += current - laststart;
975 					memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
976 					newtextlen += breakchar_len;
977 					laststart = current + 1;
978 					chk--;
979 				}
980 				lastspace = current;
981 			}
982 			/* if we are cutting, and we've accumulated enough
983 			 * characters, and we haven't see a space for this line,
984 			 * copy and insert a break. */
985 			else if (current - laststart >= linelength
986 					&& docut && laststart >= lastspace) {
987 				memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
988 				newtextlen += current - laststart;
989 				memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
990 				newtextlen += breakchar_len;
991 				laststart = lastspace = current;
992 				chk--;
993 			}
994 			/* if the current word puts us over the linelength, copy
995 			 * back up until the last space, insert a break, and move
996 			 * up the laststart */
997 			else if (current - laststart >= linelength
998 					&& laststart < lastspace) {
999 				memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, lastspace - laststart);
1000 				newtextlen += lastspace - laststart;
1001 				memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1002 				newtextlen += breakchar_len;
1003 				laststart = lastspace = lastspace + 1;
1004 				chk--;
1005 			}
1006 		}
1007 
1008 		/* copy over any stragglers */
1009 		if (laststart != current) {
1010 			memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1011 			newtextlen += current - laststart;
1012 		}
1013 
1014 		ZSTR_VAL(newtext)[newtextlen] = '\0';
1015 		/* free unused memory */
1016 		newtext = zend_string_truncate(newtext, newtextlen, 0);
1017 
1018 		RETURN_NEW_STR(newtext);
1019 	}
1020 }
1021 /* }}} */
1022 
1023 /* {{{ php_explode */
1024 PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
1025 {
1026 	const char *p1 = ZSTR_VAL(str);
1027 	const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
1028 	const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1029 	zval  tmp;
1030 
1031 	if (p2 == NULL) {
1032 		ZVAL_STR_COPY(&tmp, str);
1033 		zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1034 	} else {
1035 		zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
1036 		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
1037 			do {
1038 				ZEND_HASH_FILL_GROW();
1039 				ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, p2 - p1));
1040 				ZEND_HASH_FILL_NEXT();
1041 				p1 = p2 + ZSTR_LEN(delim);
1042 				p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1043 			} while (p2 != NULL && --limit > 1);
1044 
1045 			if (p1 <= endp) {
1046 				ZEND_HASH_FILL_GROW();
1047 				ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, endp - p1));
1048 				ZEND_HASH_FILL_NEXT();
1049 			}
1050 		} ZEND_HASH_FILL_END();
1051 	}
1052 }
1053 /* }}} */
1054 
1055 /* {{{ php_explode_negative_limit */
1056 PHPAPI void php_explode_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
1057 {
1058 #define EXPLODE_ALLOC_STEP 64
1059 	const char *p1 = ZSTR_VAL(str);
1060 	const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
1061 	const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1062 	zval  tmp;
1063 
1064 	if (p2 == NULL) {
1065 		/*
1066 		do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
1067 		by doing nothing we return empty array
1068 		*/
1069 	} else {
1070 		size_t allocated = EXPLODE_ALLOC_STEP, found = 0;
1071 		zend_long i, to_return;
1072 		const char **positions = emalloc(allocated * sizeof(char *));
1073 
1074 		positions[found++] = p1;
1075 		do {
1076 			if (found >= allocated) {
1077 				allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
1078 				positions = erealloc(ZEND_VOIDP(positions), allocated*sizeof(char *));
1079 			}
1080 			positions[found++] = p1 = p2 + ZSTR_LEN(delim);
1081 			p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1082 		} while (p2 != NULL);
1083 
1084 		to_return = limit + found;
1085 		/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
1086 		for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
1087 			ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
1088 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1089 		}
1090 		efree((void *)positions);
1091 	}
1092 #undef EXPLODE_ALLOC_STEP
1093 }
1094 /* }}} */
1095 
1096 /* {{{ 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. */
1097 PHP_FUNCTION(explode)
1098 {
1099 	zend_string *str, *delim;
1100 	zend_long limit = ZEND_LONG_MAX; /* No limit */
1101 	zval tmp;
1102 
1103 	ZEND_PARSE_PARAMETERS_START(2, 3)
1104 		Z_PARAM_STR(delim)
1105 		Z_PARAM_STR(str)
1106 		Z_PARAM_OPTIONAL
1107 		Z_PARAM_LONG(limit)
1108 	ZEND_PARSE_PARAMETERS_END();
1109 
1110 	if (ZSTR_LEN(delim) == 0) {
1111 		zend_argument_value_error(1, "cannot be empty");
1112 		RETURN_THROWS();
1113 	}
1114 
1115 	array_init(return_value);
1116 
1117 	if (ZSTR_LEN(str) == 0) {
1118 		if (limit >= 0) {
1119 			ZVAL_EMPTY_STRING(&tmp);
1120 			zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
1121 		}
1122 		return;
1123 	}
1124 
1125 	if (limit > 1) {
1126 		php_explode(delim, str, return_value, limit);
1127 	} else if (limit < 0) {
1128 		php_explode_negative_limit(delim, str, return_value, limit);
1129 	} else {
1130 		ZVAL_STR_COPY(&tmp, str);
1131 		zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
1132 	}
1133 }
1134 /* }}} */
1135 
1136 /* {{{ An alias for implode */
1137 /* }}} */
1138 
1139 /* {{{ php_implode */
1140 PHPAPI void php_implode(const zend_string *glue, HashTable *pieces, zval *return_value)
1141 {
1142 	zval         *tmp;
1143 	int           numelems;
1144 	zend_string  *str;
1145 	char         *cptr;
1146 	size_t        len = 0;
1147 	struct {
1148 		zend_string *str;
1149 		zend_long    lval;
1150 	} *strings, *ptr;
1151 	ALLOCA_FLAG(use_heap)
1152 
1153 	numelems = zend_hash_num_elements(pieces);
1154 
1155 	if (numelems == 0) {
1156 		RETURN_EMPTY_STRING();
1157 	} else if (numelems == 1) {
1158 		/* loop to search the first not undefined element... */
1159 		ZEND_HASH_FOREACH_VAL(pieces, tmp) {
1160 			RETURN_STR(zval_get_string(tmp));
1161 		} ZEND_HASH_FOREACH_END();
1162 	}
1163 
1164 	ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap);
1165 
1166 	ZEND_HASH_FOREACH_VAL(pieces, tmp) {
1167 		if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) {
1168 			ptr->str = Z_STR_P(tmp);
1169 			len += ZSTR_LEN(ptr->str);
1170 			ptr->lval = 0;
1171 			ptr++;
1172 		} else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) {
1173 			zend_long val = Z_LVAL_P(tmp);
1174 
1175 			ptr->str = NULL;
1176 			ptr->lval = val;
1177 			ptr++;
1178 			if (val <= 0) {
1179 				len++;
1180 			}
1181 			while (val) {
1182 				val /= 10;
1183 				len++;
1184 			}
1185 		} else {
1186 			ptr->str = zval_get_string_func(tmp);
1187 			len += ZSTR_LEN(ptr->str);
1188 			ptr->lval = 1;
1189 			ptr++;
1190 		}
1191 	} ZEND_HASH_FOREACH_END();
1192 
1193 	/* numelems cannot be 0, we checked above */
1194 	str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
1195 	cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
1196 	*cptr = 0;
1197 
1198 	while (1) {
1199 		ptr--;
1200 		if (EXPECTED(ptr->str)) {
1201 			cptr -= ZSTR_LEN(ptr->str);
1202 			memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
1203 			if (ptr->lval) {
1204 				zend_string_release_ex(ptr->str, 0);
1205 			}
1206 		} else {
1207 			char *oldPtr = cptr;
1208 			char oldVal = *cptr;
1209 			cptr = zend_print_long_to_buf(cptr, ptr->lval);
1210 			*oldPtr = oldVal;
1211 		}
1212 
1213 		if (ptr == strings) {
1214 			break;
1215 		}
1216 
1217 		cptr -= ZSTR_LEN(glue);
1218 		memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
1219 	}
1220 
1221 	free_alloca(strings, use_heap);
1222 	RETURN_NEW_STR(str);
1223 }
1224 /* }}} */
1225 
1226 /* {{{ Joins array elements placing glue string between items and return one string */
1227 PHP_FUNCTION(implode)
1228 {
1229 	zend_string *arg1_str = NULL;
1230 	HashTable *arg1_array = NULL;
1231 	zend_array *pieces = NULL;
1232 
1233 	ZEND_PARSE_PARAMETERS_START(1, 2)
1234 		Z_PARAM_ARRAY_HT_OR_STR(arg1_array, arg1_str)
1235 		Z_PARAM_OPTIONAL
1236 		Z_PARAM_ARRAY_HT_OR_NULL(pieces)
1237 	ZEND_PARSE_PARAMETERS_END();
1238 
1239 	if (pieces == NULL) {
1240 		if (arg1_array == NULL) {
1241 			zend_type_error("%s(): Argument #1 ($pieces) must be of type array, string given", get_active_function_name());
1242 			RETURN_THROWS();
1243 		}
1244 
1245 		arg1_str = ZSTR_EMPTY_ALLOC();
1246 		pieces = arg1_array;
1247 	} else {
1248 		if (arg1_str == NULL) {
1249 			zend_argument_type_error(1, "must be of type string, array given");
1250 			RETURN_THROWS();
1251 		}
1252 	}
1253 
1254 	php_implode(arg1_str, pieces, return_value);
1255 }
1256 /* }}} */
1257 
1258 #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1259 
1260 /* {{{ Tokenize a string */
1261 PHP_FUNCTION(strtok)
1262 {
1263 	zend_string *str, *tok = NULL;
1264 	char *token;
1265 	char *token_end;
1266 	char *p;
1267 	char *pe;
1268 	size_t skipped = 0;
1269 
1270 	ZEND_PARSE_PARAMETERS_START(1, 2)
1271 		Z_PARAM_STR(str)
1272 		Z_PARAM_OPTIONAL
1273 		Z_PARAM_STR_OR_NULL(tok)
1274 	ZEND_PARSE_PARAMETERS_END();
1275 
1276 	if (!tok) {
1277 		tok = str;
1278 	} else {
1279 		if (BG(strtok_string)) {
1280 			zend_string_release(BG(strtok_string));
1281 		}
1282 		BG(strtok_string) = zend_string_copy(str);
1283 		BG(strtok_last) = ZSTR_VAL(str);
1284 		BG(strtok_len) = ZSTR_LEN(str);
1285 	}
1286 
1287 	if (!BG(strtok_string)) {
1288 		/* String to tokenize not set. */
1289 		// TODO: Should this warn?
1290 		RETURN_FALSE;
1291 	}
1292 
1293 	p = BG(strtok_last); /* Where we start to search */
1294 	pe = ZSTR_VAL(BG(strtok_string)) + BG(strtok_len);
1295 	if (p >= pe) {
1296 		/* Reached the end of the string. */
1297 		RETURN_FALSE;
1298 	}
1299 
1300 	token = ZSTR_VAL(tok);
1301 	token_end = token + ZSTR_LEN(tok);
1302 
1303 	while (token < token_end) {
1304 		STRTOK_TABLE(token++) = 1;
1305 	}
1306 
1307 	/* Skip leading delimiters */
1308 	while (STRTOK_TABLE(p)) {
1309 		if (++p >= pe) {
1310 			/* no other chars left */
1311 			goto return_false;
1312 		}
1313 		skipped++;
1314 	}
1315 
1316 	/* We know at this place that *p is no delimiter, so skip it */
1317 	while (++p < pe) {
1318 		if (STRTOK_TABLE(p)) {
1319 			goto return_token;
1320 		}
1321 	}
1322 
1323 	if (p - BG(strtok_last)) {
1324 return_token:
1325 		RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1326 		BG(strtok_last) = p + 1;
1327 	} else {
1328 return_false:
1329 		RETVAL_FALSE;
1330 		zend_string_release(BG(strtok_string));
1331 		BG(strtok_string) = NULL;
1332 	}
1333 
1334 	/* Restore table -- usually faster then memset'ing the table on every invocation */
1335 	token = ZSTR_VAL(tok);
1336 	while (token < token_end) {
1337 		STRTOK_TABLE(token++) = 0;
1338 	}
1339 }
1340 /* }}} */
1341 
1342 /* {{{ php_strtoupper */
1343 PHPAPI char *php_strtoupper(char *s, size_t len)
1344 {
1345 	unsigned char *c;
1346 	const unsigned char *e;
1347 
1348 	c = (unsigned char *)s;
1349 	e = (unsigned char *)c+len;
1350 
1351 	while (c < e) {
1352 		*c = toupper(*c);
1353 		c++;
1354 	}
1355 	return s;
1356 }
1357 /* }}} */
1358 
1359 /* {{{ php_string_toupper */
1360 PHPAPI zend_string *php_string_toupper(zend_string *s)
1361 {
1362 	unsigned char *c;
1363 	const unsigned char *e;
1364 
1365 	if (EXPECTED(!BG(ctype_string))) {
1366 		return zend_string_toupper(s);
1367 	}
1368 	c = (unsigned char *)ZSTR_VAL(s);
1369 	e = c + ZSTR_LEN(s);
1370 
1371 	while (c < e) {
1372 		if (islower(*c)) {
1373 			unsigned char *r;
1374 			zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
1375 
1376 			if (c != (unsigned char*)ZSTR_VAL(s)) {
1377 				memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
1378 			}
1379 			r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
1380 			while (c < e) {
1381 				*r = toupper(*c);
1382 				r++;
1383 				c++;
1384 			}
1385 			*r = '\0';
1386 			return res;
1387 		}
1388 		c++;
1389 	}
1390 	return zend_string_copy(s);
1391 }
1392 /* }}} */
1393 
1394 /* {{{ Makes a string uppercase */
1395 PHP_FUNCTION(strtoupper)
1396 {
1397 	zend_string *arg;
1398 
1399 	ZEND_PARSE_PARAMETERS_START(1, 1)
1400 		Z_PARAM_STR(arg)
1401 	ZEND_PARSE_PARAMETERS_END();
1402 
1403 	RETURN_STR(php_string_toupper(arg));
1404 }
1405 /* }}} */
1406 
1407 /* {{{ php_strtolower */
1408 PHPAPI char *php_strtolower(char *s, size_t len)
1409 {
1410 	unsigned char *c;
1411 	const unsigned char *e;
1412 
1413 	c = (unsigned char *)s;
1414 	e = c+len;
1415 
1416 	while (c < e) {
1417 		*c = tolower(*c);
1418 		c++;
1419 	}
1420 	return s;
1421 }
1422 /* }}} */
1423 
1424 /* {{{ php_string_tolower */
1425 PHPAPI zend_string *php_string_tolower(zend_string *s)
1426 {
1427 	if (EXPECTED(!BG(ctype_string))) {
1428 		return zend_string_tolower(s);
1429 	}
1430 
1431 	unsigned char *c = (unsigned char *)ZSTR_VAL(s);
1432 	const unsigned char *e = c + ZSTR_LEN(s);
1433 	while (c < e) {
1434 		if (isupper(*c)) {
1435 			unsigned char *r;
1436 			zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
1437 
1438 			if (c != (unsigned char*)ZSTR_VAL(s)) {
1439 				memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
1440 			}
1441 			r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
1442 			while (c < e) {
1443 				*r = tolower(*c);
1444 				r++;
1445 				c++;
1446 			}
1447 			*r = '\0';
1448 			return res;
1449 		}
1450 		c++;
1451 	}
1452 	return zend_string_copy(s);
1453 }
1454 /* }}} */
1455 
1456 /* {{{ Makes a string lowercase */
1457 PHP_FUNCTION(strtolower)
1458 {
1459 	zend_string *str;
1460 
1461 	ZEND_PARSE_PARAMETERS_START(1, 1)
1462 		Z_PARAM_STR(str)
1463 	ZEND_PARSE_PARAMETERS_END();
1464 
1465 	RETURN_STR(php_string_tolower(str));
1466 }
1467 /* }}} */
1468 
1469 #if defined(PHP_WIN32)
1470 static bool _is_basename_start(const char *start, const char *pos)
1471 {
1472 	if (pos - start >= 1
1473 	    && *(pos-1) != '/'
1474 	    && *(pos-1) != '\\') {
1475 		if (pos - start == 1) {
1476 			return 1;
1477 		} else if (*(pos-2) == '/' || *(pos-2) == '\\') {
1478 			return 1;
1479 		} else if (*(pos-2) == ':'
1480 			&& _is_basename_start(start, pos - 2)) {
1481 			return 1;
1482 		}
1483 	}
1484 	return 0;
1485 }
1486 #endif
1487 
1488 /* {{{ php_basename */
1489 PHPAPI zend_string *php_basename(const char *s, size_t len, const char *suffix, size_t suffix_len)
1490 {
1491 	const char *basename_start;
1492 	const char *basename_end;
1493 
1494 	if (CG(ascii_compatible_locale)) {
1495 		basename_end = s + len - 1;
1496 
1497 		/* Strip trailing slashes */
1498 		while (basename_end >= s
1499 #if defined(PHP_WIN32)
1500 			&& (*basename_end == '/'
1501 				|| *basename_end == '\\'
1502 				|| (*basename_end == ':'
1503 					&& _is_basename_start(s, basename_end)))) {
1504 #else
1505 			&& *basename_end == '/') {
1506 #endif
1507 			basename_end--;
1508 		}
1509 		if (basename_end < s) {
1510 			return ZSTR_EMPTY_ALLOC();
1511 		}
1512 
1513 		/* Extract filename */
1514 		basename_start = basename_end;
1515 		basename_end++;
1516 		while (basename_start > s
1517 #if defined(PHP_WIN32)
1518 			&& *(basename_start-1) != '/'
1519 			&& *(basename_start-1) != '\\') {
1520 
1521 			if (*(basename_start-1) == ':' &&
1522 				_is_basename_start(s, basename_start - 1)) {
1523 				break;
1524 			}
1525 #else
1526 			&& *(basename_start-1) != '/') {
1527 #endif
1528 			basename_start--;
1529 		}
1530 	} else {
1531 		/* State 0 is directly after a directory separator (or at the start of the string).
1532 		 * State 1 is everything else. */
1533 		int state = 0;
1534 
1535 		basename_start = s;
1536 		basename_end = s;
1537 		while (len > 0) {
1538 			int inc_len = (*s == '\0' ? 1 : php_mblen(s, len));
1539 
1540 			switch (inc_len) {
1541 				case 0:
1542 					goto quit_loop;
1543 				case 1:
1544 #if defined(PHP_WIN32)
1545 					if (*s == '/' || *s == '\\') {
1546 #else
1547 					if (*s == '/') {
1548 #endif
1549 						if (state == 1) {
1550 							state = 0;
1551 							basename_end = s;
1552 						}
1553 #if defined(PHP_WIN32)
1554 					/* Catch relative paths in c:file.txt style. They're not to confuse
1555 					   with the NTFS streams. This part ensures also, that no drive
1556 					   letter traversing happens. */
1557 					} else if ((*s == ':' && (s - basename_start == 1))) {
1558 						if (state == 0) {
1559 							basename_start = s;
1560 							state = 1;
1561 						} else {
1562 							basename_end = s;
1563 							state = 0;
1564 						}
1565 #endif
1566 					} else {
1567 						if (state == 0) {
1568 							basename_start = s;
1569 							state = 1;
1570 						}
1571 					}
1572 					break;
1573 				default:
1574 					if (inc_len < 0) {
1575 						/* If character is invalid, treat it like other non-significant characters. */
1576 						inc_len = 1;
1577 						php_mb_reset();
1578 					}
1579 					if (state == 0) {
1580 						basename_start = s;
1581 						state = 1;
1582 					}
1583 					break;
1584 			}
1585 			s += inc_len;
1586 			len -= inc_len;
1587 		}
1588 
1589 quit_loop:
1590 		if (state == 1) {
1591 			basename_end = s;
1592 		}
1593 	}
1594 
1595 	if (suffix != NULL && suffix_len < (size_t)(basename_end - basename_start) &&
1596 			memcmp(basename_end - suffix_len, suffix, suffix_len) == 0) {
1597 		basename_end -= suffix_len;
1598 	}
1599 
1600 	return zend_string_init(basename_start, basename_end - basename_start, 0);
1601 }
1602 /* }}} */
1603 
1604 /* {{{ Returns the filename component of the path */
1605 PHP_FUNCTION(basename)
1606 {
1607 	char *string, *suffix = NULL;
1608 	size_t   string_len, suffix_len = 0;
1609 
1610 	ZEND_PARSE_PARAMETERS_START(1, 2)
1611 		Z_PARAM_STRING(string, string_len)
1612 		Z_PARAM_OPTIONAL
1613 		Z_PARAM_STRING(suffix, suffix_len)
1614 	ZEND_PARSE_PARAMETERS_END();
1615 
1616 	RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1617 }
1618 /* }}} */
1619 
1620 /* {{{ php_dirname
1621    Returns directory name component of path */
1622 PHPAPI size_t php_dirname(char *path, size_t len)
1623 {
1624 	return zend_dirname(path, len);
1625 }
1626 /* }}} */
1627 
1628 /* {{{ Returns the directory name component of the path */
1629 PHP_FUNCTION(dirname)
1630 {
1631 	char *str;
1632 	size_t str_len;
1633 	zend_string *ret;
1634 	zend_long levels = 1;
1635 
1636 	ZEND_PARSE_PARAMETERS_START(1, 2)
1637 		Z_PARAM_STRING(str, str_len)
1638 		Z_PARAM_OPTIONAL
1639 		Z_PARAM_LONG(levels)
1640 	ZEND_PARSE_PARAMETERS_END();
1641 
1642 	ret = zend_string_init(str, str_len, 0);
1643 
1644 	if (levels == 1) {
1645 		/* Default case */
1646 #ifdef PHP_WIN32
1647 		ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len);
1648 #else
1649 		ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len);
1650 #endif
1651 	} else if (levels < 1) {
1652 		zend_argument_value_error(2, "must be greater than or equal to 1");
1653 		zend_string_efree(ret);
1654 		RETURN_THROWS();
1655 	} else {
1656 		/* Some levels up */
1657 		do {
1658 #ifdef PHP_WIN32
1659 			ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1660 #else
1661 			ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1662 #endif
1663 		} while (ZSTR_LEN(ret) < str_len && --levels);
1664 	}
1665 
1666 	RETURN_NEW_STR(ret);
1667 }
1668 /* }}} */
1669 
1670 /* {{{ Returns information about a certain string */
1671 PHP_FUNCTION(pathinfo)
1672 {
1673 	zval tmp;
1674 	char *path, *dirname;
1675 	size_t path_len;
1676 	int have_basename;
1677 	zend_long opt = PHP_PATHINFO_ALL;
1678 	zend_string *ret = NULL;
1679 
1680 	ZEND_PARSE_PARAMETERS_START(1, 2)
1681 		Z_PARAM_STRING(path, path_len)
1682 		Z_PARAM_OPTIONAL
1683 		Z_PARAM_LONG(opt)
1684 	ZEND_PARSE_PARAMETERS_END();
1685 
1686 	have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
1687 
1688 	array_init(&tmp);
1689 
1690 	if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
1691 		dirname = estrndup(path, path_len);
1692 		php_dirname(dirname, path_len);
1693 		if (*dirname) {
1694 			add_assoc_string(&tmp, "dirname", dirname);
1695 		}
1696 		efree(dirname);
1697 	}
1698 
1699 	if (have_basename) {
1700 		ret = php_basename(path, path_len, NULL, 0);
1701 		add_assoc_str(&tmp, "basename", zend_string_copy(ret));
1702 	}
1703 
1704 	if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
1705 		const char *p;
1706 		ptrdiff_t idx;
1707 
1708 		if (!have_basename) {
1709 			ret = php_basename(path, path_len, NULL, 0);
1710 		}
1711 
1712 		p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1713 
1714 		if (p) {
1715 			idx = p - ZSTR_VAL(ret);
1716 			add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
1717 		}
1718 	}
1719 
1720 	if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
1721 		const char *p;
1722 		ptrdiff_t idx;
1723 
1724 		/* Have we already looked up the basename? */
1725 		if (!have_basename && !ret) {
1726 			ret = php_basename(path, path_len, NULL, 0);
1727 		}
1728 
1729 		p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1730 
1731 		idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
1732 		add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
1733 	}
1734 
1735 	if (ret) {
1736 		zend_string_release_ex(ret, 0);
1737 	}
1738 
1739 	if (opt == PHP_PATHINFO_ALL) {
1740 		RETURN_COPY_VALUE(&tmp);
1741 	} else {
1742 		zval *element;
1743 		if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
1744 			RETVAL_COPY_DEREF(element);
1745 		} else {
1746 			RETVAL_EMPTY_STRING();
1747 		}
1748 		zval_ptr_dtor(&tmp);
1749 	}
1750 }
1751 /* }}} */
1752 
1753 /* {{{ php_stristr
1754    case insensitive strstr */
1755 PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
1756 {
1757 	php_strtolower(s, s_len);
1758 	php_strtolower(t, t_len);
1759 	return (char*)php_memnstr(s, t, t_len, s + s_len);
1760 }
1761 /* }}} */
1762 
1763 /* {{{ php_strspn */
1764 PHPAPI size_t php_strspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end)
1765 {
1766 	const char *p = s1, *spanp;
1767 	char c = *p;
1768 
1769 cont:
1770 	for (spanp = s2; p != s1_end && spanp != s2_end;) {
1771 		if (*spanp++ == c) {
1772 			c = *(++p);
1773 			goto cont;
1774 		}
1775 	}
1776 	return (p - s1);
1777 }
1778 /* }}} */
1779 
1780 /* {{{ php_strcspn */
1781 PHPAPI size_t php_strcspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end)
1782 {
1783 	const char *p, *spanp;
1784 	char c = *s1;
1785 
1786 	for (p = s1;;) {
1787 		spanp = s2;
1788 		do {
1789 			if (*spanp == c || p == s1_end) {
1790 				return p - s1;
1791 			}
1792 		} while (spanp++ < (s2_end - 1));
1793 		c = *++p;
1794 	}
1795 	/* NOTREACHED */
1796 }
1797 /* }}} */
1798 
1799 /* {{{ Finds first occurrence of a string within another, case insensitive */
1800 PHP_FUNCTION(stristr)
1801 {
1802 	zend_string *haystack, *needle;
1803 	const char *found = NULL;
1804 	size_t  found_offset;
1805 	char *haystack_dup;
1806 	char *orig_needle;
1807 	bool part = 0;
1808 
1809 	ZEND_PARSE_PARAMETERS_START(2, 3)
1810 		Z_PARAM_STR(haystack)
1811 		Z_PARAM_STR(needle)
1812 		Z_PARAM_OPTIONAL
1813 		Z_PARAM_BOOL(part)
1814 	ZEND_PARSE_PARAMETERS_END();
1815 
1816 	haystack_dup = estrndup(ZSTR_VAL(haystack), ZSTR_LEN(haystack));
1817 	orig_needle = estrndup(ZSTR_VAL(needle), ZSTR_LEN(needle));
1818 	found = php_stristr(haystack_dup, orig_needle, ZSTR_LEN(haystack), ZSTR_LEN(needle));
1819 	efree(orig_needle);
1820 
1821 	if (found) {
1822 		found_offset = found - haystack_dup;
1823 		if (part) {
1824 			RETVAL_STRINGL(ZSTR_VAL(haystack), found_offset);
1825 		} else {
1826 			RETVAL_STRINGL(ZSTR_VAL(haystack) + found_offset, ZSTR_LEN(haystack) - found_offset);
1827 		}
1828 	} else {
1829 		RETVAL_FALSE;
1830 	}
1831 
1832 	efree(haystack_dup);
1833 }
1834 /* }}} */
1835 
1836 /* {{{ Finds first occurrence of a string within another */
1837 PHP_FUNCTION(strstr)
1838 {
1839 	zend_string *haystack, *needle;
1840 	const char *found = NULL;
1841 	zend_long found_offset;
1842 	bool part = 0;
1843 
1844 	ZEND_PARSE_PARAMETERS_START(2, 3)
1845 		Z_PARAM_STR(haystack)
1846 		Z_PARAM_STR(needle)
1847 		Z_PARAM_OPTIONAL
1848 		Z_PARAM_BOOL(part)
1849 	ZEND_PARSE_PARAMETERS_END();
1850 
1851 	found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1852 
1853 	if (found) {
1854 		found_offset = found - ZSTR_VAL(haystack);
1855 		if (part) {
1856 			RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1857 		} else {
1858 			RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1859 		}
1860 	}
1861 	RETURN_FALSE;
1862 }
1863 /* }}} */
1864 
1865 /* {{{ Checks if a string contains another */
1866 PHP_FUNCTION(str_contains)
1867 {
1868 	zend_string *haystack, *needle;
1869 
1870 	ZEND_PARSE_PARAMETERS_START(2, 2)
1871 		Z_PARAM_STR(haystack)
1872 		Z_PARAM_STR(needle)
1873 	ZEND_PARSE_PARAMETERS_END();
1874 
1875 	RETURN_BOOL(php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack)));
1876 }
1877 /* }}} */
1878 
1879 /* {{{ Checks if haystack starts with needle */
1880 PHP_FUNCTION(str_starts_with)
1881 {
1882 	zend_string *haystack, *needle;
1883 
1884 	ZEND_PARSE_PARAMETERS_START(2, 2)
1885 		Z_PARAM_STR(haystack)
1886 		Z_PARAM_STR(needle)
1887 	ZEND_PARSE_PARAMETERS_END();
1888 
1889 	if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
1890 		RETURN_FALSE;
1891 	}
1892 
1893 	RETURN_BOOL(memcmp(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0);
1894 }
1895 /* }}} */
1896 
1897 /* {{{ Checks if haystack ends with needle */
1898 PHP_FUNCTION(str_ends_with)
1899 {
1900 	zend_string *haystack, *needle;
1901 
1902 	ZEND_PARSE_PARAMETERS_START(2, 2)
1903 		Z_PARAM_STR(haystack)
1904 		Z_PARAM_STR(needle)
1905 	ZEND_PARSE_PARAMETERS_END();
1906 
1907 	if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
1908 		RETURN_FALSE;
1909 	}
1910 
1911 	RETURN_BOOL(memcmp(
1912 		ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - ZSTR_LEN(needle),
1913 		ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0);
1914 }
1915 /* }}} */
1916 
1917 /* {{{ An alias for strstr */
1918 /* }}} */
1919 
1920 /* {{{ Finds position of first occurrence of a string within another */
1921 PHP_FUNCTION(strpos)
1922 {
1923 	zend_string *haystack, *needle;
1924 	const char *found = NULL;
1925 	zend_long offset = 0;
1926 
1927 	ZEND_PARSE_PARAMETERS_START(2, 3)
1928 		Z_PARAM_STR(haystack)
1929 		Z_PARAM_STR(needle)
1930 		Z_PARAM_OPTIONAL
1931 		Z_PARAM_LONG(offset)
1932 	ZEND_PARSE_PARAMETERS_END();
1933 
1934 	if (offset < 0) {
1935 		offset += (zend_long)ZSTR_LEN(haystack);
1936 	}
1937 	if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1938 		zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1939 		RETURN_THROWS();
1940 	}
1941 
1942 	found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1943 						ZSTR_VAL(needle), ZSTR_LEN(needle),
1944 						ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1945 
1946 	if (found) {
1947 		RETURN_LONG(found - ZSTR_VAL(haystack));
1948 	} else {
1949 		RETURN_FALSE;
1950 	}
1951 }
1952 /* }}} */
1953 
1954 /* {{{ Finds position of first occurrence of a string within another, case insensitive */
1955 PHP_FUNCTION(stripos)
1956 {
1957 	const char *found = NULL;
1958 	zend_string *haystack, *needle;
1959 	zend_long offset = 0;
1960 	zend_string *needle_dup = NULL, *haystack_dup;
1961 
1962 	ZEND_PARSE_PARAMETERS_START(2, 3)
1963 		Z_PARAM_STR(haystack)
1964 		Z_PARAM_STR(needle)
1965 		Z_PARAM_OPTIONAL
1966 		Z_PARAM_LONG(offset)
1967 	ZEND_PARSE_PARAMETERS_END();
1968 
1969 	if (offset < 0) {
1970 		offset += (zend_long)ZSTR_LEN(haystack);
1971 	}
1972 	if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1973 		zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1974 		RETURN_THROWS();
1975 	}
1976 
1977 	if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
1978 		RETURN_FALSE;
1979 	}
1980 
1981 	haystack_dup = php_string_tolower(haystack);
1982 	needle_dup = php_string_tolower(needle);
1983 	found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
1984 			ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
1985 
1986 	if (found) {
1987 		RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
1988 	} else {
1989 		RETVAL_FALSE;
1990 	}
1991 
1992 	zend_string_release_ex(haystack_dup, 0);
1993 	zend_string_release_ex(needle_dup, 0);
1994 }
1995 /* }}} */
1996 
1997 /* {{{ Finds position of last occurrence of a string within another string */
1998 PHP_FUNCTION(strrpos)
1999 {
2000 	zend_string *needle;
2001 	zend_string *haystack;
2002 	zend_long offset = 0;
2003 	const char *p, *e, *found;
2004 
2005 	ZEND_PARSE_PARAMETERS_START(2, 3)
2006 		Z_PARAM_STR(haystack)
2007 		Z_PARAM_STR(needle)
2008 		Z_PARAM_OPTIONAL
2009 		Z_PARAM_LONG(offset)
2010 	ZEND_PARSE_PARAMETERS_END();
2011 
2012 	if (offset >= 0) {
2013 		if ((size_t)offset > ZSTR_LEN(haystack)) {
2014 			zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2015 			RETURN_THROWS();
2016 		}
2017 		p = ZSTR_VAL(haystack) + (size_t)offset;
2018 		e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2019 	} else {
2020 		if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2021 			zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2022 			RETURN_THROWS();
2023 		}
2024 
2025 		p = ZSTR_VAL(haystack);
2026 		if ((size_t)-offset < ZSTR_LEN(needle)) {
2027 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2028 		} else {
2029 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2030 		}
2031 	}
2032 
2033 	if ((found = zend_memnrstr(p, ZSTR_VAL(needle), ZSTR_LEN(needle), e))) {
2034 		RETURN_LONG(found - ZSTR_VAL(haystack));
2035 	}
2036 
2037 	RETURN_FALSE;
2038 }
2039 /* }}} */
2040 
2041 /* {{{ Finds position of last occurrence of a string within another string */
2042 PHP_FUNCTION(strripos)
2043 {
2044 	zend_string *needle;
2045 	zend_string *haystack;
2046 	zend_long offset = 0;
2047 	const char *p, *e, *found;
2048 	zend_string *needle_dup, *haystack_dup;
2049 
2050 	ZEND_PARSE_PARAMETERS_START(2, 3)
2051 		Z_PARAM_STR(haystack)
2052 		Z_PARAM_STR(needle)
2053 		Z_PARAM_OPTIONAL
2054 		Z_PARAM_LONG(offset)
2055 	ZEND_PARSE_PARAMETERS_END();
2056 
2057 	if (ZSTR_LEN(needle) == 1) {
2058 		/* Single character search can shortcut memcmps
2059 		   Can also avoid tolower emallocs */
2060 		char lowered;
2061 		if (offset >= 0) {
2062 			if ((size_t)offset > ZSTR_LEN(haystack)) {
2063 				zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2064 				RETURN_THROWS();
2065 			}
2066 			p = ZSTR_VAL(haystack) + (size_t)offset;
2067 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - 1;
2068 		} else {
2069 			p = ZSTR_VAL(haystack);
2070 			if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2071 				zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2072 				RETURN_THROWS();
2073 			}
2074 			e = ZSTR_VAL(haystack) + (ZSTR_LEN(haystack) + (size_t)offset);
2075 		}
2076 		/* Borrow that ord_needle buffer to avoid repeatedly tolower()ing needle */
2077 		lowered = tolower(*ZSTR_VAL(needle));
2078 		while (e >= p) {
2079 			if (tolower(*e) == lowered) {
2080 				RETURN_LONG(e - p + (offset > 0 ? offset : 0));
2081 			}
2082 			e--;
2083 		}
2084 		RETURN_FALSE;
2085 	}
2086 
2087 	haystack_dup = php_string_tolower(haystack);
2088 	if (offset >= 0) {
2089 		if ((size_t)offset > ZSTR_LEN(haystack)) {
2090 			zend_string_release_ex(haystack_dup, 0);
2091 			zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2092 			RETURN_THROWS();
2093 		}
2094 		p = ZSTR_VAL(haystack_dup) + offset;
2095 		e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2096 	} else {
2097 		if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2098 			zend_string_release_ex(haystack_dup, 0);
2099 			zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
2100 			RETURN_THROWS();
2101 		}
2102 
2103 		p = ZSTR_VAL(haystack_dup);
2104 		if ((size_t)-offset < ZSTR_LEN(needle)) {
2105 			e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2106 		} else {
2107 			e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2108 		}
2109 	}
2110 
2111 	needle_dup = php_string_tolower(needle);
2112 	if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) {
2113 		RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2114 		zend_string_release_ex(needle_dup, 0);
2115 		zend_string_release_ex(haystack_dup, 0);
2116 	} else {
2117 		zend_string_release_ex(needle_dup, 0);
2118 		zend_string_release_ex(haystack_dup, 0);
2119 		RETURN_FALSE;
2120 	}
2121 }
2122 /* }}} */
2123 
2124 /* {{{ Finds the last occurrence of a character in a string within another */
2125 PHP_FUNCTION(strrchr)
2126 {
2127 	zend_string *haystack, *needle;
2128 	const char *found = NULL;
2129 	zend_long found_offset;
2130 
2131 	ZEND_PARSE_PARAMETERS_START(2, 2)
2132 		Z_PARAM_STR(haystack)
2133 		Z_PARAM_STR(needle)
2134 	ZEND_PARSE_PARAMETERS_END();
2135 
2136 	found = zend_memrchr(ZSTR_VAL(haystack), *ZSTR_VAL(needle), ZSTR_LEN(haystack));
2137 	if (found) {
2138 		found_offset = found - ZSTR_VAL(haystack);
2139 		RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
2140 	} else {
2141 		RETURN_FALSE;
2142 	}
2143 }
2144 /* }}} */
2145 
2146 /* {{{ php_chunk_split */
2147 static zend_string *php_chunk_split(const char *src, size_t srclen, const char *end, size_t endlen, size_t chunklen)
2148 {
2149 	char *q;
2150 	const char *p;
2151 	size_t chunks;
2152 	size_t restlen;
2153 	zend_string *dest;
2154 
2155 	chunks = srclen / chunklen;
2156 	restlen = srclen - chunks * chunklen; /* srclen % chunklen */
2157 	if (restlen) {
2158 		/* We want chunks to be rounded up rather than rounded down.
2159 		 * Increment can't overflow because chunks <= SIZE_MAX/2 at this point. */
2160 		chunks++;
2161 	}
2162 
2163 	dest = zend_string_safe_alloc(chunks, endlen, srclen, 0);
2164 
2165 	for (p = src, q = ZSTR_VAL(dest); p < (src + srclen - chunklen + 1); ) {
2166 		memcpy(q, p, chunklen);
2167 		q += chunklen;
2168 		memcpy(q, end, endlen);
2169 		q += endlen;
2170 		p += chunklen;
2171 	}
2172 
2173 	if (restlen) {
2174 		memcpy(q, p, restlen);
2175 		q += restlen;
2176 		memcpy(q, end, endlen);
2177 		q += endlen;
2178 	}
2179 
2180 	*q = '\0';
2181 	ZEND_ASSERT(q - ZSTR_VAL(dest) == ZSTR_LEN(dest));
2182 
2183 	return dest;
2184 }
2185 /* }}} */
2186 
2187 /* {{{ Returns split line */
2188 PHP_FUNCTION(chunk_split)
2189 {
2190 	zend_string *str;
2191 	char *end    = "\r\n";
2192 	size_t endlen   = 2;
2193 	zend_long chunklen = 76;
2194 	zend_string *result;
2195 
2196 	ZEND_PARSE_PARAMETERS_START(1, 3)
2197 		Z_PARAM_STR(str)
2198 		Z_PARAM_OPTIONAL
2199 		Z_PARAM_LONG(chunklen)
2200 		Z_PARAM_STRING(end, endlen)
2201 	ZEND_PARSE_PARAMETERS_END();
2202 
2203 	if (chunklen <= 0) {
2204 		zend_argument_value_error(2, "must be greater than 0");
2205 		RETURN_THROWS();
2206 	}
2207 
2208 	if ((size_t)chunklen > ZSTR_LEN(str)) {
2209 		/* to maintain BC, we must return original string + ending */
2210 		result = zend_string_safe_alloc(ZSTR_LEN(str), 1, endlen, 0);
2211 		memcpy(ZSTR_VAL(result), ZSTR_VAL(str), ZSTR_LEN(str));
2212 		memcpy(ZSTR_VAL(result) + ZSTR_LEN(str), end, endlen);
2213 		ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2214 		RETURN_NEW_STR(result);
2215 	}
2216 
2217 	if (!ZSTR_LEN(str)) {
2218 		RETURN_EMPTY_STRING();
2219 	}
2220 
2221 	result = php_chunk_split(ZSTR_VAL(str), ZSTR_LEN(str), end, endlen, (size_t)chunklen);
2222 
2223 	RETURN_STR(result);
2224 }
2225 /* }}} */
2226 
2227 /* {{{ Returns part of a string */
2228 PHP_FUNCTION(substr)
2229 {
2230 	zend_string *str;
2231 	zend_long l = 0, f;
2232 	bool len_is_null = 1;
2233 
2234 	ZEND_PARSE_PARAMETERS_START(2, 3)
2235 		Z_PARAM_STR(str)
2236 		Z_PARAM_LONG(f)
2237 		Z_PARAM_OPTIONAL
2238 		Z_PARAM_LONG_OR_NULL(l, len_is_null)
2239 	ZEND_PARSE_PARAMETERS_END();
2240 
2241 	if (f < 0) {
2242 		/* if "from" position is negative, count start position from the end
2243 		 * of the string
2244 		 */
2245 		if (-(size_t)f > ZSTR_LEN(str)) {
2246 			f = 0;
2247 		} else {
2248 			f = (zend_long)ZSTR_LEN(str) + f;
2249 		}
2250 	} else if ((size_t)f > ZSTR_LEN(str)) {
2251 		RETURN_EMPTY_STRING();
2252 	}
2253 
2254 	if (!len_is_null) {
2255 		if (l < 0) {
2256 			/* if "length" position is negative, set it to the length
2257 			 * needed to stop that many chars from the end of the string
2258 			 */
2259 			if (-(size_t)l > ZSTR_LEN(str) - (size_t)f) {
2260 				l = 0;
2261 			} else {
2262 				l = (zend_long)ZSTR_LEN(str) - f + l;
2263 			}
2264 		} else if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
2265 			l = (zend_long)ZSTR_LEN(str) - f;
2266 		}
2267 	} else {
2268 		l = (zend_long)ZSTR_LEN(str) - f;
2269 	}
2270 
2271 	if (l == ZSTR_LEN(str)) {
2272 		RETURN_STR_COPY(str);
2273 	} else {
2274 		RETURN_STRINGL_FAST(ZSTR_VAL(str) + f, l);
2275 	}
2276 }
2277 /* }}} */
2278 
2279 /* {{{ Replaces part of a string with another string */
2280 PHP_FUNCTION(substr_replace)
2281 {
2282 	zend_string *str, *repl_str;
2283 	HashTable *str_ht, *repl_ht;
2284 	HashTable *from_ht;
2285 	zend_long from_long;
2286 	HashTable *len_ht = NULL;
2287 	zend_long len_long;
2288 	bool len_is_null = 1;
2289 	zend_long l = 0;
2290 	zend_long f;
2291 	zend_string *result;
2292 	HashPosition from_idx, repl_idx, len_idx;
2293 	zval *tmp_str = NULL, *tmp_repl, *tmp_from = NULL, *tmp_len= NULL;
2294 
2295 	ZEND_PARSE_PARAMETERS_START(3, 4)
2296 		Z_PARAM_ARRAY_HT_OR_STR(str_ht, str)
2297 		Z_PARAM_ARRAY_HT_OR_STR(repl_ht, repl_str)
2298 		Z_PARAM_ARRAY_HT_OR_LONG(from_ht, from_long)
2299 		Z_PARAM_OPTIONAL
2300 		Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(len_ht, len_long, len_is_null)
2301 	ZEND_PARSE_PARAMETERS_END();
2302 
2303 	if (len_is_null) {
2304 		if (str) {
2305 			l = ZSTR_LEN(str);
2306 		}
2307 	} else if (!len_ht) {
2308 		l = len_long;
2309 	}
2310 
2311 	if (str) {
2312 		if (from_ht) {
2313 			zend_argument_type_error(3, "cannot be an array when working on a single string");
2314 			RETURN_THROWS();
2315 		}
2316 		if (len_ht) {
2317 			zend_argument_type_error(4, "cannot be an array when working on a single string");
2318 			RETURN_THROWS();
2319 		}
2320 
2321 		f = from_long;
2322 
2323 		/* if "from" position is negative, count start position from the end
2324 		 * of the string
2325 		 */
2326 		if (f < 0) {
2327 			f = (zend_long)ZSTR_LEN(str) + f;
2328 			if (f < 0) {
2329 				f = 0;
2330 			}
2331 		} else if ((size_t)f > ZSTR_LEN(str)) {
2332 			f = ZSTR_LEN(str);
2333 		}
2334 		/* if "length" position is negative, set it to the length
2335 		 * needed to stop that many chars from the end of the string
2336 		 */
2337 		if (l < 0) {
2338 			l = ((zend_long)ZSTR_LEN(str) - f) + l;
2339 			if (l < 0) {
2340 				l = 0;
2341 			}
2342 		}
2343 
2344 		if ((size_t)l > ZSTR_LEN(str) || (l < 0 && (size_t)(-l) > ZSTR_LEN(str))) {
2345 			l = ZSTR_LEN(str);
2346 		}
2347 
2348 		if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2349 			l = ZSTR_LEN(str) - f;
2350 		}
2351 
2352 		zend_string *tmp_repl_str = NULL;
2353 		if (repl_ht) {
2354 			repl_idx = 0;
2355 			while (repl_idx < repl_ht->nNumUsed) {
2356 				tmp_repl = &repl_ht->arData[repl_idx].val;
2357 				if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2358 					break;
2359 				}
2360 				repl_idx++;
2361 			}
2362 			if (repl_idx < repl_ht->nNumUsed) {
2363 				repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2364 			} else {
2365 				repl_str = STR_EMPTY_ALLOC();
2366 			}
2367 		}
2368 
2369 		result = zend_string_safe_alloc(1, ZSTR_LEN(str) - l + ZSTR_LEN(repl_str), 0, 0);
2370 
2371 		memcpy(ZSTR_VAL(result), ZSTR_VAL(str), f);
2372 		if (ZSTR_LEN(repl_str)) {
2373 			memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2374 		}
2375 		memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(str) + f + l, ZSTR_LEN(str) - f - l);
2376 		ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2377 		zend_tmp_string_release(tmp_repl_str);
2378 		RETURN_NEW_STR(result);
2379 	} else { /* str is array of strings */
2380 		zend_string *str_index = NULL;
2381 		size_t result_len;
2382 		zend_ulong num_index;
2383 
2384 		/* TODO
2385 		if (!len_is_null && from_ht) {
2386 			if (zend_hash_num_elements(from_ht) != zend_hash_num_elements(len_ht)) {
2387 				php_error_docref(NULL, E_WARNING, "'start' and 'length' should have the same number of elements");
2388 				RETURN_STR_COPY(str);
2389 			}
2390 		}
2391 		*/
2392 
2393 		array_init(return_value);
2394 
2395 		from_idx = len_idx = repl_idx = 0;
2396 
2397 		ZEND_HASH_FOREACH_KEY_VAL(str_ht, num_index, str_index, tmp_str) {
2398 			zend_string *tmp_orig_str;
2399 			zend_string *orig_str = zval_get_tmp_string(tmp_str, &tmp_orig_str);
2400 
2401 			if (from_ht) {
2402 				while (from_idx < from_ht->nNumUsed) {
2403 					tmp_from = &from_ht->arData[from_idx].val;
2404 					if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2405 						break;
2406 					}
2407 					from_idx++;
2408 				}
2409 				if (from_idx < from_ht->nNumUsed) {
2410 					f = zval_get_long(tmp_from);
2411 
2412 					if (f < 0) {
2413 						f = (zend_long)ZSTR_LEN(orig_str) + f;
2414 						if (f < 0) {
2415 							f = 0;
2416 						}
2417 					} else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2418 						f = ZSTR_LEN(orig_str);
2419 					}
2420 					from_idx++;
2421 				} else {
2422 					f = 0;
2423 				}
2424 			} else {
2425 				f = from_long;
2426 				if (f < 0) {
2427 					f = (zend_long)ZSTR_LEN(orig_str) + f;
2428 					if (f < 0) {
2429 						f = 0;
2430 					}
2431 				} else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2432 					f = ZSTR_LEN(orig_str);
2433 				}
2434 			}
2435 
2436 			if (len_ht) {
2437 				while (len_idx < len_ht->nNumUsed) {
2438 					tmp_len = &len_ht->arData[len_idx].val;
2439 					if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2440 						break;
2441 					}
2442 					len_idx++;
2443 				}
2444 				if (len_idx < len_ht->nNumUsed) {
2445 					l = zval_get_long(tmp_len);
2446 					len_idx++;
2447 				} else {
2448 					l = ZSTR_LEN(orig_str);
2449 				}
2450 			} else if (!len_is_null) {
2451 				l = len_long;
2452 			} else {
2453 				l = ZSTR_LEN(orig_str);
2454 			}
2455 
2456 			if (l < 0) {
2457 				l = (ZSTR_LEN(orig_str) - f) + l;
2458 				if (l < 0) {
2459 					l = 0;
2460 				}
2461 			}
2462 
2463 			ZEND_ASSERT(0 <= f && f <= ZEND_LONG_MAX);
2464 			ZEND_ASSERT(0 <= l && l <= ZEND_LONG_MAX);
2465 			if (((size_t) f + l) > ZSTR_LEN(orig_str)) {
2466 				l = ZSTR_LEN(orig_str) - f;
2467 			}
2468 
2469 			result_len = ZSTR_LEN(orig_str) - l;
2470 
2471 			if (repl_ht) {
2472 				while (repl_idx < repl_ht->nNumUsed) {
2473 					tmp_repl = &repl_ht->arData[repl_idx].val;
2474 					if (repl_ht != IS_UNDEF) {
2475 						break;
2476 					}
2477 					repl_idx++;
2478 				}
2479 				if (repl_idx < repl_ht->nNumUsed) {
2480 					zend_string *tmp_repl_str;
2481 					zend_string *repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2482 
2483 					result_len += ZSTR_LEN(repl_str);
2484 					repl_idx++;
2485 					result = zend_string_safe_alloc(1, result_len, 0, 0);
2486 
2487 					memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2488 					memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2489 					memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2490 					zend_tmp_string_release(tmp_repl_str);
2491 				} else {
2492 					result = zend_string_safe_alloc(1, result_len, 0, 0);
2493 
2494 					memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2495 					memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2496 				}
2497 			} else {
2498 				result_len += ZSTR_LEN(repl_str);
2499 
2500 				result = zend_string_safe_alloc(1, result_len, 0, 0);
2501 
2502 				memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2503 				memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2504 				memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2505 			}
2506 
2507 			ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2508 
2509 			if (str_index) {
2510 				zval tmp;
2511 
2512 				ZVAL_NEW_STR(&tmp, result);
2513 				zend_symtable_update(Z_ARRVAL_P(return_value), str_index, &tmp);
2514 			} else {
2515 				add_index_str(return_value, num_index, result);
2516 			}
2517 
2518 			zend_tmp_string_release(tmp_orig_str);
2519 		} ZEND_HASH_FOREACH_END();
2520 	} /* if */
2521 }
2522 /* }}} */
2523 
2524 /* {{{ Quotes meta characters */
2525 PHP_FUNCTION(quotemeta)
2526 {
2527 	zend_string *old;
2528 	const char *old_end, *p;
2529 	char *q;
2530 	char c;
2531 	zend_string *str;
2532 
2533 	ZEND_PARSE_PARAMETERS_START(1, 1)
2534 		Z_PARAM_STR(old)
2535 	ZEND_PARSE_PARAMETERS_END();
2536 
2537 	old_end = ZSTR_VAL(old) + ZSTR_LEN(old);
2538 
2539 	if (ZSTR_LEN(old) == 0) {
2540 		RETURN_EMPTY_STRING();
2541 	}
2542 
2543 	str = zend_string_safe_alloc(2, ZSTR_LEN(old), 0, 0);
2544 
2545 	for (p = ZSTR_VAL(old), q = ZSTR_VAL(str); p != old_end; p++) {
2546 		c = *p;
2547 		switch (c) {
2548 			case '.':
2549 			case '\\':
2550 			case '+':
2551 			case '*':
2552 			case '?':
2553 			case '[':
2554 			case '^':
2555 			case ']':
2556 			case '$':
2557 			case '(':
2558 			case ')':
2559 				*q++ = '\\';
2560 				ZEND_FALLTHROUGH;
2561 			default:
2562 				*q++ = c;
2563 		}
2564 	}
2565 
2566 	*q = '\0';
2567 
2568 	RETURN_NEW_STR(zend_string_truncate(str, q - ZSTR_VAL(str), 0));
2569 }
2570 /* }}} */
2571 
2572 /* {{{ Returns ASCII value of character
2573    Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */
2574 PHP_FUNCTION(ord)
2575 {
2576 	zend_string *str;
2577 
2578 	ZEND_PARSE_PARAMETERS_START(1, 1)
2579 		Z_PARAM_STR(str)
2580 	ZEND_PARSE_PARAMETERS_END();
2581 
2582 	RETURN_LONG((unsigned char) ZSTR_VAL(str)[0]);
2583 }
2584 /* }}} */
2585 
2586 /* {{{ Converts ASCII code to a character
2587    Warning: This function is special-cased by zend_compile.c and so is bypassed for constant integer argument */
2588 PHP_FUNCTION(chr)
2589 {
2590 	zend_long c;
2591 
2592 	ZEND_PARSE_PARAMETERS_START(1, 1)
2593 		Z_PARAM_LONG(c)
2594 	ZEND_PARSE_PARAMETERS_END();
2595 
2596 	c &= 0xff;
2597 	RETURN_CHAR(c);
2598 }
2599 /* }}} */
2600 
2601 /* {{{ php_ucfirst
2602    Uppercase the first character of the word in a native string */
2603 static zend_string* php_ucfirst(zend_string *str)
2604 {
2605 	const unsigned char ch = ZSTR_VAL(str)[0];
2606 	unsigned char r = toupper(ch);
2607 	if (r == ch) {
2608 		return zend_string_copy(str);
2609 	} else {
2610 		zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2611 		ZSTR_VAL(s)[0] = r;
2612 		return s;
2613 	}
2614 }
2615 /* }}} */
2616 
2617 /* {{{ Makes a string's first character uppercase */
2618 PHP_FUNCTION(ucfirst)
2619 {
2620 	zend_string *str;
2621 
2622 	ZEND_PARSE_PARAMETERS_START(1, 1)
2623 		Z_PARAM_STR(str)
2624 	ZEND_PARSE_PARAMETERS_END();
2625 
2626 	if (!ZSTR_LEN(str)) {
2627 		RETURN_EMPTY_STRING();
2628 	}
2629 
2630 	RETURN_STR(php_ucfirst(str));
2631 }
2632 /* }}} */
2633 
2634 /* {{{
2635    Lowercase the first character of the word in a native string */
2636 static zend_string* php_lcfirst(zend_string *str)
2637 {
2638 	unsigned char r = tolower(ZSTR_VAL(str)[0]);
2639 	if (r == ZSTR_VAL(str)[0]) {
2640 		return zend_string_copy(str);
2641 	} else {
2642 		zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2643 		ZSTR_VAL(s)[0] = r;
2644 		return s;
2645 	}
2646 }
2647 /* }}} */
2648 
2649 /* {{{ Make a string's first character lowercase */
2650 PHP_FUNCTION(lcfirst)
2651 {
2652 	zend_string  *str;
2653 
2654 	ZEND_PARSE_PARAMETERS_START(1, 1)
2655 		Z_PARAM_STR(str)
2656 	ZEND_PARSE_PARAMETERS_END();
2657 
2658 	if (!ZSTR_LEN(str)) {
2659 		RETURN_EMPTY_STRING();
2660 	}
2661 
2662 	RETURN_STR(php_lcfirst(str));
2663 }
2664 /* }}} */
2665 
2666 /* {{{ Uppercase the first character of every word in a string */
2667 PHP_FUNCTION(ucwords)
2668 {
2669 	zend_string *str;
2670 	char *delims = " \t\r\n\f\v";
2671 	char *r;
2672 	const char *r_end;
2673 	size_t delims_len = 6;
2674 	char mask[256];
2675 
2676 	ZEND_PARSE_PARAMETERS_START(1, 2)
2677 		Z_PARAM_STR(str)
2678 		Z_PARAM_OPTIONAL
2679 		Z_PARAM_STRING(delims, delims_len)
2680 	ZEND_PARSE_PARAMETERS_END();
2681 
2682 	if (!ZSTR_LEN(str)) {
2683 		RETURN_EMPTY_STRING();
2684 	}
2685 
2686 	php_charmask((const unsigned char *) delims, delims_len, mask);
2687 
2688 	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2689 	r = Z_STRVAL_P(return_value);
2690 
2691 	*r = toupper((unsigned char) *r);
2692 	for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2693 		if (mask[(unsigned char)*r++]) {
2694 			*r = toupper((unsigned char) *r);
2695 		}
2696 	}
2697 }
2698 /* }}} */
2699 
2700 /* {{{ php_strtr */
2701 PHPAPI char *php_strtr(char *str, size_t len, const char *str_from, const char *str_to, size_t trlen)
2702 {
2703 	size_t i;
2704 
2705 	if (UNEXPECTED(trlen < 1)) {
2706 		return str;
2707 	} else if (trlen == 1) {
2708 		char ch_from = *str_from;
2709 		char ch_to = *str_to;
2710 
2711 		for (i = 0; i < len; i++) {
2712 			if (str[i] == ch_from) {
2713 				str[i] = ch_to;
2714 			}
2715 		}
2716 	} else {
2717 		unsigned char xlat[256];
2718 
2719 		memset(xlat, 0, sizeof(xlat));
2720 
2721 		for (i = 0; i < trlen; i++) {
2722 			xlat[(size_t)(unsigned char) str_from[i]] = str_to[i] - str_from[i];
2723 		}
2724 
2725 		for (i = 0; i < len; i++) {
2726 			str[i] += xlat[(size_t)(unsigned char) str[i]];
2727 		}
2728 	}
2729 
2730 	return str;
2731 }
2732 /* }}} */
2733 
2734 /* {{{ php_strtr_ex */
2735 static zend_string *php_strtr_ex(zend_string *str, const char *str_from, const char *str_to, size_t trlen)
2736 {
2737 	zend_string *new_str = NULL;
2738 	size_t i;
2739 
2740 	if (UNEXPECTED(trlen < 1)) {
2741 		return zend_string_copy(str);
2742 	} else if (trlen == 1) {
2743 		char ch_from = *str_from;
2744 		char ch_to = *str_to;
2745 
2746 		for (i = 0; i < ZSTR_LEN(str); i++) {
2747 			if (ZSTR_VAL(str)[i] == ch_from) {
2748 				new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2749 				memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2750 				ZSTR_VAL(new_str)[i] = ch_to;
2751 				i++;
2752 				for (; i < ZSTR_LEN(str); i++) {
2753 					ZSTR_VAL(new_str)[i] = (ZSTR_VAL(str)[i] != ch_from) ? ZSTR_VAL(str)[i] : ch_to;
2754 				}
2755 				ZSTR_VAL(new_str)[i] = 0;
2756 				return new_str;
2757 			}
2758 		}
2759 	} else {
2760 		unsigned char xlat[256];
2761 
2762 		memset(xlat, 0, sizeof(xlat));;
2763 
2764 		for (i = 0; i < trlen; i++) {
2765 			xlat[(size_t)(unsigned char) str_from[i]] = str_to[i] - str_from[i];
2766 		}
2767 
2768 		for (i = 0; i < ZSTR_LEN(str); i++) {
2769 			if (xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) {
2770 				new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2771 				memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2772 				do {
2773 					ZSTR_VAL(new_str)[i] = ZSTR_VAL(str)[i] + xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2774 					i++;
2775 				} while (i < ZSTR_LEN(str));
2776 				ZSTR_VAL(new_str)[i] = 0;
2777 				return new_str;
2778 			}
2779 		}
2780 	}
2781 
2782 	return zend_string_copy(str);
2783 }
2784 /* }}} */
2785 
2786 /* {{{ php_strtr_array */
2787 static void php_strtr_array(zval *return_value, zend_string *input, HashTable *pats)
2788 {
2789 	const char *str = ZSTR_VAL(input);
2790 	size_t slen = ZSTR_LEN(input);
2791 	zend_ulong num_key;
2792 	zend_string *str_key;
2793 	size_t len, pos, old_pos;
2794 	int num_keys = 0;
2795 	size_t minlen = 128*1024;
2796 	size_t maxlen = 0;
2797 	HashTable str_hash;
2798 	zval *entry;
2799 	const char *key;
2800 	smart_str result = {0};
2801 	zend_ulong bitset[256/sizeof(zend_ulong)];
2802 	zend_ulong *num_bitset;
2803 
2804 	/* we will collect all possible key lengths */
2805 	num_bitset = ecalloc((slen + sizeof(zend_ulong)) / sizeof(zend_ulong), sizeof(zend_ulong));
2806 	memset(bitset, 0, sizeof(bitset));
2807 
2808 	/* check if original array has numeric keys */
2809 	ZEND_HASH_FOREACH_STR_KEY(pats, str_key) {
2810 		if (UNEXPECTED(!str_key)) {
2811 			num_keys = 1;
2812 		} else {
2813 			len = ZSTR_LEN(str_key);
2814 			if (UNEXPECTED(len < 1)) {
2815 				php_error_docref(NULL, E_WARNING, "Ignoring replacement of empty string");
2816 				continue;
2817 			} else if (UNEXPECTED(len > slen)) {
2818 				/* skip long patterns */
2819 				continue;
2820 			}
2821 			if (len > maxlen) {
2822 				maxlen = len;
2823 			}
2824 			if (len < minlen) {
2825 				minlen = len;
2826 			}
2827 			/* remember possible key length */
2828 			num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
2829 			bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong));
2830 		}
2831 	} ZEND_HASH_FOREACH_END();
2832 
2833 	if (UNEXPECTED(num_keys)) {
2834 		zend_string *key_used;
2835 		/* we have to rebuild HashTable with numeric keys */
2836 		zend_hash_init(&str_hash, zend_hash_num_elements(pats), NULL, NULL, 0);
2837 		ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
2838 			if (UNEXPECTED(!str_key)) {
2839 				key_used = zend_long_to_str(num_key);
2840 				len = ZSTR_LEN(key_used);
2841 				if (UNEXPECTED(len > slen)) {
2842 					/* skip long patterns */
2843 					zend_string_release(key_used);
2844 					continue;
2845 				}
2846 				if (len > maxlen) {
2847 					maxlen = len;
2848 				}
2849 				if (len < minlen) {
2850 					minlen = len;
2851 				}
2852 				/* remember possible key length */
2853 				num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
2854 				bitset[((unsigned char)ZSTR_VAL(key_used)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(key_used)[0]) % sizeof(zend_ulong));
2855 			} else {
2856 				key_used = str_key;
2857 				len = ZSTR_LEN(key_used);
2858 				if (UNEXPECTED(len > slen)) {
2859 					/* skip long patterns */
2860 					continue;
2861 				}
2862 			}
2863 			zend_hash_add(&str_hash, key_used, entry);
2864 			if (UNEXPECTED(!str_key)) {
2865 				zend_string_release_ex(key_used, 0);
2866 			}
2867 		} ZEND_HASH_FOREACH_END();
2868 		pats = &str_hash;
2869 	}
2870 
2871 	if (UNEXPECTED(minlen > maxlen)) {
2872 		/* return the original string */
2873 		if (pats == &str_hash) {
2874 			zend_hash_destroy(&str_hash);
2875 		}
2876 		efree(num_bitset);
2877 		RETURN_STR_COPY(input);
2878 	}
2879 
2880 	old_pos = pos = 0;
2881 	while (pos <= slen - minlen) {
2882 		key = str + pos;
2883 		if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) {
2884 			len = maxlen;
2885 			if (len > slen - pos) {
2886 				len = slen - pos;
2887 			}
2888 			while (len >= minlen) {
2889 				if ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1) << (len % sizeof(zend_ulong))))) {
2890 					entry = zend_hash_str_find(pats, key, len);
2891 					if (entry != NULL) {
2892 						zend_string *tmp;
2893 						zend_string *s = zval_get_tmp_string(entry, &tmp);
2894 						smart_str_appendl(&result, str + old_pos, pos - old_pos);
2895 						smart_str_append(&result, s);
2896 						old_pos = pos + len;
2897 						pos = old_pos - 1;
2898 						zend_tmp_string_release(tmp);
2899 						break;
2900 					}
2901 				}
2902 				len--;
2903 			}
2904 		}
2905 		pos++;
2906 	}
2907 
2908 	if (result.s) {
2909 		smart_str_appendl(&result, str + old_pos, slen - old_pos);
2910 		smart_str_0(&result);
2911 		RETVAL_NEW_STR(result.s);
2912 	} else {
2913 		smart_str_free(&result);
2914 		RETVAL_STR_COPY(input);
2915 	}
2916 
2917 	if (pats == &str_hash) {
2918 		zend_hash_destroy(&str_hash);
2919 	}
2920 	efree(num_bitset);
2921 }
2922 /* }}} */
2923 
2924 /* {{{ php_char_to_str_ex */
2925 static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, size_t to_len, int case_sensitivity, zend_long *replace_count)
2926 {
2927 	zend_string *result;
2928 	size_t char_count = 0;
2929 	int lc_from = 0;
2930 	const char *source, *source_end= ZSTR_VAL(str) + ZSTR_LEN(str);
2931 	char *target;
2932 
2933 	if (case_sensitivity) {
2934 		char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str);
2935 		while ((p = memchr(p, from, (e - p)))) {
2936 			char_count++;
2937 			p++;
2938 		}
2939 	} else {
2940 		lc_from = tolower(from);
2941 		for (source = ZSTR_VAL(str); source < source_end; source++) {
2942 			if (tolower(*source) == lc_from) {
2943 				char_count++;
2944 			}
2945 		}
2946 	}
2947 
2948 	if (char_count == 0) {
2949 		return zend_string_copy(str);
2950 	}
2951 
2952 	if (to_len > 0) {
2953 		result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
2954 	} else {
2955 		result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
2956 	}
2957 	target = ZSTR_VAL(result);
2958 
2959 	if (case_sensitivity) {
2960 		char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
2961 		while ((p = memchr(p, from, (e - p)))) {
2962 			memcpy(target, s, (p - s));
2963 			target += p - s;
2964 			memcpy(target, to, to_len);
2965 			target += to_len;
2966 			p++;
2967 			s = p;
2968 			if (replace_count) {
2969 				*replace_count += 1;
2970 			}
2971 		}
2972 		if (s < e) {
2973 			memcpy(target, s, (e - s));
2974 			target += e - s;
2975 		}
2976 	} else {
2977 		for (source = ZSTR_VAL(str); source < source_end; source++) {
2978 			if (tolower(*source) == lc_from) {
2979 				if (replace_count) {
2980 					*replace_count += 1;
2981 				}
2982 				memcpy(target, to, to_len);
2983 				target += to_len;
2984 			} else {
2985 				*target = *source;
2986 				target++;
2987 			}
2988 		}
2989 	}
2990 	*target = 0;
2991 	return result;
2992 }
2993 /* }}} */
2994 
2995 /* {{{ php_str_to_str_ex */
2996 static zend_string *php_str_to_str_ex(zend_string *haystack,
2997 	const char *needle, size_t needle_len, const char *str, size_t str_len, zend_long *replace_count)
2998 {
2999 
3000 	if (needle_len < ZSTR_LEN(haystack)) {
3001 		zend_string *new_str;
3002 		const char *end;
3003 		const char *p, *r;
3004 		char *e;
3005 
3006 		if (needle_len == str_len) {
3007 			new_str = NULL;
3008 			end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3009 			for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3010 				if (!new_str) {
3011 					new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3012 				}
3013 				memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3014 				(*replace_count)++;
3015 			}
3016 			if (!new_str) {
3017 				goto nothing_todo;
3018 			}
3019 			return new_str;
3020 		} else {
3021 			size_t count = 0;
3022 			const char *o = ZSTR_VAL(haystack);
3023 			const char *n = needle;
3024 			const char *endp = o + ZSTR_LEN(haystack);
3025 
3026 			while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3027 				o += needle_len;
3028 				count++;
3029 			}
3030 			if (count == 0) {
3031 				/* Needle doesn't occur, shortcircuit the actual replacement. */
3032 				goto nothing_todo;
3033 			}
3034 			if (str_len > needle_len) {
3035 				new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3036 			} else {
3037 				new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3038 			}
3039 
3040 			e = ZSTR_VAL(new_str);
3041 			end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3042 			for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3043 				memcpy(e, p, r - p);
3044 				e += r - p;
3045 				memcpy(e, str, str_len);
3046 				e += str_len;
3047 				(*replace_count)++;
3048 			}
3049 
3050 			if (p < end) {
3051 				memcpy(e, p, end - p);
3052 				e += end - p;
3053 			}
3054 
3055 			*e = '\0';
3056 			return new_str;
3057 		}
3058 	} else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3059 nothing_todo:
3060 		return zend_string_copy(haystack);
3061 	} else {
3062 		(*replace_count)++;
3063 		return zend_string_init_fast(str, str_len);
3064 	}
3065 }
3066 /* }}} */
3067 
3068 /* {{{ php_str_to_str_i_ex */
3069 static zend_string *php_str_to_str_i_ex(zend_string *haystack, const char *lc_haystack,
3070 	zend_string *needle, const char *str, size_t str_len, zend_long *replace_count)
3071 {
3072 	zend_string *new_str = NULL;
3073 	zend_string *lc_needle;
3074 
3075 	if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3076 		const char *end;
3077 		const char *p, *r;
3078 		char *e;
3079 
3080 		if (ZSTR_LEN(needle) == str_len) {
3081 			lc_needle = php_string_tolower(needle);
3082 			end = lc_haystack + ZSTR_LEN(haystack);
3083 			for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3084 				if (!new_str) {
3085 					new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3086 				}
3087 				memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3088 				(*replace_count)++;
3089 			}
3090 			zend_string_release_ex(lc_needle, 0);
3091 
3092 			if (!new_str) {
3093 				goto nothing_todo;
3094 			}
3095 			return new_str;
3096 		} else {
3097 			size_t count = 0;
3098 			const char *o = lc_haystack;
3099 			const char *n;
3100 			const char *endp = o + ZSTR_LEN(haystack);
3101 
3102 			lc_needle = php_string_tolower(needle);
3103 			n = ZSTR_VAL(lc_needle);
3104 
3105 			while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3106 				o += ZSTR_LEN(lc_needle);
3107 				count++;
3108 			}
3109 			if (count == 0) {
3110 				/* Needle doesn't occur, shortcircuit the actual replacement. */
3111 				zend_string_release_ex(lc_needle, 0);
3112 				goto nothing_todo;
3113 			}
3114 
3115 			if (str_len > ZSTR_LEN(lc_needle)) {
3116 				new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3117 			} else {
3118 				new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3119 			}
3120 
3121 			e = ZSTR_VAL(new_str);
3122 			end = lc_haystack + ZSTR_LEN(haystack);
3123 
3124 			for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3125 				memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3126 				e += r - p;
3127 				memcpy(e, str, str_len);
3128 				e += str_len;
3129 				(*replace_count)++;
3130 			}
3131 
3132 			if (p < end) {
3133 				memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3134 				e += end - p;
3135 			}
3136 			*e = '\0';
3137 
3138 			zend_string_release_ex(lc_needle, 0);
3139 
3140 			return new_str;
3141 		}
3142 	} else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3143 nothing_todo:
3144 		return zend_string_copy(haystack);
3145 	} else {
3146 		lc_needle = php_string_tolower(needle);
3147 
3148 		if (memcmp(lc_haystack, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle))) {
3149 			zend_string_release_ex(lc_needle, 0);
3150 			goto nothing_todo;
3151 		}
3152 		zend_string_release_ex(lc_needle, 0);
3153 
3154 		new_str = zend_string_init(str, str_len, 0);
3155 
3156 		(*replace_count)++;
3157 		return new_str;
3158 	}
3159 }
3160 /* }}} */
3161 
3162 /* {{{ php_str_to_str */
3163 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)
3164 {
3165 	zend_string *new_str;
3166 
3167 	if (needle_len < length) {
3168 		const char *end;
3169 		const char *s, *p;
3170 		char *e, *r;
3171 
3172 		if (needle_len == str_len) {
3173 			new_str = zend_string_init(haystack, length, 0);
3174 			end = ZSTR_VAL(new_str) + length;
3175 			for (p = ZSTR_VAL(new_str); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3176 				memcpy(r, str, str_len);
3177 			}
3178 			return new_str;
3179 		} else {
3180 			if (str_len < needle_len) {
3181 				new_str = zend_string_alloc(length, 0);
3182 			} else {
3183 				size_t count = 0;
3184 				const char *o = haystack;
3185 				const char *n = needle;
3186 				const char *endp = o + length;
3187 
3188 				while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3189 					o += needle_len;
3190 					count++;
3191 				}
3192 				if (count == 0) {
3193 					/* Needle doesn't occur, shortcircuit the actual replacement. */
3194 					new_str = zend_string_init(haystack, length, 0);
3195 					return new_str;
3196 				} else {
3197 					if (str_len > needle_len) {
3198 						new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3199 					} else {
3200 						new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3201 					}
3202 				}
3203 			}
3204 
3205 			s = e = ZSTR_VAL(new_str);
3206 			end = haystack + length;
3207 			for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3208 				memcpy(e, p, r - p);
3209 				e += r - p;
3210 				memcpy(e, str, str_len);
3211 				e += str_len;
3212 			}
3213 
3214 			if (p < end) {
3215 				memcpy(e, p, end - p);
3216 				e += end - p;
3217 			}
3218 
3219 			*e = '\0';
3220 			new_str = zend_string_truncate(new_str, e - s, 0);
3221 			return new_str;
3222 		}
3223 	} else if (needle_len > length || memcmp(haystack, needle, length)) {
3224 		new_str = zend_string_init(haystack, length, 0);
3225 		return new_str;
3226 	} else {
3227 		new_str = zend_string_init(str, str_len, 0);
3228 
3229 		return new_str;
3230 	}
3231 }
3232 /* }}} */
3233 
3234 /* {{{ Translates characters in str using given translation tables */
3235 PHP_FUNCTION(strtr)
3236 {
3237 	zend_string *str, *from_str = NULL;
3238 	HashTable *from_ht = NULL;
3239 	char *to = NULL;
3240 	size_t to_len = 0;
3241 
3242 	ZEND_PARSE_PARAMETERS_START(2, 3)
3243 		Z_PARAM_STR(str)
3244 		Z_PARAM_ARRAY_HT_OR_STR(from_ht, from_str)
3245 		Z_PARAM_OPTIONAL
3246 		Z_PARAM_STRING_OR_NULL(to, to_len)
3247 	ZEND_PARSE_PARAMETERS_END();
3248 
3249 	if (!to && from_ht == NULL) {
3250 		zend_argument_type_error(2, "must be of type array, string given");
3251 		RETURN_THROWS();
3252 	} else if (to && from_str == NULL) {
3253 		zend_argument_type_error(2, "must be of type string, array given");
3254 		RETURN_THROWS();
3255 	}
3256 
3257 	/* shortcut for empty string */
3258 	if (ZSTR_LEN(str) == 0) {
3259 		RETURN_EMPTY_STRING();
3260 	}
3261 
3262 	if (!to) {
3263 		if (zend_hash_num_elements(from_ht) < 1) {
3264 			RETURN_STR_COPY(str);
3265 		} else if (zend_hash_num_elements(from_ht) == 1) {
3266 			zend_long num_key;
3267 			zend_string *str_key, *tmp_str, *replace, *tmp_replace;
3268 			zval *entry;
3269 
3270 			ZEND_HASH_FOREACH_KEY_VAL(from_ht, num_key, str_key, entry) {
3271 				tmp_str = NULL;
3272 				if (UNEXPECTED(!str_key)) {
3273 					str_key = tmp_str = zend_long_to_str(num_key);
3274 				}
3275 				replace = zval_get_tmp_string(entry, &tmp_replace);
3276 				if (ZSTR_LEN(str_key) < 1) {
3277 					php_error_docref(NULL, E_WARNING, "Ignoring replacement of empty string");
3278 					RETVAL_STR_COPY(str);
3279 				} else if (ZSTR_LEN(str_key) == 1) {
3280 					RETVAL_STR(php_char_to_str_ex(str,
3281 								ZSTR_VAL(str_key)[0],
3282 								ZSTR_VAL(replace),
3283 								ZSTR_LEN(replace),
3284 								1,
3285 								NULL));
3286 				} else {
3287 					zend_long dummy;
3288 					RETVAL_STR(php_str_to_str_ex(str,
3289 								ZSTR_VAL(str_key), ZSTR_LEN(str_key),
3290 								ZSTR_VAL(replace), ZSTR_LEN(replace), &dummy));
3291 				}
3292 				zend_tmp_string_release(tmp_str);
3293 				zend_tmp_string_release(tmp_replace);
3294 				return;
3295 			} ZEND_HASH_FOREACH_END();
3296 		} else {
3297 			php_strtr_array(return_value, str, from_ht);
3298 		}
3299 	} else {
3300 		RETURN_STR(php_strtr_ex(str,
3301 				  ZSTR_VAL(from_str),
3302 				  to,
3303 				  MIN(ZSTR_LEN(from_str), to_len)));
3304 	}
3305 }
3306 /* }}} */
3307 
3308 /* {{{ Reverse a string */
3309 #if ZEND_INTRIN_SSSE3_NATIVE
3310 #include <tmmintrin.h>
3311 #elif defined(__aarch64__)
3312 #include <arm_neon.h>
3313 #endif
3314 PHP_FUNCTION(strrev)
3315 {
3316 	zend_string *str;
3317 	const char *s, *e;
3318 	char *p;
3319 	zend_string *n;
3320 
3321 	ZEND_PARSE_PARAMETERS_START(1, 1)
3322 		Z_PARAM_STR(str)
3323 	ZEND_PARSE_PARAMETERS_END();
3324 
3325 	n = zend_string_alloc(ZSTR_LEN(str), 0);
3326 	p = ZSTR_VAL(n);
3327 
3328 	s = ZSTR_VAL(str);
3329 	e = s + ZSTR_LEN(str);
3330 	--e;
3331 #if ZEND_INTRIN_SSSE3_NATIVE
3332 	if (e - s > 15) {
3333 		const __m128i map = _mm_set_epi8(
3334 				0, 1, 2, 3,
3335 				4, 5, 6, 7,
3336 				8, 9, 10, 11,
3337 				12, 13, 14, 15);
3338 		do {
3339 			const __m128i str = _mm_loadu_si128((__m128i *)(e - 15));
3340 			_mm_storeu_si128((__m128i *)p, _mm_shuffle_epi8(str, map));
3341 			p += 16;
3342 			e -= 16;
3343 		} while (e - s > 15);
3344 	}
3345 #elif defined(__aarch64__)
3346 	if (e - s > 15) {
3347 		do {
3348 			const uint8x16_t str = vld1q_u8((uint8_t *)(e - 15));
3349 			/* Synthesize rev128 with a rev64 + ext. */
3350 			const uint8x16_t rev = vrev64q_u8(str);
3351 			const uint8x16_t ext = (uint8x16_t)
3352 				vextq_u64((uint64x2_t)rev, (uint64x2_t)rev, 1);
3353 			vst1q_u8((uint8_t *)p, ext);
3354 			p += 16;
3355 			e -= 16;
3356 		} while (e - s > 15);
3357 	}
3358 #endif
3359 	while (e >= s) {
3360 		*p++ = *e--;
3361 	}
3362 
3363 	*p = '\0';
3364 
3365 	RETVAL_NEW_STR(n);
3366 }
3367 /* }}} */
3368 
3369 /* {{{ php_similar_str */
3370 static 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)
3371 {
3372 	const char *p, *q;
3373 	const char *end1 = (char *) txt1 + len1;
3374 	const char *end2 = (char *) txt2 + len2;
3375 	size_t l;
3376 
3377 	*max = 0;
3378 	*count = 0;
3379 	for (p = (char *) txt1; p < end1; p++) {
3380 		for (q = (char *) txt2; q < end2; q++) {
3381 			for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3382 			if (l > *max) {
3383 				*max = l;
3384 				*count += 1;
3385 				*pos1 = p - txt1;
3386 				*pos2 = q - txt2;
3387 			}
3388 		}
3389 	}
3390 }
3391 /* }}} */
3392 
3393 /* {{{ php_similar_char */
3394 static size_t php_similar_char(const char *txt1, size_t len1, const char *txt2, size_t len2)
3395 {
3396 	size_t sum;
3397 	size_t pos1 = 0, pos2 = 0, max, count;
3398 
3399 	php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max, &count);
3400 	if ((sum = max)) {
3401 		if (pos1 && pos2 && count > 1) {
3402 			sum += php_similar_char(txt1, pos1,
3403 									txt2, pos2);
3404 		}
3405 		if ((pos1 + max < len1) && (pos2 + max < len2)) {
3406 			sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3407 									txt2 + pos2 + max, len2 - pos2 - max);
3408 		}
3409 	}
3410 
3411 	return