php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
php_pcre.c
Go to the documentation of this file.
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 | Author: Andrei Zmievski <andrei@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17#include "php.h"
18#include "php_ini.h"
19#include "php_pcre.h"
20#include "ext/standard/info.h"
22#include "zend_smart_str.h"
23#include "SAPI.h"
24
25#define PREG_PATTERN_ORDER 1
26#define PREG_SET_ORDER 2
27#define PREG_OFFSET_CAPTURE (1<<8)
28#define PREG_UNMATCHED_AS_NULL (1<<9)
29
30#define PREG_SPLIT_NO_EMPTY (1<<0)
31#define PREG_SPLIT_DELIM_CAPTURE (1<<1)
32#define PREG_SPLIT_OFFSET_CAPTURE (1<<2)
33
34#define PREG_GREP_INVERT (1<<0)
35
36#define PREG_JIT (1<<3)
37
38#define PCRE_CACHE_SIZE 4096
39
40#ifdef HAVE_PCRE_JIT_SUPPORT
41#define PHP_PCRE_JIT_SUPPORT 1
42#else
43#define PHP_PCRE_JIT_SUPPORT 0
44#endif
45
47
48#include "php_pcre_arginfo.h"
49
52 /* Pointer is not NULL (during request) when there are named captures.
53 * Length is equal to capture_count + 1 to account for capture group 0.
54 * This table cache is only valid during request.
55 * Trying to store this over multiple requests causes issues when the keys are exposed in user arrays
56 * (see GH-17122 and GH-17132). */
58 uint32_t preg_options;
59 uint32_t name_count;
60 uint32_t capture_count;
62 uint32_t refcount;
63};
64
66
67#ifdef HAVE_PCRE_JIT_SUPPORT
68#define PCRE_JIT_STACK_MIN_SIZE (32 * 1024)
69#define PCRE_JIT_STACK_MAX_SIZE (192 * 1024)
70ZEND_TLS pcre2_jit_stack *jit_stack = NULL;
71#endif
72/* General context using (infallible) system allocator. */
74/* These two are global per thread for now. Though it is possible to use these
75 per pattern. Either one can copy it and use in pce, or one does no global
76 contexts at all, but creates for every pce. */
80ZEND_TLS bool mdata_used = 0;
81ZEND_TLS uint8_t pcre2_init_ok = 0;
82#if defined(ZTS) && defined(HAVE_PCRE_JIT_SUPPORT)
83static MUTEX_T pcre_mt = NULL;
84#define php_pcre_mutex_alloc() \
85 if (tsrm_is_main_thread() && !pcre_mt) pcre_mt = tsrm_mutex_alloc();
86#define php_pcre_mutex_free() \
87 if (tsrm_is_main_thread() && pcre_mt) { tsrm_mutex_free(pcre_mt); pcre_mt = NULL; }
88#define php_pcre_mutex_lock() tsrm_mutex_lock(pcre_mt);
89#define php_pcre_mutex_unlock() tsrm_mutex_unlock(pcre_mt);
90#else
91#define php_pcre_mutex_alloc()
92#define php_pcre_mutex_free()
93#define php_pcre_mutex_lock()
94#define php_pcre_mutex_unlock()
95#endif
96
97ZEND_TLS HashTable char_tables;
98
99static void free_subpats_table(zend_string **subpat_names, uint32_t num_subpats);
100
101static void php_pcre_free_char_table(zval *data)
102{/*{{{*/
103 void *ptr = Z_PTR_P(data);
104 pefree(ptr, 1);
105}/*}}}*/
106
107static void pcre_handle_exec_error(int pcre_code) /* {{{ */
108{
109 int preg_code = 0;
110
111 switch (pcre_code) {
114 break;
115
118 break;
119
122 break;
123
124#ifdef HAVE_PCRE_JIT_SUPPORT
127 break;
128#endif
129
130 default:
131 if (pcre_code <= PCRE2_ERROR_UTF8_ERR1 && pcre_code >= PCRE2_ERROR_UTF8_ERR21) {
132 preg_code = PHP_PCRE_BAD_UTF8_ERROR;
133 } else {
134 preg_code = PHP_PCRE_INTERNAL_ERROR;
135 }
136 break;
137 }
138
139 PCRE_G(error_code) = preg_code;
140}
141/* }}} */
142
143static const char *php_pcre_get_error_msg(php_pcre_error_code error_code) /* {{{ */
144{
145 switch (error_code) {
147 return "No error";
149 return "Internal error";
151 return "Malformed UTF-8 characters, possibly incorrectly encoded";
153 return "The offset did not correspond to the beginning of a valid UTF-8 code point";
155 return "Backtrack limit exhausted";
157 return "Recursion limit exhausted";
158
159#ifdef HAVE_PCRE_JIT_SUPPORT
161 return "JIT stack limit exhausted";
162#endif
163
164 default:
165 return "Unknown error";
166 }
167}
168/* }}} */
169
170static void php_free_pcre_cache(zval *data) /* {{{ */
171{
173 if (!pce) return;
174 if (pce->subpats_table) {
175 free_subpats_table(pce->subpats_table, pce->capture_count + 1);
176 }
177 pcre2_code_free(pce->re);
178 free(pce);
179}
180/* }}} */
181
182static void *php_pcre_malloc(PCRE2_SIZE size, void *data)
183{
184 return pemalloc(size, 1);
185}
186
187static void php_pcre_free(void *block, void *data)
188{
189 pefree(block, 1);
190}
191
192static void *php_pcre_emalloc(PCRE2_SIZE size, void *data)
193{
194 return emalloc(size);
195}
196
197static void php_pcre_efree(void *block, void *data)
198{
199 efree(block);
200}
201
202#ifdef PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK
203 /* pcre 10.38 needs PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK, disabled by default */
204#define PHP_PCRE_DEFAULT_EXTRA_COPTIONS PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK
205#else
206#define PHP_PCRE_DEFAULT_EXTRA_COPTIONS 0
207#endif
208
209#define PHP_PCRE_PREALLOC_MDATA_SIZE 32
210
211static void php_pcre_init_pcre2(uint8_t jit)
212{/*{{{*/
213 if (!gctx) {
214 gctx = pcre2_general_context_create(php_pcre_malloc, php_pcre_free, NULL);
215 if (!gctx) {
216 pcre2_init_ok = 0;
217 return;
218 }
219 }
220
221 if (!cctx) {
222 cctx = pcre2_compile_context_create(gctx);
223 if (!cctx) {
224 pcre2_init_ok = 0;
225 return;
226 }
227 }
228
230
231 if (!mctx) {
232 mctx = pcre2_match_context_create(gctx);
233 if (!mctx) {
234 pcre2_init_ok = 0;
235 return;
236 }
237 }
238
239#ifdef HAVE_PCRE_JIT_SUPPORT
240 if (jit && !jit_stack) {
241 jit_stack = pcre2_jit_stack_create(PCRE_JIT_STACK_MIN_SIZE, PCRE_JIT_STACK_MAX_SIZE, gctx);
242 if (!jit_stack) {
243 pcre2_init_ok = 0;
244 return;
245 }
246 }
247#endif
248
249 if (!mdata) {
251 if (!mdata) {
252 pcre2_init_ok = 0;
253 return;
254 }
255 }
256
257 pcre2_init_ok = 1;
258}/*}}}*/
259
260static void php_pcre_shutdown_pcre2(void)
261{/*{{{*/
262 if (gctx) {
264 gctx = NULL;
265 }
266
267 if (cctx) {
269 cctx = NULL;
270 }
271
272 if (mctx) {
274 mctx = NULL;
275 }
276
277#ifdef HAVE_PCRE_JIT_SUPPORT
278 /* Stack may only be destroyed when no cached patterns
279 possibly associated with it do exist. */
280 if (jit_stack) {
281 pcre2_jit_stack_free(jit_stack);
282 jit_stack = NULL;
283 }
284#endif
285
286 if (mdata) {
288 mdata = NULL;
289 }
290
291 pcre2_init_ok = 0;
292}/*}}}*/
293
294static PHP_GINIT_FUNCTION(pcre) /* {{{ */
295{
297
298 zend_hash_init(&pcre_globals->pcre_cache, 0, NULL, php_free_pcre_cache, 1);
299
300 pcre_globals->backtrack_limit = 0;
301 pcre_globals->recursion_limit = 0;
302 pcre_globals->error_code = PHP_PCRE_NO_ERROR;
303 ZVAL_UNDEF(&pcre_globals->unmatched_null_pair);
304 ZVAL_UNDEF(&pcre_globals->unmatched_empty_pair);
305#ifdef HAVE_PCRE_JIT_SUPPORT
306 pcre_globals->jit = 1;
307#endif
308
309 php_pcre_init_pcre2(1);
310 zend_hash_init(&char_tables, 1, NULL, php_pcre_free_char_table, 1);
311}
312/* }}} */
313
314static PHP_GSHUTDOWN_FUNCTION(pcre) /* {{{ */
315{
316 zend_hash_destroy(&pcre_globals->pcre_cache);
317
318 php_pcre_shutdown_pcre2();
319 zend_hash_destroy(&char_tables);
321}
322/* }}} */
323
324static PHP_INI_MH(OnUpdateBacktrackLimit)
325{/*{{{*/
326 OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
327 if (mctx) {
329 }
330
331 return SUCCESS;
332}/*}}}*/
333
334static PHP_INI_MH(OnUpdateRecursionLimit)
335{/*{{{*/
336 OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
337 if (mctx) {
339 }
340
341 return SUCCESS;
342}/*}}}*/
343
344#ifdef HAVE_PCRE_JIT_SUPPORT
345static PHP_INI_MH(OnUpdateJit)
346{/*{{{*/
347 OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
348 if (PCRE_G(jit) && jit_stack) {
349 pcre2_jit_stack_assign(mctx, NULL, jit_stack);
350 } else {
352 }
353
354 return SUCCESS;
355}/*}}}*/
356#endif
357
359 STD_PHP_INI_ENTRY("pcre.backtrack_limit", "1000000", PHP_INI_ALL, OnUpdateBacktrackLimit, backtrack_limit, zend_pcre_globals, pcre_globals)
360 STD_PHP_INI_ENTRY("pcre.recursion_limit", "100000", PHP_INI_ALL, OnUpdateRecursionLimit, recursion_limit, zend_pcre_globals, pcre_globals)
361#ifdef HAVE_PCRE_JIT_SUPPORT
362 STD_PHP_INI_BOOLEAN("pcre.jit", "1", PHP_INI_ALL, OnUpdateJit, jit, zend_pcre_globals, pcre_globals)
363#endif
365
366static char *_pcre2_config_str(uint32_t what)
367{/*{{{*/
368 int len = pcre2_config(what, NULL);
369 char *ret = (char *) malloc(len + 1);
370
371 len = pcre2_config(what, ret);
372 if (!len) {
373 free(ret);
374 return NULL;
375 }
376
377 return ret;
378}/*}}}*/
379
380/* {{{ PHP_MINFO_FUNCTION(pcre) */
381static PHP_MINFO_FUNCTION(pcre)
382{
383#ifdef HAVE_PCRE_JIT_SUPPORT
384 uint32_t flag = 0;
385 char *jit_target = _pcre2_config_str(PCRE2_CONFIG_JITTARGET);
386#endif
387 char *version = _pcre2_config_str(PCRE2_CONFIG_VERSION);
388 char *unicode = _pcre2_config_str(PCRE2_CONFIG_UNICODE_VERSION);
389
391 php_info_print_table_row(2, "PCRE (Perl Compatible Regular Expressions) Support", "enabled" );
392 php_info_print_table_row(2, "PCRE Library Version", version);
393 free(version);
394 php_info_print_table_row(2, "PCRE Unicode Version", unicode);
395 free(unicode);
396
397#ifdef HAVE_PCRE_JIT_SUPPORT
398 if (!pcre2_config(PCRE2_CONFIG_JIT, &flag)) {
399 php_info_print_table_row(2, "PCRE JIT Support", flag ? "enabled" : "disabled");
400 } else {
401 php_info_print_table_row(2, "PCRE JIT Support", "unknown" );
402 }
403 if (jit_target) {
404 php_info_print_table_row(2, "PCRE JIT Target", jit_target);
405 }
406 free(jit_target);
407#else
408 php_info_print_table_row(2, "PCRE JIT Support", "not compiled in" );
409#endif
410
411#ifdef HAVE_PCRE_VALGRIND_SUPPORT
412 php_info_print_table_row(2, "PCRE Valgrind Support", "enabled" );
413#endif
414
416
418}
419/* }}} */
420
421/* {{{ PHP_MINIT_FUNCTION(pcre) */
422static PHP_MINIT_FUNCTION(pcre)
423{
424#ifdef HAVE_PCRE_JIT_SUPPORT
425 if (UNEXPECTED(!pcre2_init_ok)) {
426 /* Retry. */
427 php_pcre_init_pcre2(PCRE_G(jit));
428 if (!pcre2_init_ok) {
429 return FAILURE;
430 }
431 }
432#endif
433
435
436 php_pcre_version = _pcre2_config_str(PCRE2_CONFIG_VERSION);
437
438 register_php_pcre_symbols(module_number);
439
440 return SUCCESS;
441}
442/* }}} */
443
444/* {{{ PHP_MSHUTDOWN_FUNCTION(pcre) */
445static PHP_MSHUTDOWN_FUNCTION(pcre)
446{
448
449 free(php_pcre_version);
450
451 return SUCCESS;
452}
453/* }}} */
454
455/* {{{ PHP_RINIT_FUNCTION(pcre) */
456static PHP_RINIT_FUNCTION(pcre)
457{
458#ifdef HAVE_PCRE_JIT_SUPPORT
459 if (UNEXPECTED(!pcre2_init_ok)) {
460 /* Retry. */
462 php_pcre_init_pcre2(PCRE_G(jit));
463 if (!pcre2_init_ok) {
465 return FAILURE;
466 }
468 }
469
470 mdata_used = 0;
471#endif
472
474 PCRE_G(gctx_zmm) = pcre2_general_context_create(php_pcre_emalloc, php_pcre_efree, NULL);
475 if (!PCRE_G(gctx_zmm)) {
476 return FAILURE;
477 }
478
479 return SUCCESS;
480}
481/* }}} */
482
483static PHP_RSHUTDOWN_FUNCTION(pcre)
484{
485 pcre_cache_entry *pce;
487 if (pce->subpats_table) {
488 free_subpats_table(pce->subpats_table, pce->capture_count + 1);
489 pce->subpats_table = NULL;
490 }
492
495
500 return SUCCESS;
501}
502
503/* {{{ static pcre_clean_cache */
504static int pcre_clean_cache(zval *data, void *arg)
505{
507 int *num_clean = (int *)arg;
508
509 if (!pce->refcount) {
510 if (--(*num_clean) == 0) {
512 }
514 } else {
516 }
517}
518/* }}} */
519
520static void free_subpats_table(zend_string **subpat_names, uint32_t num_subpats) {
521 uint32_t i;
522 for (i = 0; i < num_subpats; i++) {
523 if (subpat_names[i]) {
524 zend_string_release_ex(subpat_names[i], false);
525 }
526 }
527 efree(subpat_names);
528}
529
530/* {{{ static make_subpats_table */
531static zend_string **make_subpats_table(uint32_t name_cnt, pcre_cache_entry *pce)
532{
533 uint32_t num_subpats = pce->capture_count + 1;
534 uint32_t name_size, ni = 0;
535 char *name_table;
536 zend_string **subpat_names;
537 int rc1, rc2;
538
539 rc1 = pcre2_pattern_info(pce->re, PCRE2_INFO_NAMETABLE, &name_table);
540 rc2 = pcre2_pattern_info(pce->re, PCRE2_INFO_NAMEENTRYSIZE, &name_size);
541 if (rc1 < 0 || rc2 < 0) {
542 php_error_docref(NULL, E_WARNING, "Internal pcre2_pattern_info() error %d", rc1 < 0 ? rc1 : rc2);
543 return NULL;
544 }
545
546 subpat_names = ecalloc(num_subpats, sizeof(zend_string *));
547 while (ni++ < name_cnt) {
548 unsigned short name_idx = 0x100 * (unsigned char)name_table[0] + (unsigned char)name_table[1];
549 const char *name = name_table + 2;
550 subpat_names[name_idx] = zend_string_init(name, strlen(name), false);
551 name_table += name_size;
552 }
553 return subpat_names;
554}
555/* }}} */
556
557static zend_string **ensure_subpats_table(uint32_t name_cnt, pcre_cache_entry *pce)
558{
559 if (!pce->subpats_table) {
560 pce->subpats_table = make_subpats_table(name_cnt, pce);
561 }
562 return pce->subpats_table;
563}
564
565/* {{{ static calculate_unit_length */
566/* Calculates the byte length of the next character. Assumes valid UTF-8 for PCRE2_UTF. */
567static zend_always_inline size_t calculate_unit_length(pcre_cache_entry *pce, const char *start)
568{
569 size_t unit_len;
570
571 if (pce->compile_options & PCRE2_UTF) {
572 const char *end = start;
573
574 /* skip continuation bytes */
575 while ((*++end & 0xC0) == 0x80);
576 unit_len = end - start;
577 } else {
578 unit_len = 1;
579 }
580 return unit_len;
581}
582/* }}} */
583
584/* {{{ pcre_get_compiled_regex_cache */
586{
587 pcre2_code *re = NULL;
588#if 10 == PCRE2_MAJOR && 37 == PCRE2_MINOR && !defined(HAVE_BUNDLED_PCRE)
589 uint32_t coptions = PCRE2_NO_START_OPTIMIZE;
590#else
591 uint32_t coptions = 0;
592#endif
593 uint32_t eoptions = PHP_PCRE_DEFAULT_EXTRA_COPTIONS;
594 PCRE2_UCHAR error[128];
595 PCRE2_SIZE erroffset;
596 int errnumber;
597 char delimiter;
598 char start_delimiter;
599 char end_delimiter;
600 char *p, *pp;
601 char *pattern;
602 size_t pattern_len;
603 uint32_t poptions = 0;
604 const uint8_t *tables = NULL;
605 zval *zv;
606 pcre_cache_entry new_entry;
607 int rc;
610
611 if (locale_aware && BG(ctype_string)) {
613 ZSTR_VAL(BG(ctype_string)), ZSTR_LEN(BG(ctype_string)),
614 ZSTR_VAL(regex), ZSTR_LEN(regex));
615 } else {
616 key = regex;
617 }
618
619 /* Try to lookup the cached regex entry, and if successful, just pass
620 back the compiled pattern, otherwise go on and compile it. */
622 if (zv) {
623 if (key != regex) {
625 }
626 return (pcre_cache_entry*)Z_PTR_P(zv);
627 }
628
629 p = ZSTR_VAL(regex);
630 const char* end_p = ZSTR_VAL(regex) + ZSTR_LEN(regex);
631
632 /* Parse through the leading whitespace, and display a warning if we
633 get to the end without encountering a delimiter. */
634 while (isspace((int)*(unsigned char *)p)) p++;
635 if (p >= end_p) {
636 if (key != regex) {
638 }
639 php_error_docref(NULL, E_WARNING, "Empty regular expression");
640 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
641 return NULL;
642 }
643
644 /* Get the delimiter and display a warning if it is alphanumeric
645 or a backslash. */
646 delimiter = *p++;
647 if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\' || delimiter == '\0') {
648 if (key != regex) {
650 }
651 php_error_docref(NULL, E_WARNING, "Delimiter must not be alphanumeric, backslash, or NUL byte");
652 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
653 return NULL;
654 }
655
656 start_delimiter = delimiter;
657 if ((pp = strchr("([{< )]}> )]}>", delimiter)))
658 delimiter = pp[5];
659 end_delimiter = delimiter;
660
661 pp = p;
662
663 if (start_delimiter == end_delimiter) {
664 /* We need to iterate through the pattern, searching for the ending delimiter,
665 but skipping the backslashed delimiters. If the ending delimiter is not
666 found, display a warning. */
667 while (pp < end_p) {
668 if (*pp == '\\' && pp + 1 < end_p) pp++;
669 else if (*pp == delimiter)
670 break;
671 pp++;
672 }
673 } else {
674 /* We iterate through the pattern, searching for the matching ending
675 * delimiter. For each matching starting delimiter, we increment nesting
676 * level, and decrement it for each matching ending delimiter. If we
677 * reach the end of the pattern without matching, display a warning.
678 */
679 int brackets = 1; /* brackets nesting level */
680 while (pp < end_p) {
681 if (*pp == '\\' && pp + 1 < end_p) pp++;
682 else if (*pp == end_delimiter && --brackets <= 0)
683 break;
684 else if (*pp == start_delimiter)
685 brackets++;
686 pp++;
687 }
688 }
689
690 if (pp >= end_p) {
691 if (key != regex) {
693 }
694 if (start_delimiter == end_delimiter) {
695 php_error_docref(NULL,E_WARNING, "No ending delimiter '%c' found", delimiter);
696 } else {
697 php_error_docref(NULL,E_WARNING, "No ending matching delimiter '%c' found", delimiter);
698 }
699 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
700 return NULL;
701 }
702
703 /* Make a copy of the actual pattern. */
704 pattern_len = pp - p;
705 pattern = estrndup(p, pattern_len);
706
707 /* Move on to the options */
708 pp++;
709
710 /* Parse through the options, setting appropriate flags. Display
711 a warning if we encounter an unknown modifier. */
712 while (pp < end_p) {
713 switch (*pp++) {
714 /* Perl compatible options */
715 case 'i': coptions |= PCRE2_CASELESS; break;
716 case 'm': coptions |= PCRE2_MULTILINE; break;
717 case 'n': coptions |= PCRE2_NO_AUTO_CAPTURE; break;
718 case 's': coptions |= PCRE2_DOTALL; break;
719 case 'x': coptions |= PCRE2_EXTENDED; break;
720
721 /* PCRE specific options */
722 case 'A': coptions |= PCRE2_ANCHORED; break;
723 case 'D': coptions |= PCRE2_DOLLAR_ENDONLY;break;
724#ifdef PCRE2_EXTRA_CASELESS_RESTRICT
725 case 'r': eoptions |= PCRE2_EXTRA_CASELESS_RESTRICT; break;
726#endif
727 case 'S': /* Pass. */ break;
728 case 'X': /* Pass. */ break;
729 case 'U': coptions |= PCRE2_UNGREEDY; break;
730 case 'u': coptions |= PCRE2_UTF;
731 /* In PCRE, by default, \d, \D, \s, \S, \w, and \W recognize only ASCII
732 characters, even in UTF-8 mode. However, this can be changed by setting
733 the PCRE2_UCP option. */
734#ifdef PCRE2_UCP
735 coptions |= PCRE2_UCP;
736#endif
737 break;
738 case 'J': coptions |= PCRE2_DUPNAMES; break;
739
740 case ' ':
741 case '\n':
742 case '\r':
743 break;
744
745 case 'e': /* legacy eval */
746 default:
747 if (pp[-1]) {
748 php_error_docref(NULL, E_WARNING, "Unknown modifier '%c'", pp[-1]);
749 } else {
750 php_error_docref(NULL, E_WARNING, "NUL byte is not a valid modifier");
751 }
752 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
753 efree(pattern);
754 if (key != regex) {
756 }
757 return NULL;
758 }
759 }
760
761 if (key != regex) {
762 tables = (uint8_t *)zend_hash_find_ptr(&char_tables, BG(ctype_string));
763 if (!tables) {
764 zend_string *_k;
765 tables = pcre2_maketables(gctx);
766 if (UNEXPECTED(!tables)) {
767 php_error_docref(NULL,E_WARNING, "Failed to generate locale character tables");
768 pcre_handle_exec_error(PCRE2_ERROR_NOMEMORY);
770 efree(pattern);
771 return NULL;
772 }
773 _k = zend_string_init(ZSTR_VAL(BG(ctype_string)), ZSTR_LEN(BG(ctype_string)), 1);
775 zend_hash_add_ptr(&char_tables, _k, (void *)tables);
776 zend_string_release(_k);
777 }
778 }
779 pcre2_set_character_tables(cctx, tables);
780
781 pcre2_set_compile_extra_options(cctx, eoptions);
782
783 /* Compile pattern and display a warning if compilation failed. */
784 re = pcre2_compile((PCRE2_SPTR)pattern, pattern_len, coptions, &errnumber, &erroffset, cctx);
785
786 if (re == NULL) {
787 if (key != regex) {
789 }
790 pcre2_get_error_message(errnumber, error, sizeof(error));
791 php_error_docref(NULL,E_WARNING, "Compilation failed: %s at offset %zu", error, erroffset);
792 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
793 efree(pattern);
794 return NULL;
795 }
796
797#ifdef HAVE_PCRE_JIT_SUPPORT
798 if (PCRE_G(jit)) {
799 /* Enable PCRE JIT compiler */
801 if (EXPECTED(rc >= 0)) {
802 size_t jit_size = 0;
803 if (!pcre2_pattern_info(re, PCRE2_INFO_JITSIZE, &jit_size) && jit_size > 0) {
804 poptions |= PREG_JIT;
805 }
806 } else if (rc == PCRE2_ERROR_NOMEMORY) {
808 "Allocation of JIT memory failed, PCRE JIT will be disabled. "
809 "This is likely caused by security restrictions. "
810 "Either grant PHP permission to allocate executable memory, or set pcre.jit=0");
811 PCRE_G(jit) = 0;
812 } else {
813 pcre2_get_error_message(rc, error, sizeof(error));
814 php_error_docref(NULL, E_WARNING, "JIT compilation failed: %s", error);
815 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
816 }
817 }
818#endif
819 efree(pattern);
820
821 /*
822 * If we reached cache limit, clean out the items from the head of the list;
823 * these are supposedly the oldest ones (but not necessarily the least used
824 * ones).
825 */
826 if (zend_hash_num_elements(&PCRE_G(pcre_cache)) == PCRE_CACHE_SIZE) {
827 int num_clean = PCRE_CACHE_SIZE / 8;
828 zend_hash_apply_with_argument(&PCRE_G(pcre_cache), pcre_clean_cache, &num_clean);
829 }
830
831 /* Store the compiled pattern and extra info in the cache. */
832 new_entry.re = re;
833 new_entry.preg_options = poptions;
834 new_entry.compile_options = coptions;
835 new_entry.refcount = 0;
836 new_entry.subpats_table = NULL;
837
839 if (rc < 0) {
840 if (key != regex) {
842 }
843 php_error_docref(NULL, E_WARNING, "Internal pcre2_pattern_info() error %d", rc);
844 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
845 return NULL;
846 }
847
849 if (rc < 0) {
850 if (key != regex) {
852 }
853 php_error_docref(NULL, E_WARNING, "Internal pcre_pattern_info() error %d", rc);
854 pcre_handle_exec_error(PCRE2_ERROR_INTERNAL);
855 return NULL;
856 }
857
858 /*
859 * Interned strings are not duplicated when stored in HashTable,
860 * but all the interned strings created during HTTP request are removed
861 * at end of request. However PCRE_G(pcre_cache) must be consistent
862 * on the next request as well. So we disable usage of interned strings
863 * as hash keys especually for this table.
864 * See bug #63180
865 */
866 if (!(GC_FLAGS(key) & IS_STR_PERMANENT)) {
867 zend_string *str = zend_string_init(ZSTR_VAL(key), ZSTR_LEN(key), 1);
869
870 ret = zend_hash_add_new_mem(&PCRE_G(pcre_cache), str, &new_entry, sizeof(pcre_cache_entry));
871 zend_string_release(str);
872 } else {
873 ret = zend_hash_add_new_mem(&PCRE_G(pcre_cache), key, &new_entry, sizeof(pcre_cache_entry));
874 }
875
876 if (key != regex) {
878 }
879
880 return ret;
881}
882/* }}} */
883
884/* {{{ pcre_get_compiled_regex_cache */
889/* }}} */
890
891/* {{{ pcre_get_compiled_regex */
892PHPAPI pcre2_code *pcre_get_compiled_regex(zend_string *regex, uint32_t *capture_count)
893{
895
896 if (capture_count) {
897 *capture_count = pce ? pce->capture_count : 0;
898 }
899
900 return pce ? pce->re : NULL;
901}
902/* }}} */
903
904/* XXX For the cases where it's only about match yes/no and no capture
905 required, perhaps just a minimum sized data would suffice. */
907{/*{{{*/
908
909 assert(NULL != re);
910
911 if (EXPECTED(!mdata_used)) {
912 int rc = 0;
913
914 if (!capture_count) {
915 /* As we deal with a non cached pattern, no other way to gather this info. */
916 rc = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &capture_count);
917 }
918
919 if (rc >= 0 && capture_count + 1 <= PHP_PCRE_PREALLOC_MDATA_SIZE) {
920 mdata_used = 1;
921 return mdata;
922 }
923 }
924
926}/*}}}*/
927
929{/*{{{*/
930 if (UNEXPECTED(match_data != mdata)) {
931 pcre2_match_data_free(match_data);
932 } else {
933 mdata_used = 0;
934 }
935}/*}}}*/
936
937static void init_unmatched_null_pair(zval *pair) {
938 zval val1, val2;
939 ZVAL_NULL(&val1);
940 ZVAL_LONG(&val2, -1);
941 ZVAL_ARR(pair, zend_new_pair(&val1, &val2));
942}
943
944static void init_unmatched_empty_pair(zval *pair) {
945 zval val1, val2;
946 ZVAL_EMPTY_STRING(&val1);
947 ZVAL_LONG(&val2, -1);
948 ZVAL_ARR(pair, zend_new_pair(&val1, &val2));
949}
950
951static zend_always_inline void populate_match_value_str(
952 zval *val, const char *subject, PCRE2_SIZE start_offset, PCRE2_SIZE end_offset) {
953 ZVAL_STRINGL_FAST(val, subject + start_offset, end_offset - start_offset);
954}
955
956static zend_always_inline void populate_match_value(
957 zval *val, const char *subject, PCRE2_SIZE start_offset, PCRE2_SIZE end_offset,
958 bool unmatched_as_null) {
959 if (PCRE2_UNSET == start_offset) {
960 if (unmatched_as_null) {
961 ZVAL_NULL(val);
962 } else {
964 }
965 } else {
966 populate_match_value_str(val, subject, start_offset, end_offset);
967 }
968}
969
970static inline void add_named(
971 HashTable *const subpats, zend_string *name, zval *val, bool unmatched) {
973
974 /* If the DUPNAMES option is used, multiple subpatterns might have the same name.
975 * In this case we want to preserve the one that actually has a value. */
976 if (!unmatched) {
977 zend_hash_update(subpats, name, val);
978 } else {
979 if (!zend_hash_add(subpats, name, val)) {
980 return;
981 }
982 }
984}
985
986/* {{{ add_offset_pair */
987static inline void add_offset_pair(
988 HashTable *const result, const char *subject, PCRE2_SIZE start_offset, PCRE2_SIZE end_offset,
989 zend_string *name, zend_long unmatched_as_null)
990{
991 zval match_pair;
992
993 /* Add (match, offset) to the return value */
994 if (PCRE2_UNSET == start_offset) {
995 if (unmatched_as_null) {
996 do {
999 init_unmatched_null_pair(&match_pair);
1000 break;
1001 } else {
1002 init_unmatched_null_pair(&PCRE_G(unmatched_null_pair));
1003 }
1004 }
1005 ZVAL_COPY(&match_pair, &PCRE_G(unmatched_null_pair));
1006 } while (0);
1007 } else {
1008 do {
1011 init_unmatched_empty_pair(&match_pair);
1012 break;
1013 } else {
1014 init_unmatched_empty_pair(&PCRE_G(unmatched_empty_pair));
1015 }
1016 }
1017 ZVAL_COPY(&match_pair, &PCRE_G(unmatched_empty_pair));
1018 } while (0);
1019 }
1020 } else {
1021 zval val1, val2;
1022 populate_match_value_str(&val1, subject, start_offset, end_offset);
1023 ZVAL_LONG(&val2, start_offset);
1024 ZVAL_ARR(&match_pair, zend_new_pair(&val1, &val2));
1025 }
1026
1027 if (name) {
1028 add_named(result, name, &match_pair, start_offset == PCRE2_UNSET);
1029 }
1031}
1032/* }}} */
1033
1034static void populate_subpat_array(
1035 zval *subpats, const char *subject, PCRE2_SIZE *offsets, zend_string **subpat_names,
1036 uint32_t num_subpats, int count, const PCRE2_SPTR mark, zend_long flags) {
1037 zend_long offset_capture = flags & PREG_OFFSET_CAPTURE;
1038 zend_long unmatched_as_null = flags & PREG_UNMATCHED_AS_NULL;
1039 zval val;
1040 int i;
1041 HashTable *subpats_ht = Z_ARRVAL_P(subpats);
1042 if (subpat_names) {
1043 if (offset_capture) {
1044 for (i = 0; i < count; i++) {
1045 add_offset_pair(
1046 subpats_ht, subject, offsets[2*i], offsets[2*i+1],
1047 subpat_names[i], unmatched_as_null);
1048 }
1049 if (unmatched_as_null) {
1050 for (i = count; i < num_subpats; i++) {
1051 add_offset_pair(subpats_ht, NULL, PCRE2_UNSET, PCRE2_UNSET, subpat_names[i], 1);
1052 }
1053 }
1054 } else {
1055 for (i = 0; i < count; i++) {
1056 populate_match_value(
1057 &val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
1058 if (subpat_names[i]) {
1059 add_named(subpats_ht, subpat_names[i], &val, offsets[2*i] == PCRE2_UNSET);
1060 }
1062 }
1063 if (unmatched_as_null) {
1064 for (i = count; i < num_subpats; i++) {
1065 ZVAL_NULL(&val);
1066 if (subpat_names[i]) {
1067 zend_hash_add(subpats_ht, subpat_names[i], &val);
1068 }
1070 }
1071 }
1072 }
1073 } else {
1074 if (offset_capture) {
1075 for (i = 0; i < count; i++) {
1076 add_offset_pair(
1077 subpats_ht, subject, offsets[2*i], offsets[2*i+1], NULL, unmatched_as_null);
1078 }
1079 if (unmatched_as_null) {
1080 for (i = count; i < num_subpats; i++) {
1081 add_offset_pair(subpats_ht, NULL, PCRE2_UNSET, PCRE2_UNSET, NULL, 1);
1082 }
1083 }
1084 } else {
1085 for (i = 0; i < count; i++) {
1086 populate_match_value(
1087 &val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
1089 }
1090 if (unmatched_as_null) {
1091 for (i = count; i < num_subpats; i++) {
1092 add_next_index_null(subpats);
1093 }
1094 }
1095 }
1096 }
1097 /* Add MARK, if available */
1098 if (mark) {
1099 add_assoc_string_ex(subpats, "MARK", sizeof("MARK") - 1, (char *)mark);
1100 }
1101}
1102
1103static void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, bool global) /* {{{ */
1104{
1105 /* parameters */
1106 zend_string *regex; /* Regular expression */
1107 zend_string *subject; /* String to match against */
1108 pcre_cache_entry *pce; /* Compiled regular expression */
1109 zval *subpats = NULL; /* Array for subpatterns */
1110 zend_long flags = 0; /* Match control flags */
1111 zend_long start_offset = 0; /* Where the new search starts */
1112
1114 Z_PARAM_STR(regex)
1115 Z_PARAM_STR(subject)
1117 Z_PARAM_ZVAL(subpats)
1119 Z_PARAM_LONG(start_offset)
1121
1122 /* Compile regex or get it from cache. */
1123 if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
1125 }
1126
1127 if (start_offset == ZEND_LONG_MIN) {
1128 zend_argument_value_error(5, "must be greater than " ZEND_LONG_FMT, ZEND_LONG_MIN);
1129 RETURN_THROWS();
1130 }
1131
1132 pce->refcount++;
1133 php_pcre_match_impl(pce, subject, return_value, subpats,
1134 global, flags, start_offset);
1135 pce->refcount--;
1136}
1137/* }}} */
1138
1139static zend_always_inline bool is_known_valid_utf8(
1140 zend_string *subject_str, PCRE2_SIZE start_offset) {
1141 if (!ZSTR_IS_VALID_UTF8(subject_str)) {
1142 /* We don't know whether the string is valid UTF-8 or not. */
1143 return 0;
1144 }
1145
1146 if (start_offset == ZSTR_LEN(subject_str)) {
1147 /* Degenerate case: Offset points to end of string. */
1148 return 1;
1149 }
1150
1151 /* Check that the offset does not point to an UTF-8 continuation byte. */
1152 return (ZSTR_VAL(subject_str)[start_offset] & 0xc0) != 0x80;
1153}
1154
1155/* {{{ php_pcre_match_impl() */
1157 zval *subpats, bool global, zend_long flags, zend_off_t start_offset)
1158{
1159 zval result_set; /* Holds a set of subpatterns after
1160 a global match */
1161 HashTable **match_sets = NULL; /* An array of sets of matches for each
1162 subpattern after a global match */
1163 uint32_t options; /* Execution options */
1164 int count; /* Count of matched subpatterns */
1165 uint32_t num_subpats; /* Number of captured subpatterns */
1166 int matched; /* Has anything matched */
1167 zend_string **subpat_names; /* Array for named subpatterns */
1168 size_t i;
1169 uint32_t subpats_order; /* Order of subpattern matches */
1170 uint32_t offset_capture; /* Capture match offsets: yes/no */
1171 zend_long unmatched_as_null; /* Null non-matches: yes/no */
1172 PCRE2_SPTR mark = NULL; /* Target for MARK name */
1173 HashTable *marks = NULL; /* Array of marks for PREG_PATTERN_ORDER */
1174 pcre2_match_data *match_data;
1175 PCRE2_SIZE start_offset2, orig_start_offset;
1176
1177 char *subject = ZSTR_VAL(subject_str);
1178 size_t subject_len = ZSTR_LEN(subject_str);
1179
1180 /* Overwrite the passed-in value for subpatterns with an empty array. */
1181 if (subpats != NULL) {
1182 subpats = zend_try_array_init(subpats);
1183 if (!subpats) {
1184 RETURN_THROWS();
1185 }
1186 }
1187
1188 subpats_order = global ? PREG_PATTERN_ORDER : 0;
1189
1190 if (flags) {
1191 offset_capture = flags & PREG_OFFSET_CAPTURE;
1192 unmatched_as_null = flags & PREG_UNMATCHED_AS_NULL;
1193
1194 /*
1195 * subpats_order is pre-set to pattern mode so we change it only if
1196 * necessary.
1197 */
1198 if (flags & 0xff) {
1199 subpats_order = flags & 0xff;
1200 if ((global && (subpats_order < PREG_PATTERN_ORDER || subpats_order > PREG_SET_ORDER)) ||
1201 (!global && subpats_order != 0)) {
1202 zend_argument_value_error(4, "must be a PREG_* constant");
1203 RETURN_THROWS();
1204 }
1205 }
1206 } else {
1207 offset_capture = 0;
1208 unmatched_as_null = 0;
1209 }
1210
1211 /* Negative offset counts from the end of the string. */
1212 if (start_offset < 0) {
1213 if ((PCRE2_SIZE)-start_offset <= subject_len) {
1214 start_offset2 = subject_len + start_offset;
1215 } else {
1216 start_offset2 = 0;
1217 }
1218 } else {
1219 start_offset2 = (PCRE2_SIZE)start_offset;
1220 }
1221
1222 if (start_offset2 > subject_len) {
1223 pcre_handle_exec_error(PCRE2_ERROR_BADOFFSET);
1225 }
1226
1227 /* Calculate the size of the offsets array, and allocate memory for it. */
1228 num_subpats = pce->capture_count + 1;
1229
1230 /*
1231 * Build a mapping from subpattern numbers to their names. We will
1232 * allocate the table only if there are any named subpatterns.
1233 */
1234 subpat_names = NULL;
1235 if (subpats && pce->name_count > 0) {
1236 subpat_names = ensure_subpats_table(pce->name_count, pce);
1237 if (UNEXPECTED(!subpat_names)) {
1239 }
1240 }
1241
1242 matched = 0;
1244
1245 if (!mdata_used && num_subpats <= PHP_PCRE_PREALLOC_MDATA_SIZE) {
1246 match_data = mdata;
1247 } else {
1249 if (!match_data) {
1252 }
1253 }
1254
1255 /* Allocate match sets array and initialize the values. */
1256 if (global && subpats && subpats_order == PREG_PATTERN_ORDER) {
1257 match_sets = safe_emalloc(num_subpats, sizeof(HashTable *), 0);
1258 for (i=0; i<num_subpats; i++) {
1259 match_sets[i] = zend_new_array(0);
1260 }
1261 }
1262
1263 /* Array of subpattern offsets */
1264 PCRE2_SIZE *const offsets = pcre2_get_ovector_pointer(match_data);
1265
1266 orig_start_offset = start_offset2;
1267 options =
1268 (pce->compile_options & PCRE2_UTF) && !is_known_valid_utf8(subject_str, orig_start_offset)
1269 ? 0 : PCRE2_NO_UTF_CHECK;
1270
1271 /* Execute the regular expression. */
1272#ifdef HAVE_PCRE_JIT_SUPPORT
1273 if ((pce->preg_options & PREG_JIT) && options) {
1274 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset2,
1275 PCRE2_NO_UTF_CHECK, match_data, mctx);
1276 } else
1277#endif
1278 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset2,
1279 options, match_data, mctx);
1280
1281 while (1) {
1282 /* If something has matched */
1283 if (count >= 0) {
1284 /* Check for too many substrings condition. */
1285 if (UNEXPECTED(count == 0)) {
1286 php_error_docref(NULL, E_NOTICE, "Matched, but too many substrings");
1287 count = num_subpats;
1288 }
1289
1290matched:
1291 matched++;
1292
1293 /* If subpatterns array has been passed, fill it in with values. */
1294 if (subpats != NULL) {
1295 /* Try to get the list of substrings and display a warning if failed. */
1296 if (UNEXPECTED(offsets[1] < offsets[0])) {
1297 if (match_sets) efree(match_sets);
1298 php_error_docref(NULL, E_WARNING, "Get subpatterns list failed");
1300 }
1301
1302 if (global) { /* global pattern matching */
1303 if (subpats_order == PREG_PATTERN_ORDER) {
1304 /* For each subpattern, insert it into the appropriate array. */
1305 if (offset_capture) {
1306 for (i = 0; i < count; i++) {
1307 add_offset_pair(
1308 match_sets[i], subject, offsets[2*i], offsets[2*i+1],
1309 NULL, unmatched_as_null);
1310 }
1311 } else {
1312 for (i = 0; i < count; i++) {
1313 zval val;
1314 populate_match_value(
1315 &val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
1316 zend_hash_next_index_insert_new(match_sets[i], &val);
1317 }
1318 }
1319 mark = pcre2_get_mark(match_data);
1320 /* Add MARK, if available */
1321 if (mark) {
1322 if (!marks) {
1323 marks = zend_new_array(0);
1324 }
1325 zval tmp;
1326 ZVAL_STRING(&tmp, (char *) mark);
1327 zend_hash_index_add_new(marks, matched - 1, &tmp);
1328 }
1329 /*
1330 * If the number of captured subpatterns on this run is
1331 * less than the total possible number, pad the result
1332 * arrays with NULLs or empty strings.
1333 */
1334 if (count < num_subpats) {
1335 for (int i = count; i < num_subpats; i++) {
1336 if (offset_capture) {
1337 add_offset_pair(
1338 match_sets[i], NULL, PCRE2_UNSET, PCRE2_UNSET,
1339 NULL, unmatched_as_null);
1340 } else if (unmatched_as_null) {
1341 zval tmp;
1342 ZVAL_NULL(&tmp);
1343 zend_hash_next_index_insert_new(match_sets[i], &tmp);
1344 } else {
1345 zval tmp;
1346 ZVAL_EMPTY_STRING(&tmp);
1347 zend_hash_next_index_insert_new(match_sets[i], &tmp);
1348 }
1349 }
1350 }
1351 } else {
1352 /* Allocate and populate the result set array */
1353 mark = pcre2_get_mark(match_data);
1354 array_init_size(&result_set, count + (mark ? 1 : 0));
1355 populate_subpat_array(
1356 &result_set, subject, offsets, subpat_names,
1357 num_subpats, count, mark, flags);
1358 /* And add it to the output array */
1359 zend_hash_next_index_insert_new(Z_ARRVAL_P(subpats), &result_set);
1360 }
1361 } else { /* single pattern matching */
1362 /* For each subpattern, insert it into the subpatterns array. */
1363 mark = pcre2_get_mark(match_data);
1364 populate_subpat_array(
1365 subpats, subject, offsets, subpat_names, num_subpats, count, mark, flags);
1366 break;
1367 }
1368 }
1369
1370 /* Advance to the next piece. */
1371 start_offset2 = offsets[1];
1372
1373 /* If we have matched an empty string, mimic what Perl's /g options does.
1374 This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
1375 the match again at the same point. If this fails (picked up above) we
1376 advance to the next character. */
1377 if (start_offset2 == offsets[0]) {
1378 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset2,
1380 if (count >= 0) {
1381 if (global) {
1382 goto matched;
1383 } else {
1384 break;
1385 }
1386 } else if (count == PCRE2_ERROR_NOMATCH) {
1387 /* If we previously set PCRE2_NOTEMPTY_ATSTART after a null match,
1388 this is not necessarily the end. We need to advance
1389 the start offset, and continue. Fudge the offset values
1390 to achieve this, unless we're already at the end of the string. */
1391 if (start_offset2 < subject_len) {
1392 size_t unit_len = calculate_unit_length(pce, subject + start_offset2);
1393
1394 start_offset2 += unit_len;
1395 } else {
1396 break;
1397 }
1398 } else {
1399 goto error;
1400 }
1401 }
1402 } else if (count == PCRE2_ERROR_NOMATCH) {
1403 break;
1404 } else {
1405error:
1406 pcre_handle_exec_error(count);
1407 break;
1408 }
1409
1410 if (!global) {
1411 break;
1412 }
1413
1414 /* Execute the regular expression. */
1415#ifdef HAVE_PCRE_JIT_SUPPORT
1416 if ((pce->preg_options & PREG_JIT)) {
1417 if (start_offset2 > subject_len) {
1418 pcre_handle_exec_error(PCRE2_ERROR_BADOFFSET);
1419 break;
1420 }
1421 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset2,
1422 PCRE2_NO_UTF_CHECK, match_data, mctx);
1423 } else
1424#endif
1425 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset2,
1426 PCRE2_NO_UTF_CHECK, match_data, mctx);
1427 }
1428 if (match_data != mdata) {
1429 pcre2_match_data_free(match_data);
1430 }
1431
1432 /* Add the match sets to the output array and clean up */
1433 if (match_sets) {
1434 if (subpat_names) {
1435 for (i = 0; i < num_subpats; i++) {
1436 zval wrapper;
1437 ZVAL_ARR(&wrapper, match_sets[i]);
1438 if (subpat_names[i]) {
1439 zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i], &wrapper);
1440 GC_ADDREF(match_sets[i]);
1441 }
1442 zend_hash_next_index_insert_new(Z_ARRVAL_P(subpats), &wrapper);
1443 }
1444 } else {
1445 for (i = 0; i < num_subpats; i++) {
1446 zval wrapper;
1447 ZVAL_ARR(&wrapper, match_sets[i]);
1448 zend_hash_next_index_insert_new(Z_ARRVAL_P(subpats), &wrapper);
1449 }
1450 }
1451 efree(match_sets);
1452
1453 if (marks) {
1454 zval tmp;
1455 ZVAL_ARR(&tmp, marks);
1456 zend_hash_str_update(Z_ARRVAL_P(subpats), "MARK", sizeof("MARK") - 1, &tmp);
1457 }
1458 }
1459
1461 /* If there was no error and we're in /u mode, remember that the string is valid UTF-8. */
1462 if ((pce->compile_options & PCRE2_UTF)
1463 && !ZSTR_IS_INTERNED(subject_str) && orig_start_offset == 0) {
1464 GC_ADD_FLAGS(subject_str, IS_STR_VALID_UTF8);
1465 }
1466
1467 RETVAL_LONG(matched);
1468 } else {
1470 }
1471}
1472/* }}} */
1473
1474/* {{{ Perform a Perl-style regular expression match */
1476{
1477 php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1478}
1479/* }}} */
1480
1482{
1483 zval regex_tmp, subject_tmp;
1484 zend_string *regex, *subject;
1485
1486 Z_FLF_PARAM_STR(1, regex, regex_tmp);
1487 Z_FLF_PARAM_STR(2, subject, subject_tmp);
1488
1489 /* Compile regex or get it from cache. */
1490 pcre_cache_entry *pce;
1491 if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
1493 }
1494
1495 pce->refcount++;
1496 php_pcre_match_impl(pce, subject, return_value, /* subpats */ NULL,
1497 /* global */ false, /* flags */ 0, /* start_offset */ 0);
1498 pce->refcount--;
1499
1500flf_clean:
1501 Z_FLF_PARAM_FREE_STR(1, regex_tmp);
1502 Z_FLF_PARAM_FREE_STR(2, subject_tmp);
1503}
1504
1505/* {{{ Perform a Perl-style global regular expression match */
1507{
1508 php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1509}
1510/* }}} */
1511
1512/* {{{ preg_get_backref */
1513static int preg_get_backref(char **str, int *backref)
1514{
1515 char in_brace = 0;
1516 char *walk = *str;
1517
1518 if (walk[1] == 0)
1519 return 0;
1520
1521 if (*walk == '$' && walk[1] == '{') {
1522 in_brace = 1;
1523 walk++;
1524 }
1525 walk++;
1526
1527 if (*walk >= '0' && *walk <= '9') {
1528 *backref = *walk - '0';
1529 walk++;
1530 } else
1531 return 0;
1532
1533 if (*walk && *walk >= '0' && *walk <= '9') {
1534 *backref = *backref * 10 + *walk - '0';
1535 walk++;
1536 }
1537
1538 if (in_brace) {
1539 if (*walk != '}')
1540 return 0;
1541 else
1542 walk++;
1543 }
1544
1545 *str = walk;
1546 return 1;
1547}
1548/* }}} */
1549
1550/* {{{ preg_do_repl_func */
1551static zend_string *preg_do_repl_func(zend_fcall_info *fci, zend_fcall_info_cache *fcc, const char *subject, PCRE2_SIZE *offsets, zend_string **subpat_names, uint32_t num_subpats, int count, const PCRE2_SPTR mark, zend_long flags)
1552{
1553 zend_string *result_str;
1554 zval retval; /* Function return value */
1555 zval arg; /* Argument to pass to function */
1556
1557 array_init_size(&arg, count + (mark ? 1 : 0));
1558 populate_subpat_array(&arg, subject, offsets, subpat_names, num_subpats, count, mark, flags);
1559
1560 fci->retval = &retval;
1561 fci->param_count = 1;
1562 fci->params = &arg;
1563
1564 if (zend_call_function(fci, fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1565 if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
1566 result_str = Z_STR(retval);
1567 } else {
1568 result_str = zval_get_string_func(&retval);
1570 }
1571 } else {
1572 if (!EG(exception)) {
1573 php_error_docref(NULL, E_WARNING, "Unable to call custom replacement function");
1574 }
1575
1576 result_str = zend_string_init(&subject[offsets[0]], offsets[1] - offsets[0], 0);
1577 }
1578
1580
1581 return result_str;
1582}
1583/* }}} */
1584
1585/* {{{ php_pcre_replace */
1587 zend_string *subject_str,
1588 const char *subject, size_t subject_len,
1589 zend_string *replace_str,
1590 size_t limit, size_t *replace_count)
1591{
1592 pcre_cache_entry *pce; /* Compiled regular expression */
1593 zend_string *result; /* Function result */
1594
1595 /* Abort on pending exception, e.g. thrown from __toString(). */
1596 if (UNEXPECTED(EG(exception))) {
1597 return NULL;
1598 }
1599
1600 /* Compile regex or get it from cache. */
1601 if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
1602 return NULL;
1603 }
1604 pce->refcount++;
1605 result = php_pcre_replace_impl(pce, subject_str, subject, subject_len, replace_str,
1606 limit, replace_count);
1607 pce->refcount--;
1608
1609 return result;
1610}
1611/* }}} */
1612
1613/* {{{ php_pcre_replace_impl() */
1614PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count)
1615{
1616 uint32_t options; /* Execution options */
1617 int count; /* Count of matched subpatterns */
1618 uint32_t num_subpats; /* Number of captured subpatterns */
1619 size_t new_len; /* Length of needed storage */
1620 size_t alloc_len; /* Actual allocated length */
1621 size_t match_len; /* Length of the current match */
1622 int backref; /* Backreference number */
1623 PCRE2_SIZE start_offset; /* Where the new search starts */
1624 size_t last_end_offset; /* Where the last search ended */
1625 char *walkbuf, /* Location of current replacement in the result */
1626 *walk, /* Used to walk the replacement string */
1627 walk_last; /* Last walked character */
1628 const char *match, /* The current match */
1629 *piece, /* The current piece of subject */
1630 *replace_end; /* End of replacement string */
1631 size_t result_len; /* Length of result */
1632 zend_string *result; /* Result of replacement */
1633 pcre2_match_data *match_data;
1634
1635 /* Calculate the size of the offsets array, and allocate memory for it. */
1636 num_subpats = pce->capture_count + 1;
1637 alloc_len = 0;
1638 result = NULL;
1639
1640 /* Initialize */
1641 match = NULL;
1642 start_offset = 0;
1643 last_end_offset = 0;
1644 result_len = 0;
1646
1647 if (!mdata_used && num_subpats <= PHP_PCRE_PREALLOC_MDATA_SIZE) {
1648 match_data = mdata;
1649 } else {
1651 if (!match_data) {
1653 return NULL;
1654 }
1655 }
1656
1658
1659 /* Array of subpattern offsets */
1660 PCRE2_SIZE *const offsets = pcre2_get_ovector_pointer(match_data);
1661
1662 /* Execute the regular expression. */
1663#ifdef HAVE_PCRE_JIT_SUPPORT
1664 if ((pce->preg_options & PREG_JIT) && options) {
1665 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1666 PCRE2_NO_UTF_CHECK, match_data, mctx);
1667 } else
1668#endif
1669 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1670 options, match_data, mctx);
1671
1672 while (1) {
1673 piece = subject + last_end_offset;
1674
1675 if (count >= 0 && limit > 0) {
1676 bool simple_string;
1677
1678 /* Check for too many substrings condition. */
1679 if (UNEXPECTED(count == 0)) {
1680 php_error_docref(NULL,E_NOTICE, "Matched, but too many substrings");
1681 count = num_subpats;
1682 }
1683
1684matched:
1685 if (UNEXPECTED(offsets[1] < offsets[0])) {
1687 if (result) {
1689 result = NULL;
1690 }
1691 break;
1692 }
1693
1694 if (replace_count) {
1695 ++*replace_count;
1696 }
1697
1698 /* Set the match location in subject */
1699 match = subject + offsets[0];
1700
1701 new_len = result_len + offsets[0] - last_end_offset; /* part before the match */
1702
1703 walk = ZSTR_VAL(replace_str);
1704 replace_end = walk + ZSTR_LEN(replace_str);
1705 walk_last = 0;
1706 simple_string = 1;
1707 while (walk < replace_end) {
1708 if ('\\' == *walk || '$' == *walk) {
1709 simple_string = 0;
1710 if (walk_last == '\\') {
1711 walk++;
1712 walk_last = 0;
1713 continue;
1714 }
1715 if (preg_get_backref(&walk, &backref)) {
1716 if (backref < count)
1717 new_len += offsets[(backref<<1)+1] - offsets[backref<<1];
1718 continue;
1719 }
1720 }
1721 new_len++;
1722 walk++;
1723 walk_last = walk[-1];
1724 }
1725
1726 if (new_len >= alloc_len) {
1727 alloc_len = zend_safe_address_guarded(2, new_len, ZSTR_MAX_OVERHEAD) - ZSTR_MAX_OVERHEAD;
1728 if (result == NULL) {
1729 result = zend_string_alloc(alloc_len, 0);
1730 } else {
1731 result = zend_string_extend(result, alloc_len, 0);
1732 }
1733 }
1734
1735 if (match-piece > 0) {
1736 /* copy the part of the string before the match */
1737 memcpy(&ZSTR_VAL(result)[result_len], piece, match-piece);
1738 result_len += (match-piece);
1739 }
1740
1741 if (simple_string) {
1742 /* copy replacement */
1743 memcpy(&ZSTR_VAL(result)[result_len], ZSTR_VAL(replace_str), ZSTR_LEN(replace_str)+1);
1744 result_len += ZSTR_LEN(replace_str);
1745 } else {
1746 /* copy replacement and backrefs */
1747 walkbuf = ZSTR_VAL(result) + result_len;
1748
1749 walk = ZSTR_VAL(replace_str);
1750 walk_last = 0;
1751 while (walk < replace_end) {
1752 if ('\\' == *walk || '$' == *walk) {
1753 if (walk_last == '\\') {
1754 *(walkbuf-1) = *walk++;
1755 walk_last = 0;
1756 continue;
1757 }
1758 if (preg_get_backref(&walk, &backref)) {
1759 if (backref < count) {
1760 if (offsets[backref<<1] < SIZE_MAX) {
1761 match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
1762 walkbuf = zend_mempcpy(walkbuf, subject + offsets[backref << 1], match_len);
1763 }
1764 }
1765 continue;
1766 }
1767 }
1768 *walkbuf++ = *walk++;
1769 walk_last = walk[-1];
1770 }
1771 *walkbuf = '\0';
1772 /* increment the result length by how much we've added to the string */
1773 result_len += (walkbuf - (ZSTR_VAL(result) + result_len));
1774 }
1775
1776 limit--;
1777
1778 /* Advance to the next piece. */
1779 start_offset = last_end_offset = offsets[1];
1780
1781 /* If we have matched an empty string, mimic what Perl's /g options does.
1782 This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
1783 the match again at the same point. If this fails (picked up above) we
1784 advance to the next character. */
1785 if (start_offset == offsets[0]) {
1786 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1788
1789 piece = subject + start_offset;
1790 if (count >= 0 && limit > 0) {
1791 goto matched;
1792 } else if (count == PCRE2_ERROR_NOMATCH || limit == 0) {
1793 /* If we previously set PCRE2_NOTEMPTY_ATSTART after a null match,
1794 this is not necessarily the end. We need to advance
1795 the start offset, and continue. Fudge the offset values
1796 to achieve this, unless we're already at the end of the string. */
1797 if (start_offset < subject_len) {
1798 size_t unit_len = calculate_unit_length(pce, piece);
1799 start_offset += unit_len;
1800 } else {
1801 goto not_matched;
1802 }
1803 } else {
1804 goto error;
1805 }
1806 }
1807
1808 } else if (count == PCRE2_ERROR_NOMATCH || limit == 0) {
1809not_matched:
1810 if (!result && subject_str) {
1811 result = zend_string_copy(subject_str);
1812 break;
1813 }
1814 /* now we know exactly how long it is */
1815 alloc_len = result_len + subject_len - last_end_offset;
1816 if (NULL != result) {
1817 result = zend_string_realloc(result, alloc_len, 0);
1818 } else {
1819 result = zend_string_alloc(alloc_len, 0);
1820 }
1821 /* stick that last bit of string on our output */
1822 memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
1823 result_len += subject_len - last_end_offset;
1824 ZSTR_VAL(result)[result_len] = '\0';
1825 ZSTR_LEN(result) = result_len;
1826 break;
1827 } else {
1828error:
1829 pcre_handle_exec_error(count);
1830 if (result) {
1832 result = NULL;
1833 }
1834 break;
1835 }
1836
1837#ifdef HAVE_PCRE_JIT_SUPPORT
1838 if (pce->preg_options & PREG_JIT) {
1839 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1840 PCRE2_NO_UTF_CHECK, match_data, mctx);
1841 } else
1842#endif
1843 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1844 PCRE2_NO_UTF_CHECK, match_data, mctx);
1845 }
1846 if (match_data != mdata) {
1847 pcre2_match_data_free(match_data);
1848 }
1849
1850 return result;
1851}
1852/* }}} */
1853
1854/* {{{ php_pcre_replace_func_impl() */
1855static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_fcall_info *fci, zend_fcall_info_cache *fcc, size_t limit, size_t *replace_count, zend_long flags)
1856{
1857 uint32_t options; /* Execution options */
1858 int count; /* Count of matched subpatterns */
1859 zend_string **subpat_names; /* Array for named subpatterns */
1860 uint32_t num_subpats; /* Number of captured subpatterns */
1861 size_t new_len; /* Length of needed storage */
1862 size_t alloc_len; /* Actual allocated length */
1863 PCRE2_SIZE start_offset; /* Where the new search starts */
1864 size_t last_end_offset; /* Where the last search ended */
1865 const char *match, /* The current match */
1866 *piece; /* The current piece of subject */
1867 size_t result_len; /* Length of result */
1868 zend_string *result; /* Result of replacement */
1869 zend_string *eval_result; /* Result of custom function */
1870 pcre2_match_data *match_data;
1871 bool old_mdata_used;
1872
1873 /* Calculate the size of the offsets array, and allocate memory for it. */
1874 num_subpats = pce->capture_count + 1;
1875 if (pce->name_count > 0) {
1876 subpat_names = ensure_subpats_table(pce->name_count, pce);
1877 if (UNEXPECTED(!subpat_names)) {
1878 return NULL;
1879 }
1880 } else {
1881 subpat_names = NULL;
1882 }
1883
1884 alloc_len = 0;
1885 result = NULL;
1886
1887 /* Initialize */
1888 match = NULL;
1889 start_offset = 0;
1890 last_end_offset = 0;
1891 result_len = 0;
1893
1894 old_mdata_used = mdata_used;
1895 if (!old_mdata_used && num_subpats <= PHP_PCRE_PREALLOC_MDATA_SIZE) {
1896 mdata_used = 1;
1897 match_data = mdata;
1898 } else {
1900 if (!match_data) {
1902 mdata_used = old_mdata_used;
1903 return NULL;
1904 }
1905 }
1906
1908
1909 /* Array of subpattern offsets */
1910 PCRE2_SIZE *const offsets = pcre2_get_ovector_pointer(match_data);
1911
1912 /* Execute the regular expression. */
1913#ifdef HAVE_PCRE_JIT_SUPPORT
1914 if ((pce->preg_options & PREG_JIT) && options) {
1915 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1916 PCRE2_NO_UTF_CHECK, match_data, mctx);
1917 } else
1918#endif
1919 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1920 options, match_data, mctx);
1921
1922 while (1) {
1923 piece = subject + last_end_offset;
1924
1925 if (count >= 0 && limit) {
1926 /* Check for too many substrings condition. */
1927 if (UNEXPECTED(count == 0)) {
1928 php_error_docref(NULL,E_NOTICE, "Matched, but too many substrings");
1929 count = num_subpats;
1930 }
1931
1932matched:
1933 if (UNEXPECTED(offsets[1] < offsets[0])) {
1935 if (result) {
1937 result = NULL;
1938 }
1939 break;
1940 }
1941
1942 if (replace_count) {
1943 ++*replace_count;
1944 }
1945
1946 /* Set the match location in subject */
1947 match = subject + offsets[0];
1948
1949 new_len = result_len + offsets[0] - last_end_offset; /* part before the match */
1950
1951 /* Use custom function to get replacement string and its length. */
1952 eval_result = preg_do_repl_func(
1953 fci, fcc, subject, offsets, subpat_names, num_subpats, count,
1954 pcre2_get_mark(match_data), flags);
1955
1956 ZEND_ASSERT(eval_result);
1957 new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result) + ZSTR_MAX_OVERHEAD, new_len) -ZSTR_MAX_OVERHEAD;
1958 if (new_len >= alloc_len) {
1959 alloc_len = zend_safe_address_guarded(2, new_len, ZSTR_MAX_OVERHEAD) - ZSTR_MAX_OVERHEAD;
1960 if (result == NULL) {
1961 result = zend_string_alloc(alloc_len, 0);
1962 } else {
1963 result = zend_string_extend(result, alloc_len, 0);
1964 }
1965 }
1966
1967 if (match-piece > 0) {
1968 /* copy the part of the string before the match */
1969 memcpy(ZSTR_VAL(result) + result_len, piece, match-piece);
1970 result_len += (match-piece);
1971 }
1972
1973 /* If using custom function, copy result to the buffer and clean up. */
1974 memcpy(ZSTR_VAL(result) + result_len, ZSTR_VAL(eval_result), ZSTR_LEN(eval_result));
1975 result_len += ZSTR_LEN(eval_result);
1976 zend_string_release_ex(eval_result, 0);
1977
1978 limit--;
1979
1980 /* Advance to the next piece. */
1981 start_offset = last_end_offset = offsets[1];
1982
1983 /* If we have matched an empty string, mimic what Perl's /g options does.
1984 This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
1985 the match again at the same point. If this fails (picked up above) we
1986 advance to the next character. */
1987 if (start_offset == offsets[0]) {
1988 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
1990
1991 piece = subject + start_offset;
1992 if (count >= 0 && limit) {
1993 goto matched;
1994 } else if (count == PCRE2_ERROR_NOMATCH || limit == 0) {
1995 /* If we previously set PCRE2_NOTEMPTY_ATSTART after a null match,
1996 this is not necessarily the end. We need to advance
1997 the start offset, and continue. Fudge the offset values
1998 to achieve this, unless we're already at the end of the string. */
1999 if (start_offset < subject_len) {
2000 size_t unit_len = calculate_unit_length(pce, piece);
2001 start_offset += unit_len;
2002 } else {
2003 goto not_matched;
2004 }
2005 } else {
2006 goto error;
2007 }
2008 }
2009
2010 } else if (count == PCRE2_ERROR_NOMATCH || limit == 0) {
2011not_matched:
2012 if (!result && subject_str) {
2013 result = zend_string_copy(subject_str);
2014 break;
2015 }
2016 /* now we know exactly how long it is */
2017 alloc_len = result_len + subject_len - last_end_offset;
2018 if (NULL != result) {
2019 result = zend_string_realloc(result, alloc_len, 0);
2020 } else {
2021 result = zend_string_alloc(alloc_len, 0);
2022 }
2023 /* stick that last bit of string on our output */
2024 memcpy(ZSTR_VAL(result) + result_len, piece, subject_len - last_end_offset);
2025 result_len += subject_len - last_end_offset;
2026 ZSTR_VAL(result)[result_len] = '\0';
2027 ZSTR_LEN(result) = result_len;
2028 break;
2029 } else {
2030error:
2031 pcre_handle_exec_error(count);
2032 if (result) {
2034 result = NULL;
2035 }
2036 break;
2037 }
2038#ifdef HAVE_PCRE_JIT_SUPPORT
2039 if ((pce->preg_options & PREG_JIT)) {
2040 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
2041 PCRE2_NO_UTF_CHECK, match_data, mctx);
2042 } else
2043#endif
2044 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, subject_len, start_offset,
2045 PCRE2_NO_UTF_CHECK, match_data, mctx);
2046 }
2047 if (match_data != mdata) {
2048 pcre2_match_data_free(match_data);
2049 }
2050 mdata_used = old_mdata_used;
2051
2052 return result;
2053}
2054/* }}} */
2055
2056/* {{{ php_pcre_replace_func */
2057static zend_always_inline zend_string *php_pcre_replace_func(zend_string *regex,
2058 zend_string *subject_str,
2060 size_t limit, size_t *replace_count, zend_long flags)
2061{
2062 pcre_cache_entry *pce; /* Compiled regular expression */
2063 zend_string *result; /* Function result */
2064
2065 /* Compile regex or get it from cache. */
2066 if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
2067 return NULL;
2068 }
2069 pce->refcount++;
2070 result = php_pcre_replace_func_impl(
2071 pce, subject_str, ZSTR_VAL(subject_str), ZSTR_LEN(subject_str), fci, fcc,
2072 limit, replace_count, flags);
2073 pce->refcount--;
2074
2075 return result;
2076}
2077/* }}} */
2078
2079/* {{{ php_pcre_replace_array */
2080static zend_string *php_pcre_replace_array(HashTable *regex,
2081 zend_string *replace_str, HashTable *replace_ht,
2082 zend_string *subject_str, size_t limit, size_t *replace_count)
2083{
2084 zval *regex_entry;
2086
2087 zend_string_addref(subject_str);
2088
2089 if (replace_ht) {
2090 uint32_t replace_idx = 0;
2091
2092 /* For each entry in the regex array, get the entry */
2093 ZEND_HASH_FOREACH_VAL(regex, regex_entry) {
2094 /* Make sure we're dealing with strings. */
2095 zend_string *tmp_regex_str;
2096 zend_string *regex_str = zval_get_tmp_string(regex_entry, &tmp_regex_str);
2097 zend_string *replace_entry_str, *tmp_replace_entry_str;
2098 zval *zv;
2099
2100 /* Get current entry */
2101 while (1) {
2102 if (replace_idx == replace_ht->nNumUsed) {
2103 replace_entry_str = ZSTR_EMPTY_ALLOC();
2104 tmp_replace_entry_str = NULL;
2105 break;
2106 }
2107 zv = ZEND_HASH_ELEMENT(replace_ht, replace_idx);
2108 replace_idx++;
2109 if (Z_TYPE_P(zv) != IS_UNDEF) {
2110 replace_entry_str = zval_get_tmp_string(zv, &tmp_replace_entry_str);
2111 break;
2112 }
2113 }
2114
2115 /* Do the actual replacement and put the result back into subject_str
2116 for further replacements. */
2117 result = php_pcre_replace(regex_str, subject_str, ZSTR_VAL(subject_str),
2118 ZSTR_LEN(subject_str), replace_entry_str, limit, replace_count);
2119 zend_tmp_string_release(tmp_replace_entry_str);
2120 zend_tmp_string_release(tmp_regex_str);
2121 zend_string_release_ex(subject_str, 0);
2122 subject_str = result;
2123 if (UNEXPECTED(result == NULL)) {
2124 break;
2125 }
2127
2128 } else {
2129 ZEND_ASSERT(replace_str != NULL);
2130
2131 /* For each entry in the regex array, get the entry */
2132 ZEND_HASH_FOREACH_VAL(regex, regex_entry) {
2133 /* Make sure we're dealing with strings. */
2134 zend_string *tmp_regex_str;
2135 zend_string *regex_str = zval_get_tmp_string(regex_entry, &tmp_regex_str);
2136
2137 /* Do the actual replacement and put the result back into subject_str
2138 for further replacements. */
2139 result = php_pcre_replace(regex_str, subject_str, ZSTR_VAL(subject_str),
2140 ZSTR_LEN(subject_str), replace_str, limit, replace_count);
2141 zend_tmp_string_release(tmp_regex_str);
2142 zend_string_release_ex(subject_str, 0);
2143 subject_str = result;
2144
2145 if (UNEXPECTED(result == NULL)) {
2146 break;
2147 }
2149 }
2150
2151 return subject_str;
2152}
2153/* }}} */
2154
2155/* {{{ php_replace_in_subject */
2156static zend_always_inline zend_string *php_replace_in_subject(
2157 zend_string *regex_str, HashTable *regex_ht,
2158 zend_string *replace_str, HashTable *replace_ht,
2159 zend_string *subject, size_t limit, size_t *replace_count)
2160{
2162
2163 if (regex_str) {
2164 ZEND_ASSERT(replace_str != NULL);
2165 result = php_pcre_replace(regex_str, subject, ZSTR_VAL(subject), ZSTR_LEN(subject),
2166 replace_str, limit, replace_count);
2167 } else {
2168 ZEND_ASSERT(regex_ht != NULL);
2169 result = php_pcre_replace_array(regex_ht, replace_str, replace_ht, subject,
2170 limit, replace_count);
2171 }
2172 return result;
2173}
2174/* }}} */
2175
2176/* {{{ php_replace_in_subject_func */
2177static zend_string *php_replace_in_subject_func(zend_string *regex_str, HashTable *regex_ht,
2179 zend_string *subject, size_t limit, size_t *replace_count, zend_long flags)
2180{
2182
2183 if (regex_str) {
2184 result = php_pcre_replace_func(
2185 regex_str, subject, fci, fcc, limit, replace_count, flags);
2186 return result;
2187 } else {
2188 /* If regex is an array */
2189 zval *regex_entry;
2190
2191 ZEND_ASSERT(regex_ht != NULL);
2192
2193 zend_string_addref(subject);
2194
2195 /* For each entry in the regex array, get the entry */
2196 ZEND_HASH_FOREACH_VAL(regex_ht, regex_entry) {
2197 /* Make sure we're dealing with strings. */
2198 zend_string *tmp_regex_entry_str;
2199 zend_string *regex_entry_str = zval_get_tmp_string(regex_entry, &tmp_regex_entry_str);
2200
2201 /* Do the actual replacement and put the result back into subject
2202 for further replacements. */
2203 result = php_pcre_replace_func(
2204 regex_entry_str, subject, fci, fcc, limit, replace_count, flags);
2205 zend_tmp_string_release(tmp_regex_entry_str);
2206 zend_string_release(subject);
2207 subject = result;
2208 if (UNEXPECTED(result == NULL)) {
2209 break;
2210 }
2212
2213 return subject;
2214 }
2215}
2216/* }}} */
2217
2218/* {{{ preg_replace_func_impl */
2219static size_t preg_replace_func_impl(zval *return_value,
2220 zend_string *regex_str, HashTable *regex_ht,
2222 zend_string *subject_str, HashTable *subject_ht, zend_long limit_val, zend_long flags)
2223{
2225 size_t replace_count = 0;
2226
2227 if (subject_str) {
2228 result = php_replace_in_subject_func(
2229 regex_str, regex_ht, fci, fcc, subject_str, limit_val, &replace_count, flags);
2230 if (result != NULL) {
2232 } else {
2233 RETVAL_NULL();
2234 }
2235 } else {
2236 /* if subject is an array */
2237 zval *subject_entry, zv;
2238 zend_string *string_key;
2239 zend_ulong num_key;
2240
2241 ZEND_ASSERT(subject_ht != NULL);
2242
2243 array_init_size(return_value, zend_hash_num_elements(subject_ht));
2244 HashTable *return_value_ht = Z_ARRVAL_P(return_value);
2245
2246 /* For each subject entry, convert it to string, then perform replacement
2247 and add the result to the return_value array. */
2248 ZEND_HASH_FOREACH_KEY_VAL(subject_ht, num_key, string_key, subject_entry) {
2249 zend_string *tmp_subject_entry_str;
2250 zend_string *subject_entry_str = zval_get_tmp_string(subject_entry, &tmp_subject_entry_str);
2251
2252 result = php_replace_in_subject_func(
2253 regex_str, regex_ht, fci, fcc, subject_entry_str, limit_val, &replace_count, flags);
2254 if (result != NULL) {
2255 /* Add to return array */
2256 ZVAL_STR(&zv, result);
2257 if (string_key) {
2258 zend_hash_add_new(return_value_ht, string_key, &zv);
2259 } else {
2260 zend_hash_index_add_new(return_value_ht, num_key, &zv);
2261 }
2262 }
2263 zend_tmp_string_release(tmp_subject_entry_str);
2265 }
2266
2267 return replace_count;
2268}
2269/* }}} */
2270
2271static void _preg_replace_common(
2273 HashTable *regex_ht, zend_string *regex_str,
2274 HashTable *replace_ht, zend_string *replace_str,
2275 HashTable *subject_ht, zend_string *subject_str,
2276 zend_long limit,
2277 zval *zcount,
2278 bool is_filter
2279) {
2280 size_t replace_count = 0;
2282 size_t old_replace_count;
2283
2284 /* If replace is an array then the regex argument needs to also be an array */
2285 if (replace_ht && !regex_ht) {
2286 zend_argument_type_error(1, "must be of type array when argument #2 ($replacement) is an array, string given");
2287 RETURN_THROWS();
2288 }
2289
2290 if (subject_str) {
2291 old_replace_count = replace_count;
2292 result = php_replace_in_subject(regex_str, regex_ht, replace_str, replace_ht,
2293 subject_str, limit, &replace_count);
2294 if (result != NULL) {
2295 if (!is_filter || replace_count > old_replace_count) {
2297 } else {
2299 RETVAL_NULL();
2300 }
2301 } else {
2302 RETVAL_NULL();
2303 }
2304 } else {
2305 /* if subject is an array */
2306 zval *subject_entry, zv;
2307 zend_string *string_key;
2308 zend_ulong num_key;
2309
2310 ZEND_ASSERT(subject_ht != NULL);
2311
2312 array_init_size(return_value, zend_hash_num_elements(subject_ht));
2313 HashTable *return_value_ht = Z_ARRVAL_P(return_value);
2314
2315 /* For each subject entry, convert it to string, then perform replacement
2316 and add the result to the return_value array. */
2317 ZEND_HASH_FOREACH_KEY_VAL(subject_ht, num_key, string_key, subject_entry) {
2318 old_replace_count = replace_count;
2319 zend_string *tmp_subject_entry_str;
2320 zend_string *subject_entry_str = zval_get_tmp_string(subject_entry, &tmp_subject_entry_str);
2321 result = php_replace_in_subject(regex_str, regex_ht, replace_str, replace_ht,
2322 subject_entry_str, limit, &replace_count);
2323
2324 if (result != NULL) {
2325 if (!is_filter || replace_count > old_replace_count) {
2326 /* Add to return array */
2327 ZVAL_STR(&zv, result);
2328 if (string_key) {
2329 zend_hash_add_new(return_value_ht, string_key, &zv);
2330 } else {
2331 zend_hash_index_add_new(return_value_ht, num_key, &zv);
2332 }
2333 } else {
2335 }
2336 }
2337 zend_tmp_string_release(tmp_subject_entry_str);
2339 }
2340
2341 if (zcount) {
2342 ZEND_TRY_ASSIGN_REF_LONG(zcount, replace_count);
2343 }
2344}
2345
2346/* {{{ preg_replace_common */
2347static void preg_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool is_filter)
2348{
2349 zend_string *regex_str, *replace_str, *subject_str;
2350 HashTable *regex_ht, *replace_ht, *subject_ht;
2351 zend_long limit = -1;
2352 zval *zcount = NULL;
2353
2354 /* Get function parameters and do error-checking. */
2356 Z_PARAM_ARRAY_HT_OR_STR(regex_ht, regex_str)
2357 Z_PARAM_ARRAY_HT_OR_STR(replace_ht, replace_str)
2358 Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
2360 Z_PARAM_LONG(limit)
2361 Z_PARAM_ZVAL(zcount)
2363
2364 _preg_replace_common(
2366 regex_ht, regex_str,
2367 replace_ht, replace_str,
2368 subject_ht, subject_str,
2369 limit, zcount, is_filter);
2370}
2371/* }}} */
2372
2373/* {{{ Perform Perl-style regular expression replacement. */
2375{
2376 preg_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
2377}
2378/* }}} */
2379
2381{
2382 zend_string *regex_str, *replace_str, *subject_str;
2383 HashTable *regex_ht, *replace_ht, *subject_ht;
2384 zval regex_tmp, replace_tmp, subject_tmp;
2385
2386 Z_FLF_PARAM_ARRAY_HT_OR_STR(1, regex_ht, regex_str, regex_tmp);
2387 Z_FLF_PARAM_ARRAY_HT_OR_STR(2, replace_ht, replace_str, replace_tmp);
2388 Z_FLF_PARAM_ARRAY_HT_OR_STR(3, subject_ht, subject_str, subject_tmp);
2389
2390 _preg_replace_common(
2392 regex_ht, regex_str,
2393 replace_ht, replace_str,
2394 subject_ht, subject_str,
2395 /* limit */ -1, /* zcount */ NULL, /* is_filter */ false);
2396
2397flf_clean:;
2398 Z_FLF_PARAM_FREE_STR(1, regex_tmp);
2399 Z_FLF_PARAM_FREE_STR(2, replace_tmp);
2400 Z_FLF_PARAM_FREE_STR(3, subject_tmp);
2401}
2402
2403/* {{{ Perform Perl-style regular expression replacement using replacement callback. */
2405{
2406 zval *zcount = NULL;
2407 zend_string *regex_str;
2408 HashTable *regex_ht;
2409 zend_string *subject_str;
2410 HashTable *subject_ht;
2411 zend_long limit = -1, flags = 0;
2412 size_t replace_count;
2413 zend_fcall_info fci;
2415
2416 /* Get function parameters and do error-checking. */
2418 Z_PARAM_ARRAY_HT_OR_STR(regex_ht, regex_str)
2419 Z_PARAM_FUNC(fci, fcc)
2420 Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
2422 Z_PARAM_LONG(limit)
2423 Z_PARAM_ZVAL(zcount)
2426
2427 replace_count = preg_replace_func_impl(return_value, regex_str, regex_ht,
2428 &fci, &fcc,
2429 subject_str, subject_ht, limit, flags);
2430 if (zcount) {
2431 ZEND_TRY_ASSIGN_REF_LONG(zcount, replace_count);
2432 }
2433}
2434/* }}} */
2435
2436/* {{{ Perform Perl-style regular expression replacement using replacement callback. */
2438{
2439 zval zv, *replace, *zcount = NULL;
2440 HashTable *pattern, *subject_ht;
2441 zend_string *subject_str, *str_idx_regex;
2442 zend_long limit = -1, flags = 0;
2443 size_t replace_count = 0;
2444 zend_fcall_info fci;
2446
2447 /* Get function parameters and do error-checking. */
2449 Z_PARAM_ARRAY_HT(pattern)
2450 Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
2452 Z_PARAM_LONG(limit)
2453 Z_PARAM_ZVAL(zcount)
2456
2457 fci.size = sizeof(fci);
2458 fci.object = NULL;
2459 fci.named_params = NULL;
2460
2461 if (subject_ht) {
2462 GC_TRY_ADDREF(subject_ht);
2463 } else {
2464 GC_TRY_ADDREF(subject_str);
2465 }
2466
2467 ZEND_HASH_FOREACH_STR_KEY_VAL(pattern, str_idx_regex, replace) {
2468 if (!zend_is_callable_ex(replace, NULL, 0, NULL, &fcc, NULL)) {
2469 zend_argument_type_error(1, "must contain only valid callbacks");
2470 goto error;
2471 }
2472 if (!str_idx_regex) {
2473 zend_argument_type_error(1, "must contain only string patterns as keys");
2474 goto error;
2475 }
2476
2477 ZVAL_COPY_VALUE(&fci.function_name, replace);
2478
2479 replace_count += preg_replace_func_impl(&zv, str_idx_regex, /* regex_ht */ NULL, &fci, &fcc,
2480 subject_str, subject_ht, limit, flags);
2481 switch (Z_TYPE(zv)) {
2482 case IS_ARRAY:
2483 ZEND_ASSERT(subject_ht);
2484 zend_array_release(subject_ht);
2485 subject_ht = Z_ARR(zv);
2486 break;
2487 case IS_STRING:
2488 ZEND_ASSERT(subject_str);
2489 zend_string_release(subject_str);
2490 subject_str = Z_STR(zv);
2491 break;
2492 case IS_NULL:
2493 RETVAL_NULL();
2494 goto error;
2496 }
2497
2498 if (EG(exception)) {
2499 goto error;
2500 }
2502
2503 if (zcount) {
2504 ZEND_TRY_ASSIGN_REF_LONG(zcount, replace_count);
2505 }
2506
2507 if (subject_ht) {
2508 RETVAL_ARR(subject_ht);
2509 // Unset the type_flags of immutable arrays to prevent the VM from performing refcounting
2510 if (GC_FLAGS(subject_ht) & IS_ARRAY_IMMUTABLE) {
2512 }
2513 return;
2514 } else {
2515 RETURN_STR(subject_str);
2516 }
2517
2518error:
2519 if (subject_ht) {
2520 zend_array_release(subject_ht);
2521 } else {
2522 zend_string_release(subject_str);
2523 }
2524}
2525/* }}} */
2526
2527/* {{{ Perform Perl-style regular expression replacement and only return matches. */
2529{
2530 preg_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
2531}
2532/* }}} */
2533
2534/* {{{ Split string into an array using a perl-style regular expression as a delimiter */
2536{
2537 zend_string *regex; /* Regular expression */
2538 zend_string *subject; /* String to match against */
2539 zend_long limit_val = -1;/* Integer value of limit */
2540 zend_long flags = 0; /* Match control flags */
2541 pcre_cache_entry *pce; /* Compiled regular expression */
2542
2543 /* Get function parameters and do error checking */
2545 Z_PARAM_STR(regex)
2546 Z_PARAM_STR(subject)
2548 Z_PARAM_LONG(limit_val)
2551
2552 /* Compile regex or get it from cache. */
2553 if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
2555 }
2556
2557 pce->refcount++;
2558 php_pcre_split_impl(pce, subject, return_value, limit_val, flags);
2559 pce->refcount--;
2560}
2561/* }}} */
2562
2563/* {{{ php_pcre_split */
2565 zend_long limit_val, zend_long flags)
2566{
2567 uint32_t options; /* Execution options */
2568 int count; /* Count of matched subpatterns */
2569 PCRE2_SIZE start_offset; /* Where the new search starts */
2570 PCRE2_SIZE last_match_offset; /* Location of last match */
2571 uint32_t no_empty; /* If NO_EMPTY flag is set */
2572 uint32_t delim_capture; /* If delimiters should be captured */
2573 uint32_t offset_capture; /* If offsets should be captured */
2574 uint32_t num_subpats; /* Number of captured subpatterns */
2575 zval tmp;
2576 pcre2_match_data *match_data;
2577 char *subject = ZSTR_VAL(subject_str);
2578
2579 no_empty = flags & PREG_SPLIT_NO_EMPTY;
2580 delim_capture = flags & PREG_SPLIT_DELIM_CAPTURE;
2581 offset_capture = flags & PREG_SPLIT_OFFSET_CAPTURE;
2582
2583 /* Initialize return value */
2585 HashTable *return_value_ht = Z_ARRVAL_P(return_value);
2586
2587 /* Calculate the size of the offsets array, and allocate memory for it. */
2588 num_subpats = pce->capture_count + 1;
2589
2590 /* Start at the beginning of the string */
2591 start_offset = 0;
2592 last_match_offset = 0;
2594
2595 if (limit_val == -1) {
2596 /* pass */
2597 } else if (limit_val == 0) {
2598 limit_val = -1;
2599 } else if (limit_val <= 1) {
2600 goto last;
2601 }
2602
2603 if (!mdata_used && num_subpats <= PHP_PCRE_PREALLOC_MDATA_SIZE) {
2604 match_data = mdata;
2605 } else {
2607 if (!match_data) {
2611 }
2612 }
2613
2615
2616 /* Array of subpattern offsets */
2617 PCRE2_SIZE *const offsets = pcre2_get_ovector_pointer(match_data);
2618
2619#ifdef HAVE_PCRE_JIT_SUPPORT
2620 if ((pce->preg_options & PREG_JIT) && options) {
2621 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, ZSTR_LEN(subject_str), start_offset,
2622 PCRE2_NO_UTF_CHECK, match_data, mctx);
2623 } else
2624#endif
2625 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, ZSTR_LEN(subject_str), start_offset,
2626 options, match_data, mctx);
2627
2628 while (1) {
2629 /* If something matched */
2630 if (count >= 0) {
2631 /* Check for too many substrings condition. */
2632 if (UNEXPECTED(count == 0)) {
2633 php_error_docref(NULL,E_NOTICE, "Matched, but too many substrings");
2634 count = num_subpats;
2635 }
2636
2637matched:
2638 if (UNEXPECTED(offsets[1] < offsets[0])) {
2640 break;
2641 }
2642
2643 if (!no_empty || offsets[0] != last_match_offset) {
2644 if (offset_capture) {
2645 /* Add (match, offset) pair to the return value */
2646 add_offset_pair(
2647 return_value_ht, subject, last_match_offset, offsets[0],
2648 NULL, 0);
2649 } else {
2650 /* Add the piece to the return value */
2651 populate_match_value_str(&tmp, subject, last_match_offset, offsets[0]);
2652 zend_hash_next_index_insert_new(return_value_ht, &tmp);
2653 }
2654
2655 /* One less left to do */
2656 if (limit_val != -1)
2657 limit_val--;
2658 }
2659
2660 if (delim_capture) {
2661 size_t i;
2662 for (i = 1; i < count; i++) {
2663 /* If we have matched a delimiter */
2664 if (!no_empty || offsets[2*i] != offsets[2*i+1]) {
2665 if (offset_capture) {
2666 add_offset_pair(
2667 return_value_ht, subject, offsets[2*i], offsets[2*i+1], NULL, 0);
2668 } else {
2669 populate_match_value_str(&tmp, subject, offsets[2*i], offsets[2*i+1]);
2670 zend_hash_next_index_insert_new(return_value_ht, &tmp);
2671 }
2672 }
2673 }
2674 }
2675
2676 /* Advance to the position right after the last full match */
2677 start_offset = last_match_offset = offsets[1];
2678
2679 /* If we have matched an empty string, mimic what Perl's /g options does.
2680 This turns out to be rather cunning. First we set PCRE2_NOTEMPTY_ATSTART and try
2681 the match again at the same point. If this fails (picked up above) we
2682 advance to the next character. */
2683 if (start_offset == offsets[0]) {
2684 /* Get next piece if no limit or limit not yet reached and something matched*/
2685 if (limit_val != -1 && limit_val <= 1) {
2686 break;
2687 }
2688 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, ZSTR_LEN(subject_str), start_offset,
2690 if (count >= 0) {
2691 goto matched;
2692 } else if (count == PCRE2_ERROR_NOMATCH) {
2693 /* If we previously set PCRE2_NOTEMPTY_ATSTART after a null match,
2694 this is not necessarily the end. We need to advance
2695 the start offset, and continue. Fudge the offset values
2696 to achieve this, unless we're already at the end of the string. */
2697 if (start_offset < ZSTR_LEN(subject_str)) {
2698 start_offset += calculate_unit_length(pce, subject + start_offset);
2699 } else {
2700 break;
2701 }
2702 } else {
2703 goto error;
2704 }
2705 }
2706
2707 } else if (count == PCRE2_ERROR_NOMATCH) {
2708 break;
2709 } else {
2710error:
2711 pcre_handle_exec_error(count);
2712 break;
2713 }
2714
2715 /* Get next piece if no limit or limit not yet reached and something matched*/
2716 if (limit_val != -1 && limit_val <= 1) {
2717 break;
2718 }
2719
2720#ifdef HAVE_PCRE_JIT_SUPPORT
2721 if (pce->preg_options & PREG_JIT) {
2722 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)subject, ZSTR_LEN(subject_str), start_offset,
2723 PCRE2_NO_UTF_CHECK, match_data, mctx);
2724 } else
2725#endif
2726 count = pcre2_match(pce->re, (PCRE2_SPTR)subject, ZSTR_LEN(subject_str), start_offset,
2727 PCRE2_NO_UTF_CHECK, match_data, mctx);
2728 }
2729 if (match_data != mdata) {
2730 pcre2_match_data_free(match_data);
2731 }
2732
2736 }
2737
2738last:
2739 start_offset = last_match_offset; /* the offset might have been incremented, but without further successful matches */
2740
2741 if (!no_empty || start_offset < ZSTR_LEN(subject_str)) {
2742 if (offset_capture) {
2743 /* Add the last (match, offset) pair to the return value */
2744 add_offset_pair(return_value_ht, subject, start_offset, ZSTR_LEN(subject_str), NULL, 0);
2745 } else {
2746 /* Add the last piece to the return value */
2747 if (start_offset == 0) {
2748 ZVAL_STR_COPY(&tmp, subject_str);
2749 } else {
2750 populate_match_value_str(&tmp, subject, start_offset, ZSTR_LEN(subject_str));
2751 }
2752 zend_hash_next_index_insert_new(return_value_ht, &tmp);
2753 }
2754 }
2755}
2756/* }}} */
2757
2758/* {{{ Quote regular expression characters plus an optional character */
2760{
2761 zend_string *str; /* Input string argument */
2762 zend_string *delim = NULL; /* Additional delimiter argument */
2763 char *in_str; /* Input string */
2764 char *in_str_end; /* End of the input string */
2765 zend_string *out_str; /* Output string with quoted characters */
2766 size_t extra_len; /* Number of additional characters */
2767 char *p, /* Iterator for input string */
2768 *q, /* Iterator for output string */
2769 delim_char = '\0', /* Delimiter character to be quoted */
2770 c; /* Current character */
2771
2772 /* Get the arguments and check for errors */
2774 Z_PARAM_STR(str)
2776 Z_PARAM_STR_OR_NULL(delim)
2778
2779 /* Nothing to do if we got an empty string */
2780 if (ZSTR_LEN(str) == 0) {
2782 }
2783
2784 in_str = ZSTR_VAL(str);
2785 in_str_end = in_str + ZSTR_LEN(str);
2786
2787 if (delim) {
2788 delim_char = ZSTR_VAL(delim)[0];
2789 }
2790
2791 /* Go through the string and quote necessary characters */
2792 extra_len = 0;
2793 p = in_str;
2794 do {
2795 c = *p;
2796 switch(c) {
2797 case '.':
2798 case '\\':
2799 case '+':
2800 case '*':
2801 case '?':
2802 case '[':
2803 case '^':
2804 case ']':
2805 case '$':
2806 case '(':
2807 case ')':
2808 case '{':
2809 case '}':
2810 case '=':
2811 case '!':
2812 case '>':
2813 case '<':
2814 case '|':
2815 case ':':
2816 case '-':
2817 case '#':
2818 extra_len++;
2819 break;
2820
2821 case '\0':
2822 extra_len+=3;
2823 break;
2824
2825 default:
2826 if (c == delim_char) {
2827 extra_len++;
2828 }
2829 break;
2830 }
2831 p++;
2832 } while (p != in_str_end);
2833
2834 if (extra_len == 0) {
2835 RETURN_STR_COPY(str);
2836 }
2837
2838 /* Allocate enough memory so that even if each character
2839 is quoted, we won't run out of room */
2840 out_str = zend_string_safe_alloc(1, ZSTR_LEN(str), extra_len, 0);
2841 q = ZSTR_VAL(out_str);
2842 p = in_str;
2843
2844 do {
2845 c = *p;
2846 switch(c) {
2847 case '.':
2848 case '\\':
2849 case '+':
2850 case '*':
2851 case '?':
2852 case '[':
2853 case '^':
2854 case ']':
2855 case '$':
2856 case '(':
2857 case ')':
2858 case '{':
2859 case '}':
2860 case '=':
2861 case '!':
2862 case '>':
2863 case '<':
2864 case '|':
2865 case ':':
2866 case '-':
2867 case '#':
2868 *q++ = '\\';
2869 *q++ = c;
2870 break;
2871
2872 case '\0':
2873 *q++ = '\\';
2874 *q++ = '0';
2875 *q++ = '0';
2876 *q++ = '0';
2877 break;
2878
2879 default:
2880 if (c == delim_char) {
2881 *q++ = '\\';
2882 }
2883 *q++ = c;
2884 break;
2885 }
2886 p++;
2887 } while (p != in_str_end);
2888 *q = '\0';
2889
2890 RETURN_NEW_STR(out_str);
2891}
2892/* }}} */
2893
2894/* {{{ Searches array and returns entries which match regex */
2896{
2897 zend_string *regex; /* Regular expression */
2898 zval *input; /* Input array */
2899 zend_long flags = 0; /* Match control flags */
2900 pcre_cache_entry *pce; /* Compiled regular expression */
2901
2902 /* Get arguments and do error checking */
2904 Z_PARAM_STR(regex)
2905 Z_PARAM_ARRAY(input)
2909
2910 /* Compile regex or get it from cache. */
2911 if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
2913 }
2914
2915 pce->refcount++;
2917 pce->refcount--;
2918}
2919/* }}} */
2920
2922{
2923 zval *entry; /* An entry in the input array */
2924 uint32_t num_subpats; /* Number of captured subpatterns */
2925 int count; /* Count of matched subpatterns */
2926 uint32_t options; /* Execution options */
2927 zend_string *string_key;
2928 zend_ulong num_key;
2929 bool invert; /* Whether to return non-matching
2930 entries */
2931 pcre2_match_data *match_data;
2932 invert = flags & PREG_GREP_INVERT ? 1 : 0;
2933
2934 /* Calculate the size of the offsets array, and allocate memory for it. */
2935 num_subpats = pce->capture_count + 1;
2936
2937 /* Initialize return array */
2939 HashTable *return_value_ht = Z_ARRVAL_P(return_value);
2940
2942
2943 if (!mdata_used && num_subpats <= PHP_PCRE_PREALLOC_MDATA_SIZE) {
2944 match_data = mdata;
2945 } else {
2947 if (!match_data) {
2949 return;
2950 }
2951 }
2952
2954
2955 /* Go through the input array */
2956 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
2957 zend_string *tmp_subject_str;
2958 zend_string *subject_str = zval_get_tmp_string(entry, &tmp_subject_str);
2959
2960 /* Perform the match */
2961#ifdef HAVE_PCRE_JIT_SUPPORT
2962 if ((pce->preg_options & PREG_JIT) && options) {
2963 count = pcre2_jit_match(pce->re, (PCRE2_SPTR)ZSTR_VAL(subject_str), ZSTR_LEN(subject_str), 0,
2964 PCRE2_NO_UTF_CHECK, match_data, mctx);
2965 } else
2966#endif
2967 count = pcre2_match(pce->re, (PCRE2_SPTR)ZSTR_VAL(subject_str), ZSTR_LEN(subject_str), 0,
2968 options, match_data, mctx);
2969
2970 /* If the entry fits our requirements */
2971 if (count >= 0) {
2972 /* Check for too many substrings condition. */
2973 if (UNEXPECTED(count == 0)) {
2974 php_error_docref(NULL, E_NOTICE, "Matched, but too many substrings");
2975 }
2976 if (!invert) {
2977 Z_TRY_ADDREF_P(entry);
2978
2979 /* Add to return array */
2980 if (string_key) {
2981 zend_hash_update(return_value_ht, string_key, entry);
2982 } else {
2983 zend_hash_index_update(return_value_ht, num_key, entry);
2984 }
2985 }
2986 } else if (count == PCRE2_ERROR_NOMATCH) {
2987 if (invert) {
2988 Z_TRY_ADDREF_P(entry);
2989
2990 /* Add to return array */
2991 if (string_key) {
2992 zend_hash_update(return_value_ht, string_key, entry);
2993 } else {
2994 zend_hash_index_update(return_value_ht, num_key, entry);
2995 }
2996 }
2997 } else {
2998 pcre_handle_exec_error(count);
2999 zend_tmp_string_release(tmp_subject_str);
3000 break;
3001 }
3002
3003 zend_tmp_string_release(tmp_subject_str);
3005 if (match_data != mdata) {
3006 pcre2_match_data_free(match_data);
3007 }
3008}
3009/* }}} */
3010
3011/* {{{ Returns the error code of the last regexp execution. */
3018/* }}} */
3019
3020/* {{{ Returns the error message of the last regexp execution. */
3022{
3024
3025 RETURN_STRING(php_pcre_get_error_msg(PCRE_G(error_code)));
3026}
3027/* }}} */
3028
3029/* {{{ module definition structures */
3030
3033 "pcre",
3034 ext_functions,
3035 PHP_MINIT(pcre),
3036 PHP_MSHUTDOWN(pcre),
3037 PHP_RINIT(pcre),
3038 PHP_RSHUTDOWN(pcre),
3039 PHP_MINFO(pcre),
3041 PHP_MODULE_GLOBALS(pcre),
3042 PHP_GINIT(pcre),
3043 PHP_GSHUTDOWN(pcre),
3044 NULL,
3046};
3047
3048#ifdef COMPILE_DL_PCRE
3049ZEND_GET_MODULE(pcre)
3050#endif
3051
3052/* }}} */
3053
3055{/*{{{*/
3056 return mctx;
3057}/*}}}*/
3058
3060{/*{{{*/
3061 return gctx;
3062}/*}}}*/
3063
3065{/*{{{*/
3066 return cctx;
3067}/*}}}*/
3068
3070{/*{{{*/
3071 assert(NULL != pce);
3072 pce->refcount++;
3073}/*}}}*/
3074
3076{/*{{{*/
3077 assert(NULL != pce);
3078 assert(0 != pce->refcount);
3079 pce->refcount--;
3080}/*}}}*/
3081
3083{/*{{{*/
3084 assert(NULL != pce);
3085 return pce->re;
3086}/*}}}*/
size_t len
Definition apprentice.c:174
bool exception
Definition assert.c:30
#define BG(v)
count(Countable|array $value, int $mode=COUNT_NORMAL)
assert(mixed $assertion, Throwable|string|null $description=null)
strchr(string $haystack, string $needle, bool $before_needle=false)
error($message)
Definition ext_skel.php:22
zval * zv
Definition ffi.c:3975
new_type size
Definition ffi.c:4365
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
zval * arg
Definition ffi.c:3975
zval * val
Definition ffi.c:4262
buf start
Definition ffi.c:4687
#define SIZE_MAX
Definition funcs.c:51
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define PCRE2_INFO_CAPTURECOUNT
Definition pcre2.h:418
#define PCRE2_DOLLAR_ENDONLY
Definition pcre2.h:123
#define PCRE2_ERROR_RECURSIONLIMIT
Definition pcre2.h:395
#define pcre2_match_data_create
Definition pcre2.h:885
#define PCRE2_ANCHORED
Definition pcre2.h:105
#define pcre2_code_free
Definition pcre2.h:852
#define PCRE2_NOTEMPTY_ATSTART
Definition pcre2.h:179
#define pcre2_jit_stack_assign
Definition pcre2.h:876
#define pcre2_set_compile_extra_options
Definition pcre2.h:897
#define pcre2_general_context
Definition pcre2.h:840
#define PCRE2_UNSET
Definition pcre2.h:482
#define PCRE2_INFO_NAMETABLE
Definition pcre2.h:433
#define PCRE2_ERROR_NOMATCH
Definition pcre2.h:327
#define PCRE2_UCP
Definition pcre2.h:136
#define PCRE2_ERROR_INTERNAL
Definition pcre2.h:385
#define PCRE2_UCHAR
Definition pcre2.h:819
#define PCRE2_ERROR_UTF8_ERR21
Definition pcre2.h:352
#define PCRE2_DOTALL
Definition pcre2.h:124
#define pcre2_jit_stack
Definition pcre2.h:824
#define pcre2_get_error_message
Definition pcre2.h:866
#define pcre2_code
Definition pcre2.h:822
#define pcre2_general_context_free
Definition pcre2.h:865
#define PCRE2_EXTENDED
Definition pcre2.h:126
#define pcre2_maketables
Definition pcre2.h:879
#define pcre2_match_context_free
Definition pcre2.h:884
#define pcre2_set_match_limit
Definition pcre2.h:903
#define pcre2_compile_context_free
Definition pcre2.h:856
#define pcre2_get_ovector_pointer
Definition pcre2.h:870
#define pcre2_jit_stack_free
Definition pcre2.h:878
#define pcre2_compile
Definition pcre2.h:853
#define PCRE2_ERROR_BADOFFSET
Definition pcre2.h:374
#define PCRE2_INFO_JITSIZE
Definition pcre2.h:424
#define pcre2_match
Definition pcre2.h:881
#define PCRE2_UNGREEDY
Definition pcre2.h:137
#define PCRE2_SIZE
Definition pcre2.h:479
#define pcre2_set_character_tables
Definition pcre2.h:896
#define pcre2_pattern_info
Definition pcre2.h:889
#define pcre2_set_depth_limit
Definition pcre2.h:899
#define pcre2_match_data_create_from_pattern
Definition pcre2.h:886
#define PCRE2_SPTR
Definition pcre2.h:820
#define pcre2_jit_match
Definition pcre2.h:874
#define PCRE2_CASELESS
Definition pcre2.h:122
#define pcre2_general_context_create
Definition pcre2.h:864
#define pcre2_match_data
Definition pcre2.h:844
#define pcre2_match_context
Definition pcre2.h:843
#define pcre2_get_mark
Definition pcre2.h:867
#define pcre2_match_data_free
Definition pcre2.h:887
#define PCRE2_ERROR_JIT_STACKLIMIT
Definition pcre2.h:387
#define PCRE2_CONFIG_UNICODE_VERSION
Definition pcre2.h:456
#define PCRE2_EXTRA_CASELESS_RESTRICT
Definition pcre2.h:156
#define PCRE2_ERROR_MATCHLIMIT
Definition pcre2.h:388
#define PCRE2_DUPNAMES
Definition pcre2.h:125
#define PCRE2_JIT_COMPLETE
Definition pcre2.h:165
#define PCRE2_INFO_NAMECOUNT
Definition pcre2.h:431
#define PCRE2_MULTILINE
Definition pcre2.h:129
#define PCRE2_NO_AUTO_CAPTURE
Definition pcre2.h:132
#define pcre2_jit_stack_create
Definition pcre2.h:877
#define PCRE2_UTF
Definition pcre2.h:138
#define PCRE2_CONFIG_JIT
Definition pcre2.h:446
#define PCRE2_NO_UTF_CHECK
Definition pcre2.h:106
#define pcre2_config
Definition pcre2.h:857
#define pcre2_match_context_create
Definition pcre2.h:883
#define PCRE2_NO_START_OPTIMIZE
Definition pcre2.h:135
#define pcre2_compile_context_create
Definition pcre2.h:855
#define pcre2_jit_compile
Definition pcre2.h:873
#define pcre2_compile_context
Definition pcre2.h:841
#define PCRE2_INFO_NAMEENTRYSIZE
Definition pcre2.h:432
#define PCRE2_CONFIG_VERSION
Definition pcre2.h:457
#define PCRE2_CONFIG_JITTARGET
Definition pcre2.h:447
#define PCRE2_ERROR_BADUTFOFFSET
Definition pcre2.h:377
#define PCRE2_ERROR_NOMEMORY
Definition pcre2.h:389
php_info_print_table_start()
Definition info.c:1064
php_info_print_table_row(2, "PDO Driver for Firebird", "enabled")
php_info_print_table_end()
Definition info.c:1074
#define PHP_GINIT
Definition php.h:397
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MSHUTDOWN_FUNCTION
Definition php.h:401
#define PHP_MINFO
Definition php.h:396
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define PHP_RINIT
Definition php.h:394
#define PHP_MSHUTDOWN
Definition php.h:393
#define PHP_MINFO_FUNCTION
Definition php.h:404
#define PHP_GINIT_FUNCTION
Definition php.h:405
#define PHP_RSHUTDOWN
Definition php.h:395
#define PHP_RINIT_FUNCTION
Definition php.h:402
#define PHP_RSHUTDOWN_FUNCTION
Definition php.h:403
#define PHP_GSHUTDOWN_FUNCTION
Definition php.h:406
#define PHP_MINIT
Definition php.h:392
#define PHPAPI
Definition php.h:71
#define PHP_MODULE_GLOBALS
Definition php.h:408
#define PHP_GSHUTDOWN
Definition php.h:398
unsigned const char * end
Definition php_ffi.h:51
#define PHP_INI_ALL
Definition php_ini.h:45
#define PHP_INI_BEGIN
Definition php_ini.h:52
#define STD_PHP_INI_ENTRY
Definition php_ini.h:64
#define STD_PHP_INI_BOOLEAN
Definition php_ini.h:66
#define PHP_INI_MH
Definition php_ini.h:49
#define PHP_INI_END
Definition php_ini.h:53
PHP_JSON_API size_t int options
Definition php_json.h:102
php_json_error_code error_code
Definition php_json.h:92
#define PREG_OFFSET_CAPTURE
#define php_pcre_mutex_alloc()
#define PHP_PCRE_PREALLOC_MDATA_SIZE
Definition php_pcre.c:209
#define PREG_GREP_INVERT
Definition php_pcre.c:34
PHPAPI pcre2_match_context * php_pcre_mctx(void)
Definition php_pcre.c:3054
PHPAPI pcre2_code * php_pcre_pce_re(pcre_cache_entry *pce)
Definition php_pcre.c:3082
PHPAPI zend_string * php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count)
Definition php_pcre.c:1614
PHPAPI pcre2_compile_context * php_pcre_cctx(void)
Definition php_pcre.c:3064
PHPAPI void php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return_value, zend_long flags)
Definition php_pcre.c:2921
PHPAPI pcre2_match_data * php_pcre_create_match_data(uint32_t capture_count, pcre2_code *re)
Definition php_pcre.c:906
PHPAPI void php_pcre_pce_decref(pcre_cache_entry *pce)
Definition php_pcre.c:3075
#define PCRE_CACHE_SIZE
Definition php_pcre.c:38
#define PREG_UNMATCHED_AS_NULL
Definition php_pcre.c:28
PHPAPI pcre2_code * pcre_get_compiled_regex(zend_string *regex, uint32_t *capture_count)
Definition php_pcre.c:892
#define PREG_SET_ORDER
Definition php_pcre.c:26
PHPAPI pcre_cache_entry * pcre_get_compiled_regex_cache_ex(zend_string *regex, bool locale_aware)
Definition php_pcre.c:585
#define PHP_PCRE_DEFAULT_EXTRA_COPTIONS
Definition php_pcre.c:204
#define PREG_SPLIT_DELIM_CAPTURE
Definition php_pcre.c:31
PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str, zval *return_value, zval *subpats, bool global, zend_long flags, zend_off_t start_offset)
Definition php_pcre.c:1156
#define PREG_SPLIT_OFFSET_CAPTURE
Definition php_pcre.c:32
#define PREG_JIT
Definition php_pcre.c:36
PHPAPI void php_pcre_pce_incref(pcre_cache_entry *pce)
Definition php_pcre.c:3069
zend_module_entry pcre_module_entry
Definition php_pcre.c:3031
char * php_pcre_version
Definition php_pcre.c:46
#define PREG_SPLIT_NO_EMPTY
Definition php_pcre.c:30
#define php_pcre_mutex_unlock()
#define php_pcre_mutex_free()
#define php_pcre_mutex_lock()
#define PREG_PATTERN_ORDER
Definition php_pcre.c:25
PHPAPI pcre_cache_entry * pcre_get_compiled_regex_cache(zend_string *regex)
Definition php_pcre.c:885
PHPAPI void php_pcre_free_match_data(pcre2_match_data *match_data)
Definition php_pcre.c:928
PHPAPI zend_string * php_pcre_replace(zend_string *regex, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count)
Definition php_pcre.c:1586
PHPAPI pcre2_general_context * php_pcre_gctx(void)
Definition php_pcre.c:3059
PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, zend_string *subject_str, zval *return_value, zend_long limit_val, zend_long flags)
Definition php_pcre.c:2564
HashTable pcre_cache
Definition php_pcre.h:75
php_pcre_error_code
Definition php_pcre.h:39
@ PHP_PCRE_JIT_STACKLIMIT_ERROR
Definition php_pcre.h:46
@ PHP_PCRE_BACKTRACK_LIMIT_ERROR
Definition php_pcre.h:42
@ PHP_PCRE_RECURSION_LIMIT_ERROR
Definition php_pcre.h:43
@ PHP_PCRE_BAD_UTF8_ERROR
Definition php_pcre.h:44
@ PHP_PCRE_NO_ERROR
Definition php_pcre.h:40
@ PHP_PCRE_BAD_UTF8_OFFSET_ERROR
Definition php_pcre.h:45
@ PHP_PCRE_INTERNAL_ERROR
Definition php_pcre.h:41
pcre2_general_context * gctx_zmm
Definition php_pcre.h:86
zend_long backtrack_limit
Definition php_pcre.h:76
struct _pcre_cache_entry pcre_cache_entry
Definition php_pcre.h:37
zval unmatched_empty_pair
Definition php_pcre.h:84
#define PCRE_G(v)
zval unmatched_null_pair
Definition php_pcre.h:83
#define PHP_PCRE_VERSION
Definition php_pcre.h:35
zend_long recursion_limit
Definition php_pcre.h:77
preg_replace(string|array $pattern, string|array $replacement, string|array $subject, int $limit=-1, &$count=null)
preg_last_error_msg()
preg_replace_callback(string|array $pattern, callable $callback, string|array $subject, int $limit=-1, &$count=null, int $flags=0)
preg_match_all(string $pattern, string $subject, &$matches=null, int $flags=0, int $offset=0)
preg_last_error()
preg_quote(string $str, ?string $delimiter=null)
preg_grep(string $pattern, array $array, int $flags=0)
preg_replace_callback_array(array $pattern, string|array $subject, int $limit=-1, &$count=null, int $flags=0)
preg_split(string $pattern, string $subject, int $limit=-1, int $flags=0)
preg_filter(string|array $pattern, string|array $replacement, string|array $subject, int $limit=-1, &$count=null)
preg_match(string $pattern, string $subject, &$matches=null, int $flags=0, int $offset=0)
unsigned char key[REFLECTION_KEY_LEN]
zend_constant * data
p
Definition session.c:1105
file_private int match(struct magic_set *, struct magic *, size_t, const struct buffer *, size_t, int, int, int, uint16_t *, uint16_t *, int *, int *, int *, int *, int *)
Definition softmagic.c:212
Definition php_pcre.c:50
pcre2_code * re
Definition php_pcre.c:51
uint32_t preg_options
Definition php_pcre.c:58
zend_string ** subpats_table
Definition php_pcre.c:57
uint32_t capture_count
Definition php_pcre.c:60
uint32_t name_count
Definition php_pcre.c:59
uint32_t refcount
Definition php_pcre.c:62
uint32_t compile_options
Definition php_pcre.c:61
uint32_t nNumUsed
Definition zend_types.h:406
HashTable * named_params
Definition zend_API.h:56
uint32_t param_count
Definition zend_API.h:51
zend_object * object
Definition zend_API.h:50
#define INTERNAL_FUNCTION_PARAMETERS
Definition zend.h:49
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
Definition zend.h:50
ZEND_API zend_result add_next_index_null(zval *arg)
Definition zend_API.c:2141
ZEND_API void add_assoc_string_ex(zval *arg, const char *key, size_t key_len, const char *str)
Definition zend_API.c:1982
ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error)
Definition zend_API.c:4271
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:423
#define Z_PARAM_FUNC(dest_fci, dest_fcc)
Definition zend_API.h:1824
struct _zend_fcall_info_cache zend_fcall_info_cache
#define RETURN_STRING(s)
Definition zend_API.h:1043
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define Z_PARAM_STR_OR_NULL(dest)
Definition zend_API.h:2089
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define ZVAL_STRING(z, s)
Definition zend_API.h:956
#define ZEND_DECLARE_MODULE_GLOBALS(module_name)
Definition zend_API.h:268
#define array_init_size(arg, size)
Definition zend_API.h:538
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_GET_MODULE(name)
Definition zend_API.h:241
#define RETVAL_ARR(r)
Definition zend_API.h:1024
#define Z_PARAM_STR(dest)
Definition zend_API.h:2086
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define ZEND_TRY_ASSIGN_REF_LONG(zv, lval)
Definition zend_API.h:1205
#define Z_PARAM_ARRAY_HT_OR_STR(dest_ht, dest_str)
Definition zend_API.h:2151
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETVAL_NULL()
Definition zend_API.h:1010
#define RETURN_NEW_STR(s)
Definition zend_API.h:1041
struct _zend_fcall_info zend_fcall_info
#define RETURN_THROWS()
Definition zend_API.h:1060
#define Z_PARAM_ARRAY_HT(dest)
Definition zend_API.h:1852
#define RETURN_STR(s)
Definition zend_API.h:1039
#define RETVAL_LONG(l)
Definition zend_API.h:1011
#define RETURN_EMPTY_STRING()
Definition zend_API.h:1047
#define Z_PARAM_ARRAY(dest)
Definition zend_API.h:1682
#define Z_PARAM_ZVAL(dest)
Definition zend_API.h:2100
#define RETVAL_STR(s)
Definition zend_API.h:1013
#define ZVAL_STRINGL_FAST(z, s, l)
Definition zend_API.h:983
#define RETVAL_FALSE
Definition zend_API.h:1032
ZEND_API zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache)
#define RETURN_STR_COPY(s)
Definition zend_API.h:1042
#define array_init(arg)
Definition zend_API.h:537
#define ZVAL_EMPTY_STRING(z)
Definition zend_API.h:961
#define estrndup(s, length)
Definition zend_alloc.h:165
#define ecalloc(nmemb, size)
Definition zend_alloc.h:158
#define efree(ptr)
Definition zend_alloc.h:155
#define pefree(ptr, persistent)
Definition zend_alloc.h:191
#define pemalloc(size, persistent)
Definition zend_alloc.h:189
#define safe_emalloc(nmemb, size, offset)
Definition zend_alloc.h:154
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
zend_string_release_ex(func->internal_function.function_name, 0)
#define E_NOTICE
Definition zend_errors.h:26
#define E_WARNING
Definition zend_errors.h:24
#define Z_FLF_PARAM_STR(arg_num, dest, tmp)
#define ZEND_FRAMELESS_FUNCTION(name, arity)
#define Z_FLF_PARAM_ARRAY_HT_OR_STR(arg_num, dest_ht, dest_str, str_tmp)
#define Z_FLF_PARAM_FREE_STR(arg_num, tmp)
#define EG_FLAGS_IN_SHUTDOWN
#define EG(v)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
Definition zend_hash.c:1229
ZEND_API zval *ZEND_FASTCALL zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1214
ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument)
Definition zend_hash.c:2099
ZEND_API zval *ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:1007
ZEND_API zval *ZEND_FASTCALL zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1219
ZEND_API zval *ZEND_FASTCALL zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData)
Definition zend_hash.c:1031
ZEND_API HashTable *ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2)
Definition zend_hash.c:296
ZEND_API zval *ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:997
ZEND_API zval *ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
Definition zend_hash.c:2668
ZEND_API zval *ZEND_FASTCALL zend_hash_add(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:992
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
#define ZEND_HASH_ELEMENT(__ht, _idx)
Definition zend_hash.h:1005
#define ZEND_HASH_APPLY_STOP
Definition zend_hash.h:148
#define ZEND_HASH_MAP_FOREACH_PTR(ht, _ptr)
Definition zend_hash.h:1326
#define ZEND_HASH_APPLY_REMOVE
Definition zend_hash.h:147
#define zend_new_array(size)
Definition zend_hash.h:338
#define ZEND_HASH_APPLY_KEEP
Definition zend_hash.h:146
#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val)
Definition zend_hash.h:1181
#define ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _key, _val)
Definition zend_hash.h:1166
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
#define ZEND_HASH_FOREACH_VAL(ht, _val)
Definition zend_hash.h:1102
#define UNREGISTER_INI_ENTRIES()
Definition zend_ini.h:204
#define REGISTER_INI_ENTRIES()
Definition zend_ini.h:203
#define DISPLAY_INI_ENTRIES()
Definition zend_ini.h:205
int32_t zend_long
Definition zend_long.h:42
#define ZEND_LONG_MIN
Definition zend_long.h:46
uint32_t zend_ulong
Definition zend_long.h:43
int32_t zend_off_t
Definition zend_long.h:44
#define ZEND_LONG_FMT
Definition zend_long.h:87
struct _zend_string zend_string
#define STANDARD_MODULE_HEADER
struct _zend_module_entry zend_module_entry
#define STANDARD_MODULE_PROPERTIES_EX
ZEND_API zend_string *ZEND_FASTCALL zval_get_string_func(zval *op)
int last
#define EXPECTED(condition)
#define zend_always_inline
#define ZEND_ASSERT(c)
#define EMPTY_SWITCH_DEFAULT_CASE()
#define UNEXPECTED(condition)
ZEND_API zend_string * zend_string_concat2(const char *str1, size_t str1_len, const char *str2, size_t str2_len)
#define ZSTR_IS_VALID_UTF8(s)
Definition zend_string.h:85
#define ZSTR_IS_INTERNED(s)
Definition zend_string.h:84
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_MAX_OVERHEAD
#define ZSTR_EMPTY_ALLOC()
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_STR(z, s)
#define IS_STR_PERMANENT
Definition zend_types.h:819
#define Z_TRY_ADDREF_P(pz)
#define ZVAL_UNDEF(z)
#define IS_UNDEF
Definition zend_types.h:600
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_NULL(z)
#define ZVAL_LONG(z, l)
#define IS_STRING
Definition zend_types.h:606
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_ARRAY
Definition zend_types.h:607
#define Z_ISUNDEF(zval)
Definition zend_types.h:956
#define IS_STR_VALID_UTF8
Definition zend_types.h:820
#define Z_PTR_P(zval_p)
#define Z_STR(zval)
Definition zend_types.h:971
#define GC_FLAGS(p)
Definition zend_types.h:756
#define GC_ADDREF(p)
Definition zend_types.h:709
#define GC_MAKE_PERSISTENT_LOCAL(p)
#define GC_TRY_ADDREF(p)
Definition zend_types.h:713
#define IS_NULL
Definition zend_types.h:601
@ FAILURE
Definition zend_types.h:61
#define ZVAL_ARR(z, a)
#define ZVAL_COPY(z, v)
#define Z_ARR(zval)
Definition zend_types.h:983
#define IS_ARRAY_IMMUTABLE
Definition zend_types.h:823
#define Z_TYPE_FLAGS_P(zval_p)
Definition zend_types.h:663
#define ZEND_TLS
Definition zend_types.h:84
#define Z_TYPE(zval)
Definition zend_types.h:659
#define ZVAL_COPY_VALUE(z, v)
#define IS_STR_PERSISTENT
Definition zend_types.h:818
#define GC_ADD_FLAGS(p, flags)
Definition zend_types.h:759
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
zval * return_value
zend_string * name
bool result
zval * ret