Coverage Report

Created: 2026-04-01 06:49

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