Coverage Report

Created: 2026-06-02 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/string.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
12
   |          Stig Sæther Bakken <ssb@php.net>                          |
13
   |          Zeev Suraski <zeev@php.net>                                 |
14
   +----------------------------------------------------------------------+
15
 */
16
17
#include <stdio.h>
18
#include "php.h"
19
#include "php_string.h"
20
#include "php_variables.h"
21
#include <locale.h>
22
#ifdef HAVE_LANGINFO_H
23
# include <langinfo.h>
24
#endif
25
26
#ifdef HAVE_LIBINTL
27
# include <libintl.h> /* For LC_MESSAGES */
28
#endif
29
30
#include "scanf.h"
31
#include "zend_API.h"
32
#include "zend_execute.h"
33
#include "basic_functions.h"
34
#include "zend_smart_str.h"
35
#include <Zend/zend_exceptions.h>
36
#ifdef ZTS
37
#include "TSRM.h"
38
#endif
39
40
/* For str_getcsv() support */
41
#include "ext/standard/file.h"
42
/* For php_next_utf8_char() */
43
#include "ext/standard/html.h"
44
#include "ext/random/php_random.h"
45
46
#ifdef __SSE2__
47
#include "Zend/zend_bitset.h"
48
#endif
49
50
#include "zend_simd.h"
51
52
/* this is read-only, so it's ok */
53
ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
54
55
/* localeconv mutex */
56
#ifdef ZTS
57
static MUTEX_T locale_mutex = NULL;
58
#endif
59
60
/* {{{ php_bin2hex */
61
static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
62
0
{
63
0
  zend_string *result;
64
0
  size_t i, j;
65
66
0
  result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
67
68
0
  for (i = j = 0; i < oldlen; i++) {
69
0
    ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
70
0
    ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
71
0
  }
72
0
  ZSTR_VAL(result)[j] = '\0';
73
74
0
  return result;
75
0
}
76
/* }}} */
77
78
/* {{{ php_hex2bin */
79
static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
80
0
{
81
0
  size_t target_length = oldlen >> 1;
82
0
  zend_string *str = zend_string_alloc(target_length, 0);
83
0
  unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
84
0
  size_t i, j;
85
86
0
  for (i = j = 0; i < target_length; i++) {
87
0
    unsigned char c = old[j++];
88
0
    unsigned char l = c & ~0x20;
89
0
    int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
90
0
    unsigned char d;
91
92
    /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
93
0
    if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
94
0
      d = (l - 0x10 - 0x27 * is_letter) << 4;
95
0
    } else {
96
0
      zend_string_efree(str);
97
0
      return NULL;
98
0
    }
99
0
    c = old[j++];
100
0
    l = c & ~0x20;
101
0
    is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
102
0
    if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
103
0
      d |= l - 0x10 - 0x27 * is_letter;
104
0
    } else {
105
0
      zend_string_efree(str);
106
0
      return NULL;
107
0
    }
108
0
    ret[i] = d;
109
0
  }
110
0
  ret[i] = '\0';
111
112
0
  return str;
113
0
}
114
/* }}} */
115
116
/* {{{ localeconv_r
117
 * glibc's localeconv is not reentrant, so lets make it so ... sorta */
118
PHPAPI struct lconv *localeconv_r(struct lconv *out)
119
0
{
120
121
#ifdef ZTS
122
  tsrm_mutex_lock( locale_mutex );
123
#endif
124
125
  /* localeconv doesn't return an error condition */
126
0
  *out = *localeconv();
127
128
#ifdef ZTS
129
  tsrm_mutex_unlock( locale_mutex );
130
#endif
131
132
0
  return out;
133
0
}
134
/* }}} */
135
136
#ifdef ZTS
137
/* {{{ PHP_MINIT_FUNCTION */
138
PHP_MINIT_FUNCTION(localeconv)
139
{
140
  locale_mutex = tsrm_mutex_alloc();
141
  return SUCCESS;
142
}
143
/* }}} */
144
145
/* {{{ PHP_MSHUTDOWN_FUNCTION */
146
PHP_MSHUTDOWN_FUNCTION(localeconv)
147
{
148
  tsrm_mutex_free( locale_mutex );
149
  locale_mutex = NULL;
150
  return SUCCESS;
151
}
152
/* }}} */
153
#endif
154
155
/* {{{ Converts the binary representation of data to hex */
156
PHP_FUNCTION(bin2hex)
157
0
{
158
0
  zend_string *result;
159
0
  zend_string *data;
160
161
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
162
0
    Z_PARAM_STR(data)
163
0
  ZEND_PARSE_PARAMETERS_END();
164
165
0
  result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
166
167
0
  RETURN_STR(result);
168
0
}
169
/* }}} */
170
171
/* {{{ Converts the hex representation of data to binary */
172
PHP_FUNCTION(hex2bin)
173
0
{
174
0
  zend_string *result, *data;
175
176
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
177
0
    Z_PARAM_STR(data)
178
0
  ZEND_PARSE_PARAMETERS_END();
179
180
0
  if (ZSTR_LEN(data) % 2 != 0) {
181
0
    php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
182
0
    RETURN_FALSE;
183
0
  }
184
185
0
  result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
186
187
0
  if (!result) {
188
0
    php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
189
0
    RETURN_FALSE;
190
0
  }
191
192
0
  RETVAL_STR(result);
193
0
}
194
/* }}} */
195
196
static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, bool is_strspn) /* {{{ */
197
0
{
198
0
  zend_string *s11, *s22;
199
0
  zend_long start = 0, len = 0;
200
0
  bool len_is_null = 1;
201
202
0
  ZEND_PARSE_PARAMETERS_START(2, 4)
203
0
    Z_PARAM_STR(s11)
204
0
    Z_PARAM_STR(s22)
205
0
    Z_PARAM_OPTIONAL
206
0
    Z_PARAM_LONG(start)
207
0
    Z_PARAM_LONG_OR_NULL(len, len_is_null)
208
0
  ZEND_PARSE_PARAMETERS_END();
209
210
0
  size_t remain_len = ZSTR_LEN(s11);
211
0
  if (start < 0) {
212
0
    start += remain_len;
213
0
    if (start < 0) {
214
0
      start = 0;
215
0
    }
216
0
  } else if ((size_t) start > remain_len) {
217
0
    start = remain_len;
218
0
  }
219
220
0
  remain_len -= start;
221
0
  if (!len_is_null) {
222
0
    if (len < 0) {
223
0
      len += remain_len;
224
0
      if (len < 0) {
225
0
        len = 0;
226
0
      }
227
0
    } else if ((size_t) len > remain_len) {
228
0
      len = remain_len;
229
0
    }
230
0
  } else {
231
0
    len = remain_len;
232
0
  }
233
234
0
  if (len == 0) {
235
0
    RETURN_LONG(0);
236
0
  }
237
238
0
  if (is_strspn) {
239
0
    RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
240
0
            ZSTR_VAL(s22) /*str2_start*/,
241
0
            ZSTR_VAL(s11) + start + len /*str1_end*/,
242
0
            ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
243
0
  } else {
244
0
    RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
245
0
            ZSTR_VAL(s22) /*str2_start*/,
246
0
            ZSTR_VAL(s11) + start + len /*str1_end*/,
247
0
            ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
248
0
  }
249
0
}
250
/* }}} */
251
252
/* {{{ Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars) */
253
PHP_FUNCTION(strspn)
254
0
{
255
0
  php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* is_strspn */ true);
256
0
}
257
/* }}} */
258
259
/* {{{ Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars) */
260
PHP_FUNCTION(strcspn)
261
0
{
262
0
  php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* is_strspn */ false);
263
0
}
264
/* }}} */
265
266
#ifdef HAVE_NL_LANGINFO
267
/* {{{ Query language and locale information */
268
PHP_FUNCTION(nl_langinfo)
269
0
{
270
0
  zend_long item;
271
0
  char *value;
272
273
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
274
0
    Z_PARAM_LONG(item)
275
0
  ZEND_PARSE_PARAMETERS_END();
276
277
0
  switch(item) { /* {{{ */
278
0
#ifdef ABDAY_1
279
0
    case ABDAY_1:
280
0
    case ABDAY_2:
281
0
    case ABDAY_3:
282
0
    case ABDAY_4:
283
0
    case ABDAY_5:
284
0
    case ABDAY_6:
285
0
    case ABDAY_7:
286
0
#endif
287
0
#ifdef DAY_1
288
0
    case DAY_1:
289
0
    case DAY_2:
290
0
    case DAY_3:
291
0
    case DAY_4:
292
0
    case DAY_5:
293
0
    case DAY_6:
294
0
    case DAY_7:
295
0
#endif
296
0
#ifdef ABMON_1
297
0
    case ABMON_1:
298
0
    case ABMON_2:
299
0
    case ABMON_3:
300
0
    case ABMON_4:
301
0
    case ABMON_5:
302
0
    case ABMON_6:
303
0
    case ABMON_7:
304
0
    case ABMON_8:
305
0
    case ABMON_9:
306
0
    case ABMON_10:
307
0
    case ABMON_11:
308
0
    case ABMON_12:
309
0
#endif
310
0
#ifdef MON_1
311
0
    case MON_1:
312
0
    case MON_2:
313
0
    case MON_3:
314
0
    case MON_4:
315
0
    case MON_5:
316
0
    case MON_6:
317
0
    case MON_7:
318
0
    case MON_8:
319
0
    case MON_9:
320
0
    case MON_10:
321
0
    case MON_11:
322
0
    case MON_12:
323
0
#endif
324
0
#ifdef AM_STR
325
0
    case AM_STR:
326
0
#endif
327
0
#ifdef PM_STR
328
0
    case PM_STR:
329
0
#endif
330
0
#ifdef D_T_FMT
331
0
    case D_T_FMT:
332
0
#endif
333
0
#ifdef D_FMT
334
0
    case D_FMT:
335
0
#endif
336
0
#ifdef T_FMT
337
0
    case T_FMT:
338
0
#endif
339
0
#ifdef T_FMT_AMPM
340
0
    case T_FMT_AMPM:
341
0
#endif
342
0
#ifdef ERA
343
0
    case ERA:
344
0
#endif
345
0
#ifdef ERA_YEAR
346
0
    case ERA_YEAR:
347
0
#endif
348
0
#ifdef ERA_D_T_FMT
349
0
    case ERA_D_T_FMT:
350
0
#endif
351
0
#ifdef ERA_D_FMT
352
0
    case ERA_D_FMT:
353
0
#endif
354
0
#ifdef ERA_T_FMT
355
0
    case ERA_T_FMT:
356
0
#endif
357
0
#ifdef ALT_DIGITS
358
0
    case ALT_DIGITS:
359
0
#endif
360
0
#ifdef INT_CURR_SYMBOL
361
0
    case INT_CURR_SYMBOL:
362
0
#endif
363
0
#ifdef CURRENCY_SYMBOL
364
0
    case CURRENCY_SYMBOL:
365
0
#endif
366
0
#ifdef CRNCYSTR
367
0
    case CRNCYSTR:
368
0
#endif
369
0
#ifdef MON_DECIMAL_POINT
370
0
    case MON_DECIMAL_POINT:
371
0
#endif
372
0
#ifdef MON_THOUSANDS_SEP
373
0
    case MON_THOUSANDS_SEP:
374
0
#endif
375
0
#ifdef MON_GROUPING
376
0
    case MON_GROUPING:
377
0
#endif
378
0
#ifdef POSITIVE_SIGN
379
0
    case POSITIVE_SIGN:
380
0
#endif
381
0
#ifdef NEGATIVE_SIGN
382
0
    case NEGATIVE_SIGN:
383
0
#endif
384
0
#ifdef INT_FRAC_DIGITS
385
0
    case INT_FRAC_DIGITS:
386
0
#endif
387
0
#ifdef FRAC_DIGITS
388
0
    case FRAC_DIGITS:
389
0
#endif
390
0
#ifdef P_CS_PRECEDES
391
0
    case P_CS_PRECEDES:
392
0
#endif
393
0
#ifdef P_SEP_BY_SPACE
394
0
    case P_SEP_BY_SPACE:
395
0
#endif
396
0
#ifdef N_CS_PRECEDES
397
0
    case N_CS_PRECEDES:
398
0
#endif
399
0
#ifdef N_SEP_BY_SPACE
400
0
    case N_SEP_BY_SPACE:
401
0
#endif
402
0
#ifdef P_SIGN_POSN
403
0
    case P_SIGN_POSN:
404
0
#endif
405
0
#ifdef N_SIGN_POSN
406
0
    case N_SIGN_POSN:
407
0
#endif
408
0
#ifdef DECIMAL_POINT
409
0
    case DECIMAL_POINT:
410
#elif defined(RADIXCHAR)
411
    case RADIXCHAR:
412
#endif
413
0
#ifdef THOUSANDS_SEP
414
0
    case THOUSANDS_SEP:
415
#elif defined(THOUSEP)
416
    case THOUSEP:
417
#endif
418
0
#ifdef GROUPING
419
0
    case GROUPING:
420
0
#endif
421
0
#ifdef YESEXPR
422
0
    case YESEXPR:
423
0
#endif
424
0
#ifdef NOEXPR
425
0
    case NOEXPR:
426
0
#endif
427
0
#ifdef YESSTR
428
0
    case YESSTR:
429
0
#endif
430
0
#ifdef NOSTR
431
0
    case NOSTR:
432
0
#endif
433
0
#ifdef CODESET
434
0
    case CODESET:
435
0
#endif
436
0
      break;
437
0
    default:
438
0
      php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
439
0
      RETURN_FALSE;
440
0
  }
441
  /* }}} */
442
443
0
  value = nl_langinfo(item);
444
0
  if (value == NULL) {
445
0
    RETURN_FALSE;
446
0
  } else {
447
0
    RETURN_STRING(value);
448
0
  }
449
0
}
450
#endif
451
/* }}} */
452
453
/* {{{ Compares two strings using the current locale */
454
PHP_FUNCTION(strcoll)
455
0
{
456
0
  zend_string *s1, *s2;
457
458
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
459
0
    Z_PARAM_STR(s1)
460
0
    Z_PARAM_STR(s2)
461
0
  ZEND_PARSE_PARAMETERS_END();
462
463
0
  RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
464
0
                      (const char *) ZSTR_VAL(s2)));
465
0
}
466
/* }}} */
467
468
/* {{{ php_charmask
469
 * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
470
 * it needs to be incrementing.
471
 * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
472
 */
473
static inline zend_result php_charmask(const unsigned char *input, size_t len, char *mask)
474
885
{
475
885
  const unsigned char *end;
476
885
  unsigned char c;
477
885
  zend_result result = SUCCESS;
478
479
885
  memset(mask, 0, 256);
480
13.9k
  for (end = input+len; input < end; input++) {
481
13.0k
    c=*input;
482
13.0k
    if ((input+3 < end) && input[1] == '.' && input[2] == '.'
483
12
        && input[3] >= c) {
484
12
      memset(mask+c, 1, input[3] - c + 1);
485
12
      input+=3;
486
13.0k
    } 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
0
      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
0
      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
0
      if (input[-1] > input[2]) { /* wrong order */
500
0
        php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
501
0
        result = FAILURE;
502
0
        continue;
503
0
      }
504
      /* FIXME: better error (a..b..c is the only left possibility?) */
505
0
      php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
506
0
      result = FAILURE;
507
0
      continue;
508
13.0k
    } else {
509
13.0k
      mask[c]=1;
510
13.0k
    }
511
13.0k
  }
512
885
  return result;
513
885
}
514
/* }}} */
515
516
static zend_always_inline bool php_is_whitespace(unsigned char c)
517
808
{
518
808
  return c <= ' ' && (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0');
519
808
}
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
376
{
529
376
  const char *start = ZSTR_VAL(str);
530
376
  const char *end = start + ZSTR_LEN(str);
531
376
  char mask[256];
532
533
376
  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
376
  } else {
577
376
    if (mode & 1) {
578
375
      while (start != end) {
579
339
        if (php_is_whitespace((unsigned char)*start)) {
580
53
          start++;
581
286
        } else {
582
286
          break;
583
286
        }
584
339
      }
585
322
    }
586
376
    if (mode & 2) {
587
559
      while (start != end) {
588
469
        if (php_is_whitespace((unsigned char)*(end-1))) {
589
183
          end--;
590
286
        } else {
591
286
          break;
592
286
        }
593
469
      }
594
376
    }
595
376
  }
596
597
376
  if (ZSTR_LEN(str) == end - start) {
598
305
    return zend_string_copy(str);
599
305
  } else if (end - start == 0) {
600
0
    return ZSTR_EMPTY_ALLOC();
601
71
  } else {
602
71
    return zend_string_init(start, end - start, 0);
603
71
  }
604
376
}
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
376
{
624
376
  zend_string *str;
625
376
  zend_string *what = NULL;
626
627
1.12k
  ZEND_PARSE_PARAMETERS_START(1, 2)
628
1.50k
    Z_PARAM_STR(str)
629
376
    Z_PARAM_OPTIONAL
630
752
    Z_PARAM_STR(what)
631
376
  ZEND_PARSE_PARAMETERS_END();
632
633
376
  ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
634
376
}
635
/* }}} */
636
637
/* {{{ Strips whitespace from the beginning and end of a string */
638
PHP_FUNCTION(trim)
639
322
{
640
322
  php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
641
322
}
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
54
{
675
54
  php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
676
54
}
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
3
{
832
3
  const char *p1 = ZSTR_VAL(str);
833
3
  const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
834
3
  const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
835
3
  zval  tmp;
836
837
3
  if (p2 == NULL) {
838
3
    ZVAL_STR_COPY(&tmp, str);
839
3
    zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
840
3
  } 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
3
}
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
9
{
905
9
  zend_string *str, *delim;
906
9
  zend_long limit = ZEND_LONG_MAX; /* No limit */
907
9
  zval tmp;
908
909
24
  ZEND_PARSE_PARAMETERS_START(2, 3)
910
24
    Z_PARAM_STR(delim)
911
15
    Z_PARAM_STR(str)
912
3
    Z_PARAM_OPTIONAL
913
6
    Z_PARAM_LONG(limit)
914
9
  ZEND_PARSE_PARAMETERS_END();
915
916
3
  if (ZSTR_LEN(delim) == 0) {
917
0
    zend_argument_must_not_be_empty_error(1);
918
0
    RETURN_THROWS();
919
0
  }
920
921
3
  array_init(return_value);
922
923
3
  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
3
  if (limit > 1) {
932
3
    php_explode(delim, str, return_value, limit);
933
3
  } 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
3
}
940
/* }}} */
941
942
/* {{{ php_implode */
943
PHPAPI void php_implode(const zend_string *glue, HashTable *pieces, zval *return_value)
944
39
{
945
39
  zval         *tmp;
946
39
  uint32_t      numelems;
947
39
  zend_string  *str;
948
39
  char         *cptr;
949
39
  size_t        len = 0;
950
39
  struct {
951
39
    zend_string *str;
952
39
    zend_long    lval;
953
39
  } *strings, *ptr;
954
39
  ALLOCA_FLAG(use_heap)
955
956
39
  numelems = zend_hash_num_elements(pieces);
957
958
39
  if (numelems == 0) {
959
9
    RETURN_EMPTY_STRING();
960
30
  } else if (numelems == 1) {
961
    /* loop to search the first not undefined element... */
962
81
    ZEND_HASH_FOREACH_VAL(pieces, tmp) {
963
81
      RETURN_STR(zval_get_string(tmp));
964
81
    } ZEND_HASH_FOREACH_END();
965
27
  }
966
967
3
  ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap);
968
969
3
  uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(glue);
970
971
15
  ZEND_HASH_FOREACH_VAL(pieces, tmp) {
972
15
    if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) {
973
6
      ptr->str = Z_STR_P(tmp);
974
6
      len += ZSTR_LEN(ptr->str);
975
6
      ptr->lval = 0;
976
6
      flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(ptr->str);
977
6
      ptr++;
978
6
    } 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
15
  } ZEND_HASH_FOREACH_END();
999
1000
  /* numelems cannot be 0, we checked above */
1001
3
  str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
1002
3
  GC_ADD_FLAGS(str, flags);
1003
3
  cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
1004
3
  *cptr = 0;
1005
1006
6
  while (1) {
1007
6
    ptr--;
1008
6
    if (EXPECTED(ptr->str)) {
1009
6
      cptr -= ZSTR_LEN(ptr->str);
1010
6
      memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
1011
6
      if (ptr->lval) {
1012
0
        zend_string_release_ex(ptr->str, 0);
1013
0
      }
1014
6
    } 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
6
    if (ptr == strings) {
1022
3
      break;
1023
3
    }
1024
1025
3
    cptr -= ZSTR_LEN(glue);
1026
3
    if (ZSTR_LEN(glue) == 1) {
1027
0
      *cptr = ZSTR_VAL(glue)[0];
1028
3
    } else {
1029
3
      memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
1030
3
    }
1031
3
  }
1032
1033
3
  free_alloca(strings, use_heap);
1034
3
  RETURN_NEW_STR(str);
1035
3
}
1036
/* }}} */
1037
1038
/* {{{ Joins array elements placing glue string between items and return one string */
1039
PHP_FUNCTION(implode)
1040
39
{
1041
39
  zend_string *arg1_str = NULL;
1042
39
  HashTable *arg1_array = NULL;
1043
39
  zend_array *pieces = NULL;
1044
1045
117
  ZEND_PARSE_PARAMETERS_START(1, 2)
1046
195
    Z_PARAM_ARRAY_HT_OR_STR(arg1_array, arg1_str)
1047
195
    Z_PARAM_OPTIONAL
1048
195
    Z_PARAM_ARRAY_HT_OR_NULL(pieces)
1049
39
  ZEND_PARSE_PARAMETERS_END();
1050
1051
39
  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
39
  } else {
1064
39
    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
39
  }
1069
1070
39
  php_implode(arg1_str, pieces, return_value);
1071
39
}
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
2.31k
#define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1120
1121
/* {{{ Tokenize a string */
1122
PHP_FUNCTION(strtok)
1123
511
{
1124
511
  zend_string *str, *tok = NULL;
1125
511
  char *token;
1126
511
  char *token_end;
1127
511
  char *p;
1128
511
  char *pe;
1129
511
  size_t skipped = 0;
1130
1131
1.53k
  ZEND_PARSE_PARAMETERS_START(1, 2)
1132
2.04k
    Z_PARAM_STR(str)
1133
511
    Z_PARAM_OPTIONAL
1134
1.06k
    Z_PARAM_STR_OR_NULL(tok)
1135
511
  ZEND_PARSE_PARAMETERS_END();
1136
1137
511
  if (!tok) {
1138
490
    tok = str;
1139
490
  } else {
1140
21
    if (BG(strtok_string)) {
1141
0
      zend_string_release(BG(strtok_string));
1142
0
    }
1143
21
    BG(strtok_string) = zend_string_copy(str);
1144
21
    BG(strtok_last) = ZSTR_VAL(str);
1145
21
    BG(strtok_len) = ZSTR_LEN(str);
1146
21
  }
1147
1148
511
  if (!BG(strtok_string)) {
1149
    /* String to tokenize not set. */
1150
97
    php_error_docref(NULL, E_WARNING, "Both arguments must be provided when starting tokenization");
1151
97
    RETURN_FALSE;
1152
97
  }
1153
1154
414
  p = BG(strtok_last); /* Where we start to search */
1155
414
  pe = ZSTR_VAL(BG(strtok_string)) + BG(strtok_len);
1156
414
  if (p >= pe) {
1157
    /* Reached the end of the string. */
1158
293
    RETURN_FALSE;
1159
293
  }
1160
1161
121
  token = ZSTR_VAL(tok);
1162
121
  token_end = token + ZSTR_LEN(tok);
1163
1164
543
  while (token < token_end) {
1165
422
    STRTOK_TABLE(token++) = 1;
1166
422
  }
1167
1168
  /* Skip leading delimiters */
1169
137
  while (STRTOK_TABLE(p)) {
1170
16
    if (++p >= pe) {
1171
      /* no other chars left */
1172
0
      goto return_false;
1173
0
    }
1174
16
    skipped++;
1175
16
  }
1176
1177
  /* We know at this place that *p is no delimiter, so skip it */
1178
1.35k
  while (++p < pe) {
1179
1.33k
    if (STRTOK_TABLE(p)) {
1180
103
      goto return_token;
1181
103
    }
1182
1.33k
  }
1183
1184
18
  if (p - BG(strtok_last)) {
1185
121
return_token:
1186
121
    RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1187
121
    BG(strtok_last) = p + 1;
1188
121
  } 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
121
  token = ZSTR_VAL(tok);
1197
543
  while (token < token_end) {
1198
422
    STRTOK_TABLE(token++) = 0;
1199
422
  }
1200
121
}
1201
/* }}} */
1202
1203
/* {{{ Makes a string uppercase */
1204
PHP_FUNCTION(strtoupper)
1205
873
{
1206
873
  zend_string *arg;
1207
1208
2.61k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1209
3.49k
    Z_PARAM_STR(arg)
1210
873
  ZEND_PARSE_PARAMETERS_END();
1211
1212
873
  RETURN_STR(zend_string_toupper(arg));
1213
873
}
1214
/* }}} */
1215
1216
/* {{{ Makes a string lowercase */
1217
PHP_FUNCTION(strtolower)
1218
45
{
1219
45
  zend_string *str;
1220
1221
135
  ZEND_PARSE_PARAMETERS_START(1, 1)
1222
180
    Z_PARAM_STR(str)
1223
45
  ZEND_PARSE_PARAMETERS_END();
1224
1225
45
  RETURN_STR(zend_string_tolower(str));
1226
45
}
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
6
{
1363
6
  const char *basename_start;
1364
6
  const char *basename_end;
1365
1366
6
  if (CG(ascii_compatible_locale)) {
1367
6
    basename_end = s + len - 1;
1368
1369
    /* Strip trailing slashes */
1370
6
    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
6
      && *basename_end == '/') {
1378
0
#endif
1379
0
      basename_end--;
1380
0
    }
1381
6
    if (basename_end < s) {
1382
0
      return ZSTR_EMPTY_ALLOC();
1383
0
    }
1384
1385
    /* Extract filename */
1386
6
    basename_start = basename_end;
1387
6
    basename_end++;
1388
60
    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
60
      && *(basename_start-1) != '/') {
1399
54
#endif
1400
54
      basename_start--;
1401
54
    }
1402
6
  } 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
6
  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
6
  return zend_string_init(basename_start, basename_end - basename_start, 0);
1473
6
}
1474
/* }}} */
1475
1476
/* {{{ Returns the filename component of the path */
1477
PHP_FUNCTION(basename)
1478
6
{
1479
6
  char *string, *suffix = NULL;
1480
6
  size_t   string_len, suffix_len = 0;
1481
1482
18
  ZEND_PARSE_PARAMETERS_START(1, 2)
1483
24
    Z_PARAM_STRING(string, string_len)
1484
6
    Z_PARAM_OPTIONAL
1485
12
    Z_PARAM_STRING(suffix, suffix_len)
1486
6
  ZEND_PARSE_PARAMETERS_END();
1487
1488
6
  RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1489
6
}
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
42
{
1502
42
  zend_string *ret;
1503
1504
42
  ret = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
1505
1506
42
  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
42
    ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), ZSTR_LEN(str));
1512
42
#endif
1513
42
  } 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
42
  RETURN_NEW_STR(ret);
1530
42
}
1531
1532
/* {{{ Returns the directory name component of the path */
1533
PHP_FUNCTION(dirname)
1534
42
{
1535
42
  zend_string *str;
1536
42
  zend_long levels = 1;
1537
1538
126
  ZEND_PARSE_PARAMETERS_START(1, 2)
1539
168
    Z_PARAM_STR(str)
1540
42
    Z_PARAM_OPTIONAL
1541
84
    Z_PARAM_LONG(levels)
1542
42
  ZEND_PARSE_PARAMETERS_END();
1543
1544
42
  _zend_dirname(return_value, str, levels);
1545
42
}
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
36
{
1673
36
  return (char*)php_memnistr(s, t, t_len, s + s_len);
1674
36
}
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
39
{
1730
39
  zend_string *haystack, *needle;
1731
39
  const char *found = NULL;
1732
39
  size_t  found_offset;
1733
39
  bool part = 0;
1734
1735
114
  ZEND_PARSE_PARAMETERS_START(2, 3)
1736
144
    Z_PARAM_STR(haystack)
1737
180
    Z_PARAM_STR(needle)
1738
36
    Z_PARAM_OPTIONAL
1739
72
    Z_PARAM_BOOL(part)
1740
39
  ZEND_PARSE_PARAMETERS_END();
1741
1742
36
  found = php_stristr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(haystack), ZSTR_LEN(needle));
1743
1744
36
  if (UNEXPECTED(!found)) {
1745
0
    RETURN_FALSE;
1746
0
  }
1747
36
  found_offset = found - ZSTR_VAL(haystack);
1748
36
  if (part) {
1749
0
    RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1750
0
  }
1751
36
  RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1752
36
}
1753
/* }}} */
1754
1755
static zend_always_inline void _zend_strstr(zval *return_value, zend_string *haystack, zend_string *needle, bool part)
1756
110
{
1757
110
  const char *found = NULL;
1758
110
  zend_long found_offset;
1759
1760
110
  found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1761
1762
110
  if (UNEXPECTED(!found)) {
1763
33
    RETURN_FALSE;
1764
33
  }
1765
77
  found_offset = found - ZSTR_VAL(haystack);
1766
77
  if (part) {
1767
77
    RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1768
77
  }
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
110
{
1775
110
  zend_string *haystack, *needle;
1776
110
  bool part = 0;
1777
1778
330
  ZEND_PARSE_PARAMETERS_START(2, 3)
1779
440
    Z_PARAM_STR(haystack)
1780
550
    Z_PARAM_STR(needle)
1781
110
    Z_PARAM_OPTIONAL
1782
440
    Z_PARAM_BOOL(part)
1783
110
  ZEND_PARSE_PARAMETERS_END();
1784
1785
110
  _zend_strstr(return_value, haystack, needle, part);
1786
110
}
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
166
{
1901
166
  const char *found = NULL;
1902
1903
166
  if (offset < 0) {
1904
0
    offset += (zend_long)ZSTR_LEN(haystack);
1905
0
  }
1906
166
  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
166
  found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1912
166
            ZSTR_VAL(needle), ZSTR_LEN(needle),
1913
166
            ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1914
1915
166
  if (UNEXPECTED(!found)) {
1916
163
    RETURN_FALSE;
1917
163
  }
1918
3
  RETURN_LONG(found - ZSTR_VAL(haystack));
1919
3
}
1920
1921
/* {{{ Finds position of first occurrence of a string within another */
1922
PHP_FUNCTION(strpos)
1923
166
{
1924
166
  zend_string *haystack, *needle;
1925
166
  zend_long offset = 0;
1926
1927
498
  ZEND_PARSE_PARAMETERS_START(2, 3)
1928
664
    Z_PARAM_STR(haystack)
1929
830
    Z_PARAM_STR(needle)
1930
166
    Z_PARAM_OPTIONAL
1931
332
    Z_PARAM_LONG(offset)
1932
166
  ZEND_PARSE_PARAMETERS_END();
1933
1934
166
  _zend_strpos(return_value, haystack, needle, offset);
1935
166
}
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
54
{
1973
54
  const char *found = NULL;
1974
54
  zend_string *haystack, *needle;
1975
54
  zend_long offset = 0;
1976
1977
162
  ZEND_PARSE_PARAMETERS_START(2, 3)
1978
216
    Z_PARAM_STR(haystack)
1979
270
    Z_PARAM_STR(needle)
1980
54
    Z_PARAM_OPTIONAL
1981
108
    Z_PARAM_LONG(offset)
1982
54
  ZEND_PARSE_PARAMETERS_END();
1983
1984
54
  if (offset < 0) {
1985
0
    offset += (zend_long)ZSTR_LEN(haystack);
1986
0
  }
1987
54
  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
54
  found = (char*)php_memnistr(ZSTR_VAL(haystack) + offset,
1993
54
      ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1994
1995
54
  if (UNEXPECTED(!found)) {
1996
18
    RETURN_FALSE;
1997
18
  }
1998
36
  RETURN_LONG(found - ZSTR_VAL(haystack));
1999
36
}
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
54
{
2233
54
  if (f < 0) {
2234
    /* if "from" position is negative, count start position from the end
2235
     * of the string
2236
     */
2237
0
    if (-(size_t)f > ZSTR_LEN(str)) {
2238
0
      f = 0;
2239
0
    } else {
2240
0
      f = (zend_long)ZSTR_LEN(str) + f;
2241
0
    }
2242
54
  } else if ((size_t)f > ZSTR_LEN(str)) {
2243
3
    RETURN_EMPTY_STRING();
2244
3
  }
2245
2246
51
  if (!len_is_null) {
2247
45
    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
6
      if (-(size_t)l > ZSTR_LEN(str) - (size_t)f) {
2252
0
        l = 0;
2253
6
      } else {
2254
6
        l = (zend_long)ZSTR_LEN(str) - f + l;
2255
6
      }
2256
39
    } else if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
2257
15
      l = (zend_long)ZSTR_LEN(str) - f;
2258
15
    }
2259
45
  } else {
2260
6
    l = (zend_long)ZSTR_LEN(str) - f;
2261
6
  }
2262
2263
51
  if (l == ZSTR_LEN(str)) {
2264
15
    RETURN_STR_COPY(str);
2265
36
  } else {
2266
36
    RETURN_STRINGL_FAST(ZSTR_VAL(str) + f, l);
2267
36
  }
2268
51
}
2269
2270
/* {{{ Returns part of a string */
2271
PHP_FUNCTION(substr)
2272
72
{
2273
72
  zend_string *str;
2274
72
  zend_long l = 0, f;
2275
72
  bool len_is_null = 1;
2276
2277
207
  ZEND_PARSE_PARAMETERS_START(2, 3)
2278
252
    Z_PARAM_STR(str)
2279
270
    Z_PARAM_LONG(f)
2280
54
    Z_PARAM_OPTIONAL
2281
204
    Z_PARAM_LONG_OR_NULL(l, len_is_null)
2282
72
  ZEND_PARSE_PARAMETERS_END();
2283
2284
54
  _zend_substr(return_value, str, f, len_is_null, l);
2285
54
}
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
15
{
2323
15
  zend_string *str, *repl_str;
2324
15
  HashTable *str_ht, *repl_ht;
2325
15
  HashTable *from_ht;
2326
15
  zend_long from_long;
2327
15
  HashTable *len_ht = NULL;
2328
15
  zend_long len_long;
2329
15
  bool len_is_null = 1;
2330
15
  zend_long l = 0;
2331
15
  zend_long f;
2332
15
  zend_string *result;
2333
15
  HashPosition from_idx, repl_idx, len_idx;
2334
15
  zval *tmp_str = NULL, *tmp_repl, *tmp_from = NULL, *tmp_len= NULL;
2335
2336
45
  ZEND_PARSE_PARAMETERS_START(3, 4)
2337
75
    Z_PARAM_ARRAY_HT_OR_STR(str_ht, str)
2338
75
    Z_PARAM_ARRAY_HT_OR_STR(repl_ht, repl_str)
2339
72
    Z_PARAM_ARRAY_HT_OR_LONG(from_ht, from_long)
2340
6
    Z_PARAM_OPTIONAL
2341
12
    Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(len_ht, len_long, len_is_null)
2342
15
  ZEND_PARSE_PARAMETERS_END();
2343
2344
6
  if (len_is_null) {
2345
6
    if (str) {
2346
6
      l = ZSTR_LEN(str);
2347
6
    }
2348
6
  } else if (!len_ht) {
2349
0
    l = len_long;
2350
0
  }
2351
2352
6
  if (str) {
2353
6
    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
6
    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
6
    f = from_long;
2363
2364
    /* if "from" position is negative, count start position from the end
2365
     * of the string
2366
     */
2367
6
    if (f < 0) {
2368
0
      f = (zend_long)ZSTR_LEN(str) + f;
2369
0
      if (f < 0) {
2370
0
        f = 0;
2371
0
      }
2372
6
    } 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
6
    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
6
    if ((size_t)l > ZSTR_LEN(str)) {
2386
0
      l = ZSTR_LEN(str);
2387
0
    }
2388
2389
6
    if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2390
3
      l = ZSTR_LEN(str) - f;
2391
3
    }
2392
2393
6
    zend_string *tmp_repl_str = NULL;
2394
6
    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
6
    result = zend_string_safe_alloc(1, ZSTR_LEN(str) - l + ZSTR_LEN(repl_str), 0, 0);
2421
2422
6
    memcpy(ZSTR_VAL(result), ZSTR_VAL(str), f);
2423
6
    if (ZSTR_LEN(repl_str)) {
2424
0
      memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2425
0
    }
2426
6
    memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(str) + f + l, ZSTR_LEN(str) - f - l);
2427
6
    ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2428
6
    zend_tmp_string_release(tmp_repl_str);
2429
6
    RETURN_NEW_STR(result);
2430
6
  } 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
6
}
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
464
{
2657
464
  zend_string *str;
2658
2659
1.39k
  ZEND_PARSE_PARAMETERS_START(1, 1)
2660
1.85k
    Z_PARAM_STR(str)
2661
464
  ZEND_PARSE_PARAMETERS_END();
2662
2663
461
  if (UNEXPECTED(ZSTR_LEN(str) != 1)) {
2664
100
    if (ZSTR_LEN(str) == 0) {
2665
97
      php_error_docref(NULL, E_DEPRECATED,
2666
97
        "Providing an empty string is deprecated");
2667
97
    } else {
2668
3
      php_error_docref(NULL, E_DEPRECATED,
2669
3
        "Providing a string that is not one byte long is deprecated. Use ord($str[0]) instead");
2670
3
    }
2671
100
  }
2672
461
  RETURN_LONG((unsigned char) ZSTR_VAL(str)[0]);
2673
461
}
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
168
{
2680
168
  zend_long c;
2681
2682
504
  ZEND_PARSE_PARAMETERS_START(1, 1)
2683
672
    Z_PARAM_LONG(c)
2684
168
  ZEND_PARSE_PARAMETERS_END();
2685
2686
162
  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
162
  c &= 0xff;
2693
162
  RETURN_CHAR(c);
2694
162
}
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
66
{
2701
66
  const unsigned char ch = ZSTR_VAL(str)[0];
2702
66
  unsigned char r = zend_toupper_ascii(ch);
2703
66
  if (r == ch) {
2704
3
    return zend_string_copy(str);
2705
63
  } else {
2706
63
    zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2707
63
    ZSTR_VAL(s)[0] = r;
2708
63
    return s;
2709
63
  }
2710
66
}
2711
/* }}} */
2712
2713
/* {{{ Makes a string's first character uppercase */
2714
PHP_FUNCTION(ucfirst)
2715
66
{
2716
66
  zend_string *str;
2717
2718
198
  ZEND_PARSE_PARAMETERS_START(1, 1)
2719
264
    Z_PARAM_STR(str)
2720
66
  ZEND_PARSE_PARAMETERS_END();
2721
2722
66
  if (!ZSTR_LEN(str)) {
2723
0
    RETURN_EMPTY_STRING();
2724
0
  }
2725
2726
66
  RETURN_STR(php_ucfirst(str));
2727
66
}
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
365
{
3062
365
  zend_long count = 0;
3063
365
  const char *endp;
3064
3065
365
#ifdef XSSE2
3066
365
  if (length >= sizeof(__m128i)) {
3067
221
    __m128i search = _mm_set1_epi8(ch);
3068
3069
663
    do {
3070
663
      __m128i src = _mm_loadu_si128((__m128i*)(p));
3071
663
      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
663
#if 1
3074
1.83k
      while (mask != 0) {
3075
1.16k
        count++;
3076
1.16k
        mask = mask & (mask - 1);
3077
1.16k
      }
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
663
      p += sizeof(__m128i);
3088
663
      length -= sizeof(__m128i);
3089
663
    } while (length >= sizeof(__m128i));
3090
221
  }
3091
365
  endp = p + length;
3092
2.03k
  while (p != endp) {
3093
1.66k
    count += (*p == ch);
3094
1.66k
    p++;
3095
1.66k
  }
3096
#else
3097
  endp = p + length;
3098
  while ((p = memchr(p, ch, endp-p))) {
3099
    count++;
3100
    p++;
3101
  }
3102
#endif
3103
365
  return count;
3104
365
}
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
365
{
3110
365
  zend_string *result;
3111
365
  size_t char_count;
3112
365
  int lc_from = 0;
3113
365
  const char *source, *source_end;
3114
365
  char *target;
3115
3116
365
  if (case_sensitivity) {
3117
365
    char_count = count_chars(ZSTR_VAL(str), ZSTR_LEN(str), from);
3118
365
  } else {
3119
0
    char_count = 0;
3120
0
    lc_from = zend_tolower_ascii(from);
3121
0
    source_end = ZSTR_VAL(str) + ZSTR_LEN(str);
3122
0
    for (source = ZSTR_VAL(str); source < source_end; source++) {
3123
0
      if (zend_tolower_ascii(*source) == lc_from) {
3124
0
        char_count++;
3125
0
      }
3126
0
    }
3127
0
  }
3128
3129
365
  if (char_count == 0) {
3130
27
    return zend_string_copy(str);
3131
27
  }
3132
3133
338
  if (replace_count) {
3134
338
    *replace_count += char_count;
3135
338
  }
3136
3137
338
  if (to_len > 0) {
3138
12
    result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3139
326
  } else {
3140
326
    result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3141
326
  }
3142
338
  target = ZSTR_VAL(result);
3143
3144
338
  if (case_sensitivity) {
3145
338
    char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3146
3147
1.40k
    while ((p = memchr(p, from, (e - p)))) {
3148
1.40k
      target = zend_mempcpy(target, s, (p - s));
3149
1.40k
      target = zend_mempcpy(target, to, to_len);
3150
1.40k
      p++;
3151
1.40k
      s = p;
3152
1.40k
      if (--char_count == 0) break;
3153
1.40k
    }
3154
338
    if (s < e) {
3155
0
      target = zend_mempcpy(target, s, e - s);
3156
0
    }
3157
338
  } 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
338
  *target = 0;
3169
338
  return result;
3170
365
}
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
617
{
3177
3178
617
  if (needle_len < ZSTR_LEN(haystack)) {
3179
611
    zend_string *new_str;
3180
611
    const char *end;
3181
611
    const char *p, *r;
3182
611
    char *e;
3183
3184
611
    if (needle_len == str_len) {
3185
179
      new_str = NULL;
3186
179
      end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3187
282
      for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3188
103
        if (!new_str) {
3189
103
          new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3190
103
        }
3191
103
        memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3192
103
        (*replace_count)++;
3193
103
      }
3194
179
      if (!new_str) {
3195
76
        goto nothing_todo;
3196
76
      }
3197
103
      return new_str;
3198
432
    } else {
3199
432
      size_t count = 0;
3200
432
      const char *o = ZSTR_VAL(haystack);
3201
432
      const char *n = needle;
3202
432
      const char *endp = o + ZSTR_LEN(haystack);
3203
3204
534
      while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3205
102
        o += needle_len;
3206
102
        count++;
3207
102
      }
3208
432
      if (count == 0) {
3209
        /* Needle doesn't occur, shortcircuit the actual replacement. */
3210
387
        goto nothing_todo;
3211
387
      }
3212
45
      if (str_len > needle_len) {
3213
12
        new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3214
33
      } else {
3215
33
        new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3216
33
      }
3217
3218
45
      e = ZSTR_VAL(new_str);
3219
45
      end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3220
147
      for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3221
102
        e = zend_mempcpy(e, p, r - p);
3222
102
        e = zend_mempcpy(e, str, str_len);
3223
102
        (*replace_count)++;
3224
102
      }
3225
3226
45
      if (p < end) {
3227
45
        e = zend_mempcpy(e, p, end - p);
3228
45
      }
3229
3230
45
      *e = '\0';
3231
45
      return new_str;
3232
432
    }
3233
611
  } else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3234
469
nothing_todo:
3235
469
    return zend_string_copy(haystack);
3236
6
  } else {
3237
0
    (*replace_count)++;
3238
0
    return zend_string_init_fast(str, str_len);
3239
0
  }
3240
617
}
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
0
{
3247
0
  zend_string *new_str = NULL;
3248
0
  zend_string *lc_needle;
3249
3250
0
  if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3251
0
    const char *end;
3252
0
    const char *p, *r;
3253
0
    char *e;
3254
3255
0
    if (ZSTR_LEN(needle) == str_len) {
3256
0
      lc_needle = zend_string_tolower(needle);
3257
0
      end = lc_haystack + ZSTR_LEN(haystack);
3258
0
      for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3259
0
        if (!new_str) {
3260
0
          new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3261
0
        }
3262
0
        memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3263
0
        (*replace_count)++;
3264
0
      }
3265
0
      zend_string_release_ex(lc_needle, 0);
3266
3267
0
      if (!new_str) {
3268
0
        goto nothing_todo;
3269
0
      }
3270
0
      return new_str;
3271
0
    } else {
3272
0
      size_t count = 0;
3273
0
      const char *o = lc_haystack;
3274
0
      const char *n;
3275
0
      const char *endp = o + ZSTR_LEN(haystack);
3276
3277
0
      lc_needle = zend_string_tolower(needle);
3278
0
      n = ZSTR_VAL(lc_needle);
3279
3280
0
      while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3281
0
        o += ZSTR_LEN(lc_needle);
3282
0
        count++;
3283
0
      }
3284
0
      if (count == 0) {
3285
        /* Needle doesn't occur, shortcircuit the actual replacement. */
3286
0
        zend_string_release_ex(lc_needle, 0);
3287
0
        goto nothing_todo;
3288
0
      }
3289
3290
0
      if (str_len > ZSTR_LEN(lc_needle)) {
3291
0
        new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3292
0
      } else {
3293
0
        new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3294
0
      }
3295
3296
0
      e = ZSTR_VAL(new_str);
3297
0
      end = lc_haystack + ZSTR_LEN(haystack);
3298
3299
0
      for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3300
0
        e = zend_mempcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3301
0
        e = zend_mempcpy(e, str, str_len);
3302
0
        (*replace_count)++;
3303
0
      }
3304
3305
0
      if (p < end) {
3306
0
        e = zend_mempcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3307
0
      }
3308
0
      *e = '\0';
3309
3310
0
      zend_string_release_ex(lc_needle, 0);
3311
3312
0
      return new_str;
3313
0
    }
3314
0
  } else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3315
0
nothing_todo:
3316
0
    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
0
}
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
450
{
3337
450
  zend_string *new_str;
3338
3339
450
  if (needle_len < length) {
3340
381
    const char *end;
3341
381
    const char *s, *p;
3342
381
    char *e, *r;
3343
3344
381
    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
381
    } else {
3352
381
      if (str_len < needle_len) {
3353
0
        new_str = zend_string_alloc(length, 0);
3354
381
      } else {
3355
381
        size_t count = 0;
3356
381
        const char *o = haystack;
3357
381
        const char *n = needle;
3358
381
        const char *endp = o + length;
3359
3360
624
        while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3361
243
          o += needle_len;
3362
243
          count++;
3363
243
        }
3364
381
        if (count == 0) {
3365
          /* Needle doesn't occur, shortcircuit the actual replacement. */
3366
336
          new_str = zend_string_init(haystack, length, 0);
3367
336
          return new_str;
3368
336
        } else {
3369
45
          if (str_len > needle_len) {
3370
45
            new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3371
45
          } else {
3372
0
            new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3373
0
          }
3374
45
        }
3375
381
      }
3376
3377
45
      s = e = ZSTR_VAL(new_str);
3378
45
      end = haystack + length;
3379
288
      for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3380
243
        e = zend_mempcpy(e, p, r - p);
3381
243
        e = zend_mempcpy(e, str, str_len);
3382
243
      }
3383
3384
45
      if (p < end) {
3385
39
        e = zend_mempcpy(e, p, end - p);
3386
39
      }
3387
3388
45
      *e = '\0';
3389
45
      new_str = zend_string_truncate(new_str, e - s, 0);
3390
45
      return new_str;
3391
381
    }
3392
381
  } else if (needle_len > length || memcmp(haystack, needle, length)) {
3393
66
    new_str = zend_string_init(haystack, length, 0);
3394
66
    return new_str;
3395
66
  } else {
3396
3
    new_str = zend_string_init(str, str_len, 0);
3397
3398
3
    return new_str;
3399
3
  }
3400
450
}
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
57
{
3530
57
  zend_string *str;
3531
57
  const char *s, *e;
3532
57
  char *p;
3533
57
  zend_string *n;
3534
3535
171
  ZEND_PARSE_PARAMETERS_START(1, 1)
3536
228
    Z_PARAM_STR(str)
3537
57
  ZEND_PARSE_PARAMETERS_END();
3538
3539
57
  n = zend_string_alloc(ZSTR_LEN(str), 0);
3540
57
  p = ZSTR_VAL(n);
3541
3542
57
  s = ZSTR_VAL(str);
3543
57
  e = s + ZSTR_LEN(str);
3544
57
  --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
228
  while (e >= s) {
3587
171
    *p++ = *e--;
3588
171
  }
3589
3590
57
  *p = '\0';
3591
3592
57
  RETVAL_NEW_STR(n);
3593
57
}
3594
/* }}} */
3595
3596
/* {{{ php_similar_str */
3597
static void php_similar_str(const char *txt1, size_t len1, const char *txt2, size_t len2, size_t *pos1, size_t *pos2, size_t *max, size_t *count)
3598
0
{
3599
0
  const char *p, *q;
3600
0
  const char *end1 = (char *) txt1 + len1;
3601
0
  const char *end2 = (char *) txt2 + len2;
3602
0
  size_t l;
3603
3604
0
  *max = 0;
3605
0
  *count = 0;
3606
0
  for (p = (char *) txt1; p < end1; p++) {
3607
0
    for (q = (char *) txt2; q < end2; q++) {
3608
0
      for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3609
0
      if (l > *max) {
3610
0
        *max = l;
3611
0
        *count += 1;
3612
0
        *pos1 = p - txt1;
3613
0
        *pos2 = q - txt2;
3614
0
      }
3615
0
    }
3616
0
  }
3617
0
}
3618
/* }}} */
3619
3620
/* {{{ php_similar_char */
3621
static size_t php_similar_char(const char *txt1, size_t len1, const char *txt2, size_t len2)
3622
0
{
3623
0
  size_t sum;
3624
0
  size_t pos1 = 0, pos2 = 0, max, count;
3625
3626
0
  php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max, &count);
3627
0
  if ((sum = max)) {
3628
0
    if (pos1 && pos2 && count > 1) {
3629
0
      sum += php_similar_char(txt1, pos1,
3630
0
                  txt2, pos2);
3631
0
    }
3632
0
    if ((pos1 + max < len1) && (pos2 + max < len2)) {
3633
0
      sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3634
0
                  txt2 + pos2 + max, len2 - pos2 - max);
3635
0
    }
3636
0
  }
3637
3638
0
  return sum;
3639
0
}
3640
/* }}} */
3641
3642
/* {{{ Calculates the similarity between two strings */
3643
PHP_FUNCTION(similar_text)
3644
0
{
3645
0
  zend_string *t1, *t2;
3646
0
  zval *percent = NULL;
3647
0
  bool compute_percentage = ZEND_NUM_ARGS() >= 3;
3648
0
  size_t sim;
3649
3650
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
3651
0
    Z_PARAM_STR(t1)
3652
0
    Z_PARAM_STR(t2)
3653
0
    Z_PARAM_OPTIONAL
3654
0
    Z_PARAM_ZVAL(percent)
3655
0
  ZEND_PARSE_PARAMETERS_END();
3656
3657
0
  if (ZSTR_LEN(t1) + ZSTR_LEN(t2) == 0) {
3658
0
    if (compute_percentage) {
3659
0
      ZEND_TRY_ASSIGN_REF_DOUBLE(percent, 0);
3660
0
    }
3661
3662
0
    RETURN_LONG(0);
3663
0
  }
3664
3665
0
  sim = php_similar_char(ZSTR_VAL(t1), ZSTR_LEN(t1), ZSTR_VAL(t2), ZSTR_LEN(t2));
3666
3667
0
  if (compute_percentage) {
3668
0
    ZEND_TRY_ASSIGN_REF_DOUBLE(percent, sim * 200.0 / (ZSTR_LEN(t1) + ZSTR_LEN(t2)));
3669
0
  }
3670
3671
0
  RETURN_LONG(sim);
3672
0
}
3673
/* }}} */
3674
3675
/* {{{ Escapes all chars mentioned in charlist with backslash. It creates octal representations if asked to backslash characters with 8th bit set or with ASCII<32 (except '\n', '\r', '\t' etc...) */
3676
PHP_FUNCTION(addcslashes)
3677
48
{
3678
48
  zend_string *str, *what;
3679
3680
144
  ZEND_PARSE_PARAMETERS_START(2, 2)
3681
192
    Z_PARAM_STR(str)
3682
240
    Z_PARAM_STR(what)
3683
48
  ZEND_PARSE_PARAMETERS_END();
3684
3685
48
  if (ZSTR_LEN(str) == 0) {
3686
0
    RETURN_EMPTY_STRING();
3687
0
  }
3688
3689
48
  if (ZSTR_LEN(what) == 0) {
3690
0
    RETURN_STR_COPY(str);
3691
0
  }
3692
3693
48
  RETURN_STR(php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), ZSTR_VAL(what), ZSTR_LEN(what)));
3694
48
}
3695
/* }}} */
3696
3697
/* {{{ Escapes single quote, double quotes and backslash characters in a string with backslashes */
3698
PHP_FUNCTION(addslashes)
3699
0
{
3700
0
  zend_string *str;
3701
3702
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3703
0
    Z_PARAM_STR(str)
3704
0
  ZEND_PARSE_PARAMETERS_END();
3705
3706
0
  if (ZSTR_LEN(str) == 0) {
3707
0
    RETURN_EMPTY_STRING();
3708
0
  }
3709
3710
0
  RETURN_STR(php_addslashes(str));
3711
0
}
3712
/* }}} */
3713
3714
/* {{{ Strips backslashes from a string. Uses C-style conventions */
3715
PHP_FUNCTION(stripcslashes)
3716
0
{
3717
0
  zend_string *str;
3718
3719
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3720
0
    Z_PARAM_STR(str)
3721
0
  ZEND_PARSE_PARAMETERS_END();
3722
3723
0
  ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3724
0
  php_stripcslashes(Z_STR_P(return_value));
3725
0
}
3726
/* }}} */
3727
3728
/* {{{ Strips backslashes from a string */
3729
PHP_FUNCTION(stripslashes)
3730
0
{
3731
0
  zend_string *str;
3732
3733
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3734
0
    Z_PARAM_STR(str)
3735
0
  ZEND_PARSE_PARAMETERS_END();
3736
3737
0
  ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3738
0
  php_stripslashes(Z_STR_P(return_value));
3739
0
}
3740
/* }}} */
3741
3742
/* {{{ php_stripcslashes */
3743
PHPAPI void php_stripcslashes(zend_string *str)
3744
0
{
3745
0
  const char *source, *end;
3746
0
  char *target;
3747
0
  size_t  nlen = ZSTR_LEN(str), i;
3748
0
  char numtmp[4];
3749
3750
0
  for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(str); source < end; source++) {
3751
0
    if (*source == '\\' && source + 1 < end) {
3752
0
      source++;
3753
0
      switch (*source) {
3754
0
        case 'n':  *target++='\n'; nlen--; break;
3755
0
        case 'r':  *target++='\r'; nlen--; break;
3756
0
        case 'a':  *target++='\a'; nlen--; break;
3757
0
        case 't':  *target++='\t'; nlen--; break;
3758
0
        case 'v':  *target++='\v'; nlen--; break;
3759
0
        case 'b':  *target++='\b'; nlen--; break;
3760
0
        case 'f':  *target++='\f'; nlen--; break;
3761
0
        case '\\': *target++='\\'; nlen--; break;
3762
0
        case 'x':
3763
0
          if (source+1 < end && isxdigit((unsigned char)source[1])) {
3764
0
            numtmp[0] = *++source;
3765
0
            if (source+1 < end && isxdigit((unsigned char)source[1])) {
3766
0
              numtmp[1] = *++source;
3767
0
              numtmp[2] = '\0';
3768
0
              nlen-=3;
3769
0
            } else {
3770
0
              numtmp[1] = '\0';
3771
0
              nlen-=2;
3772
0
            }
3773
0
            *target++=(char)strtol(numtmp, NULL, 16);
3774
0
            break;
3775
0
          }
3776
0
          ZEND_FALLTHROUGH;
3777
0
        default:
3778
0
          i=0;
3779
0
          while (source < end && *source >= '0' && *source <= '7' && i<3) {
3780
0
            numtmp[i++] = *source++;
3781
0
          }
3782
0
          if (i) {
3783
0
            numtmp[i]='\0';
3784
0
            *target++=(char)strtol(numtmp, NULL, 8);
3785
0
            nlen-=i;
3786
0
            source--;
3787
0
          } else {
3788
0
            *target++=*source;
3789
0
            nlen--;
3790
0
          }
3791
0
      }
3792
0
    } else {
3793
0
      *target++=*source;
3794
0
    }
3795
0
  }
3796
3797
0
  if (nlen != 0) {
3798
0
    *target='\0';
3799
0
  }
3800
3801
0
  ZSTR_LEN(str) = nlen;
3802
0
}
3803
/* }}} */
3804
3805
/* {{{ php_addcslashes_str */
3806
PHPAPI zend_string *php_addcslashes_str(const char *str, size_t len, const char *what, size_t wlength)
3807
885
{
3808
885
  char flags[256];
3809
885
  char *target;
3810
885
  const char *source, *end;
3811
885
  char c;
3812
885
  size_t  newlen;
3813
885
  zend_string *new_str = zend_string_safe_alloc(4, len, 0, 0);
3814
3815
885
  php_charmask((const unsigned char *) what, wlength, flags);
3816
3817
11.0k
  for (source = str, end = source + len, target = ZSTR_VAL(new_str); source < end; source++) {
3818
10.1k
    c = *source;
3819
10.1k
    if (flags[(unsigned char)c]) {
3820
129
      if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3821
102
        *target++ = '\\';
3822
102
        switch (c) {
3823
48
          case '\n': *target++ = 'n'; break;
3824
0
          case '\t': *target++ = 't'; break;
3825
54
          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
0
          default: target += snprintf(target, 4, "%03o", (unsigned char) c);
3831
102
        }
3832
102
        continue;
3833
102
      }
3834
27
      *target++ = '\\';
3835
27
    }
3836
10.0k
    *target++ = c;
3837
10.0k
  }
3838
885
  *target = 0;
3839
885
  newlen = target - ZSTR_VAL(new_str);
3840
885
  if (newlen < len * 4) {
3841
852
    new_str = zend_string_truncate(new_str, newlen, 0);
3842
852
  }
3843
885
  return new_str;
3844
885
}
3845
/* }}} */
3846
3847
/* {{{ php_addcslashes */
3848
PHPAPI zend_string *php_addcslashes(zend_string *str, const char *what, size_t wlength)
3849
450
{
3850
450
  return php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), what, wlength);
3851
450
}
3852
/* }}} */
3853
3854
/* {{{ php_addslashes */
3855
3856
#ifdef ZEND_INTRIN_SSE4_2_NATIVE
3857
# include <nmmintrin.h>
3858
# include "Zend/zend_bitset.h"
3859
#elif defined(ZEND_INTRIN_SSE4_2_RESOLVER)
3860
# include <nmmintrin.h>
3861
# include "Zend/zend_bitset.h"
3862
# include "Zend/zend_cpuinfo.h"
3863
3864
ZEND_INTRIN_SSE4_2_FUNC_DECL(zend_string *php_addslashes_sse42(zend_string *str));
3865
zend_string *php_addslashes_default(zend_string *str);
3866
3867
# ifdef ZEND_INTRIN_SSE4_2_FUNC_PROTO
3868
PHPAPI zend_string *php_addslashes(zend_string *str) __attribute__((ifunc("resolve_addslashes")));
3869
3870
typedef zend_string *(*php_addslashes_func_t)(zend_string *);
3871
3872
ZEND_NO_SANITIZE_ADDRESS
3873
ZEND_ATTRIBUTE_UNUSED /* clang mistakenly warns about this */
3874
2
static php_addslashes_func_t resolve_addslashes(void) {
3875
2
  if (zend_cpu_supports_sse42()) {
3876
2
    return php_addslashes_sse42;
3877
2
  }
3878
0
  return php_addslashes_default;
3879
2
}
3880
# else /* ZEND_INTRIN_SSE4_2_FUNC_PTR */
3881
3882
static zend_string *(*php_addslashes_ptr)(zend_string *str) = NULL;
3883
3884
PHPAPI zend_string *php_addslashes(zend_string *str) {
3885
  return php_addslashes_ptr(str);
3886
}
3887
3888
/* {{{ PHP_MINIT_FUNCTION */
3889
PHP_MINIT_FUNCTION(string_intrin)
3890
{
3891
  if (zend_cpu_supports_sse42()) {
3892
    php_addslashes_ptr = php_addslashes_sse42;
3893
  } else {
3894
    php_addslashes_ptr = php_addslashes_default;
3895
  }
3896
  return SUCCESS;
3897
}
3898
/* }}} */
3899
# endif
3900
#endif
3901
3902
#if defined(ZEND_INTRIN_SSE4_2_NATIVE) || defined(ZEND_INTRIN_SSE4_2_RESOLVER)
3903
# ifdef ZEND_INTRIN_SSE4_2_NATIVE
3904
PHPAPI zend_string *php_addslashes(zend_string *str) /* {{{ */
3905
# elif defined(ZEND_INTRIN_SSE4_2_RESOLVER)
3906
zend_string *php_addslashes_sse42(zend_string *str)
3907
# endif
3908
0
{
3909
0
  ZEND_SET_ALIGNED(16, static const char slashchars[16]) = "\'\"\\\0";
3910
0
  __m128i w128, s128;
3911
0
  uint32_t res = 0;
3912
  /* maximum string length, worst case situation */
3913
0
  char *target;
3914
0
  const char *source, *end;
3915
0
  size_t offset;
3916
0
  zend_string *new_str;
3917
3918
0
  if (!str) {
3919
0
    return ZSTR_EMPTY_ALLOC();
3920
0
  }
3921
3922
0
  source = ZSTR_VAL(str);
3923
0
  end = source + ZSTR_LEN(str);
3924
3925
0
  if (ZSTR_LEN(str) > 15) {
3926
0
    w128 = _mm_load_si128((__m128i *)slashchars);
3927
0
    do {
3928
0
      s128 = _mm_loadu_si128((__m128i *)source);
3929
0
      res = _mm_cvtsi128_si32(_mm_cmpestrm(w128, 4, s128, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK));
3930
0
      if (res) {
3931
0
        goto do_escape;
3932
0
      }
3933
0
      source += 16;
3934
0
    } while ((end - source) > 15);
3935
0
  }
3936
3937
0
  while (source < end) {
3938
0
    switch (*source) {
3939
0
      case '\0':
3940
0
      case '\'':
3941
0
      case '\"':
3942
0
      case '\\':
3943
0
        goto do_escape;
3944
0
      default:
3945
0
        source++;
3946
0
        break;
3947
0
    }
3948
0
  }
3949
3950
0
  return zend_string_copy(str);
3951
3952
0
do_escape:
3953
0
  offset = source - (char *)ZSTR_VAL(str);
3954
0
  new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0);
3955
0
  memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset);
3956
0
  target = ZSTR_VAL(new_str) + offset;
3957
3958
0
  if (res) {
3959
0
    int pos = 0;
3960
0
    do {
3961
0
      int i, n = zend_ulong_ntz(res);
3962
0
      for (i = 0; i < n; i++) {
3963
0
        *target++ = source[pos + i];
3964
0
      }
3965
0
      pos += n;
3966
0
      *target++ = '\\';
3967
0
      if (source[pos] == '\0') {
3968
0
        *target++ = '0';
3969
0
      } else {
3970
0
        *target++ = source[pos];
3971
0
      }
3972
0
      pos++;
3973
0
      res = res >> (n + 1);
3974
0
    } while (res);
3975
3976
0
    for (; pos < 16; pos++) {
3977
0
      *target++ = source[pos];
3978
0
    }
3979
0
    source += 16;
3980
0
  } else if (end - source > 15) {
3981
0
    w128 = _mm_load_si128((__m128i *)slashchars);
3982
0
  }
3983
3984
0
  for (; end - source > 15; source += 16) {
3985
0
    int pos = 0;
3986
0
    s128 = _mm_loadu_si128((__m128i *)source);
3987
0
    res = _mm_cvtsi128_si32(_mm_cmpestrm(w128, 4, s128, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK));
3988
0
    if (res) {
3989
0
      do {
3990
0
        int i, n = zend_ulong_ntz(res);
3991
0
        for (i = 0; i < n; i++) {
3992
0
          *target++ = source[pos + i];
3993
0
        }
3994
0
        pos += n;
3995
0
        *target++ = '\\';
3996
0
        if (source[pos] == '\0') {
3997
0
          *target++ = '0';
3998
0
        } else {
3999
0
          *target++ = source[pos];
4000
0
        }
4001
0
        pos++;
4002
0
        res = res >> (n + 1);
4003
0
      } while (res);
4004
0
      for (; pos < 16; pos++) {
4005
0
        *target++ = source[pos];
4006
0
      }
4007
0
    } else {
4008
0
      _mm_storeu_si128((__m128i*)target, s128);
4009
0
      target += 16;
4010
0
    }
4011
0
  }
4012
4013
0
  while (source < end) {
4014
0
    switch (*source) {
4015
0
      case '\0':
4016
0
        *target++ = '\\';
4017
0
        *target++ = '0';
4018
0
        break;
4019
0
      case '\'':
4020
0
      case '\"':
4021
0
      case '\\':
4022
0
        *target++ = '\\';
4023
0
        ZEND_FALLTHROUGH;
4024
0
      default:
4025
0
        *target++ = *source;
4026
0
        break;
4027
0
    }
4028
0
    source++;
4029
0
  }
4030
4031
0
  *target = '\0';
4032
4033
0
  if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) {
4034
0
    new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0);
4035
0
  } else {
4036
0
    ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str);
4037
0
  }
4038
4039
0
  return new_str;
4040
0
}
4041
/* }}} */
4042
#endif
4043
4044
#if defined(__aarch64__) || defined(_M_ARM64)
4045
typedef union {
4046
  uint8_t mem[16];
4047
  uint64_t dw[2];
4048
} quad_word;
4049
4050
static zend_always_inline quad_word aarch64_contains_slash_chars(uint8x16_t x) {
4051
  uint8x16_t s0 = vceqq_u8(x, vdupq_n_u8('\0'));
4052
  uint8x16_t s1 = vceqq_u8(x, vdupq_n_u8('\''));
4053
  uint8x16_t s2 = vceqq_u8(x, vdupq_n_u8('\"'));
4054
  uint8x16_t s3 = vceqq_u8(x, vdupq_n_u8('\\'));
4055
  uint8x16_t s01 = vorrq_u8(s0, s1);
4056
  uint8x16_t s23 = vorrq_u8(s2, s3);
4057
  uint8x16_t s0123 = vorrq_u8(s01, s23);
4058
  quad_word qw;
4059
  vst1q_u8(qw.mem, s0123);
4060
  return qw;
4061
}
4062
4063
static zend_always_inline char *aarch64_add_slashes(quad_word res, const char *source, char *target)
4064
{
4065
  for (int i = 0; i < 16; i++) {
4066
    char s = source[i];
4067
    if (res.mem[i] == 0)
4068
      *target++ = s;
4069
    else {
4070
      *target++ = '\\';
4071
      if (s == '\0')
4072
        *target++ = '0';
4073
      else
4074
        *target++ = s;
4075
    }
4076
  }
4077
  return target;
4078
}
4079
#endif /* defined(__aarch64__) || defined(_M_ARM64) */
4080
4081
#ifndef ZEND_INTRIN_SSE4_2_NATIVE
4082
# ifdef ZEND_INTRIN_SSE4_2_RESOLVER
4083
zend_string *php_addslashes_default(zend_string *str) /* {{{ */
4084
# else
4085
PHPAPI zend_string *php_addslashes(zend_string *str)
4086
# endif
4087
0
{
4088
  /* maximum string length, worst case situation */
4089
0
  char *target;
4090
0
  const char *source, *end;
4091
0
  size_t offset;
4092
0
  zend_string *new_str;
4093
4094
0
  if (!str) {
4095
0
    return ZSTR_EMPTY_ALLOC();
4096
0
  }
4097
4098
0
  source = ZSTR_VAL(str);
4099
0
  end = source + ZSTR_LEN(str);
4100
4101
# if defined(__aarch64__) || defined(_M_ARM64)
4102
  quad_word res = {0};
4103
  if (ZSTR_LEN(str) > 15) {
4104
    do {
4105
      res = aarch64_contains_slash_chars(vld1q_u8((uint8_t *)source));
4106
      if (res.dw[0] | res.dw[1])
4107
        goto do_escape;
4108
      source += 16;
4109
    } while ((end - source) > 15);
4110
  }
4111
  /* Finish the last 15 bytes or less with the scalar loop. */
4112
# endif /* defined(__aarch64__) || defined(_M_ARM64) */
4113
4114
0
  while (source < end) {
4115
0
    switch (*source) {
4116
0
      case '\0':
4117
0
      case '\'':
4118
0
      case '\"':
4119
0
      case '\\':
4120
0
        goto do_escape;
4121
0
      default:
4122
0
        source++;
4123
0
        break;
4124
0
    }
4125
0
  }
4126
4127
0
  return zend_string_copy(str);
4128
4129
0
do_escape:
4130
0
  offset = source - (char *)ZSTR_VAL(str);
4131
0
  new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0);
4132
0
  memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset);
4133
0
  target = ZSTR_VAL(new_str) + offset;
4134
4135
# if defined(__aarch64__) || defined(_M_ARM64)
4136
  if (res.dw[0] | res.dw[1]) {
4137
    target = aarch64_add_slashes(res, source, target);
4138
    source += 16;
4139
  }
4140
  for (; end - source > 15; source += 16) {
4141
    uint8x16_t x = vld1q_u8((uint8_t *)source);
4142
    res = aarch64_contains_slash_chars(x);
4143
    if (res.dw[0] | res.dw[1]) {
4144
      target = aarch64_add_slashes(res, source, target);
4145
    } else {
4146
      vst1q_u8((uint8_t*)target, x);
4147
      target += 16;
4148
    }
4149
  }
4150
  /* Finish the last 15 bytes or less with the scalar loop. */
4151
# endif /* defined(__aarch64__) || defined(_M_ARM64) */
4152
4153
0
  while (source < end) {
4154
0
    switch (*source) {
4155
0
      case '\0':
4156
0
        *target++ = '\\';
4157
0
        *target++ = '0';
4158
0
        break;
4159
0
      case '\'':
4160
0
      case '\"':
4161
0
      case '\\':
4162
0
        *target++ = '\\';
4163
0
        ZEND_FALLTHROUGH;
4164
0
      default:
4165
0
        *target++ = *source;
4166
0
        break;
4167
0
    }
4168
0
    source++;
4169
0
  }
4170
4171
0
  *target = '\0';
4172
4173
0
  if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) {
4174
0
    new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0);
4175
0
  } else {
4176
0
    ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str);
4177
0
  }
4178
4179
0
  return new_str;
4180
0
}
4181
#endif
4182
/* }}} */
4183
/* }}} */
4184
4185
/* {{{ php_stripslashes
4186
 *
4187
 * be careful, this edits the string in-place */
4188
static zend_always_inline char *php_stripslashes_impl(const char *str, char *out, size_t len)
4189
0
{
4190
#if defined(__aarch64__) || defined(_M_ARM64)
4191
  while (len > 15) {
4192
    uint8x16_t x = vld1q_u8((uint8_t *)str);
4193
    quad_word q;
4194
    vst1q_u8(q.mem, vceqq_u8(x, vdupq_n_u8('\\')));
4195
    if (q.dw[0] | q.dw[1]) {
4196
      unsigned int i = 0;
4197
      while (i < 16) {
4198
        if (q.mem[i] == 0) {
4199
          *out++ = str[i];
4200
          i++;
4201
          continue;
4202
        }
4203
4204
        i++;      /* skip the slash */
4205
        if (i < len) {
4206
          char s = str[i];
4207
          if (s == '0')
4208
            *out++ = '\0';
4209
          else
4210
            *out++ = s; /* preserve the next character */
4211
          i++;
4212
        }
4213
      }
4214
      str += i;
4215
      len -= i;
4216
    } else {
4217
      vst1q_u8((uint8_t*)out, x);
4218
      out += 16;
4219
      str += 16;
4220
      len -= 16;
4221
    }
4222
  }
4223
  /* Finish the last 15 bytes or less with the scalar loop. */
4224
#endif /* defined(__aarch64__) || defined(_M_ARM64) */
4225
0
  while (len > 0) {
4226
0
    if (*str == '\\') {
4227
0
      str++;        /* skip the slash */
4228
0
      len--;
4229
0
      if (len > 0) {
4230
0
        if (*str == '0') {
4231
0
          *out++='\0';
4232
0
          str++;
4233
0
        } else {
4234
0
          *out++ = *str++;  /* preserve the next character */
4235
0
        }
4236
0
        len--;
4237
0
      }
4238
0
    } else {
4239
0
      *out++ = *str++;
4240
0
      len--;
4241
0
    }
4242
0
  }
4243
4244
0
  return out;
4245
0
}
4246
4247
#ifdef __SSE2__
4248
PHPAPI void php_stripslashes(zend_string *str)
4249
0
{
4250
0
  const char *s = ZSTR_VAL(str);
4251
0
  char *t = ZSTR_VAL(str);
4252
0
  size_t l = ZSTR_LEN(str);
4253
4254
0
  if (l > 15) {
4255
0
    const __m128i slash = _mm_set1_epi8('\\');
4256
4257
0
    do {
4258
0
      __m128i in = _mm_loadu_si128((__m128i *)s);
4259
0
      __m128i any_slash = _mm_cmpeq_epi8(in, slash);
4260
0
      uint32_t res = _mm_movemask_epi8(any_slash);
4261
4262
0
      if (res) {
4263
0
        int i, n = zend_ulong_ntz(res);
4264
0
        const char *e = s + 15;
4265
0
        l -= n;
4266
0
        for (i = 0; i < n; i++) {
4267
0
          *t++ = *s++;
4268
0
        }
4269
0
        for (; s < e; s++) {
4270
0
          if (*s == '\\') {
4271
0
            s++;
4272
0
            l--;
4273
0
            if (*s == '0') {
4274
0
              *t = '\0';
4275
0
            } else {
4276
0
              *t = *s;
4277
0
            }
4278
0
          } else {
4279
0
            *t = *s;
4280
0
          }
4281
0
          t++;
4282
0
          l--;
4283
0
        }
4284
0
      } else {
4285
0
        _mm_storeu_si128((__m128i *)t, in);
4286
0
        s += 16;
4287
0
        t += 16;
4288
0
        l -= 16;
4289
0
      }
4290
0
    } while (l > 15);
4291
0
  }
4292
4293
0
  t = php_stripslashes_impl(s, t, l);
4294
0
  if (t != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
4295
0
    ZSTR_LEN(str) = t - ZSTR_VAL(str);
4296
0
    ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
4297
0
  }
4298
0
}
4299
#else
4300
PHPAPI void php_stripslashes(zend_string *str)
4301
{
4302
  const char *t = php_stripslashes_impl(ZSTR_VAL(str), ZSTR_VAL(str), ZSTR_LEN(str));
4303
  if (t != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
4304
    ZSTR_LEN(str) = t - ZSTR_VAL(str);
4305
    ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
4306
  }
4307
}
4308
#endif
4309
/* }}} */
4310
4311
0
#define _HEB_BLOCK_TYPE_ENG 1
4312
0
#define _HEB_BLOCK_TYPE_HEB 2
4313
0
#define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
4314
0
#define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
4315
0
#define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
4316
4317
/* {{{ php_str_replace_in_subject */
4318
static zend_long php_str_replace_in_subject(
4319
  zend_string *search_str, HashTable *search_ht, zend_string *replace_str, HashTable *replace_ht,
4320
  zend_string *subject_str, zval *result, bool case_sensitivity
4321
1.09k
) {
4322
1.09k
  zval    *search_entry;
4323
1.09k
  zend_string *tmp_result;
4324
1.09k
  char    *replace_value = NULL;
4325
1.09k
  size_t     replace_len = 0;
4326
1.09k
  zend_long  replace_count = 0;
4327
1.09k
  zend_string *lc_subject_str = NULL;
4328
1.09k
  uint32_t     replace_idx;
4329
4330
1.09k
  if (ZSTR_LEN(subject_str) == 0) {
4331
116
    ZVAL_EMPTY_STRING(result);
4332
116
    return 0;
4333
116
  }
4334
4335
  /* If search is an array */
4336
982
  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
982
  } else {
4452
982
    ZEND_ASSERT(search_str);
4453
982
    if (ZSTR_LEN(search_str) == 1) {
4454
365
      ZVAL_STR(result,
4455
365
        php_char_to_str_ex(subject_str,
4456
365
              ZSTR_VAL(search_str)[0],
4457
365
              ZSTR_VAL(replace_str),
4458
365
              ZSTR_LEN(replace_str),
4459
365
              case_sensitivity,
4460
365
              &replace_count));
4461
617
    } else if (ZSTR_LEN(search_str) > 1) {
4462
617
      if (case_sensitivity) {
4463
617
        ZVAL_STR(result, php_str_to_str_ex(subject_str,
4464
617
            ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4465
617
            ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4466
617
      } else {
4467
0
        lc_subject_str = zend_string_tolower(subject_str);
4468
0
        ZVAL_STR(result, php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str),
4469
0
            search_str, ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count));
4470
0
        zend_string_release_ex(lc_subject_str, 0);
4471
0
      }
4472
617
    } else {
4473
0
      ZVAL_STR_COPY(result, subject_str);
4474
0
    }
4475
982
  }
4476
982
  return replace_count;
4477
982
}
4478
/* }}} */
4479
4480
static void _php_str_replace_common(
4481
  zval *return_value,
4482
  HashTable *search_ht, zend_string *search_str,
4483
  HashTable *replace_ht, zend_string *replace_str,
4484
  HashTable *subject_ht, zend_string *subject_str,
4485
  zval *zcount,
4486
  bool case_sensitivity
4487
1.09k
) {
4488
1.09k
  zval *subject_entry;
4489
1.09k
  zval result;
4490
1.09k
  zend_string *string_key;
4491
1.09k
  zend_ulong num_key;
4492
1.09k
  zend_long count = 0;
4493
4494
  /* Make sure we're dealing with strings and do the replacement. */
4495
1.09k
  if (search_str && replace_ht) {
4496
0
    zend_argument_type_error(2, "must be of type string when argument #1 ($search) is a string");
4497
0
    RETURN_THROWS();
4498
0
  }
4499
4500
  /* if subject is an array */
4501
1.09k
  if (subject_ht) {
4502
3
    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
9
    ZEND_HASH_FOREACH_KEY_VAL(subject_ht, num_key, string_key, subject_entry) {
4507
9
      zend_string *tmp_subject_str;
4508
9
      ZVAL_DEREF(subject_entry);
4509
9
      subject_str = zval_get_tmp_string(subject_entry, &tmp_subject_str);
4510
9
      count += php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, &result, case_sensitivity);
4511
9
      zend_tmp_string_release(tmp_subject_str);
4512
4513
      /* Add to return array */
4514
9
      if (string_key) {
4515
0
        zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, &result);
4516
3
      } else {
4517
3
        zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
4518
3
      }
4519
9
    } ZEND_HASH_FOREACH_END();
4520
1.09k
  } else { /* if subject is not an array */
4521
1.09k
    count = php_str_replace_in_subject(search_str, search_ht, replace_str, replace_ht, subject_str, return_value, case_sensitivity);
4522
1.09k
  }
4523
1.09k
  if (zcount) {
4524
21
    ZEND_TRY_ASSIGN_REF_LONG(zcount, count);
4525
21
  }
4526
1.09k
}
4527
4528
/* {{{ php_str_replace_common */
4529
static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool case_sensitivity)
4530
1.10k
{
4531
1.10k
  zend_string *search_str;
4532
1.10k
  HashTable *search_ht;
4533
1.10k
  zend_string *replace_str;
4534
1.10k
  HashTable *replace_ht;
4535
1.10k
  zend_string *subject_str;
4536
1.10k
  HashTable *subject_ht;
4537
1.10k
  zval *zcount = NULL;
4538
4539
3.30k
  ZEND_PARSE_PARAMETERS_START(3, 4)
4540
5.49k
    Z_PARAM_ARRAY_HT_OR_STR(search_ht, search_str)
4541
5.49k
    Z_PARAM_ARRAY_HT_OR_STR(replace_ht, replace_str)
4542
5.49k
    Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
4543
5.49k
    Z_PARAM_OPTIONAL
4544
5.49k
    Z_PARAM_ZVAL(zcount)
4545
2.23k
  ZEND_PARSE_PARAMETERS_END();
4546
4547
1.09k
  _php_str_replace_common(return_value, search_ht, search_str, replace_ht, replace_str, subject_ht, subject_str, zcount, case_sensitivity);
4548
1.09k
}
4549
/* }}} */
4550
4551
/* {{{ Replaces all occurrences of search in haystack with replace */
4552
PHP_FUNCTION(str_replace)
4553
1.10k
{
4554
1.10k
  php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4555
1.10k
}
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
0
{
4579
0
  php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4580
0
}
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
787
{
4829
787
  zend_string *buf;
4830
787
  zend_string *str;
4831
787
  zend_string *allow_str = NULL;
4832
787
  HashTable *allow_ht = NULL;
4833
787
  const char *allowed_tags=NULL;
4834
787
  size_t allowed_tags_len=0;
4835
787
  smart_str tags_ss = {0};
4836
4837
2.35k
  ZEND_PARSE_PARAMETERS_START(1, 2)
4838
3.13k
    Z_PARAM_STR(str)
4839
784
    Z_PARAM_OPTIONAL
4840
1.71k
    Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(allow_ht, allow_str)
4841
1.71k
  ZEND_PARSE_PARAMETERS_END();
4842
4843
782
  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
782
  } else if (allow_str) {
4860
51
    allowed_tags = ZSTR_VAL(allow_str);
4861
51
    allowed_tags_len = ZSTR_LEN(allow_str);
4862
51
  }
4863
4864
782
  buf = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
4865
782
  ZSTR_LEN(buf) = php_strip_tags_ex(ZSTR_VAL(buf), ZSTR_LEN(str), allowed_tags, allowed_tags_len, 0);
4866
782
  smart_str_free(&tags_ss);
4867
782
  RETURN_NEW_STR(buf);
4868
782
}
4869
/* }}} */
4870
4871
6
static zend_string *try_setlocale_str(zend_long cat, zend_string *loc) {
4872
6
  const char *retval;
4873
4874
6
  if (zend_string_equals_literal(loc, "0")) {
4875
0
    loc = NULL;
4876
6
  } else {
4877
6
    if (ZSTR_LEN(loc) >= 255) {
4878
0
      php_error_docref(NULL, E_WARNING, "Specified locale name is too long");
4879
0
      return NULL;
4880
0
    }
4881
6
  }
4882
4883
6
# ifndef PHP_WIN32
4884
6
  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
6
  if (!retval) {
4906
6
    return NULL;
4907
6
  }
4908
4909
0
  if (loc) {
4910
    /* Remember if locale was changed */
4911
0
    size_t len = strlen(retval);
4912
4913
0
    BG(locale_changed) = 1;
4914
0
    if (cat == LC_CTYPE || cat == LC_ALL) {
4915
0
      zend_update_current_locale();
4916
0
      if (BG(ctype_string)) {
4917
0
        zend_string_release_ex(BG(ctype_string), 0);
4918
0
      }
4919
0
      if (len == 1 && *retval == 'C') {
4920
        /* C locale is represented as NULL. */
4921
0
        BG(ctype_string) = NULL;
4922
0
        return ZSTR_CHAR('C');
4923
0
      } 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
0
    } else if (zend_string_equals_cstr(loc, retval, len)) {
4931
0
      return zend_string_copy(loc);
4932
0
    }
4933
0
  }
4934
0
  return zend_string_init(retval, strlen(retval), 0);
4935
0
}
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
6
{
4951
6
  zend_long cat;
4952
6
  zval *args = NULL;
4953
6
  uint32_t num_args;
4954
6
  ALLOCA_FLAG(use_heap);
4955
4956
18
  ZEND_PARSE_PARAMETERS_START(2, -1)
4957
24
    Z_PARAM_LONG(cat)
4958
6
    Z_PARAM_VARIADIC('+', args, num_args)
4959
6
  ZEND_PARSE_PARAMETERS_END();
4960
4961
6
  zend_string **strings = do_alloca(sizeof(zend_string *) * num_args, use_heap);
4962
4963
12
  for (uint32_t i = 0; i < num_args; i++) {
4964
6
    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
6
  }
4969
4970
12
  for (uint32_t i = 0; i < num_args; i++) {
4971
6
    zend_string *result;
4972
6
    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
6
    } else if (Z_ISNULL(args[i])) {
4986
0
      result = try_setlocale_str(cat, ZSTR_EMPTY_ALLOC());
4987
6
    } else {
4988
6
      result = try_setlocale_str(cat, strings[i]);
4989
6
    }
4990
6
    if (EG(exception)) {
4991
0
      goto out;
4992
0
    }
4993
6
    if (result) {
4994
0
      RETVAL_STR(result);
4995
0
      goto out;
4996
0
    }
4997
6
  }
4998
4999
6
  RETVAL_FALSE;
5000
5001
6
out:
5002
6
  free_alloca(strings, use_heap);
5003
6
}
5004
/* }}} */
5005
5006
/* {{{ Parses GET/POST/COOKIE data and sets global variables */
5007
PHP_FUNCTION(parse_str)
5008
6
{
5009
6
  char *arg;
5010
6
  zval *arrayArg = NULL;
5011
6
  char *res = NULL;
5012
6
  size_t arglen;
5013
5014
18
  ZEND_PARSE_PARAMETERS_START(2, 2)
5015
24
    Z_PARAM_PATH(arg, arglen)
5016
30
    Z_PARAM_ZVAL(arrayArg)
5017
30
  ZEND_PARSE_PARAMETERS_END();
5018
5019
6
  arrayArg = zend_try_array_init(arrayArg);
5020
6
  if (!arrayArg) {
5021
0
    RETURN_THROWS();
5022
0
  }
5023
5024
6
  res = estrndup(arg, arglen);
5025
6
  sapi_module.treat_data(PARSE_STRING, res, arrayArg);
5026
6
}
5027
/* }}} */
5028
5029
9.06k
#define PHP_TAG_BUF_SIZE 1023
5030
5031
/* {{{ php_tag_find
5032
 *
5033
 * Check if tag is in a set of tags
5034
 *
5035
 * states:
5036
 *
5037
 * 0 start tag
5038
 * 1 first non-whitespace char seen
5039
 */
5040
42
static bool php_tag_find(char *tag, size_t len, const char *set) {
5041
42
  char c, *n;
5042
42
  const char *t;
5043
42
  int state = 0;
5044
42
  bool done = false;
5045
42
  char *norm;
5046
5047
42
  if (len == 0) {
5048
0
    return false;
5049
0
  }
5050
5051
42
  norm = emalloc(len+1);
5052
5053
42
  n = norm;
5054
42
  t = tag;
5055
42
  c = zend_tolower_ascii(*t);
5056
  /*
5057
     normalize the tag removing leading and trailing whitespace
5058
     and turn any <a whatever...> into just <a> and any </tag>
5059
     into <tag>
5060
  */
5061
408
  while (!done) {
5062
366
    switch (c) {
5063
42
      case '<':
5064
42
        *(n++) = c;
5065
42
        break;
5066
33
      case '>':
5067
33
        done = true;
5068
33
        break;
5069
291
      default:
5070
291
        if (!isspace((unsigned char)c)) {
5071
282
          if (state == 0) {
5072
42
            state=1;
5073
42
          }
5074
282
          if (c != '/' || (*(t-1) != '<' && *(t+1) != '>')) {
5075
282
            *(n++) = c;
5076
282
          }
5077
282
        } else {
5078
9
          if (state == 1)
5079
9
            done = true;
5080
9
        }
5081
291
        break;
5082
366
    }
5083
366
    c = zend_tolower_ascii(*(++t));
5084
366
  }
5085
42
  *(n++) = '>';
5086
42
  *n = '\0';
5087
42
  if (strstr(set, norm)) {
5088
9
    done = true;
5089
33
  } else {
5090
33
    done = false;
5091
33
  }
5092
42
  efree(norm);
5093
42
  return done;
5094
42
}
5095
/* }}} */
5096
5097
PHPAPI size_t php_strip_tags(char *rbuf, size_t len, const char *allow, size_t allow_len) /* {{{ */
5098
0
{
5099
0
  return php_strip_tags_ex(rbuf, len, allow, allow_len, false);
5100
0
}
5101
/* }}} */
5102
5103
/* {{{ php_strip_tags
5104
5105
  A simple little state-machine to strip out html and php tags
5106
5107
  State 0 is the output state, State 1 means we are inside a
5108
  normal html tag and state 2 means we are inside a php tag.
5109
5110
  The state variable is passed in to allow a function like fgetss
5111
  to maintain state across calls to the function.
5112
5113
  lc holds the last significant character read and br is a bracket
5114
  counter.
5115
5116
  When an allow string is passed in we keep track of the string
5117
  in state 1 and when the tag is closed check it against the
5118
  allow string to see if we should allow it.
5119
5120
  swm: Added ability to strip <?xml tags without assuming it PHP
5121
  code.
5122
*/
5123
PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, const char *allow, size_t allow_len, bool allow_tag_spaces)
5124
782
{
5125
782
  char *tbuf, *tp, *rp, c, lc;
5126
782
  const char *buf, *p, *end;
5127
782
  int br, depth=0, in_q = 0;
5128
782
  uint8_t state = 0;
5129
782
  size_t pos;
5130
782
  char *allow_free = NULL;
5131
782
  char is_xml = 0;
5132
5133
782
  buf = estrndup(rbuf, len);
5134
782
  end = buf + len;
5135
782
  lc = '\0';
5136
782
  p = buf;
5137
782
  rp = rbuf;
5138
782
  br = 0;
5139
782
  if (allow) {
5140
51
    allow_free = zend_str_tolower_dup_ex(allow, allow_len);
5141
51
    allow = allow_free ? allow_free : allow;
5142
51
    tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
5143
51
    tp = tbuf;
5144
731
  } else {
5145
731
    tbuf = tp = NULL;
5146
731
  }
5147
5148
405k
state_0:
5149
405k
  if (p >= end) {
5150
380
    goto finish;
5151
380
  }
5152
405k
  c = *p;
5153
405k
  switch (c) {
5154
31.0k
    case '\0':
5155
31.0k
      break;
5156
34.7k
    case '<':
5157
34.7k
      if (in_q) {
5158
0
        break;
5159
0
      }
5160
34.7k
      if (isspace((unsigned char)p[1]) && !allow_tag_spaces) {
5161
3
        *(rp++) = c;
5162
3
        break;
5163
3
      }
5164
34.7k
      lc = '<';
5165
34.7k
      state = 1;
5166
34.7k
      if (allow) {
5167
96
        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
96
        *(tp++) = '<';
5173
96
      }
5174
34.7k
      p++;
5175
34.7k
      goto state_1;
5176
261
    case '>':
5177
261
      if (depth) {
5178
0
        depth--;
5179
0
        break;
5180
0
      }
5181
5182
261
      if (in_q) {
5183
0
        break;
5184
0
      }
5185
5186
261
      *(rp++) = c;
5187
261
      break;
5188
339k
    default:
5189
339k
      *(rp++) = c;
5190
339k
      break;
5191
405k
  }
5192
370k
  p++;
5193
370k
  goto state_0;
5194
5195
618k
state_1:
5196
618k
  if (p >= end) {
5197
146
    goto finish;
5198
146
  }
5199
618k
  c = *p;
5200
618k
  switch (c) {
5201
4.14k
    case '\0':
5202
4.14k
      break;
5203
1.40k
    case '<':
5204
1.40k
      if (in_q) {
5205
552
        break;
5206
552
      }
5207
852
      if (isspace((unsigned char)p[1]) && !allow_tag_spaces) {
5208
6
        goto reg_char_1;
5209
6
      }
5210
846
      depth++;
5211
846
      break;
5212
34.8k
    case '>':
5213
34.8k
      if (depth) {
5214
510
        depth--;
5215
510
        break;
5216
510
      }
5217
34.3k
      if (in_q) {
5218
126
        break;
5219
126
      }
5220
5221
34.2k
      lc = '>';
5222
34.2k
      if (is_xml && p >= buf + 1 && *(p -1) == '-') {
5223
0
        break;
5224
0
      }
5225
34.2k
      in_q = state = is_xml = 0;
5226
34.2k
      if (allow) {
5227
42
        if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
5228
0
          pos = tp - tbuf;
5229
0
          tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
5230
0
          tp = tbuf + pos;
5231
0
        }
5232
42
        *(tp++) = '>';
5233
42
        *tp='\0';
5234
42
        if (php_tag_find(tbuf, tp-tbuf, allow)) {
5235
9
          rp = zend_mempcpy(rp, tbuf, tp - tbuf);
5236
9
        }
5237
42
        tp = tbuf;
5238
42
      }
5239
34.2k
      p++;
5240
34.2k
      goto state_0;
5241
33.6k
    case '"':
5242
33.9k
    case '\'':
5243
33.9k
      if (p != buf && (!in_q || *p == in_q)) {
5244
33.9k
        if (in_q) {
5245
16.9k
          in_q = 0;
5246
17.0k
        } else {
5247
17.0k
          in_q = *p;
5248
17.0k
        }
5249
33.9k
      }
5250
33.9k
      goto reg_char_1;
5251
408
    case '!':
5252
      /* JavaScript & Other HTML scripting languages */
5253
408
      if (p >= buf + 1 && *(p-1) == '<') {
5254
138
        state = 3;
5255
138
        lc = c;
5256
138
        p++;
5257
138
        goto state_3;
5258
270
      } else {
5259
270
        goto reg_char_1;
5260
270
      }
5261
0
      break;
5262
781
    case '?':
5263
781
      if (p >= buf + 1 && *(p-1) == '<') {
5264
223
        br=0;
5265
223
        state = 2;
5266
223
        p++;
5267
223
        goto state_2;
5268
558
      } else {
5269
558
        goto reg_char_1;
5270
558
      }
5271
0
      break;
5272
542k
    default:
5273
577k
reg_char_1:
5274
577k
      if (allow) {
5275
8.92k
        if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
5276
1.59k
          pos = tp - tbuf;
5277
1.59k
          tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
5278
1.59k
          tp = tbuf + pos;
5279
1.59k
        }
5280
8.92k
        *(tp++) = c;
5281
8.92k
      }
5282
577k
      break;
5283
618k
  }
5284
583k
  p++;
5285
583k
  goto state_1;
5286
5287
125k
state_2:
5288
125k
  if (p >= end) {
5289
199
    goto finish;
5290
199
  }
5291
125k
  c = *p;
5292
125k
  switch (c) {
5293
1.38k
    case '(':
5294
1.38k
      if (lc != '"' && lc != '\'') {
5295
917
        lc = '(';
5296
917
        br++;
5297
917
      }
5298
1.38k
      break;
5299
946
    case ')':
5300
946
      if (lc != '"' && lc != '\'') {
5301
648
        lc = ')';
5302
648
        br--;
5303
648
      }
5304
946
      break;
5305
938
    case '>':
5306
938
      if (depth) {
5307
150
        depth--;
5308
150
        break;
5309
150
      }
5310
788
      if (in_q) {
5311
314
        break;
5312
314
      }
5313
5314
474
      if (!br && p >= buf + 1 && lc != '\"' && *(p-1) == '?') {
5315
24
        in_q = state = 0;
5316
24
        tp = tbuf;
5317
24
        p++;
5318
24
        goto state_0;
5319
24
      }
5320
450
      break;
5321
784
    case '"':
5322
1.07k
    case '\'':
5323
1.07k
      if (p >= buf + 1 && *(p-1) != '\\') {
5324
1.07k
        if (lc == c) {
5325
474
          lc = '\0';
5326
601
        } else if (lc != '\\') {
5327
601
          lc = c;
5328
601
        }
5329
1.07k
        if (p != buf && (!in_q || *p == in_q)) {
5330
1.07k
          if (in_q) {
5331
480
            in_q = 0;
5332
592
          } else {
5333
592
            in_q = *p;
5334
592
          }
5335
1.07k
        }
5336
1.07k
      }
5337
1.07k
      break;
5338
1.65k
    case 'l':
5339
1.87k
    case 'L':
5340
      /* swm: If we encounter '<?xml' then we shouldn't be in
5341
       * state == 2 (PHP). Switch back to HTML.
5342
       */
5343
1.87k
      if (state == 2 && p > buf+4
5344
1.87k
             && (*(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
1.87k
      break;
5353
119k
    default:
5354
119k
      break;
5355
125k
  }
5356
125k
  p++;
5357
125k
  goto state_2;
5358
5359
15.2k
state_3:
5360
15.2k
  if (p >= end) {
5361
51
    goto finish;
5362
51
  }
5363
15.1k
  c = *p;
5364
15.1k
  switch (c) {
5365
258
    case '>':
5366
258
      if (depth) {
5367
138
        depth--;
5368
138
        break;
5369
138
      }
5370
120
      if (in_q) {
5371
42
        break;
5372
42
      }
5373
78
      in_q = state = 0;
5374
78
      tp = tbuf;
5375
78
      p++;
5376
78
      goto state_0;
5377
0
    case '"':
5378
225
    case '\'':
5379
225
      if (p != buf && *(p-1) != '\\' && (!in_q || *p == in_q)) {
5380
189
        if (in_q) {
5381
96
          in_q = 0;
5382
96
        } else {
5383
93
          in_q = *p;
5384
93
        }
5385
189
      }
5386
225
      break;
5387
288
    case '-':
5388
288
      if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
5389
9
        state = 4;
5390
9
        p++;
5391
9
        goto state_4;
5392
9
      }
5393
279
      break;
5394
279
    case 'E':
5395
327
    case 'e':
5396
      /* !DOCTYPE exception */
5397
327
      if (p > buf+6
5398
327
           && (*(p-1) == 'p' || *(p-1) == 'P')
5399
15
           && (*(p-2) == 'y' || *(p-2) == 'Y')
5400
0
           && (*(p-3) == 't' || *(p-3) == 'T')
5401
0
           && (*(p-4) == 'c' || *(p-4) == 'C')
5402
0
           && (*(p-5) == 'o' || *(p-5) == 'O')
5403
0
           && (*(p-6) == 'd' || *(p-6) == 'D')) {
5404
0
        state = 1;
5405
0
        p++;
5406
0
        goto state_1;
5407
0
      }
5408
327
      break;
5409
14.1k
    default:
5410
14.1k
      break;
5411
15.1k
  }
5412
15.1k
  p++;
5413
15.1k
  goto state_3;
5414
5415
9
state_4:
5416
813
  while (p < end) {
5417
807
    c = *p;
5418
807
    if (c == '>' && !in_q) {
5419
6
      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
6
    }
5426
804
    p++;
5427
804
  }
5428
5429
782
finish:
5430
782
  if (rp < rbuf + len) {
5431
761
    *rp = '\0';
5432
761
  }
5433
782
  efree((void *)buf);
5434
782
  if (tbuf) {
5435
51
    efree(tbuf);
5436
51
  }
5437
782
  if (allow_free) {
5438
48
    efree(allow_free);
5439
48
  }
5440
5441
782
  return (size_t)(rp - rbuf);
5442
9
}
5443
/* }}} */
5444
5445
/* {{{ Parse a CSV string into an array */
5446
PHP_FUNCTION(str_getcsv)
5447
105
{
5448
105
  zend_string *str;
5449
105
  char delimiter = ',', enclosure = '"';
5450
105
  char *delimiter_str = NULL, *enclosure_str = NULL;
5451
105
  size_t delimiter_str_len = 0, enclosure_str_len = 0;
5452
105
  zend_string *escape_str = NULL;
5453
5454
315
  ZEND_PARSE_PARAMETERS_START(1, 4)
5455
420
    Z_PARAM_STR(str)
5456
105
    Z_PARAM_OPTIONAL
5457
396
    Z_PARAM_STRING(delimiter_str, delimiter_str_len)
5458
411
    Z_PARAM_STRING(enclosure_str, enclosure_str_len)
5459
210
    Z_PARAM_STR(escape_str)
5460
105
  ZEND_PARSE_PARAMETERS_END();
5461
5462
105
  if (delimiter_str != NULL) {
5463
    /* Make sure that there is at least one character in string */
5464
93
    if (delimiter_str_len != 1) {
5465
9
      zend_argument_value_error(2, "must be a single character");
5466
9
      RETURN_THROWS();
5467
9
    }
5468
    /* use first character from string */
5469
84
    delimiter = delimiter_str[0];
5470
84
  }
5471
96
  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
93
  int escape_char = php_csv_handle_escape_argument(escape_str, 4);
5481
93
  if (escape_char == PHP_CSV_ESCAPE_ERROR) {
5482
0
    RETURN_THROWS();
5483
0
  }
5484
5485
93
  HashTable *values = php_fgetcsv(NULL, delimiter, enclosure, escape_char, ZSTR_LEN(str), ZSTR_VAL(str));
5486
93
  if (values == NULL) {
5487
0
    values = php_bc_fgetcsv_empty_line();
5488
0
  }
5489
93
  RETURN_ARR(values);
5490
93
}
5491
/* }}} */
5492
5493
/* {{{ Returns the input string repeat mult times */
5494
PHP_FUNCTION(str_repeat)
5495
412
{
5496
412
  zend_string   *input_str;   /* Input string */
5497
412
  zend_long     mult;     /* Multiplier */
5498
412
  zend_string *result;    /* Resulting string */
5499
412
  size_t    result_len;   /* Length of the resulting string */
5500
5501
1.23k
  ZEND_PARSE_PARAMETERS_START(2, 2)
5502
1.63k
    Z_PARAM_STR(input_str)
5503
2.04k
    Z_PARAM_LONG(mult)
5504
412
  ZEND_PARSE_PARAMETERS_END();
5505
5506
409
  if (mult < 0) {
5507
0
    zend_argument_value_error(2, "must be greater than or equal to 0");
5508
0
    RETURN_THROWS();
5509
0
  }
5510
5511
  /* Don't waste our time if it's empty */
5512
  /* ... or if the multiplier is zero */
5513
409
  if (ZSTR_LEN(input_str) == 0 || mult == 0)
5514
12
    RETURN_EMPTY_STRING();
5515
5516
  /* Initialize the result string */
5517
397
  result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
5518
397
  result_len = ZSTR_LEN(input_str) * mult;
5519
397
  ZSTR_COPY_CONCAT_PROPERTIES(result, input_str);
5520
5521
  /* Heavy optimization for situations where input string is 1 byte long */
5522
397
  if (ZSTR_LEN(input_str) == 1) {
5523
347
    memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
5524
347
  } else {
5525
50
    const char *s, *ee;
5526
50
    char *e;
5527
50
    ptrdiff_t l=0;
5528
50
    memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
5529
50
    s = ZSTR_VAL(result);
5530
50
    e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
5531
50
    ee = ZSTR_VAL(result) + result_len;
5532
5533
334
    while (e<ee) {
5534
284
      l = (e-s) < (ee-e) ? (e-s) : (ee-e);
5535
284
      memmove(e, s, l);
5536
284
      e += l;
5537
284
    }
5538
50
  }
5539
5540
397
  ZSTR_VAL(result)[result_len] = '\0';
5541
5542
397
  RETURN_NEW_STR(result);
5543
397
}
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
198
static void php_str_pad_fill(zend_string *result, size_t pad_chars, const char *pad_str, size_t pad_str_len) {
5765
198
  char *p = ZSTR_VAL(result) + ZSTR_LEN(result);
5766
  
5767
198
  if (pad_str_len == 1) {
5768
198
    memset(p, pad_str[0], pad_chars);
5769
198
    ZSTR_LEN(result) += pad_chars;
5770
198
    return;
5771
198
  }
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
216
{
5788
  /* Input arguments */
5789
216
  zend_string *input;       /* Input string */
5790
216
  zend_long pad_length;     /* Length to pad to */
5791
5792
  /* Helper variables */
5793
216
  size_t num_pad_chars;   /* Number of padding characters (total - input size) */
5794
216
  char *pad_str = " "; /* Pointer to padding string */
5795
216
  size_t pad_str_len = 1;
5796
216
  zend_long   pad_type_val = PHP_STR_PAD_RIGHT; /* The padding type value */
5797
216
  size_t left_pad=0, right_pad=0;
5798
216
  zend_string *result = NULL; /* Resulting string */
5799
5800
648
  ZEND_PARSE_PARAMETERS_START(2, 4)
5801
864
    Z_PARAM_STR(input)
5802
1.08k
    Z_PARAM_LONG(pad_length)
5803
216
    Z_PARAM_OPTIONAL
5804
438
    Z_PARAM_STRING(pad_str, pad_str_len)
5805
15
    Z_PARAM_LONG(pad_type_val)
5806
216
  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
216
  if (pad_length < 0  || (size_t)pad_length <= ZSTR_LEN(input)) {
5811
18
    RETURN_STR_COPY(input);
5812
18
  }
5813
5814
198
  if (pad_str_len == 0) {
5815
0
    zend_argument_must_not_be_empty_error(3);
5816
0
    RETURN_THROWS();
5817
0
  }
5818
5819
198
  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
198
  num_pad_chars = pad_length - ZSTR_LEN(input);
5825
198
  result = zend_string_safe_alloc(1, ZSTR_LEN(input), num_pad_chars, 0);
5826
198
  ZSTR_LEN(result) = 0;
5827
5828
  /* We need to figure out the left/right padding lengths. */
5829
198
  switch (pad_type_val) {
5830
195
    case PHP_STR_PAD_RIGHT:
5831
195
      left_pad = 0;
5832
195
      right_pad = num_pad_chars;
5833
195
      break;
5834
5835
3
    case PHP_STR_PAD_LEFT:
5836
3
      left_pad = num_pad_chars;
5837
3
      right_pad = 0;
5838
3
      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
198
  }
5845
5846
  /* First we pad on the left. */
5847
198
  if (left_pad > 0) {
5848
3
    php_str_pad_fill(result, left_pad, pad_str, pad_str_len);
5849
3
  }
5850
5851
  /* Then we copy the input string. */
5852
198
  memcpy(ZSTR_VAL(result) + ZSTR_LEN(result), ZSTR_VAL(input), ZSTR_LEN(input));
5853
198
  ZSTR_LEN(result) += ZSTR_LEN(input);
5854
5855
  /* Finally, we pad on the right. */
5856
198
  if (right_pad > 0) {
5857
195
    php_str_pad_fill(result, right_pad, pad_str, pad_str_len);
5858
195
  }
5859
5860
198
  ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
5861
5862
198
  RETURN_NEW_STR(result);
5863
198
}
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
12
{
5998
12
  const php_random_algo *algo = engine.algo;
5999
12
  void *state = engine.state;
6000
6001
12
  int64_t n_elems, rnd_idx, n_left;
6002
12
  char temp;
6003
6004
  /* The implementation is stolen from array_data_shuffle       */
6005
  /* Thus the characteristics of the randomization are the same */
6006
12
  n_elems = len;
6007
6008
12
  if (n_elems <= 1) {
6009
0
    return true;
6010
0
  }
6011
6012
12
  n_left = n_elems;
6013
6014
948
  while (--n_left) {
6015
936
    rnd_idx = algo->range(state, 0, n_left);
6016
936
    if (EG(exception)) {
6017
0
      return false;
6018
0
    }
6019
936
    if (rnd_idx != n_left) {
6020
887
      temp = str[n_left];
6021
887
      str[n_left] = str[rnd_idx];
6022
887
      str[rnd_idx] = temp;
6023
887
    }
6024
936
  }
6025
6026
12
  return true;
6027
12
}
6028
/* }}} */
6029
6030
/* {{{ Shuffles string. One permutation of all possible is created */
6031
PHP_FUNCTION(str_shuffle)
6032
12
{
6033
12
  zend_string *arg;
6034
6035
36
  ZEND_PARSE_PARAMETERS_START(1, 1)
6036
48
    Z_PARAM_STR(arg)
6037
12
  ZEND_PARSE_PARAMETERS_END();
6038
6039
12
  RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg));
6040
12
  if (Z_STRLEN_P(return_value) > 1) {
6041
12
    php_binary_string_shuffle(
6042
12
      php_random_default_engine(),
6043
12
      Z_STRVAL_P(return_value),
6044
12
      Z_STRLEN_P(return_value)
6045
12
    );
6046
12
  }
6047
12
}
6048
/* }}} */
6049
6050
/* {{{ Counts the number of words inside a string. If format of 1 is specified,
6051
    then the function will return an array containing all the words
6052
    found inside the string. If format of 2 is specified, then the function
6053
    will return an associated array where the position of the word is the key
6054
    and the word itself is the value.
6055
    For the purpose of this function, 'word' is defined as a locale dependent
6056
    string containing alphabetic characters, which also may contain, but not start
6057
    with "'" and "-" characters.
6058
*/
6059
PHP_FUNCTION(str_word_count)
6060
0
{
6061
0
  zend_string *str;
6062
0
  char *char_list = NULL, ch[256];
6063
0
  const char *p, *e, *s;
6064
0
  size_t char_list_len = 0, word_count = 0;
6065
0
  zend_long type = 0;
6066
6067
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
6068
0
    Z_PARAM_STR(str)
6069
0
    Z_PARAM_OPTIONAL
6070
0
    Z_PARAM_LONG(type)
6071
0
    Z_PARAM_STRING_OR_NULL(char_list, char_list_len)
6072
0
  ZEND_PARSE_PARAMETERS_END();
6073
6074
0
  switch(type) {
6075
0
    case 1:
6076
0
    case 2:
6077
0
      array_init(return_value);
6078
0
      if (!ZSTR_LEN(str)) {
6079
0
        return;
6080
0
      }
6081
0
      break;
6082
0
    case 0:
6083
0
      if (!ZSTR_LEN(str)) {
6084
0
        RETURN_LONG(0);
6085
0
      }
6086
      /* nothing to be done */
6087
0
      break;
6088
0
    default:
6089
0
      zend_argument_value_error(2, "must be a valid format value");
6090
0
      RETURN_THROWS();
6091
0
  }
6092
6093
0
  if (char_list) {
6094
0
    php_charmask((const unsigned char *) char_list, char_list_len, ch);
6095
0
  }
6096
6097
0
  p = ZSTR_VAL(str);
6098
0
  e = ZSTR_VAL(str) + ZSTR_LEN(str);
6099
6100
  /* first character cannot be ' or -, unless explicitly allowed by the user */
6101
0
  if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
6102
0
    p++;
6103
0
  }
6104
  /* last character cannot be -, unless explicitly allowed by the user */
6105
0
  if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
6106
0
    e--;
6107
0
  }
6108
6109
0
  while (p < e) {
6110
0
    s = p;
6111
0
    while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
6112
0
      p++;
6113
0
    }
6114
0
    if (p > s) {
6115
0
      switch (type)
6116
0
      {
6117
0
        case 1:
6118
0
          add_next_index_stringl(return_value, s, p - s);
6119
0
          break;
6120
0
        case 2:
6121
0
          add_index_stringl(return_value, (s - ZSTR_VAL(str)), s, p - s);
6122
0
          break;
6123
0
        default:
6124
0
          word_count++;
6125
0
          break;
6126
0
      }
6127
0
    }
6128
0
    p++;
6129
0
  }
6130
6131
0
  if (!type) {
6132
0
    RETURN_LONG(word_count);
6133
0
  }
6134
0
}
6135
6136
/* }}} */
6137
6138
/* {{{ Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
6139
PHP_FUNCTION(str_split)
6140
0
{
6141
0
  zend_string *str;
6142
0
  zend_long split_length = 1;
6143
0
  const char *p;
6144
0
  size_t n_reg_segments;
6145
6146
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
6147
0
    Z_PARAM_STR(str)
6148
0
    Z_PARAM_OPTIONAL
6149
0
    Z_PARAM_LONG(split_length)
6150
0
  ZEND_PARSE_PARAMETERS_END();
6151
6152
0
  if (split_length <= 0) {
6153
0
    zend_argument_value_error(2, "must be greater than 0");
6154
0
    RETURN_THROWS();
6155
0
  }
6156
6157
0
  if ((size_t)split_length >= ZSTR_LEN(str)) {
6158
0
    if (0 == ZSTR_LEN(str)) {
6159
0
      RETURN_EMPTY_ARRAY();
6160
0
    }
6161
6162
0
    array_init_size(return_value, 1);
6163
0
    GC_TRY_ADDREF(str);
6164
0
    add_next_index_str(return_value, str);
6165
0
    return;
6166
0
  }
6167
6168
0
  array_init_size(return_value, (uint32_t)(((ZSTR_LEN(str) - 1) / split_length) + 1));
6169
0
  zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
6170
6171
0
  n_reg_segments = ZSTR_LEN(str) / split_length;
6172
0
  p = ZSTR_VAL(str);
6173
6174
0
  ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
6175
0
    zval zv;
6176
0
    while (n_reg_segments-- > 0) {
6177
0
      ZEND_ASSERT(split_length > 0);
6178
0
      ZVAL_STRINGL_FAST(&zv, p, split_length);
6179
0
      ZEND_HASH_FILL_ADD(&zv);
6180
0
      p += split_length;
6181
0
    }
6182
6183
0
    if (p != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
6184
0
      ZVAL_STRINGL_FAST(&zv, p, (ZSTR_VAL(str) + ZSTR_LEN(str) - p));
6185
0
      ZEND_HASH_FILL_ADD(&zv);
6186
0
    }
6187
0
  } ZEND_HASH_FILL_END();
6188
0
}
6189
/* }}} */
6190
6191
/* {{{ Search a string for any of a set of characters */
6192
PHP_FUNCTION(strpbrk)
6193
0
{
6194
0
  zend_string *haystack, *char_list;
6195
6196
0
  ZEND_PARSE_PARAMETERS_START(2, 2)
6197
0
    Z_PARAM_STR(haystack)
6198
0
    Z_PARAM_STR(char_list)
6199
0
  ZEND_PARSE_PARAMETERS_END();
6200
6201
0
  if (!ZSTR_LEN(char_list)) {
6202
0
    zend_argument_value_error(2, "must be a non-empty string");
6203
0
    RETURN_THROWS();
6204
0
  }
6205
6206
0
  size_t shift = php_strcspn(
6207
0
    ZSTR_VAL(haystack),
6208
0
    ZSTR_VAL(char_list),
6209
0
    ZSTR_VAL(haystack) + ZSTR_LEN(haystack),
6210
0
    ZSTR_VAL(char_list) + ZSTR_LEN(char_list)
6211
0
  );
6212
0
  if (shift < ZSTR_LEN(haystack)) {
6213
0
    RETURN_STRINGL(ZSTR_VAL(haystack) + shift, ZSTR_LEN(haystack) - shift);
6214
0
  }
6215
6216
0
  RETURN_FALSE;
6217
0
}
6218
/* }}} */
6219
6220
/* {{{ Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
6221
PHP_FUNCTION(substr_compare)
6222
0
{
6223
0
  zend_string *s1, *s2;
6224
0
  zend_long offset, len=0;
6225
0
  bool len_is_default=1;
6226
0
  bool cs=0;
6227
0
  size_t cmp_len;
6228
6229
0
  ZEND_PARSE_PARAMETERS_START(3, 5)
6230
0
    Z_PARAM_STR(s1)
6231
0
    Z_PARAM_STR(s2)
6232
0
    Z_PARAM_LONG(offset)
6233
0
    Z_PARAM_OPTIONAL
6234
0
    Z_PARAM_LONG_OR_NULL(len, len_is_default)
6235
0
    Z_PARAM_BOOL(cs)
6236
0
  ZEND_PARSE_PARAMETERS_END();
6237
6238
0
  if (!len_is_default && len <= 0) {
6239
0
    if (len == 0) {
6240
0
      RETURN_LONG(0L);
6241
0
    } else {
6242
0
      zend_argument_value_error(4, "must be greater than or equal to 0");
6243
0
      RETURN_THROWS();
6244
0
    }
6245
0
  }
6246
6247
0
  if (offset < 0) {
6248
0
    offset = ZSTR_LEN(s1) + offset;
6249
0
    offset = (offset < 0) ? 0 : offset;
6250
0
  }
6251
6252
0
  if ((size_t)offset > ZSTR_LEN(s1)) {
6253
0
    zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
6254
0
    RETURN_THROWS();
6255
0
  }
6256
6257
0
  cmp_len = len ? (size_t)len : MAX(ZSTR_LEN(s2), (ZSTR_LEN(s1) - offset));
6258
6259
0
  if (!cs) {
6260
0
    RETURN_LONG(zend_binary_strncmp(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
6261
0
  } else {
6262
0
    RETURN_LONG(zend_binary_strncasecmp_l(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
6263
0
  }
6264
0
}
6265
/* }}} */
6266
6267
/* {{{ */
6268
static zend_string *php_utf8_encode(const char *s, size_t len)
6269
0
{
6270
0
  size_t pos = len;
6271
0
  zend_string *str;
6272
0
  unsigned char c;
6273
6274
0
  str = zend_string_safe_alloc(len, 2, 0, 0);
6275
0
  ZSTR_LEN(str) = 0;
6276
0
  while (pos > 0) {
6277
    /* The lower 256 codepoints of Unicode are identical to Latin-1,
6278
     * so we don't need to do any mapping here. */
6279
0
    c = (unsigned char)(*s);
6280
0
    if (c < 0x80) {
6281
0
      ZSTR_VAL(str)[ZSTR_LEN(str)++] = (char) c;
6282
    /* We only account for the single-byte and two-byte cases because
6283
     * we're only dealing with the first 256 Unicode codepoints. */
6284
0
    } else {
6285
0
      ZSTR_VAL(str)[ZSTR_LEN(str)++] = (0xc0 | (c >> 6));
6286
0
      ZSTR_VAL(str)[ZSTR_LEN(str)++] = (0x80 | (c & 0x3f));
6287
0
    }
6288
0
    pos--;
6289
0
    s++;
6290
0
  }
6291
0
  ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
6292
0
  str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6293
0
  return str;
6294
0
}
6295
/* }}} */
6296
6297
/* {{{ */
6298
static zend_string *php_utf8_decode(const char *s, size_t len)
6299
0
{
6300
0
  size_t pos = 0;
6301
0
  unsigned int c;
6302
0
  zend_string *str;
6303
6304
0
  str = zend_string_alloc(len, 0);
6305
0
  ZSTR_LEN(str) = 0;
6306
0
  while (pos < len) {
6307
0
    zend_result status = FAILURE;
6308
0
    c = php_next_utf8_char((const unsigned char*)s, (size_t) len, &pos, &status);
6309
6310
    /* The lower 256 codepoints of Unicode are identical to Latin-1,
6311
     * so we don't need to do any mapping here beyond replacing non-Latin-1
6312
     * characters. */
6313
0
    if (status == FAILURE || c > 0xFFU) {
6314
0
      c = '?';
6315
0
    }
6316
6317
0
    ZSTR_VAL(str)[ZSTR_LEN(str)++] = c;
6318
0
  }
6319
0
  ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
6320
0
  if (ZSTR_LEN(str) < len) {
6321
0
    str = zend_string_truncate(str, ZSTR_LEN(str), 0);
6322
0
  }
6323
6324
0
  return str;
6325
0
}
6326
/* }}} */
6327
6328
/* {{{ Encodes an ISO-8859-1 string to UTF-8 */
6329
PHP_FUNCTION(utf8_encode)
6330
0
{
6331
0
  char *arg;
6332
0
  size_t arg_len;
6333
6334
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
6335
0
    Z_PARAM_STRING(arg, arg_len)
6336
0
  ZEND_PARSE_PARAMETERS_END();
6337
6338
0
  RETURN_STR(php_utf8_encode(arg, arg_len));
6339
0
}
6340
/* }}} */
6341
6342
/* {{{ Converts a UTF-8 encoded string to ISO-8859-1 */
6343
PHP_FUNCTION(utf8_decode)
6344
0
{
6345
0
  char *arg;
6346
0
  size_t arg_len;
6347
6348
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
6349
0
    Z_PARAM_STRING(arg, arg_len)
6350
0
  ZEND_PARSE_PARAMETERS_END();
6351
6352
0
  RETURN_STR(php_utf8_decode(arg, arg_len));
6353
0
}
6354
/* }}} */