Coverage Report

Created: 2026-09-14 06:25

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