Coverage Report

Created: 2026-06-02 06:40

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