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
1
{
63
1
  zend_string *result;
64
1
  size_t i, j;
65
66
1
  result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
67
68
5
  for (i = j = 0; i < oldlen; i++) {
69
4
    ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
70
4
    ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
71
4
  }
72
1
  ZSTR_VAL(result)[j] = '\0';
73
74
1
  return result;
75
1
}
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
1
{
158
1
  zend_string *result;
159
1
  zend_string *data;
160
161
3
  ZEND_PARSE_PARAMETERS_START(1, 1)
162
4
    Z_PARAM_STR(data)
163
1
  ZEND_PARSE_PARAMETERS_END();
164
165
1
  result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
166
167
1
  RETURN_STR(result);
168
1
}
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
1.40k
{
475
1.40k
  const unsigned char *end;
476
1.40k
  unsigned char c;
477
1.40k
  zend_result result = SUCCESS;
478
479
1.40k
  memset(mask, 0, 256);
480
15.9k
  for (end = input+len; input < end; input++) {
481
14.4k
    c=*input;
482
14.4k
    if ((input+3 < end) && input[1] == '.' && input[2] == '.'
483
36
        && input[3] >= c) {
484
32
      memset(mask+c, 1, input[3] - c + 1);
485
32
      input+=3;
486
14.4k
    } 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
14.4k
    } else {
509
14.4k
      mask[c]=1;
510
14.4k
    }
511
14.4k
  }
512
1.40k
  return result;
513
1.40k
}
514
/* }}} */
515
516
static zend_always_inline bool php_is_whitespace(unsigned char c)
517
1.46k
{
518
1.46k
  return c <= ' ' && (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0');
519
1.46k
}
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
742
{
529
742
  const char *start = ZSTR_VAL(str);
530
742
  const char *end = start + ZSTR_LEN(str);
531
742
  char mask[256];
532
533
742
  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
742
  } else {
577
742
    if (mode & 1) {
578
757
      while (start != end) {
579
668
        if (php_is_whitespace((unsigned char)*start)) {
580
81
          start++;
581
587
        } else {
582
587
          break;
583
587
        }
584
668
      }
585
676
    }
586
742
    if (mode & 2) {
587
953
      while (start != end) {
588
798
        if (php_is_whitespace((unsigned char)*(end-1))) {
589
211
          end--;
590
587
        } else {
591
587
          break;
592
587
        }
593
798
      }
594
742
    }
595
742
  }
596
597
742
  if (ZSTR_LEN(str) == end - start) {
598
641
    return zend_string_copy(str);
599
641
  } else if (end - start == 0) {
600
0
    return ZSTR_EMPTY_ALLOC();
601
101
  } else {
602
101
    return zend_string_init(start, end - start, 0);
603
101
  }
604
742
}
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
742
{
624
742
  zend_string *str;
625
742
  zend_string *what = NULL;
626
627
2.22k
  ZEND_PARSE_PARAMETERS_START(1, 2)
628
2.96k
    Z_PARAM_STR(str)
629
742
    Z_PARAM_OPTIONAL
630
1.48k
    Z_PARAM_STR(what)
631
742
  ZEND_PARSE_PARAMETERS_END();
632
633
742
  ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
634
742
}
635
/* }}} */
636
637
/* {{{ Strips whitespace from the beginning and end of a string */
638
PHP_FUNCTION(trim)
639
676
{
640
676
  php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
641
676
}
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
66
{
675
66
  php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
676
66
}
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
5
{
832
5
  const char *p1 = ZSTR_VAL(str);
833
5
  const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
834
5
  const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
835
5
  zval  tmp;
836
837
5
  if (p2 == NULL) {
838
5
    ZVAL_STR_COPY(&tmp, str);
839
5
    zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
840
5
  } 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
5
}
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
16
{
905
16
  zend_string *str, *delim;
906
16
  zend_long limit = ZEND_LONG_MAX; /* No limit */
907
16
  zval tmp;
908
909
42
  ZEND_PARSE_PARAMETERS_START(2, 3)
910
42
    Z_PARAM_STR(delim)
911
25
    Z_PARAM_STR(str)
912
5
    Z_PARAM_OPTIONAL
913
10
    Z_PARAM_LONG(limit)
914
16
  ZEND_PARSE_PARAMETERS_END();
915
916
5
  if (ZSTR_LEN(delim) == 0) {
917
0
    zend_argument_must_not_be_empty_error(1);
918
0
    RETURN_THROWS();
919
0
  }
920
921
5
  array_init(return_value);
922
923
5
  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
5
  if (limit > 1) {
932
5
    php_explode(delim, str, return_value, limit);
933
5
  } 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
5
}
940
/* }}} */
941
942
/* {{{ php_implode */
943
PHPAPI void php_implode(const zend_string *glue, HashTable *pieces, zval *return_value)
944
79
{
945
79
  zval         *tmp;
946
79
  uint32_t      numelems;
947
79
  zend_string  *str;
948
79
  char         *cptr;
949
79
  size_t        len = 0;
950
79
  struct {
951
79
    zend_string *str;
952
79
    zend_long    lval;
953
79
  } *strings, *ptr;
954
79
  ALLOCA_FLAG(use_heap)
955
956
79
  numelems = zend_hash_num_elements(pieces);
957
958
79
  if (numelems == 0) {
959
23
    RETURN_EMPTY_STRING();
960
56
  } else if (numelems == 1) {
961
    /* loop to search the first not undefined element... */
962
153
    ZEND_HASH_FOREACH_VAL(pieces, tmp) {
963
153
      RETURN_STR(zval_get_string(tmp));
964
153
    } ZEND_HASH_FOREACH_END();
965
51
  }
966
967
5
  ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap);
968
969
5
  uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(glue);
970
971
25
  ZEND_HASH_FOREACH_VAL(pieces, tmp) {
972
25
    if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) {
973
10
      ptr->str = Z_STR_P(tmp);
974
10
      len += ZSTR_LEN(ptr->str);
975
10
      ptr->lval = 0;
976
10
      flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(ptr->str);
977
10
      ptr++;
978
10
    } 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
25
  } ZEND_HASH_FOREACH_END();
999
1000
  /* numelems cannot be 0, we checked above */
1001
5
  str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
1002
5
  GC_ADD_FLAGS(str, flags);
1003
5
  cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
1004
5
  *cptr = 0;
1005
1006
10
  while (1) {
1007
10
    ptr--;
1008
10
    if (EXPECTED(ptr->str)) {
1009
10
      cptr -= ZSTR_LEN(ptr->str);
1010
10
      memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
1011
10
      if (ptr->lval) {
1012
0
        zend_string_release_ex(ptr->str, 0);
1013
0
      }
1014
10
    } 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
10
    if (ptr == strings) {
1022
5
      break;
1023
5
    }
1024
1025
5
    cptr -= ZSTR_LEN(glue);
1026
5
    if (ZSTR_LEN(glue) == 1) {
1027
0
      *cptr = ZSTR_VAL(glue)[0];
1028
5
    } else {
1029
5
      memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
1030
5
    }
1031
5
  }
1032
1033
5
  free_alloca(strings, use_heap);
1034
5
  RETURN_NEW_STR(str);
1035
5
}
1036
/* }}} */
1037
1038
/* {{{ Joins array elements placing glue string between items and return one string */
1039
PHP_FUNCTION(implode)
1040
81
{
1041
81
  zend_string *arg1_str = NULL;
1042
81
  HashTable *arg1_array = NULL;
1043
81
  zend_array *pieces = NULL;
1044
1045
243
  ZEND_PARSE_PARAMETERS_START(1, 2)
1046
405
    Z_PARAM_ARRAY_HT_OR_STR(arg1_array, arg1_str)
1047
405
    Z_PARAM_OPTIONAL
1048
405
    Z_PARAM_ARRAY_HT_OR_NULL(pieces)
1049
81
  ZEND_PARSE_PARAMETERS_END();
1050
1051
81
  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
79
  } else {
1064
79
    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
79
  }
1069
1070
79
  php_implode(arg1_str, pieces, return_value);
1071
79
}
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
8.53k
#define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1120
1121
/* {{{ Tokenize a string */
1122
PHP_FUNCTION(strtok)
1123
1.24k
{
1124
1.24k
  zend_string *str, *tok = NULL;
1125
1.24k
  char *token;
1126
1.24k
  char *token_end;
1127
1.24k
  char *p;
1128
1.24k
  char *pe;
1129
1.24k
  size_t skipped = 0;
1130
1131
3.74k
  ZEND_PARSE_PARAMETERS_START(1, 2)
1132
4.98k
    Z_PARAM_STR(str)
1133
1.24k
    Z_PARAM_OPTIONAL
1134
2.57k
    Z_PARAM_STR_OR_NULL(tok)
1135
1.24k
  ZEND_PARSE_PARAMETERS_END();
1136
1137
1.24k
  if (!tok) {
1138
1.20k
    tok = str;
1139
1.20k
  } else {
1140
38
    if (BG(strtok_string)) {
1141
0
      zend_string_release(BG(strtok_string));
1142
0
    }
1143
38
    BG(strtok_string) = zend_string_copy(str);
1144
38
    BG(strtok_last) = ZSTR_VAL(str);
1145
38
    BG(strtok_len) = ZSTR_LEN(str);
1146
38
  }
1147
1148
1.24k
  if (!BG(strtok_string)) {
1149
    /* String to tokenize not set. */
1150
102
    php_error_docref(NULL, E_WARNING, "Both arguments must be provided when starting tokenization");
1151
102
    RETURN_FALSE;
1152
102
  }
1153
1154
1.14k
  p = BG(strtok_last); /* Where we start to search */
1155
1.14k
  pe = ZSTR_VAL(BG(strtok_string)) + BG(strtok_len);
1156
1.14k
  if (p >= pe) {
1157
    /* Reached the end of the string. */
1158
749
    RETURN_FALSE;
1159
749
  }
1160
1161
396
  token = ZSTR_VAL(tok);
1162
396
  token_end = token + ZSTR_LEN(tok);
1163
1164
2.44k
  while (token < token_end) {
1165
2.05k
    STRTOK_TABLE(token++) = 1;
1166
2.05k
  }
1167
1168
  /* Skip leading delimiters */
1169
983
  while (STRTOK_TABLE(p)) {
1170
587
    if (++p >= pe) {
1171
      /* no other chars left */
1172
0
      goto return_false;
1173
0
    }
1174
587
    skipped++;
1175
587
  }
1176
1177
  /* We know at this place that *p is no delimiter, so skip it */
1178
3.47k
  while (++p < pe) {
1179
3.44k
    if (STRTOK_TABLE(p)) {
1180
365
      goto return_token;
1181
365
    }
1182
3.44k
  }
1183
1184
31
  if (p - BG(strtok_last)) {
1185
396
return_token:
1186
396
    RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1187
396
    BG(strtok_last) = p + 1;
1188
396
  } 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
396
  token = ZSTR_VAL(tok);
1197
2.44k
  while (token < token_end) {
1198
2.05k
    STRTOK_TABLE(token++) = 0;
1199
2.05k
  }
1200
396
}
1201
/* }}} */
1202
1203
/* {{{ Makes a string uppercase */
1204
PHP_FUNCTION(strtoupper)
1205
1.31k
{
1206
1.31k
  zend_string *arg;
1207
1208
3.94k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1209
5.25k
    Z_PARAM_STR(arg)
1210
1.31k
  ZEND_PARSE_PARAMETERS_END();
1211
1212
1.31k
  RETURN_STR(zend_string_toupper(arg));
1213
1.31k
}
1214
/* }}} */
1215
1216
/* {{{ Makes a string lowercase */
1217
PHP_FUNCTION(strtolower)
1218
73
{
1219
73
  zend_string *str;
1220
1221
219
  ZEND_PARSE_PARAMETERS_START(1, 1)
1222
292
    Z_PARAM_STR(str)
1223
73
  ZEND_PARSE_PARAMETERS_END();
1224
1225
73
  RETURN_STR(zend_string_tolower(str));
1226
73
}
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
7
{
1363
7
  const char *basename_start;
1364
7
  const char *basename_end;
1365
1366
7
  if (CG(ascii_compatible_locale)) {
1367
7
    basename_end = s + len - 1;
1368
1369
    /* Strip trailing slashes */
1370
7
    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
7
      && *basename_end == '/') {
1378
0
#endif
1379
0
      basename_end--;
1380
0
    }
1381
7
    if (basename_end < s) {
1382
0
      return ZSTR_EMPTY_ALLOC();
1383
0
    }
1384
1385
    /* Extract filename */
1386
7
    basename_start = basename_end;
1387
7
    basename_end++;
1388
70
    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
70
      && *(basename_start-1) != '/') {
1399
63
#endif
1400
63
      basename_start--;
1401
63
    }
1402
7
  } 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
7
  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
7
  return zend_string_init(basename_start, basename_end - basename_start, 0);
1473
7
}
1474
/* }}} */
1475
1476
/* {{{ Returns the filename component of the path */
1477
PHP_FUNCTION(basename)
1478
7
{
1479
7
  char *string, *suffix = NULL;
1480
7
  size_t   string_len, suffix_len = 0;
1481
1482
21
  ZEND_PARSE_PARAMETERS_START(1, 2)
1483
28
    Z_PARAM_STRING(string, string_len)
1484
7
    Z_PARAM_OPTIONAL
1485
14
    Z_PARAM_STRING(suffix, suffix_len)
1486
7
  ZEND_PARSE_PARAMETERS_END();
1487
1488
7
  RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1489
7
}
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
66
{
1502
66
  zend_string *ret;
1503
1504
66
  ret = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
1505
1506
66
  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
66
    ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), ZSTR_LEN(str));
1512
66
#endif
1513
66
  } 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
66
  RETURN_NEW_STR(ret);
1530
66
}
1531
1532
/* {{{ Returns the directory name component of the path */
1533
PHP_FUNCTION(dirname)
1534
66
{
1535
66
  zend_string *str;
1536
66
  zend_long levels = 1;
1537
1538
198
  ZEND_PARSE_PARAMETERS_START(1, 2)
1539
264
    Z_PARAM_STR(str)
1540
66
    Z_PARAM_OPTIONAL
1541
132
    Z_PARAM_LONG(levels)
1542
66
  ZEND_PARSE_PARAMETERS_END();
1543
1544
66
  _zend_dirname(return_value, str, levels);
1545
66
}
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
46
{
1673
46
  return (char*)php_memnistr(s, t, t_len, s + s_len);
1674
46
}
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
49
{
1730
49
  zend_string *haystack, *needle;
1731
49
  const char *found = NULL;
1732
49
  size_t  found_offset;
1733
49
  bool part = 0;
1734
1735
144
  ZEND_PARSE_PARAMETERS_START(2, 3)
1736
184
    Z_PARAM_STR(haystack)
1737
230
    Z_PARAM_STR(needle)
1738
46
    Z_PARAM_OPTIONAL
1739
92
    Z_PARAM_BOOL(part)
1740
49
  ZEND_PARSE_PARAMETERS_END();
1741
1742
46
  found = php_stristr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(haystack), ZSTR_LEN(needle));
1743
1744
46
  if (UNEXPECTED(!found)) {
1745
0
    RETURN_FALSE;
1746
0
  }
1747
46
  found_offset = found - ZSTR_VAL(haystack);
1748
46
  if (part) {
1749
0
    RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1750
0
  }
1751
46
  RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1752
46
}
1753
/* }}} */
1754
1755
static zend_always_inline void _zend_strstr(zval *return_value, zend_string *haystack, zend_string *needle, bool part)
1756
207
{
1757
207
  const char *found = NULL;
1758
207
  zend_long found_offset;
1759
1760
207
  found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1761
1762
207
  if (UNEXPECTED(!found)) {
1763
65
    RETURN_FALSE;
1764
65
  }
1765
142
  found_offset = found - ZSTR_VAL(haystack);
1766
142
  if (part) {
1767
142
    RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1768
142
  }
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
207
{
1775
207
  zend_string *haystack, *needle;
1776
207
  bool part = 0;
1777
1778
621
  ZEND_PARSE_PARAMETERS_START(2, 3)
1779
828
    Z_PARAM_STR(haystack)
1780
1.03k
    Z_PARAM_STR(needle)
1781
207
    Z_PARAM_OPTIONAL
1782
828
    Z_PARAM_BOOL(part)
1783
207
  ZEND_PARSE_PARAMETERS_END();
1784
1785
207
  _zend_strstr(return_value, haystack, needle, part);
1786
207
}
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
826
{
1901
826
  const char *found = NULL;
1902
1903
826
  if (offset < 0) {
1904
0
    offset += (zend_long)ZSTR_LEN(haystack);
1905
0
  }
1906
826
  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
826
  found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1912
826
            ZSTR_VAL(needle), ZSTR_LEN(needle),
1913
826
            ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1914
1915
826
  if (UNEXPECTED(!found)) {
1916
815
    RETURN_FALSE;
1917
815
  }
1918
11
  RETURN_LONG(found - ZSTR_VAL(haystack));
1919
11
}
1920
1921
/* {{{ Finds position of first occurrence of a string within another */
1922
PHP_FUNCTION(strpos)
1923
826
{
1924
826
  zend_string *haystack, *needle;
1925
826
  zend_long offset = 0;
1926
1927
2.47k
  ZEND_PARSE_PARAMETERS_START(2, 3)
1928
3.30k
    Z_PARAM_STR(haystack)
1929
4.13k
    Z_PARAM_STR(needle)
1930
826
    Z_PARAM_OPTIONAL
1931
1.65k
    Z_PARAM_LONG(offset)
1932
826
  ZEND_PARSE_PARAMETERS_END();
1933
1934
826
  _zend_strpos(return_value, haystack, needle, offset);
1935
826
}
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
76
{
1973
76
  const char *found = NULL;
1974
76
  zend_string *haystack, *needle;
1975
76
  zend_long offset = 0;
1976
1977
228
  ZEND_PARSE_PARAMETERS_START(2, 3)
1978
304
    Z_PARAM_STR(haystack)
1979
380
    Z_PARAM_STR(needle)
1980
76
    Z_PARAM_OPTIONAL
1981
152
    Z_PARAM_LONG(offset)
1982
76
  ZEND_PARSE_PARAMETERS_END();
1983
1984
76
  if (offset < 0) {
1985
0
    offset += (zend_long)ZSTR_LEN(haystack);
1986
0
  }
1987
76
  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
76
  found = (char*)php_memnistr(ZSTR_VAL(haystack) + offset,
1993
76
      ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1994
1995
76
  if (UNEXPECTED(!found)) {
1996
22
    RETURN_FALSE;
1997
22
  }
1998
54
  RETURN_LONG(found - ZSTR_VAL(haystack));
1999
54
}
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
172
{
2233
172
  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
172
  } else if ((size_t)f > ZSTR_LEN(str)) {
2243
14
    RETURN_EMPTY_STRING();
2244
14
  }
2245
2246
158
  if (!len_is_null) {
2247
146
    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
14
      if (-(size_t)l > ZSTR_LEN(str) - (size_t)f) {
2252
0
        l = 0;
2253
14
      } else {
2254
14
        l = (zend_long)ZSTR_LEN(str) - f + l;
2255
14
      }
2256
132
    } else if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
2257
85
      l = (zend_long)ZSTR_LEN(str) - f;
2258
85
    }
2259
146
  } else {
2260
12
    l = (zend_long)ZSTR_LEN(str) - f;
2261
12
  }
2262
2263
158
  if (l == ZSTR_LEN(str)) {
2264
87
    RETURN_STR_COPY(str);
2265
87
  } else {
2266
71
    RETURN_STRINGL_FAST(ZSTR_VAL(str) + f, l);
2267
71
  }
2268
158
}
2269
2270
/* {{{ Returns part of a string */
2271
PHP_FUNCTION(substr)
2272
201
{
2273
201
  zend_string *str;
2274
201
  zend_long l = 0, f;
2275
201
  bool len_is_null = 1;
2276
2277
591
  ZEND_PARSE_PARAMETERS_START(2, 3)
2278
756
    Z_PARAM_STR(str)
2279
870
    Z_PARAM_LONG(f)
2280
172
    Z_PARAM_OPTIONAL
2281
668
    Z_PARAM_LONG_OR_NULL(l, len_is_null)
2282
201
  ZEND_PARSE_PARAMETERS_END();
2283
2284
172
  _zend_substr(return_value, str, f, len_is_null, l);
2285
172
}
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
17
{
2323
17
  zend_string *str, *repl_str;
2324
17
  HashTable *str_ht, *repl_ht;
2325
17
  HashTable *from_ht;
2326
17
  zend_long from_long;
2327
17
  HashTable *len_ht = NULL;
2328
17
  zend_long len_long;
2329
17
  bool len_is_null = 1;
2330
17
  zend_long l = 0;
2331
17
  zend_long f;
2332
17
  zend_string *result;
2333
17
  HashPosition from_idx, repl_idx, len_idx;
2334
17
  zval *tmp_str = NULL, *tmp_repl, *tmp_from = NULL, *tmp_len= NULL;
2335
2336
51
  ZEND_PARSE_PARAMETERS_START(3, 4)
2337
85
    Z_PARAM_ARRAY_HT_OR_STR(str_ht, str)
2338
85
    Z_PARAM_ARRAY_HT_OR_STR(repl_ht, repl_str)
2339
82
    Z_PARAM_ARRAY_HT_OR_LONG(from_ht, from_long)
2340
8
    Z_PARAM_OPTIONAL
2341
16
    Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(len_ht, len_long, len_is_null)
2342
17
  ZEND_PARSE_PARAMETERS_END();
2343
2344
8
  if (len_is_null) {
2345
8
    if (str) {
2346
8
      l = ZSTR_LEN(str);
2347
8
    }
2348
8
  } else if (!len_ht) {
2349
0
    l = len_long;
2350
0
  }
2351
2352
8
  if (str) {
2353
8
    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
8
    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
8
    f = from_long;
2363
2364
    /* if "from" position is negative, count start position from the end
2365
     * of the string
2366
     */
2367
8
    if (f < 0) {
2368
0
      f = (zend_long)ZSTR_LEN(str) + f;
2369
0
      if (f < 0) {
2370
0
        f = 0;
2371
0
      }
2372
8
    } else if ((size_t)f > ZSTR_LEN(str)) {
2373
3
      f = ZSTR_LEN(str);
2374
3
    }
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
8
    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
8
    if ((size_t)l > ZSTR_LEN(str)) {
2386
0
      l = ZSTR_LEN(str);
2387
0
    }
2388
2389
8
    if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2390
5
      l = ZSTR_LEN(str) - f;
2391
5
    }
2392
2393
8
    zend_string *tmp_repl_str = NULL;
2394
8
    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
8
    result = zend_string_safe_alloc(1, ZSTR_LEN(str) - l + ZSTR_LEN(repl_str), 0, 0);
2421
2422
8
    memcpy(ZSTR_VAL(result), ZSTR_VAL(str), f);
2423
8
    if (ZSTR_LEN(repl_str)) {
2424
0
      memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2425
0
    }
2426
8
    memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(str) + f + l, ZSTR_LEN(str) - f - l);
2427
8
    ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2428
8
    zend_tmp_string_release(tmp_repl_str);
2429
8
    RETURN_NEW_STR(result);
2430
8
  } 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
8
}
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
649
{
2657
649
  zend_string *str;
2658
2659
1.94k
  ZEND_PARSE_PARAMETERS_START(1, 1)
2660
2.59k
    Z_PARAM_STR(str)
2661
649
  ZEND_PARSE_PARAMETERS_END();
2662
2663
642
  if (UNEXPECTED(ZSTR_LEN(str) != 1)) {
2664
114
    if (ZSTR_LEN(str) == 0) {
2665
105
      php_error_docref(NULL, E_DEPRECATED,
2666
105
        "Providing an empty string is deprecated");
2667
105
    } else {
2668
9
      php_error_docref(NULL, E_DEPRECATED,
2669
9
        "Providing a string that is not one byte long is deprecated. Use ord($str[0]) instead");
2670
9
    }
2671
114
  }
2672
642
  RETURN_LONG((unsigned char) ZSTR_VAL(str)[0]);
2673
642
}
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
476
{
2680
476
  zend_long c;
2681
2682
1.42k
  ZEND_PARSE_PARAMETERS_START(1, 1)
2683
1.90k
    Z_PARAM_LONG(c)
2684
476
  ZEND_PARSE_PARAMETERS_END();
2685
2686
470
  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
470
  c &= 0xff;
2693
470
  RETURN_CHAR(c);
2694
470
}
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
81
{
2701
81
  const unsigned char ch = ZSTR_VAL(str)[0];
2702
81
  unsigned char r = zend_toupper_ascii(ch);
2703
81
  if (r == ch) {
2704
6
    return zend_string_copy(str);
2705
75
  } else {
2706
75
    zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2707
75
    ZSTR_VAL(s)[0] = r;
2708
75
    return s;
2709
75
  }
2710
81
}
2711
/* }}} */
2712
2713
/* {{{ Makes a string's first character uppercase */
2714
PHP_FUNCTION(ucfirst)
2715
83
{
2716
83
  zend_string *str;
2717
2718
249
  ZEND_PARSE_PARAMETERS_START(1, 1)
2719
332
    Z_PARAM_STR(str)
2720
83
  ZEND_PARSE_PARAMETERS_END();
2721
2722
83
  if (!ZSTR_LEN(str)) {
2723
2
    RETURN_EMPTY_STRING();
2724
2
  }
2725
2726
81
  RETURN_STR(php_ucfirst(str));
2727
81
}
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
867
{
3062
867
  zend_long count = 0;
3063
867
  const char *endp;
3064
3065
867
#ifdef XSSE2
3066
867
  if (length >= sizeof(__m128i)) {
3067
528
    __m128i search = _mm_set1_epi8(ch);
3068
3069
3.02k
    do {
3070
3.02k
      __m128i src = _mm_loadu_si128((__m128i*)(p));
3071
3.02k
      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
3.02k
#if 1
3074
5.88k
      while (mask != 0) {
3075
2.85k
        count++;
3076
2.85k
        mask = mask & (mask - 1);
3077
2.85k
      }
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
3.02k
      p += sizeof(__m128i);
3088
3.02k
      length -= sizeof(__m128i);
3089
3.02k
    } while (length >= sizeof(__m128i));
3090
528
  }
3091
867
  endp = p + length;
3092
4.68k
  while (p != endp) {
3093
3.81k
    count += (*p == ch);
3094
3.81k
    p++;
3095
3.81k
  }
3096
#else
3097
  endp = p + length;
3098
  while ((p = memchr(p, ch, endp-p))) {
3099
    count++;
3100
    p++;
3101
  }
3102
#endif
3103
867
  return count;
3104
867
}
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
869
{
3110
869
  zend_string *result;
3111
869
  size_t char_count;
3112
869
  int lc_from = 0;
3113
869
  const char *source, *source_end;
3114
869
  char *target;
3115
3116
869
  if (case_sensitivity) {
3117
867
    char_count = count_chars(ZSTR_VAL(str), ZSTR_LEN(str), from);
3118
867
  } 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
869
  if (char_count == 0) {
3130
47
    return zend_string_copy(str);
3131
47
  }
3132
3133
822
  if (replace_count) {
3134
822
    *replace_count += char_count;
3135
822
  }
3136
3137
822
  if (to_len > 0) {
3138
96
    result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3139
726
  } else {
3140
726
    result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3141
726
  }
3142
822
  target = ZSTR_VAL(result);
3143
3144
822
  if (case_sensitivity) {
3145
822
    char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3146
3147
3.38k
    while ((p = memchr(p, from, (e - p)))) {
3148
3.38k
      target = zend_mempcpy(target, s, (p - s));
3149
3.38k
      target = zend_mempcpy(target, to, to_len);
3150
3.38k
      p++;
3151
3.38k
      s = p;
3152
3.38k
      if (--char_count == 0) break;
3153
3.38k
    }
3154
822
    if (s < e) {
3155
31
      target = zend_mempcpy(target, s, e - s);
3156
31
    }
3157
822
  } 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
822
  *target = 0;
3169
822
  return result;
3170
869
}
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
1.14k
{
3177
3178
1.14k
  if (needle_len < ZSTR_LEN(haystack)) {
3179
1.07k
    zend_string *new_str;
3180
1.07k
    const char *end;
3181
1.07k
    const char *p, *r;
3182
1.07k
    char *e;
3183
3184
1.07k
    if (needle_len == str_len) {
3185
275
      new_str = NULL;
3186
275
      end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3187
394
      for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3188
119
        if (!new_str) {
3189
119
          new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3190
119
        }
3191
119
        memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3192
119
        (*replace_count)++;
3193
119
      }
3194
275
      if (!new_str) {
3195
156
        goto nothing_todo;
3196
156
      }
3197
119
      return new_str;
3198
796
    } else {
3199
796
      size_t count = 0;
3200
796
      const char *o = ZSTR_VAL(haystack);
3201
796
      const char *n = needle;
3202
796
      const char *endp = o + ZSTR_LEN(haystack);
3203
3204
926
      while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3205
130
        o += needle_len;
3206
130
        count++;
3207
130
      }
3208
796
      if (count == 0) {
3209
        /* Needle doesn't occur, shortcircuit the actual replacement. */
3210
733
        goto nothing_todo;
3211
733
      }
3212
63
      if (str_len > needle_len) {
3213
15
        new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3214
48
      } else {
3215
48
        new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3216
48
      }
3217
3218
63
      e = ZSTR_VAL(new_str);
3219
63
      end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3220
193
      for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3221
130
        e = zend_mempcpy(e, p, r - p);
3222
130
        e = zend_mempcpy(e, str, str_len);
3223
130
        (*replace_count)++;
3224
130
      }
3225
3226
63
      if (p < end) {
3227
63
        e = zend_mempcpy(e, p, end - p);
3228
63
      }
3229
3230
63
      *e = '\0';
3231
63
      return new_str;
3232
796
    }
3233
1.07k
  } else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3234
958
nothing_todo:
3235
958
    return zend_string_copy(haystack);
3236
69
  } else {
3237
0
    (*replace_count)++;
3238
0
    return zend_string_init_fast(str, str_len);
3239
0
  }
3240
1.14k
}
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
705
{
3337
705
  zend_string *new_str;
3338
3339
705
  if (needle_len < length) {
3340
580
    const char *end;
3341
580
    const char *s, *p;
3342
580
    char *e, *r;
3343
3344
580
    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
580
    } else {
3352
580
      if (str_len < needle_len) {
3353
0
        new_str = zend_string_alloc(length, 0);
3354
580
      } else {
3355
580
        size_t count = 0;
3356
580
        const char *o = haystack;
3357
580
        const char *n = needle;
3358
580
        const char *endp = o + length;
3359
3360
859
        while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3361
279
          o += needle_len;
3362
279
          count++;
3363
279
        }
3364
580
        if (count == 0) {
3365
          /* Needle doesn't occur, shortcircuit the actual replacement. */
3366
517
          new_str = zend_string_init(haystack, length, 0);
3367
517
          return new_str;
3368
517
        } else {
3369
63
          if (str_len > needle_len) {
3370
63
            new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3371
63
          } else {
3372
0
            new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3373
0
          }
3374
63
        }
3375
580
      }
3376
3377
63
      s = e = ZSTR_VAL(new_str);
3378
63
      end = haystack + length;
3379
342
      for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3380
279
        e = zend_mempcpy(e, p, r - p);
3381
279
        e = zend_mempcpy(e, str, str_len);
3382
279
      }
3383
3384
63
      if (p < end) {
3385
57
        e = zend_mempcpy(e, p, end - p);
3386
57
      }
3387
3388
63
      *e = '\0';
3389
63
      new_str = zend_string_truncate(new_str, e - s, 0);
3390
63
      return new_str;
3391
580
    }
3392
580
  } else if (needle_len > length || memcmp(haystack, needle, length)) {
3393
122
    new_str = zend_string_init(haystack, length, 0);
3394
122
    return new_str;
3395
122
  } else {
3396
3
    new_str = zend_string_init(str, str_len, 0);
3397
3398
3
    return new_str;
3399
3
  }
3400
705
}
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
87
{
3530
87
  zend_string *str;
3531
87
  const char *s, *e;
3532
87
  char *p;
3533
87
  zend_string *n;
3534
3535
261
  ZEND_PARSE_PARAMETERS_START(1, 1)
3536
348
    Z_PARAM_STR(str)
3537
87
  ZEND_PARSE_PARAMETERS_END();
3538
3539
87
  n = zend_string_alloc(ZSTR_LEN(str), 0);
3540
87
  p = ZSTR_VAL(n);
3541
3542
87
  s = ZSTR_VAL(str);
3543
87
  e = s + ZSTR_LEN(str);
3544
87
  --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
449
  while (e >= s) {
3587
362
    *p++ = *e--;
3588
362
  }
3589
3590
87
  *p = '\0';
3591
3592
87
  RETVAL_NEW_STR(n);
3593
87
}
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
96
{
3678
96
  zend_string *str, *what;
3679
3680
288
  ZEND_PARSE_PARAMETERS_START(2, 2)
3681
384
    Z_PARAM_STR(str)
3682
480
    Z_PARAM_STR(what)
3683
96
  ZEND_PARSE_PARAMETERS_END();
3684
3685
96
  if (ZSTR_LEN(str) == 0) {
3686
8
    RETURN_EMPTY_STRING();
3687
8
  }
3688
3689
88
  if (ZSTR_LEN(what) == 0) {
3690
0
    RETURN_STR_COPY(str);
3691
0
  }
3692
3693
88
  RETURN_STR(php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), ZSTR_VAL(what), ZSTR_LEN(what)));
3694
88
}
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
2
{
3717
2
  zend_string *str;
3718
3719
6
  ZEND_PARSE_PARAMETERS_START(1, 1)
3720
8
    Z_PARAM_STR(str)
3721
2
  ZEND_PARSE_PARAMETERS_END();
3722
3723
2
  ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3724
2
  php_stripcslashes(Z_STR_P(return_value));
3725
2
}
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
2
{
3745
2
  const char *source, *end;
3746
2
  char *target;
3747
2
  size_t  nlen = ZSTR_LEN(str), i;
3748
2
  char numtmp[4];
3749
3750
26
  for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(str); source < end; source++) {
3751
24
    if (*source == '\\' && source + 1 < end) {
3752
1
      source++;
3753
1
      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
1
        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
1
      }
3792
23
    } else {
3793
23
      *target++=*source;
3794
23
    }
3795
24
  }
3796
3797
2
  if (nlen != 0) {
3798
1
    *target='\0';
3799
1
  }
3800
3801
2
  ZSTR_LEN(str) = nlen;
3802
2
}
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
1.40k
{
3808
1.40k
  char flags[256];
3809
1.40k
  char *target;
3810
1.40k
  const char *source, *end;
3811
1.40k
  char c;
3812
1.40k
  size_t  newlen;
3813
1.40k
  zend_string *new_str = zend_string_safe_alloc(4, len, 0, 0);
3814
3815
1.40k
  php_charmask((const unsigned char *) what, wlength, flags);
3816
3817
14.9k
  for (source = str, end = source + len, target = ZSTR_VAL(new_str); source < end; source++) {
3818
13.5k
    c = *source;
3819
13.5k
    if (flags[(unsigned char)c]) {
3820
227
      if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3821
190
        *target++ = '\\';
3822
190
        switch (c) {
3823
94
          case '\n': *target++ = 'n'; break;
3824
0
          case '\t': *target++ = 't'; break;
3825
94
          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
190
        }
3832
190
        continue;
3833
190
      }
3834
37
      *target++ = '\\';
3835
37
    }
3836
13.3k
    *target++ = c;
3837
13.3k
  }
3838
1.40k
  *target = 0;
3839
1.40k
  newlen = target - ZSTR_VAL(new_str);
3840
1.40k
  if (newlen < len * 4) {
3841
1.34k
    new_str = zend_string_truncate(new_str, newlen, 0);
3842
1.34k
  }
3843
1.40k
  return new_str;
3844
1.40k
}
3845
/* }}} */
3846
3847
/* {{{ php_addcslashes */
3848
PHPAPI zend_string *php_addcslashes(zend_string *str, const char *what, size_t wlength)
3849
705
{
3850
705
  return php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), what, wlength);
3851
705
}
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
16
static php_addslashes_func_t resolve_addslashes(void) {
3875
16
  if (zend_cpu_supports_sse42()) {
3876
16
    return php_addslashes_sse42;
3877
16
  }
3878
0
  return php_addslashes_default;
3879
16
}
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
2.37k
) {
4322
2.37k
  zval    *search_entry;
4323
2.37k
  zend_string *tmp_result;
4324
2.37k
  char    *replace_value = NULL;
4325
2.37k
  size_t     replace_len = 0;
4326
2.37k
  zend_long  replace_count = 0;
4327
2.37k
  zend_string *lc_subject_str = NULL;
4328
2.37k
  uint32_t     replace_idx;
4329
4330
2.37k
  if (ZSTR_LEN(subject_str) == 0) {
4331
299
    ZVAL_EMPTY_STRING(result);
4332
299
    return 0;
4333
299
  }
4334
4335
  /* If search is an array */
4336
2.08k
  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
2.08k
  } else {
4452
2.08k
    ZEND_ASSERT(search_str);
4453
2.08k
    if (ZSTR_LEN(search_str) == 1) {
4454
869
      ZVAL_STR(result,
4455
869
        php_char_to_str_ex(subject_str,
4456
869
              ZSTR_VAL(search_str)[0],
4457
869
              ZSTR_VAL(replace_str),
4458
869
              ZSTR_LEN(replace_str),
4459
869
              case_sensitivity,
4460
869
              &replace_count));
4461
1.21k
    } else if (ZSTR_LEN(search_str) > 1) {
4462
1.20k
      if (case_sensitivity) {
4463
1.14k
        ZVAL_STR(result, php_str_to_str_ex(subject_str,
4464
1.14k
            ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4465
1.14k
            ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4466
1.14k
      } 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
1.20k
    } else {
4473
2
      ZVAL_STR_COPY(result, subject_str);
4474
2
    }
4475
2.08k
  }
4476
2.08k
  return replace_count;
4477
2.08k
}
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
2.37k
) {
4488
2.37k
  zval *subject_entry;
4489
2.37k
  zval result;
4490
2.37k
  zend_string *string_key;
4491
2.37k
  zend_ulong num_key;
4492
2.37k
  zend_long count = 0;
4493
4494
  /* Make sure we're dealing with strings and do the replacement. */
4495
2.37k
  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
2.37k
  if (subject_ht) {
4502
8
    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
31
    ZEND_HASH_FOREACH_KEY_VAL(subject_ht, num_key, string_key, subject_entry) {
4507
31
      zend_string *tmp_subject_str;
4508
31
      ZVAL_DEREF(subject_entry);
4509
31
      subject_str = zval_get_tmp_string(subject_entry, &tmp_subject_str);
4510
31
      count += php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, &result, case_sensitivity);
4511
31
      zend_tmp_string_release(tmp_subject_str);
4512
4513
      /* Add to return array */
4514
31
      if (string_key) {
4515
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, &result);
4516
10
      } else {
4517
10
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
4518
10
      }
4519
31
    } ZEND_HASH_FOREACH_END();
4520
2.36k
  } else { /* if subject is not an array */
4521
2.36k
    count = php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, return_value, case_sensitivity);
4522
2.36k
  }
4523
2.37k
  if (zcount) {
4524
36
    ZEND_TRY_ASSIGN_REF_LONG(zcount, count);
4525
36
  }
4526
2.37k
}
4527
4528
/* {{{ php_str_replace_common */
4529
static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool case_sensitivity)
4530
2.38k
{
4531
2.38k
  zend_string *search_str;
4532
2.38k
  HashTable *search_ht;
4533
2.38k
  zend_string *replace_str;
4534
2.38k
  HashTable *replace_ht;
4535
2.38k
  zend_string *subject_str;
4536
2.38k
  HashTable *subject_ht;
4537
2.38k
  zval *zcount = NULL;
4538
4539
7.13k
  ZEND_PARSE_PARAMETERS_START(3, 4)
4540
11.8k
    Z_PARAM_ARRAY_HT_OR_STR(search_ht, search_str)
4541
11.8k
    Z_PARAM_ARRAY_HT_OR_STR(replace_ht, replace_str)
4542
11.8k
    Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
4543
11.8k
    Z_PARAM_OPTIONAL
4544
11.8k
    Z_PARAM_ZVAL(zcount)
4545
4.82k
  ZEND_PARSE_PARAMETERS_END();
4546
4547
2.37k
  _php_str_replace_common(return_value, search_ht, search_str, replace_ht, replace_str, subject_ht, subject_str, zcount, case_sensitivity);
4548
2.37k
}
4549
/* }}} */
4550
4551
/* {{{ Replaces all occurrences of search in haystack with replace */
4552
PHP_FUNCTION(str_replace)
4553
2.30k
{
4554
2.30k
  php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4555
2.30k
}
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
1.35k
{
4829
1.35k
  zend_string *buf;
4830
1.35k
  zend_string *str;
4831
1.35k
  zend_string *allow_str = NULL;
4832
1.35k
  HashTable *allow_ht = NULL;
4833
1.35k
  const char *allowed_tags=NULL;
4834
1.35k
  size_t allowed_tags_len=0;
4835
1.35k
  smart_str tags_ss = {0};
4836
4837
4.05k
  ZEND_PARSE_PARAMETERS_START(1, 2)
4838
5.39k
    Z_PARAM_STR(str)
4839
1.34k
    Z_PARAM_OPTIONAL
4840
2.84k
    Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(allow_ht, allow_str)
4841
2.84k
  ZEND_PARSE_PARAMETERS_END();
4842
4843
1.34k
  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
1.34k
  } else if (allow_str) {
4860
52
    allowed_tags = ZSTR_VAL(allow_str);
4861
52
    allowed_tags_len = ZSTR_LEN(allow_str);
4862
52
  }
4863
4864
1.34k
  buf = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
4865
1.34k
  ZSTR_LEN(buf) = php_strip_tags_ex(ZSTR_VAL(buf), ZSTR_LEN(str), allowed_tags, allowed_tags_len, 0);
4866
1.34k
  smart_str_free(&tags_ss);
4867
1.34k
  RETURN_NEW_STR(buf);
4868
1.34k
}
4869
/* }}} */
4870
4871
18
static zend_string *try_setlocale_str(zend_long cat, zend_string *loc) {
4872
18
  const char *retval;
4873
4874
18
  if (zend_string_equals_literal(loc, "0")) {
4875
0
    loc = NULL;
4876
18
  } else {
4877
18
    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
18
  }
4882
4883
16
# ifndef PHP_WIN32
4884
16
  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
16
  if (!retval) {
4906
14
    return NULL;
4907
14
  }
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
18
{
4951
18
  zend_long cat;
4952
18
  zval *args = NULL;
4953
18
  uint32_t num_args;
4954
18
  ALLOCA_FLAG(use_heap);
4955
4956
54
  ZEND_PARSE_PARAMETERS_START(2, -1)
4957
72
    Z_PARAM_LONG(cat)
4958
18
    Z_PARAM_VARIADIC('+', args, num_args)
4959
18
  ZEND_PARSE_PARAMETERS_END();
4960
4961
18
  zend_string **strings = do_alloca(sizeof(zend_string *) * num_args, use_heap);
4962
4963
36
  for (uint32_t i = 0; i < num_args; i++) {
4964
18
    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
18
  }
4969
4970
34
  for (uint32_t i = 0; i < num_args; i++) {
4971
18
    zend_string *result;
4972
18
    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
18
    } else if (Z_ISNULL(args[i])) {
4986
2
      result = try_setlocale_str(cat, ZSTR_EMPTY_ALLOC());
4987
16
    } else {
4988
16
      result = try_setlocale_str(cat, strings[i]);
4989
16
    }
4990
18
    if (EG(exception)) {
4991
0
      goto out;
4992
0
    }
4993
18
    if (result) {
4994
2
      RETVAL_STR(result);
4995
2
      goto out;
4996
2
    }
4997
18
  }
4998
4999
16
  RETVAL_FALSE;
5000
5001
18
out:
5002
18
  free_alloca(strings, use_heap);
5003
18
}
5004
/* }}} */
5005
5006
/* {{{ Parses GET/POST/COOKIE data and sets global variables */
5007
PHP_FUNCTION(parse_str)
5008
10
{
5009
10
  char *arg;
5010
10
  zval *arrayArg = NULL;
5011
10
  char *res = NULL;
5012
10
  size_t arglen;
5013
5014
30
  ZEND_PARSE_PARAMETERS_START(2, 2)
5015
40
    Z_PARAM_PATH(arg, arglen)
5016
50
    Z_PARAM_ZVAL(arrayArg)
5017
50
  ZEND_PARSE_PARAMETERS_END();
5018
5019
10
  arrayArg = zend_try_array_init(arrayArg);
5020
10
  if (!arrayArg) {
5021
0
    RETURN_THROWS();
5022
0
  }
5023
5024
10
  res = estrndup(arg, arglen);
5025
10
  sapi_module.treat_data(PARSE_STRING, res, arrayArg);
5026
10
}
5027
/* }}} */
5028
5029
9.11k
#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
42
static bool php_tag_find(char *tag, size_t len, const char *set) {
5041
42
  char c, *n;
5042
42
  const char *t;
5043
42
  int state = 0;
5044
42
  bool done = false;
5045
42
  char *norm;
5046
5047
42
  if (len == 0) {
5048
0
    return false;
5049
0
  }
5050
5051
42
  norm = emalloc(len+1);
5052
5053
42
  n = norm;
5054
42
  t = tag;
5055
42
  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
408
  while (!done) {
5062
366
    switch (c) {
5063
42
      case '<':
5064
42
        *(n++) = c;
5065
42
        break;
5066
33
      case '>':
5067
33
        done = true;
5068
33
        break;
5069
291
      default:
5070
291
        if (!isspace((unsigned char)c)) {
5071
282
          if (state == 0) {
5072
42
            state=1;
5073
42
          }
5074
282
          if (c != '/' || (*(t-1) != '<' && *(t+1) != '>')) {
5075
282
            *(n++) = c;
5076
282
          }
5077
282
        } else {
5078
9
          if (state == 1)
5079
9
            done = true;
5080
9
        }
5081
291
        break;
5082
366
    }
5083
366
    c = zend_tolower_ascii(*(++t));
5084
366
  }
5085
42
  *(n++) = '>';
5086
42
  *n = '\0';
5087
42
  if (strstr(set, norm)) {
5088
9
    done = true;
5089
33
  } else {
5090
33
    done = false;
5091
33
  }
5092
42
  efree(norm);
5093
42
  return done;
5094
42
}
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
1.34k
{
5125
1.34k
  char *tbuf, *tp, *rp, c, lc;
5126
1.34k
  const char *buf, *p, *end;
5127
1.34k
  int br, depth=0, in_q = 0;
5128
1.34k
  uint8_t state = 0;
5129
1.34k
  size_t pos;
5130
1.34k
  char *allow_free = NULL;
5131
1.34k
  char is_xml = 0;
5132
5133
1.34k
  buf = estrndup(rbuf, len);
5134
1.34k
  end = buf + len;
5135
1.34k
  lc = '\0';
5136
1.34k
  p = buf;
5137
1.34k
  rp = rbuf;
5138
1.34k
  br = 0;
5139
1.34k
  if (allow) {
5140
52
    allow_free = zend_str_tolower_dup_ex(allow, allow_len);
5141
52
    allow = allow_free ? allow_free : allow;
5142
52
    tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
5143
52
    tp = tbuf;
5144
1.29k
  } else {
5145
1.29k
    tbuf = tp = NULL;
5146
1.29k
  }
5147
5148
1.02M
state_0:
5149
1.02M
  if (p >= end) {
5150
809
    goto finish;
5151
809
  }
5152
1.02M
  c = *p;
5153
1.02M
  switch (c) {
5154
46.0k
    case '\0':
5155
46.0k
      break;
5156
54.3k
    case '<':
5157
54.3k
      if (in_q) {
5158
0
        break;
5159
0
      }
5160
54.3k
      if (isspace((unsigned char)p[1]) && !allow_tag_spaces) {
5161
17
        *(rp++) = c;
5162
17
        break;
5163
17
      }
5164
54.3k
      lc = '<';
5165
54.3k
      state = 1;
5166
54.3k
      if (allow) {
5167
97
        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
97
        *(tp++) = '<';
5173
97
      }
5174
54.3k
      p++;
5175
54.3k
      goto state_1;
5176
723
    case '>':
5177
723
      if (depth) {
5178
0
        depth--;
5179
0
        break;
5180
0
      }
5181
5182
723
      if (in_q) {
5183
0
        break;
5184
0
      }
5185
5186
723
      *(rp++) = c;
5187
723
      break;
5188
924k
    default:
5189
924k
      *(rp++) = c;
5190
924k
      break;
5191
1.02M
  }
5192
970k
  p++;
5193
970k
  goto state_0;
5194
5195
1.02M
state_1:
5196
1.02M
  if (p >= end) {
5197
187
    goto finish;
5198
187
  }
5199
1.02M
  c = *p;
5200
1.02M
  switch (c) {
5201
4.60k
    case '\0':
5202
4.60k
      break;
5203
2.08k
    case '<':
5204
2.08k
      if (in_q) {
5205
718
        break;
5206
718
      }
5207
1.36k
      if (isspace((unsigned char)p[1]) && !allow_tag_spaces) {
5208
9
        goto reg_char_1;
5209
9
      }
5210
1.36k
      depth++;
5211
1.36k
      break;
5212
54.6k
    case '>':
5213
54.6k
      if (depth) {
5214
671
        depth--;
5215
671
        break;
5216
671
      }
5217
54.0k
      if (in_q) {
5218
357
        break;
5219
357
      }
5220
5221
53.6k
      lc = '>';
5222
53.6k
      if (is_xml && p >= buf + 1 && *(p -1) == '-') {
5223
0
        break;
5224
0
      }
5225
53.6k
      in_q = state = is_xml = 0;
5226
53.6k
      if (allow) {
5227
42
        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
42
        *(tp++) = '>';
5233
42
        *tp='\0';
5234
42
        if (php_tag_find(tbuf, tp-tbuf, allow)) {
5235
9
          rp = zend_mempcpy(rp, tbuf, tp - tbuf);
5236
9
        }
5237
42
        tp = tbuf;
5238
42
      }
5239
53.6k
      p++;
5240
53.6k
      goto state_0;
5241
52.6k
    case '"':
5242
53.1k
    case '\'':
5243
53.1k
      if (p != buf && (!in_q || *p == in_q)) {
5244
53.0k
        if (in_q) {
5245
26.4k
          in_q = 0;
5246
26.5k
        } else {
5247
26.5k
          in_q = *p;
5248
26.5k
        }
5249
53.0k
      }
5250
53.1k
      goto reg_char_1;
5251
516
    case '!':
5252
      /* JavaScript & Other HTML scripting languages */
5253
516
      if (p >= buf + 1 && *(p-1) == '<') {
5254
189
        state = 3;
5255
189
        lc = c;
5256
189
        p++;
5257
189
        goto state_3;
5258
327
      } else {
5259
327
        goto reg_char_1;
5260
327
      }
5261
0
      break;
5262
1.18k
    case '?':
5263
1.18k
      if (p >= buf + 1 && *(p-1) == '<') {
5264
297
        br=0;
5265
297
        state = 2;
5266
297
        p++;
5267
297
        goto state_2;
5268
889
      } else {
5269
889
        goto reg_char_1;
5270
889
      }
5271
0
      break;
5272
908k
    default:
5273
963k
reg_char_1:
5274
963k
      if (allow) {
5275
8.97k
        if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
5276
1.59k
          pos = tp - tbuf;
5277
1.59k
          tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
5278
1.59k
          tp = tbuf + pos;
5279
1.59k
        }
5280
8.97k
        *(tp++) = c;
5281
8.97k
      }
5282
963k
      break;
5283
1.02M
  }
5284
970k
  p++;
5285
970k
  goto state_1;
5286
5287
174k
state_2:
5288
174k
  if (p >= end) {
5289
253
    goto finish;
5290
253
  }
5291
174k
  c = *p;
5292
174k
  switch (c) {
5293
1.75k
    case '(':
5294
1.75k
      if (lc != '"' && lc != '\'') {
5295
1.11k
        lc = '(';
5296
1.11k
        br++;
5297
1.11k
      }
5298
1.75k
      break;
5299
1.29k
    case ')':
5300
1.29k
      if (lc != '"' && lc != '\'') {
5301
847
        lc = ')';
5302
847
        br--;
5303
847
      }
5304
1.29k
      break;
5305
1.95k
    case '>':
5306
1.95k
      if (depth) {
5307
213
        depth--;
5308
213
        break;
5309
213
      }
5310
1.74k
      if (in_q) {
5311
878
        break;
5312
878
      }
5313
5314
864
      if (!br && p >= buf + 1 && lc != '\"' && *(p-1) == '?') {
5315
44
        in_q = state = 0;
5316
44
        tp = tbuf;
5317
44
        p++;
5318
44
        goto state_0;
5319
44
      }
5320
820
      break;
5321
1.11k
    case '"':
5322
1.42k
    case '\'':
5323
1.42k
      if (p >= buf + 1 && *(p-1) != '\\') {
5324
1.42k
        if (lc == c) {
5325
630
          lc = '\0';
5326
792
        } else if (lc != '\\') {
5327
792
          lc = c;
5328
792
        }
5329
1.42k
        if (p != buf && (!in_q || *p == in_q)) {
5330
1.32k
          if (in_q) {
5331
599
            in_q = 0;
5332
730
          } else {
5333
730
            in_q = *p;
5334
730
          }
5335
1.32k
        }
5336
1.42k
      }
5337
1.42k
      break;
5338
1.88k
    case 'l':
5339
2.13k
    case 'L':
5340
      /* swm: If we encounter '<?xml' then we shouldn't be in
5341
       * state == 2 (PHP). Switch back to HTML.
5342
       */
5343
2.13k
      if (state == 2 && p > buf+4
5344
2.13k
             && (*(p-1) == 'm' || *(p-1) == 'M')
5345
6
             && (*(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
2.13k
      break;
5353
165k
    default:
5354
165k
      break;
5355
174k
  }
5356
174k
  p++;
5357
174k
  goto state_2;
5358
5359
39.6k
state_3:
5360
39.6k
  if (p >= end) {
5361
85
    goto finish;
5362
85
  }
5363
39.5k
  c = *p;
5364
39.5k
  switch (c) {
5365
626
    case '>':
5366
626
      if (depth) {
5367
203
        depth--;
5368
203
        break;
5369
203
      }
5370
423
      if (in_q) {
5371
332
        break;
5372
332
      }
5373
91
      in_q = state = 0;
5374
91
      tp = tbuf;
5375
91
      p++;
5376
91
      goto state_0;
5377
187
    case '"':
5378
691
    case '\'':
5379
691
      if (p != buf && *(p-1) != '\\' && (!in_q || *p == in_q)) {
5380
598
        if (in_q) {
5381
296
          in_q = 0;
5382
302
        } else {
5383
302
          in_q = *p;
5384
302
        }
5385
598
      }
5386
691
      break;
5387
717
    case '-':
5388
717
      if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
5389
13
        state = 4;
5390
13
        p++;
5391
13
        goto state_4;
5392
13
      }
5393
704
      break;
5394
704
    case 'E':
5395
637
    case 'e':
5396
      /* !DOCTYPE exception */
5397
637
      if (p > buf+6
5398
637
           && (*(p-1) == 'p' || *(p-1) == 'P')
5399
25
           && (*(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
637
      break;
5409
36.8k
    default:
5410
36.8k
      break;
5411
39.5k
  }
5412
39.4k
  p++;
5413
39.4k
  goto state_3;
5414
5415
13
state_4:
5416
1.41k
  while (p < end) {
5417
1.40k
    c = *p;
5418
1.40k
    if (c == '>' && !in_q) {
5419
30
      if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
5420
3
        in_q = state = 0;
5421
3
        tp = tbuf;
5422
3
        p++;
5423
3
        goto state_0;
5424
3
      }
5425
30
    }
5426
1.39k
    p++;
5427
1.39k
  }
5428
5429
1.34k
finish:
5430
1.34k
  if (rp < rbuf + len) {
5431
1.21k
    *rp = '\0';
5432
1.21k
  }
5433
1.34k
  efree((void *)buf);
5434
1.34k
  if (tbuf) {
5435
52
    efree(tbuf);
5436
52
  }
5437
1.34k
  if (allow_free) {
5438
48
    efree(allow_free);
5439
48
  }
5440
5441
1.34k
  return (size_t)(rp - rbuf);
5442
13
}
5443
/* }}} */
5444
5445
/* {{{ Parse a CSV string into an array */
5446
PHP_FUNCTION(str_getcsv)
5447
137
{
5448
137
  zend_string *str;
5449
137
  char delimiter = ',', enclosure = '"';
5450
137
  char *delimiter_str = NULL, *enclosure_str = NULL;
5451
137
  size_t delimiter_str_len = 0, enclosure_str_len = 0;
5452
137
  zend_string *escape_str = NULL;
5453
5454
411
  ZEND_PARSE_PARAMETERS_START(1, 4)
5455
548
    Z_PARAM_STR(str)
5456
137
    Z_PARAM_OPTIONAL
5457
464
    Z_PARAM_STRING(delimiter_str, delimiter_str_len)
5458
421
    Z_PARAM_STRING(enclosure_str, enclosure_str_len)
5459
216
    Z_PARAM_STR(escape_str)
5460
137
  ZEND_PARSE_PARAMETERS_END();
5461
5462
137
  if (delimiter_str != NULL) {
5463
    /* Make sure that there is at least one character in string */
5464
95
    if (delimiter_str_len != 1) {
5465
11
      zend_argument_value_error(2, "must be a single character");
5466
11
      RETURN_THROWS();
5467
11
    }
5468
    /* use first character from string */
5469
84
    delimiter = delimiter_str[0];
5470
84
  }
5471
126
  if (enclosure_str != NULL) {
5472
57
    if (enclosure_str_len != 1) {
5473
3
      zend_argument_value_error(3, "must be a single character");
5474
3
      RETURN_THROWS();
5475
3
    }
5476
    /* use first character from string */
5477
54
    enclosure = enclosure_str[0];
5478
54
  }
5479
5480
123
  int escape_char = php_csv_handle_escape_argument(escape_str, 4);
5481
123
  if (escape_char == PHP_CSV_ESCAPE_ERROR) {
5482
0
    RETURN_THROWS();
5483
0
  }
5484
5485
123
  HashTable *values = php_fgetcsv(NULL, delimiter, enclosure, escape_char, ZSTR_LEN(str), ZSTR_VAL(str));
5486
123
  if (values == NULL) {
5487
0
    values = php_bc_fgetcsv_empty_line();
5488
0
  }
5489
123
  RETURN_ARR(values);
5490
123
}
5491
/* }}} */
5492
5493
/* {{{ Returns the input string repeat mult times */
5494
PHP_FUNCTION(str_repeat)
5495
755
{
5496
755
  zend_string   *input_str;   /* Input string */
5497
755
  zend_long     mult;     /* Multiplier */
5498
755
  zend_string *result;    /* Resulting string */
5499
755
  size_t    result_len;   /* Length of the resulting string */
5500
5501
2.26k
  ZEND_PARSE_PARAMETERS_START(2, 2)
5502
3.00k
    Z_PARAM_STR(input_str)
5503
3.76k
    Z_PARAM_LONG(mult)
5504
755
  ZEND_PARSE_PARAMETERS_END();
5505
5506
750
  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
744
  if (ZSTR_LEN(input_str) == 0 || mult == 0)
5514
16
    RETURN_EMPTY_STRING();
5515
5516
  /* Initialize the result string */
5517
728
  result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
5518
728
  result_len = ZSTR_LEN(input_str) * mult;
5519
728
  ZSTR_COPY_CONCAT_PROPERTIES(result, input_str);
5520
5521
  /* Heavy optimization for situations where input string is 1 byte long */
5522
728
  if (ZSTR_LEN(input_str) == 1) {
5523
642
    memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
5524
642
  } else {
5525
86
    const char *s, *ee;
5526
86
    char *e;
5527
86
    ptrdiff_t l=0;
5528
86
    memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
5529
86
    s = ZSTR_VAL(result);
5530
86
    e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
5531
86
    ee = ZSTR_VAL(result) + result_len;
5532
5533
556
    while (e<ee) {
5534
470
      l = (e-s) < (ee-e) ? (e-s) : (ee-e);
5535
470
      memmove(e, s, l);
5536
470
      e += l;
5537
470
    }
5538
86
  }
5539
5540
728
  ZSTR_VAL(result)[result_len] = '\0';
5541
5542
728
  RETURN_NEW_STR(result);
5543
728
}
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
318
static void php_str_pad_fill(zend_string *result, size_t pad_chars, const char *pad_str, size_t pad_str_len) {
5765
318
  char *p = ZSTR_VAL(result) + ZSTR_LEN(result);
5766
  
5767
318
  if (pad_str_len == 1) {
5768
318
    memset(p, pad_str[0], pad_chars);
5769
318
    ZSTR_LEN(result) += pad_chars;
5770
318
    return;
5771
318
  }
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
386
{
5788
  /* Input arguments */
5789
386
  zend_string *input;       /* Input string */
5790
386
  zend_long pad_length;     /* Length to pad to */
5791
5792
  /* Helper variables */
5793
386
  size_t num_pad_chars;   /* Number of padding characters (total - input size) */
5794
386
  char *pad_str = " "; /* Pointer to padding string */
5795
386
  size_t pad_str_len = 1;
5796
386
  zend_long   pad_type_val = PHP_STR_PAD_RIGHT; /* The padding type value */
5797
386
  size_t left_pad=0, right_pad=0;
5798
386
  zend_string *result = NULL; /* Resulting string */
5799
5800
1.15k
  ZEND_PARSE_PARAMETERS_START(2, 4)
5801
1.54k
    Z_PARAM_STR(input)
5802
1.93k
    Z_PARAM_LONG(pad_length)
5803
386
    Z_PARAM_OPTIONAL
5804
782
    Z_PARAM_STRING(pad_str, pad_str_len)
5805
25
    Z_PARAM_LONG(pad_type_val)
5806
386
  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
386
  if (pad_length < 0  || (size_t)pad_length <= ZSTR_LEN(input)) {
5811
68
    RETURN_STR_COPY(input);
5812
68
  }
5813
5814
318
  if (pad_str_len == 0) {
5815
0
    zend_argument_must_not_be_empty_error(3);
5816
0
    RETURN_THROWS();
5817
0
  }
5818
5819
318
  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
318
  num_pad_chars = pad_length - ZSTR_LEN(input);
5825
318
  result = zend_string_safe_alloc(1, ZSTR_LEN(input), num_pad_chars, 0);
5826
318
  ZSTR_LEN(result) = 0;
5827
5828
  /* We need to figure out the left/right padding lengths. */
5829
318
  switch (pad_type_val) {
5830
313
    case PHP_STR_PAD_RIGHT:
5831
313
      left_pad = 0;
5832
313
      right_pad = num_pad_chars;
5833
313
      break;
5834
5835
5
    case PHP_STR_PAD_LEFT:
5836
5
      left_pad = num_pad_chars;
5837
5
      right_pad = 0;
5838
5
      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
318
  }
5845
5846
  /* First we pad on the left. */
5847
318
  if (left_pad > 0) {
5848
5
    php_str_pad_fill(result, left_pad, pad_str, pad_str_len);
5849
5
  }
5850
5851
  /* Then we copy the input string. */
5852
318
  memcpy(ZSTR_VAL(result) + ZSTR_LEN(result), ZSTR_VAL(input), ZSTR_LEN(input));
5853
318
  ZSTR_LEN(result) += ZSTR_LEN(input);
5854
5855
  /* Finally, we pad on the right. */
5856
318
  if (right_pad > 0) {
5857
313
    php_str_pad_fill(result, right_pad, pad_str, pad_str_len);
5858
313
  }
5859
5860
318
  ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
5861
5862
318
  RETURN_NEW_STR(result);
5863
318
}
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
20
{
5998
20
  const php_random_algo *algo = engine.algo;
5999
20
  void *state = engine.state;
6000
6001
20
  int64_t n_elems, rnd_idx, n_left;
6002
20
  char temp;
6003
6004
  /* The implementation is stolen from array_data_shuffle       */
6005
  /* Thus the characteristics of the randomization are the same */
6006
20
  n_elems = len;
6007
6008
20
  if (n_elems <= 1) {
6009
0
    return true;
6010
0
  }
6011
6012
20
  n_left = n_elems;
6013
6014
1.58k
  while (--n_left) {
6015
1.56k
    rnd_idx = algo->range(state, 0, n_left);
6016
1.56k
    if (EG(exception)) {
6017
0
      return false;
6018
0
    }
6019
1.56k
    if (rnd_idx != n_left) {
6020
1.48k
      temp = str[n_left];
6021
1.48k
      str[n_left] = str[rnd_idx];
6022
1.48k
      str[rnd_idx] = temp;
6023
1.48k
    }
6024
1.56k
  }
6025
6026
20
  return true;
6027
20
}
6028
/* }}} */
6029
6030
/* {{{ Shuffles string. One permutation of all possible is created */
6031
PHP_FUNCTION(str_shuffle)
6032
22
{
6033
22
  zend_string *arg;
6034
6035
66
  ZEND_PARSE_PARAMETERS_START(1, 1)
6036
88
    Z_PARAM_STR(arg)
6037
22
  ZEND_PARSE_PARAMETERS_END();
6038
6039
22
  RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg));
6040
22
  if (Z_STRLEN_P(return_value) > 1) {
6041
20
    php_binary_string_shuffle(
6042
20
      php_random_default_engine(),
6043
20
      Z_STRVAL_P(return_value),
6044
20
      Z_STRLEN_P(return_value)
6045
20
    );
6046
20
  }
6047
22
}
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
/* }}} */