Coverage Report

Created: 2025-09-27 06:26

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