Coverage Report

Created: 2025-11-16 06:23

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