@@ -49,9 +49,9 @@ char *php_pcre_version;
49
49
50
50
struct _pcre_cache_entry {
51
51
pcre2_code * re ;
52
+ zend_string * * subpats_table ;
52
53
uint32_t preg_options ;
53
54
uint32_t capture_count ;
54
- uint32_t name_count ;
55
55
uint32_t compile_options ;
56
56
uint32_t refcount ;
57
57
};
@@ -90,6 +90,8 @@ static MUTEX_T pcre_mt = NULL;
90
90
91
91
ZEND_TLS HashTable char_tables ;
92
92
93
+ static void free_subpats_table (zend_string * * subpat_names , uint32_t num_subpats , bool persistent );
94
+
93
95
static void php_pcre_free_char_table (zval * data )
94
96
{/*{{{*/
95
97
void * ptr = Z_PTR_P (data );
@@ -163,6 +165,9 @@ static void php_free_pcre_cache(zval *data) /* {{{ */
163
165
{
164
166
pcre_cache_entry * pce = (pcre_cache_entry * ) Z_PTR_P (data );
165
167
if (!pce ) return ;
168
+ if (pce -> subpats_table ) {
169
+ free_subpats_table (pce -> subpats_table , pce -> capture_count + 1 , true);
170
+ }
166
171
pcre2_code_free (pce -> re );
167
172
free (pce );
168
173
}
@@ -172,6 +177,9 @@ static void php_efree_pcre_cache(zval *data) /* {{{ */
172
177
{
173
178
pcre_cache_entry * pce = (pcre_cache_entry * ) Z_PTR_P (data );
174
179
if (!pce ) return ;
180
+ if (pce -> subpats_table ) {
181
+ free_subpats_table (pce -> subpats_table , pce -> capture_count + 1 , false);
182
+ }
175
183
pcre2_code_free (pce -> re );
176
184
efree (pce );
177
185
}
@@ -520,20 +528,21 @@ static int pcre_clean_cache(zval *data, void *arg)
520
528
}
521
529
/* }}} */
522
530
523
- static void free_subpats_table (zend_string * * subpat_names , uint32_t num_subpats ) {
531
+ static void free_subpats_table (zend_string * * subpat_names , uint32_t num_subpats , bool persistent ) {
524
532
uint32_t i ;
525
533
for (i = 0 ; i < num_subpats ; i ++ ) {
526
534
if (subpat_names [i ]) {
527
- zend_string_release_ex (subpat_names [i ], false );
535
+ zend_string_release_ex (subpat_names [i ], persistent );
528
536
}
529
537
}
530
- efree (subpat_names );
538
+ pefree (subpat_names , persistent );
531
539
}
532
540
533
541
/* {{{ static make_subpats_table */
534
- static zend_string * * make_subpats_table (uint32_t num_subpats , pcre_cache_entry * pce )
542
+ static zend_string * * make_subpats_table (uint32_t name_cnt , pcre_cache_entry * pce , bool persistent )
535
543
{
536
- uint32_t name_cnt = pce -> name_count , name_size , ni = 0 ;
544
+ uint32_t num_subpats = pce -> capture_count + 1 ;
545
+ uint32_t name_size , ni = 0 ;
537
546
char * name_table ;
538
547
zend_string * * subpat_names ;
539
548
int rc1 , rc2 ;
@@ -545,14 +554,21 @@ static zend_string **make_subpats_table(uint32_t num_subpats, pcre_cache_entry *
545
554
return NULL ;
546
555
}
547
556
548
- subpat_names = ecalloc (num_subpats , sizeof (zend_string * ));
557
+ subpat_names = pecalloc (num_subpats , sizeof (zend_string * ), persistent );
549
558
while (ni ++ < name_cnt ) {
550
559
unsigned short name_idx = 0x100 * (unsigned char )name_table [0 ] + (unsigned char )name_table [1 ];
551
560
const char * name = name_table + 2 ;
552
- subpat_names [name_idx ] = zend_string_init (name , strlen (name ), 0 );
561
+ /* Note: if we're making persistent strings, they will only be used within this thread.
562
+ * Although we will be storing them in user-exposed arrays, they cannot cause problems
563
+ * because they only live in this thread and the last reference is deleted on shutdown
564
+ * instead of by user code. */
565
+ subpat_names [name_idx ] = zend_string_init (name , strlen (name ), persistent );
566
+ if (persistent ) {
567
+ GC_MAKE_PERSISTENT_LOCAL (subpat_names [name_idx ]);
568
+ }
553
569
if (is_numeric_string (ZSTR_VAL (subpat_names [name_idx ]), ZSTR_LEN (subpat_names [name_idx ]), NULL , NULL , 0 ) > 0 ) {
554
570
php_error_docref (NULL , E_WARNING , "Numeric named subpatterns are not allowed" );
555
- free_subpats_table (subpat_names , num_subpats );
571
+ free_subpats_table (subpat_names , num_subpats , persistent );
556
572
return NULL ;
557
573
}
558
574
name_table += name_size ;
@@ -843,7 +859,8 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo
843
859
return NULL ;
844
860
}
845
861
846
- rc = pcre2_pattern_info (re , PCRE2_INFO_NAMECOUNT , & new_entry .name_count );
862
+ uint32_t name_count ;
863
+ rc = pcre2_pattern_info (re , PCRE2_INFO_NAMECOUNT , & name_count );
847
864
if (rc < 0 ) {
848
865
if (key != regex ) {
849
866
zend_string_release_ex (key , 0 );
@@ -853,6 +870,17 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo
853
870
return NULL ;
854
871
}
855
872
873
+ /* Compute and cache the subpattern table to avoid computing it again over and over. */
874
+ new_entry .subpats_table = make_subpats_table (name_count , & new_entry , !PCRE_G (per_request_cache ));
875
+ if (!new_entry .subpats_table ) {
876
+ if (key != regex ) {
877
+ zend_string_release_ex (key , 0 );
878
+ }
879
+ /* Warning already emitted by make_subpats_table() */
880
+ pcre_handle_exec_error (PCRE2_ERROR_INTERNAL );
881
+ return NULL ;
882
+ }
883
+
856
884
/*
857
885
* Interned strings are not duplicated when stored in HashTable,
858
886
* but all the interned strings created during HTTP request are removed
@@ -1209,11 +1237,8 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1209
1237
* allocate the table only if there are any named subpatterns.
1210
1238
*/
1211
1239
subpat_names = NULL ;
1212
- if (subpats && pce -> name_count > 0 ) {
1213
- subpat_names = make_subpats_table (num_subpats , pce );
1214
- if (!subpat_names ) {
1215
- RETURN_FALSE ;
1216
- }
1240
+ if (subpats ) {
1241
+ subpat_names = pce -> subpats_table ;
1217
1242
}
1218
1243
1219
1244
matched = 0 ;
@@ -1225,9 +1250,6 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1225
1250
match_data = pcre2_match_data_create_from_pattern (pce -> re , PCRE_G (gctx_zmm ));
1226
1251
if (!match_data ) {
1227
1252
PCRE_G (error_code ) = PHP_PCRE_INTERNAL_ERROR ;
1228
- if (subpat_names ) {
1229
- free_subpats_table (subpat_names , num_subpats );
1230
- }
1231
1253
RETURN_FALSE ;
1232
1254
}
1233
1255
}
@@ -1274,9 +1296,6 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1274
1296
if (subpats != NULL ) {
1275
1297
/* Try to get the list of substrings and display a warning if failed. */
1276
1298
if (UNEXPECTED (offsets [1 ] < offsets [0 ])) {
1277
- if (subpat_names ) {
1278
- free_subpats_table (subpat_names , num_subpats );
1279
- }
1280
1299
if (match_sets ) efree (match_sets );
1281
1300
php_error_docref (NULL , E_WARNING , "Get subpatterns list failed" );
1282
1301
RETURN_FALSE ;
@@ -1440,10 +1459,6 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1440
1459
}
1441
1460
}
1442
1461
1443
- if (subpat_names ) {
1444
- free_subpats_table (subpat_names , num_subpats );
1445
- }
1446
-
1447
1462
if (PCRE_G (error_code ) == PHP_PCRE_NO_ERROR ) {
1448
1463
/* If there was no error and we're in /u mode, remember that the string is valid UTF-8. */
1449
1464
if ((pce -> compile_options & PCRE2_UTF )
@@ -1857,18 +1872,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
1857
1872
1858
1873
/* Calculate the size of the offsets array, and allocate memory for it. */
1859
1874
num_subpats = pce -> capture_count + 1 ;
1860
-
1861
- /*
1862
- * Build a mapping from subpattern numbers to their names. We will
1863
- * allocate the table only if there are any named subpatterns.
1864
- */
1865
- subpat_names = NULL ;
1866
- if (UNEXPECTED (pce -> name_count > 0 )) {
1867
- subpat_names = make_subpats_table (num_subpats , pce );
1868
- if (!subpat_names ) {
1869
- return NULL ;
1870
- }
1871
- }
1875
+ subpat_names = pce -> subpats_table ;
1872
1876
1873
1877
alloc_len = 0 ;
1874
1878
result = NULL ;
@@ -1888,9 +1892,6 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
1888
1892
match_data = pcre2_match_data_create_from_pattern (pce -> re , PCRE_G (gctx_zmm ));
1889
1893
if (!match_data ) {
1890
1894
PCRE_G (error_code ) = PHP_PCRE_INTERNAL_ERROR ;
1891
- if (subpat_names ) {
1892
- free_subpats_table (subpat_names , num_subpats );
1893
- }
1894
1895
mdata_used = old_mdata_used ;
1895
1896
return NULL ;
1896
1897
}
@@ -2041,10 +2042,6 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
2041
2042
}
2042
2043
mdata_used = old_mdata_used ;
2043
2044
2044
- if (UNEXPECTED (subpat_names )) {
2045
- free_subpats_table (subpat_names , num_subpats );
2046
- }
2047
-
2048
2045
return result ;
2049
2046
}
2050
2047
/* }}} */
0 commit comments