Coverage Report

Created: 2025-06-13 06:43

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