Coverage Report

Created: 2026-07-25 06:39

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