Coverage Report

Created: 2025-12-14 06:10

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.02k
{
477
1.02k
  const unsigned char *end;
478
1.02k
  unsigned char c;
479
1.02k
  zend_result result = SUCCESS;
480
481
1.02k
  memset(mask, 0, 256);
482
15.9k
  for (end = input+len; input < end; input++) {
483
14.9k
    c=*input;
484
14.9k
    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
14.8k
    } 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
14.8k
    } else {
511
14.8k
      mask[c]=1;
512
14.8k
    }
513
14.9k
  }
514
1.02k
  return result;
515
1.02k
}
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
920
{
526
920
  const char *start = ZSTR_VAL(str);
527
920
  const char *end = start + ZSTR_LEN(str);
528
920
  char mask[256];
529
530
920
  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
920
  } else {
574
920
    if (mode & 1) {
575
974
      while (start != end) {
576
903
        unsigned char c = (unsigned char)*start;
577
578
903
        if (c <= ' ' &&
579
183
            (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
580
102
          start++;
581
801
        } else {
582
801
          break;
583
801
        }
584
903
      }
585
872
    }
586
920
    if (mode & 2) {
587
1.16k
      while (start != end) {
588
1.04k
        unsigned char c = (unsigned char)*(end-1);
589
590
1.04k
        if (c <= ' ' &&
591
282
            (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
592
246
          end--;
593
801
        } else {
594
801
          break;
595
801
        }
596
1.04k
      }
597
920
    }
598
920
  }
599
600
920
  if (ZSTR_LEN(str) == end - start) {
601
795
    return zend_string_copy(str);
602
795
  } 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
920
}
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
920
{
627
920
  zend_string *str;
628
920
  zend_string *what = NULL;
629
630
2.76k
  ZEND_PARSE_PARAMETERS_START(1, 2)
631
3.68k
    Z_PARAM_STR(str)
632
920
    Z_PARAM_OPTIONAL
633
1.84k
    Z_PARAM_STR(what)
634
920
  ZEND_PARSE_PARAMETERS_END();
635
636
920
  ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
637
920
}
638
/* }}} */
639
640
/* {{{ Strips whitespace from the beginning and end of a string */
641
PHP_FUNCTION(trim)
642
872
{
643
872
  php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
644
872
}
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
493
{
1127
493
  zend_string *str, *tok = NULL;
1128
493
  char *token;
1129
493
  char *token_end;
1130
493
  char *p;
1131
493
  char *pe;
1132
493
  size_t skipped = 0;
1133
1134
1.47k
  ZEND_PARSE_PARAMETERS_START(1, 2)
1135
1.97k
    Z_PARAM_STR(str)
1136
493
    Z_PARAM_OPTIONAL
1137
1.03k
    Z_PARAM_STR_OR_NULL(tok)
1138
493
  ZEND_PARSE_PARAMETERS_END();
1139
1140
493
  if (!tok) {
1141
467
    tok = str;
1142
467
  } 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
493
  if (!BG(strtok_string)) {
1152
    /* String to tokenize not set. */
1153
185
    php_error_docref(NULL, E_WARNING, "Both arguments must be provided when starting tokenization");
1154
185
    RETURN_FALSE;
1155
185
  }
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.01k
{
1209
1.01k
  zend_string *arg;
1210
1211
3.05k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1212
4.06k
    Z_PARAM_STR(arg)
1213
1.01k
  ZEND_PARSE_PARAMETERS_END();
1214
1215
1.01k
  RETURN_STR(zend_string_toupper(arg));
1216
1.01k
}
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
11
{
1366
11
  const char *basename_start;
1367
11
  const char *basename_end;
1368
1369
11
  if (CG(ascii_compatible_locale)) {
1370
11
    basename_end = s + len - 1;
1371
1372
    /* Strip trailing slashes */
1373
11
    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
11
      && *basename_end == '/') {
1381
0
#endif
1382
0
      basename_end--;
1383
0
    }
1384
11
    if (basename_end < s) {
1385
0
      return ZSTR_EMPTY_ALLOC();
1386
0
    }
1387
1388
    /* Extract filename */
1389
11
    basename_start = basename_end;
1390
11
    basename_end++;
1391
110
    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
110
      && *(basename_start-1) != '/') {
1402
99
#endif
1403
99
      basename_start--;
1404
99
    }
1405
11
  } 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
11
  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
11
  return zend_string_init(basename_start, basename_end - basename_start, 0);
1476
11
}
1477
/* }}} */
1478
1479
/* {{{ Returns the filename component of the path */
1480
PHP_FUNCTION(basename)
1481
11
{
1482
11
  char *string, *suffix = NULL;
1483
11
  size_t   string_len, suffix_len = 0;
1484
1485
33
  ZEND_PARSE_PARAMETERS_START(1, 2)
1486
44
    Z_PARAM_STRING(string, string_len)
1487
11
    Z_PARAM_OPTIONAL
1488
22
    Z_PARAM_STRING(suffix, suffix_len)
1489
11
  ZEND_PARSE_PARAMETERS_END();
1490
1491
11
  RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1492
11
}
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
93
{
1505
93
  zend_string *ret;
1506
1507
93
  ret = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
1508
1509
93
  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
93
    ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), ZSTR_LEN(str));
1515
93
#endif
1516
93
  } 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
93
  RETURN_NEW_STR(ret);
1533
93
}
1534
1535
/* {{{ Returns the directory name component of the path */
1536
PHP_FUNCTION(dirname)
1537
93
{
1538
93
  zend_string *str;
1539
93
  zend_long levels = 1;
1540
1541
279
  ZEND_PARSE_PARAMETERS_START(1, 2)
1542
372
    Z_PARAM_STR(str)
1543
93
    Z_PARAM_OPTIONAL
1544
186
    Z_PARAM_LONG(levels)
1545
93
  ZEND_PARSE_PARAMETERS_END();
1546
1547
93
  _zend_dirname(return_value, str, levels);
1548
93
}
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
63
{
1666
63
  return (char*)php_memnistr(s, t, t_len, s + s_len);
1667
63
}
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
66
{
1723
66
  zend_string *haystack, *needle;
1724
66
  const char *found = NULL;
1725
66
  size_t  found_offset;
1726
66
  bool part = 0;
1727
1728
195
  ZEND_PARSE_PARAMETERS_START(2, 3)
1729
252
    Z_PARAM_STR(haystack)
1730
315
    Z_PARAM_STR(needle)
1731
63
    Z_PARAM_OPTIONAL
1732
126
    Z_PARAM_BOOL(part)
1733
66
  ZEND_PARSE_PARAMETERS_END();
1734
1735
63
  found = php_stristr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(haystack), ZSTR_LEN(needle));
1736
1737
63
  if (UNEXPECTED(!found)) {
1738
0
    RETURN_FALSE;
1739
0
  }
1740
63
  found_offset = found - ZSTR_VAL(haystack);
1741
63
  if (part) {
1742
0
    RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1743
0
  }
1744
63
  RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1745
63
}
1746
/* }}} */
1747
1748
static inline void _zend_strstr(zval *return_value, zend_string *haystack, zend_string *needle, bool part)
1749
169
{
1750
169
  const char *found = NULL;
1751
169
  zend_long found_offset;
1752
1753
169
  found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1754
1755
169
  if (UNEXPECTED(!found)) {
1756
16
    RETURN_FALSE;
1757
16
  }
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
169
{
1768
169
  zend_string *haystack, *needle;
1769
169
  bool part = 0;
1770
1771
507
  ZEND_PARSE_PARAMETERS_START(2, 3)
1772
676
    Z_PARAM_STR(haystack)
1773
845
    Z_PARAM_STR(needle)
1774
169
    Z_PARAM_OPTIONAL
1775
676
    Z_PARAM_BOOL(part)
1776
169
  ZEND_PARSE_PARAMETERS_END();
1777
1778
169
  _zend_strstr(return_value, haystack, needle, part);
1779
169
}
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
494
{
1894
494
  const char *found = NULL;
1895
1896
494
  if (offset < 0) {
1897
0
    offset += (zend_long)ZSTR_LEN(haystack);
1898
0
  }
1899
494
  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
494
  found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1905
494
            ZSTR_VAL(needle), ZSTR_LEN(needle),
1906
494
            ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1907
1908
494
  if (UNEXPECTED(!found)) {
1909
489
    RETURN_FALSE;
1910
489
  }
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
494
{
1917
494
  zend_string *haystack, *needle;
1918
494
  zend_long offset = 0;
1919
1920
1.48k
  ZEND_PARSE_PARAMETERS_START(2, 3)
1921
1.97k
    Z_PARAM_STR(haystack)
1922
2.47k
    Z_PARAM_STR(needle)
1923
494
    Z_PARAM_OPTIONAL
1924
988
    Z_PARAM_LONG(offset)
1925
494
  ZEND_PARSE_PARAMETERS_END();
1926
1927
494
  _zend_strpos(return_value, haystack, needle, offset);
1928
494
}
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
85
{
1966
85
  const char *found = NULL;
1967
85
  zend_string *haystack, *needle;
1968
85
  zend_long offset = 0;
1969
1970
255
  ZEND_PARSE_PARAMETERS_START(2, 3)
1971
340
    Z_PARAM_STR(haystack)
1972
425
    Z_PARAM_STR(needle)
1973
85
    Z_PARAM_OPTIONAL
1974
170
    Z_PARAM_LONG(offset)
1975
85
  ZEND_PARSE_PARAMETERS_END();
1976
1977
85
  if (offset < 0) {
1978
0
    offset += (zend_long)ZSTR_LEN(haystack);
1979
0
  }
1980
85
  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
85
  found = (char*)php_memnistr(ZSTR_VAL(haystack) + offset,
1986
85
      ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1987
1988
85
  if (UNEXPECTED(!found)) {
1989
8
    RETURN_FALSE;
1990
8
  }
1991
77
  RETURN_LONG(found - ZSTR_VAL(haystack));
1992
77
}
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
118
{
2226
118
  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
118
  } else if ((size_t)f > ZSTR_LEN(str)) {
2236
6
    RETURN_EMPTY_STRING();
2237
6
  }
2238
2239
112
  if (!len_is_null) {
2240
100
    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
21
      if (-(size_t)l > ZSTR_LEN(str) - (size_t)f) {
2245
0
        l = 0;
2246
21
      } else {
2247
21
        l = (zend_long)ZSTR_LEN(str) - f + l;
2248
21
      }
2249
79
    } else if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
2250
24
      l = (zend_long)ZSTR_LEN(str) - f;
2251
24
    }
2252
100
  } else {
2253
12
    l = (zend_long)ZSTR_LEN(str) - f;
2254
12
  }
2255
2256
112
  if (l == ZSTR_LEN(str)) {
2257
24
    RETURN_STR_COPY(str);
2258
88
  } else {
2259
88
    RETURN_STRINGL_FAST(ZSTR_VAL(str) + f, l);
2260
88
  }
2261
112
}
2262
2263
/* {{{ Returns part of a string */
2264
PHP_FUNCTION(substr)
2265
145
{
2266
145
  zend_string *str;
2267
145
  zend_long l = 0, f;
2268
145
  bool len_is_null = 1;
2269
2270
418
  ZEND_PARSE_PARAMETERS_START(2, 3)
2271
512
    Z_PARAM_STR(str)
2272
605
    Z_PARAM_LONG(f)
2273
118
    Z_PARAM_OPTIONAL
2274
446
    Z_PARAM_LONG_OR_NULL(l, len_is_null)
2275
145
  ZEND_PARSE_PARAMETERS_END();
2276
2277
118
  _zend_substr(return_value, str, f, len_is_null, l);
2278
118
}
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
684
{
2650
684
  zend_string *str;
2651
2652
2.05k
  ZEND_PARSE_PARAMETERS_START(1, 1)
2653
2.73k
    Z_PARAM_STR(str)
2654
684
  ZEND_PARSE_PARAMETERS_END();
2655
2656
679
  if (UNEXPECTED(ZSTR_LEN(str) != 1)) {
2657
140
    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
15
      php_error_docref(NULL, E_DEPRECATED,
2662
15
        "Providing a string that is not one byte long is deprecated. Use ord($str[0]) instead");
2663
15
    }
2664
140
  }
2665
679
  RETURN_LONG((unsigned char) ZSTR_VAL(str)[0]);
2666
679
}
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
102
{
2673
102
  zend_long c;
2674
2675
306
  ZEND_PARSE_PARAMETERS_START(1, 1)
2676
408
    Z_PARAM_LONG(c)
2677
102
  ZEND_PARSE_PARAMETERS_END();
2678
2679
96
  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
96
  c &= 0xff;
2686
96
  RETURN_CHAR(c);
2687
96
}
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
39
{
2694
39
  const unsigned char ch = ZSTR_VAL(str)[0];
2695
39
  unsigned char r = zend_toupper_ascii(ch);
2696
39
  if (r == ch) {
2697
0
    return zend_string_copy(str);
2698
39
  } else {
2699
39
    zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2700
39
    ZSTR_VAL(s)[0] = r;
2701
39
    return s;
2702
39
  }
2703
39
}
2704
/* }}} */
2705
2706
/* {{{ Makes a string's first character uppercase */
2707
PHP_FUNCTION(ucfirst)
2708
41
{
2709
41
  zend_string *str;
2710
2711
121
  ZEND_PARSE_PARAMETERS_START(1, 1)
2712
156
    Z_PARAM_STR(str)
2713
41
  ZEND_PARSE_PARAMETERS_END();
2714
2715
39
  if (!ZSTR_LEN(str)) {
2716
0
    RETURN_EMPTY_STRING();
2717
0
  }
2718
2719
39
  RETURN_STR(php_ucfirst(str));
2720
39
}
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
823
{
3055
823
  zend_long count = 0;
3056
823
  const char *endp;
3057
3058
823
#ifdef XSSE2
3059
823
  if (length >= sizeof(__m128i)) {
3060
477
    __m128i search = _mm_set1_epi8(ch);
3061
3062
2.52k
    do {
3063
2.52k
      __m128i src = _mm_loadu_si128((__m128i*)(p));
3064
2.52k
      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.52k
#if 1
3067
5.02k
      while (mask != 0) {
3068
2.49k
        count++;
3069
2.49k
        mask = mask & (mask - 1);
3070
2.49k
      }
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.52k
      p += sizeof(__m128i);
3081
2.52k
      length -= sizeof(__m128i);
3082
2.52k
    } while (length >= sizeof(__m128i));
3083
477
  }
3084
823
  endp = p + length;
3085
4.39k
  while (p != endp) {
3086
3.57k
    count += (*p == ch);
3087
3.57k
    p++;
3088
3.57k
  }
3089
#else
3090
  endp = p + length;
3091
  while ((p = memchr(p, ch, endp-p))) {
3092
    count++;
3093
    p++;
3094
  }
3095
#endif
3096
823
  return count;
3097
823
}
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
827
{
3103
827
  zend_string *result;
3104
827
  size_t char_count;
3105
827
  int lc_from = 0;
3106
827
  const char *source, *source_end;
3107
827
  char *target;
3108
3109
827
  if (case_sensitivity) {
3110
823
    char_count = count_chars(ZSTR_VAL(str), ZSTR_LEN(str), from);
3111
823
  } else {
3112
4
    char_count = 0;
3113
4
    lc_from = zend_tolower_ascii(from);
3114
4
    source_end = ZSTR_VAL(str) + ZSTR_LEN(str);
3115
17.9k
    for (source = ZSTR_VAL(str); source < source_end; source++) {
3116
17.9k
      if (zend_tolower_ascii(*source) == lc_from) {
3117
0
        char_count++;
3118
0
      }
3119
17.9k
    }
3120
4
  }
3121
3122
827
  if (char_count == 0) {
3123
55
    return zend_string_copy(str);
3124
55
  }
3125
3126
772
  if (replace_count) {
3127
772
    *replace_count += char_count;
3128
772
  }
3129
3130
772
  if (to_len > 0) {
3131
82
    result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3132
690
  } else {
3133
690
    result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3134
690
  }
3135
772
  target = ZSTR_VAL(result);
3136
3137
772
  if (case_sensitivity) {
3138
772
    char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3139
3140
3.00k
    while ((p = memchr(p, from, (e - p)))) {
3141
3.00k
      target = zend_mempcpy(target, s, (p - s));
3142
3.00k
      target = zend_mempcpy(target, to, to_len);
3143
3.00k
      p++;
3144
3.00k
      s = p;
3145
3.00k
      if (--char_count == 0) break;
3146
3.00k
    }
3147
772
    if (s < e) {
3148
25
      target = zend_mempcpy(target, s, e - s);
3149
25
    }
3150
772
  } 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
772
  *target = 0;
3162
772
  return result;
3163
827
}
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
957
{
3170
3171
957
  if (needle_len < ZSTR_LEN(haystack)) {
3172
882
    zend_string *new_str;
3173
882
    const char *end;
3174
882
    const char *p, *r;
3175
882
    char *e;
3176
3177
882
    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
862
    } else {
3192
862
      size_t count = 0;
3193
862
      const char *o = ZSTR_VAL(haystack);
3194
862
      const char *n = needle;
3195
862
      const char *endp = o + ZSTR_LEN(haystack);
3196
3197
1.07k
      while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3198
212
        o += needle_len;
3199
212
        count++;
3200
212
      }
3201
862
      if (count == 0) {
3202
        /* Needle doesn't occur, shortcircuit the actual replacement. */
3203
821
        goto nothing_todo;
3204
821
      }
3205
41
      if (str_len > needle_len) {
3206
1
        new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3207
40
      } else {
3208
40
        new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3209
40
      }
3210
3211
41
      e = ZSTR_VAL(new_str);
3212
41
      end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3213
253
      for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3214
212
        e = zend_mempcpy(e, p, r - p);
3215
212
        e = zend_mempcpy(e, str, str_len);
3216
212
        (*replace_count)++;
3217
212
      }
3218
3219
41
      if (p < end) {
3220
41
        e = zend_mempcpy(e, p, end - p);
3221
41
      }
3222
3223
41
      *e = '\0';
3224
41
      return new_str;
3225
862
    }
3226
882
  } else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3227
896
nothing_todo:
3228
896
    return zend_string_copy(haystack);
3229
75
  } else {
3230
0
    (*replace_count)++;
3231
0
    return zend_string_init_fast(str, str_len);
3232
0
  }
3233
957
}
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
167
{
3240
167
  zend_string *new_str = NULL;
3241
167
  zend_string *lc_needle;
3242
3243
167
  if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3244
163
    const char *end;
3245
163
    const char *p, *r;
3246
163
    char *e;
3247
3248
163
    if (ZSTR_LEN(needle) == str_len) {
3249
135
      lc_needle = zend_string_tolower(needle);
3250
135
      end = lc_haystack + ZSTR_LEN(haystack);
3251
580
      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
445
        if (!new_str) {
3253
37
          new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3254
37
        }
3255
445
        memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3256
445
        (*replace_count)++;
3257
445
      }
3258
135
      zend_string_release_ex(lc_needle, 0);
3259
3260
135
      if (!new_str) {
3261
98
        goto nothing_todo;
3262
98
      }
3263
37
      return new_str;
3264
135
    } else {
3265
28
      size_t count = 0;
3266
28
      const char *o = lc_haystack;
3267
28
      const char *n;
3268
28
      const char *endp = o + ZSTR_LEN(haystack);
3269
3270
28
      lc_needle = zend_string_tolower(needle);
3271
28
      n = ZSTR_VAL(lc_needle);
3272
3273
28
      while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3274
0
        o += ZSTR_LEN(lc_needle);
3275
0
        count++;
3276
0
      }
3277
28
      if (count == 0) {
3278
        /* Needle doesn't occur, shortcircuit the actual replacement. */
3279
28
        zend_string_release_ex(lc_needle, 0);
3280
28
        goto nothing_todo;
3281
28
      }
3282
3283
0
      if (str_len > ZSTR_LEN(lc_needle)) {
3284
0
        new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3285
0
      } else {
3286
0
        new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3287
0
      }
3288
3289
0
      e = ZSTR_VAL(new_str);
3290
0
      end = lc_haystack + ZSTR_LEN(haystack);
3291
3292
0
      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
0
        e = zend_mempcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3294
0
        e = zend_mempcpy(e, str, str_len);
3295
0
        (*replace_count)++;
3296
0
      }
3297
3298
0
      if (p < end) {
3299
0
        e = zend_mempcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3300
0
      }
3301
0
      *e = '\0';
3302
3303
0
      zend_string_release_ex(lc_needle, 0);
3304
3305
0
      return new_str;
3306
28
    }
3307
163
  } else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3308
130
nothing_todo:
3309
130
    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
167
}
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
515
{
3330
515
  zend_string *new_str;
3331
3332
515
  if (needle_len < length) {
3333
463
    const char *end;
3334
463
    const char *s, *p;
3335
463
    char *e, *r;
3336
3337
463
    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
463
    } else {
3345
463
      if (str_len < needle_len) {
3346
0
        new_str = zend_string_alloc(length, 0);
3347
463
      } else {
3348
463
        size_t count = 0;
3349
463
        const char *o = haystack;
3350
463
        const char *n = needle;
3351
463
        const char *endp = o + length;
3352
3353
656
        while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3354
193
          o += needle_len;
3355
193
          count++;
3356
193
        }
3357
463
        if (count == 0) {
3358
          /* Needle doesn't occur, shortcircuit the actual replacement. */
3359
419
          new_str = zend_string_init(haystack, length, 0);
3360
419
          return new_str;
3361
419
        } 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
463
      }
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
463
    }
3385
463
  } else if (needle_len > length || memcmp(haystack, needle, length)) {
3386
52
    new_str = zend_string_init(haystack, length, 0);
3387
52
    return new_str;
3388
52
  } else {
3389
0
    new_str = zend_string_init(str, str_len, 0);
3390
3391
0
    return new_str;
3392
0
  }
3393
515
}
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
127
{
3523
127
  zend_string *str;
3524
127
  const char *s, *e;
3525
127
  char *p;
3526
127
  zend_string *n;
3527
3528
381
  ZEND_PARSE_PARAMETERS_START(1, 1)
3529
508
    Z_PARAM_STR(str)
3530
127
  ZEND_PARSE_PARAMETERS_END();
3531
3532
127
  n = zend_string_alloc(ZSTR_LEN(str), 0);
3533
127
  p = ZSTR_VAL(n);
3534
3535
127
  s = ZSTR_VAL(str);
3536
127
  e = s + ZSTR_LEN(str);
3537
127
  --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
1.03k
  while (e >= s) {
3580
903
    *p++ = *e--;
3581
903
  }
3582
3583
127
  *p = '\0';
3584
3585
127
  RETVAL_NEW_STR(n);
3586
127
}
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
81
{
3671
81
  zend_string *str, *what;
3672
3673
243
  ZEND_PARSE_PARAMETERS_START(2, 2)
3674
324
    Z_PARAM_STR(str)
3675
405
    Z_PARAM_STR(what)
3676
81
  ZEND_PARSE_PARAMETERS_END();
3677
3678
81
  if (ZSTR_LEN(str) == 0) {
3679
11
    RETURN_EMPTY_STRING();
3680
11
  }
3681
3682
70
  if (ZSTR_LEN(what) == 0) {
3683
0
    RETURN_STR_COPY(str);
3684
0
  }
3685
3686
70
  RETURN_STR(php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), ZSTR_VAL(what), ZSTR_LEN(what)));
3687
70
}
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.02k
{
3801
1.02k
  char flags[256];
3802
1.02k
  char *target;
3803
1.02k
  const char *source, *end;
3804
1.02k
  char c;
3805
1.02k
  size_t  newlen;
3806
1.02k
  zend_string *new_str = zend_string_safe_alloc(4, len, 0, 0);
3807
3808
1.02k
  php_charmask((const unsigned char *) what, wlength, flags);
3809
3810
8.66k
  for (source = str, end = source + len, target = ZSTR_VAL(new_str); source < end; source++) {
3811
7.63k
    c = *source;
3812
7.63k
    if (flags[(unsigned char)c]) {
3813
175
      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
12
      *target++ = '\\';
3828
12
    }
3829
7.47k
    *target++ = c;
3830
7.47k
  }
3831
1.02k
  *target = 0;
3832
1.02k
  newlen = target - ZSTR_VAL(new_str);
3833
1.02k
  if (newlen < len * 4) {
3834
992
    new_str = zend_string_truncate(new_str, newlen, 0);
3835
992
  }
3836
1.02k
  return new_str;
3837
1.02k
}
3838
/* }}} */
3839
3840
/* {{{ php_addcslashes */
3841
PHPAPI zend_string *php_addcslashes(zend_string *str, const char *what, size_t wlength)
3842
515
{
3843
515
  return php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), what, wlength);
3844
515
}
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
2.08k
) {
4315
2.08k
  zval    *search_entry;
4316
2.08k
  zend_string *tmp_result;
4317
2.08k
  char    *replace_value = NULL;
4318
2.08k
  size_t     replace_len = 0;
4319
2.08k
  zend_long  replace_count = 0;
4320
2.08k
  zend_string *lc_subject_str = NULL;
4321
2.08k
  uint32_t     replace_idx;
4322
4323
2.08k
  if (ZSTR_LEN(subject_str) == 0) {
4324
133
    ZVAL_EMPTY_STRING(result);
4325
133
    return 0;
4326
133
  }
4327
4328
  /* If search is an array */
4329
1.95k
  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.95k
  } else {
4445
1.95k
    ZEND_ASSERT(search_str);
4446
1.95k
    if (ZSTR_LEN(search_str) == 1) {
4447
827
      ZVAL_STR(result,
4448
827
        php_char_to_str_ex(subject_str,
4449
827
              ZSTR_VAL(search_str)[0],
4450
827
              ZSTR_VAL(replace_str),
4451
827
              ZSTR_LEN(replace_str),
4452
827
              case_sensitivity,
4453
827
              &replace_count));
4454
1.12k
    } else if (ZSTR_LEN(search_str) > 1) {
4455
1.12k
      if (case_sensitivity) {
4456
957
        ZVAL_STR(result, php_str_to_str_ex(subject_str,
4457
957
            ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4458
957
            ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4459
957
      } else {
4460
167
        lc_subject_str = zend_string_tolower(subject_str);
4461
167
        ZVAL_STR(result, php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str),
4462
167
            search_str, ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4463
167
        zend_string_release_ex(lc_subject_str, 0);
4464
167
      }
4465
1.12k
    } else {
4466
0
      ZVAL_STR_COPY(result, subject_str);
4467
0
    }
4468
1.95k
  }
4469
1.95k
  return replace_count;
4470
1.95k
}
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
2.08k
) {
4481
2.08k
  zval *subject_entry;
4482
2.08k
  zval result;
4483
2.08k
  zend_string *string_key;
4484
2.08k
  zend_ulong num_key;
4485
2.08k
  zend_long count = 0;
4486
4487
  /* Make sure we're dealing with strings and do the replacement. */
4488
2.08k
  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
2.08k
  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
2.06k
  } else { /* if subject is not an array */
4514
2.06k
    count = php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, return_value, case_sensitivity);
4515
2.06k
  }
4516
2.08k
  if (zcount) {
4517
44
    ZEND_TRY_ASSIGN_REF_LONG(zcount, count);
4518
44
  }
4519
2.08k
}
4520
4521
/* {{{ php_str_replace_common */
4522
static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool case_sensitivity)
4523
2.09k
{
4524
2.09k
  zend_string *search_str;
4525
2.09k
  HashTable *search_ht;
4526
2.09k
  zend_string *replace_str;
4527
2.09k
  HashTable *replace_ht;
4528
2.09k
  zend_string *subject_str;
4529
2.09k
  HashTable *subject_ht;
4530
2.09k
  zval *zcount = NULL;
4531
4532
6.27k
  ZEND_PARSE_PARAMETERS_START(3, 4)
4533
10.4k
    Z_PARAM_ARRAY_HT_OR_STR(search_ht, search_str)
4534
10.4k
    Z_PARAM_ARRAY_HT_OR_STR(replace_ht, replace_str)
4535
10.4k
    Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
4536
10.4k
    Z_PARAM_OPTIONAL
4537
10.4k
    Z_PARAM_ZVAL(zcount)
4538
4.25k
  ZEND_PARSE_PARAMETERS_END();
4539
4540
2.08k
  _php_str_replace_common(return_value, search_ht, search_str, replace_ht, replace_str, subject_ht, subject_str, zcount, case_sensitivity);
4541
2.08k
}
4542
/* }}} */
4543
4544
/* {{{ Replaces all occurrences of search in haystack with replace */
4545
PHP_FUNCTION(str_replace)
4546
1.92k
{
4547
1.92k
  php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4548
1.92k
}
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
173
{
4572
173
  php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4573
173
}
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.68k
{
4822
1.68k
  zend_string *buf;
4823
1.68k
  zend_string *str;
4824
1.68k
  zend_string *allow_str = NULL;
4825
1.68k
  HashTable *allow_ht = NULL;
4826
1.68k
  const char *allowed_tags=NULL;
4827
1.68k
  size_t allowed_tags_len=0;
4828
1.68k
  smart_str tags_ss = {0};
4829
4830
5.04k
  ZEND_PARSE_PARAMETERS_START(1, 2)
4831
6.72k
    Z_PARAM_STR(str)
4832
1.68k
    Z_PARAM_OPTIONAL
4833
3.41k
    Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(allow_ht, allow_str)
4834
3.41k
  ZEND_PARSE_PARAMETERS_END();
4835
4836
1.67k
  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.67k
  } else if (allow_str) {
4853
22
    allowed_tags = ZSTR_VAL(allow_str);
4854
22
    allowed_tags_len = ZSTR_LEN(allow_str);
4855
22
  }
4856
4857
1.67k
  buf = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
4858
1.67k
  ZSTR_LEN(buf) = php_strip_tags_ex(ZSTR_VAL(buf), ZSTR_LEN(str), allowed_tags, allowed_tags_len, 0);
4859
1.67k
  smart_str_free(&tags_ss);
4860
1.67k
  RETURN_NEW_STR(buf);
4861
1.67k
}
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_OR_NULL, &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
304
#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.67k
{
5118
1.67k
  char *tbuf, *tp, *rp, c, lc;
5119
1.67k
  const char *buf, *p, *end;
5120
1.67k
  int br, depth=0, in_q = 0;
5121
1.67k
  uint8_t state = 0;
5122
1.67k
  size_t pos;
5123
1.67k
  char *allow_free = NULL;
5124
1.67k
  char is_xml = 0;
5125
5126
1.67k
  buf = estrndup(rbuf, len);
5127
1.67k
  end = buf + len;
5128
1.67k
  lc = '\0';
5129
1.67k
  p = buf;
5130
1.67k
  rp = rbuf;
5131
1.67k
  br = 0;
5132
1.67k
  if (allow) {
5133
22
    allow_free = zend_str_tolower_dup_ex(allow, allow_len);
5134
22
    allow = allow_free ? allow_free : allow;
5135
22
    tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
5136
22
    tp = tbuf;
5137
1.65k
  } else {
5138
1.65k
    tbuf = tp = NULL;
5139
1.65k
  }
5140
5141
1.30M
state_0:
5142
1.30M
  if (p >= end) {
5143
922
    goto finish;
5144
922
  }
5145
1.30M
  c = *p;
5146
1.30M
  switch (c) {
5147
54.0k
    case '\0':
5148
54.0k
      break;
5149
53.2k
    case '<':
5150
53.2k
      if (in_q) {
5151
0
        break;
5152
0
      }
5153
53.2k
      if (isspace(*(p + 1)) && !allow_tag_spaces) {
5154
15
        *(rp++) = c;
5155
15
        break;
5156
15
      }
5157
53.2k
      lc = '<';
5158
53.2k
      state = 1;
5159
53.2k
      if (allow) {
5160
34
        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
34
        *(tp++) = '<';
5166
34
      }
5167
53.2k
      p++;
5168
53.2k
      goto state_1;
5169
837
    case '>':
5170
837
      if (depth) {
5171
0
        depth--;
5172
0
        break;
5173
0
      }
5174
5175
837
      if (in_q) {
5176
0
        break;
5177
0
      }
5178
5179
837
      *(rp++) = c;
5180
837
      break;
5181
1.19M
    default:
5182
1.19M
      *(rp++) = c;
5183
1.19M
      break;
5184
1.30M
  }
5185
1.24M
  p++;
5186
1.24M
  goto state_0;
5187
5188
973k
state_1:
5189
973k
  if (p >= end) {
5190
183
    goto finish;
5191
183
  }
5192
972k
  c = *p;
5193
972k
  switch (c) {
5194
3.87k
    case '\0':
5195
3.87k
      break;
5196
1.63k
    case '<':
5197
1.63k
      if (in_q) {
5198
647
        break;
5199
647
      }
5200
983
      if (isspace(*(p + 1)) && !allow_tag_spaces) {
5201
0
        goto reg_char_1;
5202
0
      }
5203
983
      depth++;
5204
983
      break;
5205
53.1k
    case '>':
5206
53.1k
      if (depth) {
5207
468
        depth--;
5208
468
        break;
5209
468
      }
5210
52.7k
      if (in_q) {
5211
400
        break;
5212
400
      }
5213
5214
52.3k
      lc = '>';
5215
52.3k
      if (is_xml && p >= buf + 1 && *(p -1) == '-') {
5216
0
        break;
5217
0
      }
5218
52.3k
      in_q = state = is_xml = 0;
5219
52.3k
      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
52.3k
      p++;
5233
52.3k
      goto state_0;
5234
51.3k
    case '"':
5235
51.8k
    case '\'':
5236
51.8k
      if (p != buf && (!in_q || *p == in_q)) {
5237
51.8k
        if (in_q) {
5238
25.8k
          in_q = 0;
5239
25.9k
        } else {
5240
25.9k
          in_q = *p;
5241
25.9k
        }
5242
51.8k
      }
5243
51.8k
      goto reg_char_1;
5244
509
    case '!':
5245
      /* JavaScript & Other HTML scripting languages */
5246
509
      if (p >= buf + 1 && *(p-1) == '<') {
5247
183
        state = 3;
5248
183
        lc = c;
5249
183
        p++;
5250
183
        goto state_3;
5251
326
      } else {
5252
326
        goto reg_char_1;
5253
326
      }
5254
0
      break;
5255
1.10k
    case '?':
5256
1.10k
      if (p >= buf + 1 && *(p-1) == '<') {
5257
512
        br=0;
5258
512
        state = 2;
5259
512
        p++;
5260
512
        goto state_2;
5261
591
      } else {
5262
591
        goto reg_char_1;
5263
591
      }
5264
0
      break;
5265
860k
    default:
5266
913k
reg_char_1:
5267
913k
      if (allow) {
5268
264
        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
264
        *(tp++) = c;
5274
264
      }
5275
913k
      break;
5276
972k
  }
5277
919k
  p++;
5278
919k
  goto state_1;
5279
5280
330k
state_2:
5281
330k
  if (p >= end) {
5282
487
    goto finish;
5283
487
  }
5284
330k
  c = *p;
5285
330k
  switch (c) {
5286
2.67k
    case '(':
5287
2.67k
      if (lc != '"' && lc != '\'') {
5288
1.46k
        lc = '(';
5289
1.46k
        br++;
5290
1.46k
      }
5291
2.67k
      break;
5292
1.56k
    case ')':
5293
1.56k
      if (lc != '"' && lc != '\'') {
5294
803
        lc = ')';
5295
803
        br--;
5296
803
      }
5297
1.56k
      break;
5298
2.88k
    case '>':
5299
2.88k
      if (depth) {
5300
136
        depth--;
5301
136
        break;
5302
136
      }
5303
2.75k
      if (in_q) {
5304
1.42k
        break;
5305
1.42k
      }
5306
5307
1.32k
      if (!br && p >= buf + 1 && lc != '\"' && *(p-1) == '?') {
5308
25
        in_q = state = 0;
5309
25
        tp = tbuf;
5310
25
        p++;
5311
25
        goto state_0;
5312
25
      }
5313
1.29k
      break;
5314
1.88k
    case '"':
5315
2.01k
    case '\'':
5316
2.01k
      if (p >= buf + 1 && *(p-1) != '\\') {
5317
2.01k
        if (lc == c) {
5318
828
          lc = '\0';
5319
1.18k
        } else if (lc != '\\') {
5320
1.18k
          lc = c;
5321
1.18k
        }
5322
2.01k
        if (p != buf && (!in_q || *p == in_q)) {
5323
1.90k
          if (in_q) {
5324
789
            in_q = 0;
5325
1.11k
          } else {
5326
1.11k
            in_q = *p;
5327
1.11k
          }
5328
1.90k
        }
5329
2.01k
      }
5330
2.01k
      break;
5331
2.91k
    case 'l':
5332
3.32k
    case 'L':
5333
      /* swm: If we encounter '<?xml' then we shouldn't be in
5334
       * state == 2 (PHP). Switch back to HTML.
5335
       */
5336
3.32k
      if (state == 2 && p > buf+4
5337
3.32k
             && (*(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
3.32k
      break;
5346
318k
    default:
5347
318k
      break;
5348
330k
  }
5349
330k
  p++;
5350
330k
  goto state_2;
5351
5352
57.3k
state_3:
5353
57.3k
  if (p >= end) {
5354
84
    goto finish;
5355
84
  }
5356
57.2k
  c = *p;
5357
57.2k
  switch (c) {
5358
735
    case '>':
5359
735
      if (depth) {
5360
222
        depth--;
5361
222
        break;
5362
222
      }
5363
513
      if (in_q) {
5364
414
        break;
5365
414
      }
5366
99
      in_q = state = 0;
5367
99
      tp = tbuf;
5368
99
      p++;
5369
99
      goto state_0;
5370
147
    case '"':
5371
576
    case '\'':
5372
576
      if (p != buf && *(p-1) != '\\' && (!in_q || *p == in_q)) {
5373
494
        if (in_q) {
5374
249
          in_q = 0;
5375
249
        } else {
5376
245
          in_q = *p;
5377
245
        }
5378
494
      }
5379
576
      break;
5380
863
    case '-':
5381
863
      if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
5382
0
        state = 4;
5383
0
        p++;
5384
0
        goto state_4;
5385
0
      }
5386
863
      break;
5387
863
    case 'E':
5388
695
    case 'e':
5389
      /* !DOCTYPE exception */
5390
695
      if (p > buf+6
5391
695
           && (*(p-1) == 'p' || *(p-1) == 'P')
5392
49
           && (*(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
695
      break;
5402
54.3k
    default:
5403
54.3k
      break;
5404
57.2k
  }
5405
57.1k
  p++;
5406
57.1k
  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.67k
finish:
5423
1.67k
  if (rp < rbuf + len) {
5424
1.48k
    *rp = '\0';
5425
1.48k
  }
5426
1.67k
  efree((void *)buf);
5427
1.67k
  if (tbuf) {
5428
22
    efree(tbuf);
5429
22
  }
5430
1.67k
  if (allow_free) {
5431
21
    efree(allow_free);
5432
21
  }
5433
5434
1.67k
  return (size_t)(rp - rbuf);
5435
0
}
5436
/* }}} */
5437
5438
/* {{{ Parse a CSV string into an array */
5439
PHP_FUNCTION(str_getcsv)
5440
135
{
5441
135
  zend_string *str;
5442
135
  char delimiter = ',', enclosure = '"';
5443
135
  char *delimiter_str = NULL, *enclosure_str = NULL;
5444
135
  size_t delimiter_str_len = 0, enclosure_str_len = 0;
5445
135
  zend_string *escape_str = NULL;
5446
5447
402
  ZEND_PARSE_PARAMETERS_START(1, 4)
5448
528
    Z_PARAM_STR(str)
5449
132
    Z_PARAM_OPTIONAL
5450
468
    Z_PARAM_STRING(delimiter_str, delimiter_str_len)
5451
438
    Z_PARAM_STRING(enclosure_str, enclosure_str_len)
5452
210
    Z_PARAM_STR(escape_str)
5453
135
  ZEND_PARSE_PARAMETERS_END();
5454
5455
132
  if (delimiter_str != NULL) {
5456
    /* Make sure that there is at least one character in string */
5457
102
    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
93
    delimiter = delimiter_str[0];
5463
93
  }
5464
123
  if (enclosure_str != NULL) {
5465
57
    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
57
    enclosure = enclosure_str[0];
5471
57
  }
5472
5473
123
  int escape_char = php_csv_handle_escape_argument(escape_str, 4);
5474
123
  if (escape_char == PHP_CSV_ESCAPE_ERROR) {
5475
0
    RETURN_THROWS();
5476
0
  }
5477
5478
123
  HashTable *values = php_fgetcsv(NULL, delimiter, enclosure, escape_char, ZSTR_LEN(str), ZSTR_VAL(str));
5479
123
  if (values == NULL) {
5480
3
    values = php_bc_fgetcsv_empty_line();
5481
3
  }
5482
123
  RETURN_ARR(values);
5483
123
}
5484
/* }}} */
5485
5486
/* {{{ Returns the input string repeat mult times */
5487
PHP_FUNCTION(str_repeat)
5488
653
{
5489
653
  zend_string   *input_str;   /* Input string */
5490
653
  zend_long     mult;     /* Multiplier */
5491
653
  zend_string *result;    /* Resulting string */
5492
653
  size_t    result_len;   /* Length of the resulting string */
5493
5494
1.95k
  ZEND_PARSE_PARAMETERS_START(2, 2)
5495
2.60k
    Z_PARAM_STR(input_str)
5496
3.25k
    Z_PARAM_LONG(mult)
5497
653
  ZEND_PARSE_PARAMETERS_END();
5498
5499
651
  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
645
  if (ZSTR_LEN(input_str) == 0 || mult == 0)
5507
25
    RETURN_EMPTY_STRING();
5508
5509
  /* Initialize the result string */
5510
620
  result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
5511
620
  result_len = ZSTR_LEN(input_str) * mult;
5512
620
  ZSTR_COPY_CONCAT_PROPERTIES(result, input_str);
5513
5514
  /* Heavy optimization for situations where input string is 1 byte long */
5515
620
  if (ZSTR_LEN(input_str) == 1) {
5516
539
    memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
5517
539
  } else {
5518
81
    const char *s, *ee;
5519
81
    char *e;
5520
81
    ptrdiff_t l=0;
5521
81
    memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
5522
81
    s = ZSTR_VAL(result);
5523
81
    e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
5524
81
    ee = ZSTR_VAL(result) + result_len;
5525
5526
475
    while (e<ee) {
5527
394
      l = (e-s) < (ee-e) ? (e-s) : (ee-e);
5528
394
      memmove(e, s, l);
5529
394
      e += l;
5530
394
    }
5531
81
  }
5532
5533
620
  ZSTR_VAL(result)[result_len] = '\0';
5534
5535
620
  RETURN_NEW_STR(result);
5536
620
}
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
955
      temp = str[n_left];
6014
955
      str[n_left] = str[rnd_idx];
6015
955
      str[rnd_idx] = temp;
6016
955
    }
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
/* }}} */