@@ -49,9 +49,11 @@ char *php_pcre_version;
49
49
50
50
struct _pcre_cache_entry {
51
51
pcre2_code * re ;
52
+ /* Pointer is not NULL when there are named captures.
53
+ * Length is equal to capture_count + 1 to account for capture group 0. */
54
+ zend_string * * subpats_table ;
52
55
uint32_t preg_options ;
53
56
uint32_t capture_count ;
54
- uint32_t name_count ;
55
57
uint32_t compile_options ;
56
58
uint32_t refcount ;
57
59
};
@@ -90,6 +92,8 @@ static MUTEX_T pcre_mt = NULL;
90
92
91
93
ZEND_TLS HashTable char_tables ;
92
94
95
+ static void free_subpats_table (zend_string * * subpat_names , uint32_t num_subpats , bool persistent );
96
+
93
97
static void php_pcre_free_char_table (zval * data )
94
98
{/*{{{*/
95
99
void * ptr = Z_PTR_P (data );
@@ -163,6 +167,9 @@ static void php_free_pcre_cache(zval *data) /* {{{ */
163
167
{
164
168
pcre_cache_entry * pce = (pcre_cache_entry * ) Z_PTR_P (data );
165
169
if (!pce ) return ;
170
+ if (pce -> subpats_table ) {
171
+ free_subpats_table (pce -> subpats_table , pce -> capture_count + 1 , true);
172
+ }
166
173
pcre2_code_free (pce -> re );
167
174
free (pce );
168
175
}
@@ -172,6 +179,9 @@ static void php_efree_pcre_cache(zval *data) /* {{{ */
172
179
{
173
180
pcre_cache_entry * pce = (pcre_cache_entry * ) Z_PTR_P (data );
174
181
if (!pce ) return ;
182
+ if (pce -> subpats_table ) {
183
+ free_subpats_table (pce -> subpats_table , pce -> capture_count + 1 , false);
184
+ }
175
185
pcre2_code_free (pce -> re );
176
186
efree (pce );
177
187
}
@@ -520,20 +530,21 @@ static int pcre_clean_cache(zval *data, void *arg)
520
530
}
521
531
/* }}} */
522
532
523
- static void free_subpats_table (zend_string * * subpat_names , uint32_t num_subpats ) {
533
+ static void free_subpats_table (zend_string * * subpat_names , uint32_t num_subpats , bool persistent ) {
524
534
uint32_t i ;
525
535
for (i = 0 ; i < num_subpats ; i ++ ) {
526
536
if (subpat_names [i ]) {
527
- zend_string_release_ex (subpat_names [i ], false );
537
+ zend_string_release_ex (subpat_names [i ], persistent );
528
538
}
529
539
}
530
- efree (subpat_names );
540
+ pefree (subpat_names , persistent );
531
541
}
532
542
533
543
/* {{{ static make_subpats_table */
534
- static zend_string * * make_subpats_table (uint32_t num_subpats , pcre_cache_entry * pce )
544
+ static zend_string * * make_subpats_table (uint32_t name_cnt , pcre_cache_entry * pce , bool persistent )
535
545
{
536
- uint32_t name_cnt = pce -> name_count , name_size , ni = 0 ;
546
+ uint32_t num_subpats = pce -> capture_count + 1 ;
547
+ uint32_t name_size , ni = 0 ;
537
548
char * name_table ;
538
549
zend_string * * subpat_names ;
539
550
int rc1 , rc2 ;
@@ -545,11 +556,20 @@ static zend_string **make_subpats_table(uint32_t num_subpats, pcre_cache_entry *
545
556
return NULL ;
546
557
}
547
558
548
- subpat_names = ecalloc (num_subpats , sizeof (zend_string * ));
559
+ subpat_names = pecalloc (num_subpats , sizeof (zend_string * ), persistent );
549
560
while (ni ++ < name_cnt ) {
550
561
unsigned short name_idx = 0x100 * (unsigned char )name_table [0 ] + (unsigned char )name_table [1 ];
551
562
const char * name = name_table + 2 ;
552
- subpat_names [name_idx ] = zend_string_init (name , strlen (name ), 0 );
563
+ /* Note: this makes a persistent string when the cache is not request-based because the string
564
+ * has to outlive the request. In that case, they will only be used within this thread
565
+ * and never be shared.
566
+ * Although we will be storing them in user-exposed arrays, they cannot cause problems
567
+ * because they only live in this thread and the last reference is deleted on shutdown
568
+ * instead of by user code. */
569
+ subpat_names [name_idx ] = zend_string_init (name , strlen (name ), persistent );
570
+ if (persistent ) {
571
+ GC_MAKE_PERSISTENT_LOCAL (subpat_names [name_idx ]);
572
+ }
553
573
name_table += name_size ;
554
574
}
555
575
return subpat_names ;
@@ -838,7 +858,8 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo
838
858
return NULL ;
839
859
}
840
860
841
- rc = pcre2_pattern_info (re , PCRE2_INFO_NAMECOUNT , & new_entry .name_count );
861
+ uint32_t name_count ;
862
+ rc = pcre2_pattern_info (re , PCRE2_INFO_NAMECOUNT , & name_count );
842
863
if (rc < 0 ) {
843
864
if (key != regex ) {
844
865
zend_string_release_ex (key , 0 );
@@ -848,6 +869,21 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo
848
869
return NULL ;
849
870
}
850
871
872
+ /* Compute and cache the subpattern table to avoid computing it again over and over. */
873
+ if (name_count > 0 ) {
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 , false);
878
+ }
879
+ /* Warning already emitted by make_subpats_table() */
880
+ pcre_handle_exec_error (PCRE2_ERROR_INTERNAL );
881
+ return NULL ;
882
+ }
883
+ } else {
884
+ new_entry .subpats_table = NULL ;
885
+ }
886
+
851
887
/*
852
888
* Interned strings are not duplicated when stored in HashTable,
853
889
* but all the interned strings created during HTTP request are removed
@@ -1204,11 +1240,8 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1204
1240
* allocate the table only if there are any named subpatterns.
1205
1241
*/
1206
1242
subpat_names = NULL ;
1207
- if (subpats && pce -> name_count > 0 ) {
1208
- subpat_names = make_subpats_table (num_subpats , pce );
1209
- if (!subpat_names ) {
1210
- RETURN_FALSE ;
1211
- }
1243
+ if (subpats ) {
1244
+ subpat_names = pce -> subpats_table ;
1212
1245
}
1213
1246
1214
1247
matched = 0 ;
@@ -1220,9 +1253,6 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1220
1253
match_data = pcre2_match_data_create_from_pattern (pce -> re , PCRE_G (gctx_zmm ));
1221
1254
if (!match_data ) {
1222
1255
PCRE_G (error_code ) = PHP_PCRE_INTERNAL_ERROR ;
1223
- if (subpat_names ) {
1224
- free_subpats_table (subpat_names , num_subpats );
1225
- }
1226
1256
RETURN_FALSE ;
1227
1257
}
1228
1258
}
@@ -1269,9 +1299,6 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1269
1299
if (subpats != NULL ) {
1270
1300
/* Try to get the list of substrings and display a warning if failed. */
1271
1301
if (UNEXPECTED (offsets [1 ] < offsets [0 ])) {
1272
- if (subpat_names ) {
1273
- free_subpats_table (subpat_names , num_subpats );
1274
- }
1275
1302
if (match_sets ) efree (match_sets );
1276
1303
php_error_docref (NULL , E_WARNING , "Get subpatterns list failed" );
1277
1304
RETURN_FALSE ;
@@ -1435,10 +1462,6 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
1435
1462
}
1436
1463
}
1437
1464
1438
- if (subpat_names ) {
1439
- free_subpats_table (subpat_names , num_subpats );
1440
- }
1441
-
1442
1465
if (PCRE_G (error_code ) == PHP_PCRE_NO_ERROR ) {
1443
1466
/* If there was no error and we're in /u mode, remember that the string is valid UTF-8. */
1444
1467
if ((pce -> compile_options & PCRE2_UTF )
@@ -1852,18 +1875,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
1852
1875
1853
1876
/* Calculate the size of the offsets array, and allocate memory for it. */
1854
1877
num_subpats = pce -> capture_count + 1 ;
1855
-
1856
- /*
1857
- * Build a mapping from subpattern numbers to their names. We will
1858
- * allocate the table only if there are any named subpatterns.
1859
- */
1860
- subpat_names = NULL ;
1861
- if (UNEXPECTED (pce -> name_count > 0 )) {
1862
- subpat_names = make_subpats_table (num_subpats , pce );
1863
- if (!subpat_names ) {
1864
- return NULL ;
1865
- }
1866
- }
1878
+ subpat_names = pce -> subpats_table ;
1867
1879
1868
1880
alloc_len = 0 ;
1869
1881
result = NULL ;
@@ -1883,9 +1895,6 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
1883
1895
match_data = pcre2_match_data_create_from_pattern (pce -> re , PCRE_G (gctx_zmm ));
1884
1896
if (!match_data ) {
1885
1897
PCRE_G (error_code ) = PHP_PCRE_INTERNAL_ERROR ;
1886
- if (subpat_names ) {
1887
- free_subpats_table (subpat_names , num_subpats );
1888
- }
1889
1898
mdata_used = old_mdata_used ;
1890
1899
return NULL ;
1891
1900
}
@@ -2036,10 +2045,6 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
2036
2045
}
2037
2046
mdata_used = old_mdata_used ;
2038
2047
2039
- if (UNEXPECTED (subpat_names )) {
2040
- free_subpats_table (subpat_names , num_subpats );
2041
- }
2042
-
2043
2048
return result ;
2044
2049
}
2045
2050
/* }}} */
0 commit comments