Coverage Report

Created: 2026-06-13 07:01

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