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