PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/pcre - php_pcre.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 800 872 91.7 %
Date: 2015-01-26 Functions: 32 33 97.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | PHP Version 7                                                        |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1997-2015 The PHP Group                                |
       6             :    +----------------------------------------------------------------------+
       7             :    | This source file is subject to version 3.01 of the PHP license,      |
       8             :    | that is bundled with this package in the file LICENSE, and is        |
       9             :    | available through the world-wide-web at the following url:           |
      10             :    | http://www.php.net/license/3_01.txt                                  |
      11             :    | If you did not receive a copy of the PHP license and are unable to   |
      12             :    | obtain it through the world-wide-web, please send a note to          |
      13             :    | license@php.net so we can mail you a copy immediately.               |
      14             :    +----------------------------------------------------------------------+
      15             :    | Author: Andrei Zmievski <andrei@php.net>                             |
      16             :    +----------------------------------------------------------------------+
      17             :  */
      18             : 
      19             : /* $Id$ */
      20             : 
      21             : #include "php.h"
      22             : #include "php_ini.h"
      23             : #include "php_globals.h"
      24             : #include "php_pcre.h"
      25             : #include "ext/standard/info.h"
      26             : #include "ext/standard/basic_functions.h"
      27             : #include "zend_smart_str.h"
      28             : 
      29             : #if HAVE_PCRE || HAVE_BUNDLED_PCRE
      30             : 
      31             : #include "ext/standard/php_string.h"
      32             : 
      33             : #define PREG_PATTERN_ORDER                      1
      34             : #define PREG_SET_ORDER                          2
      35             : #define PREG_OFFSET_CAPTURE                     (1<<8)
      36             : 
      37             : #define PREG_SPLIT_NO_EMPTY                     (1<<0)
      38             : #define PREG_SPLIT_DELIM_CAPTURE        (1<<1)
      39             : #define PREG_SPLIT_OFFSET_CAPTURE       (1<<2)
      40             : 
      41             : #define PREG_REPLACE_EVAL                       (1<<0)
      42             : 
      43             : #define PREG_GREP_INVERT                        (1<<0)
      44             : 
      45             : #define PCRE_CACHE_SIZE 4096
      46             : 
      47             : enum {
      48             :         PHP_PCRE_NO_ERROR = 0,
      49             :         PHP_PCRE_INTERNAL_ERROR,
      50             :         PHP_PCRE_BACKTRACK_LIMIT_ERROR,
      51             :         PHP_PCRE_RECURSION_LIMIT_ERROR,
      52             :         PHP_PCRE_BAD_UTF8_ERROR,
      53             :         PHP_PCRE_BAD_UTF8_OFFSET_ERROR
      54             : };
      55             : 
      56             : 
      57             : ZEND_DECLARE_MODULE_GLOBALS(pcre)
      58             : 
      59             : 
      60           9 : static void pcre_handle_exec_error(int pcre_code) /* {{{ */
      61             : {
      62           9 :         int preg_code = 0;
      63             : 
      64           9 :         switch (pcre_code) {
      65             :                 case PCRE_ERROR_MATCHLIMIT:
      66           4 :                         preg_code = PHP_PCRE_BACKTRACK_LIMIT_ERROR;
      67           4 :                         break;
      68             : 
      69             :                 case PCRE_ERROR_RECURSIONLIMIT:
      70           2 :                         preg_code = PHP_PCRE_RECURSION_LIMIT_ERROR;
      71           2 :                         break;
      72             : 
      73             :                 case PCRE_ERROR_BADUTF8:
      74           2 :                         preg_code = PHP_PCRE_BAD_UTF8_ERROR;
      75           2 :                         break;
      76             : 
      77             :                 case PCRE_ERROR_BADUTF8_OFFSET:
      78           1 :                         preg_code = PHP_PCRE_BAD_UTF8_OFFSET_ERROR;
      79           1 :                         break;
      80             : 
      81             :                 default:
      82           0 :                         preg_code = PHP_PCRE_INTERNAL_ERROR;
      83             :                         break;
      84             :         }
      85             : 
      86           9 :         PCRE_G(error_code) = preg_code;
      87           9 : }
      88             : /* }}} */
      89             : 
      90       24439 : static void php_free_pcre_cache(zval *data) /* {{{ */
      91             : {
      92       24439 :         pcre_cache_entry *pce = (pcre_cache_entry *) Z_PTR_P(data);
      93       24439 :         if (!pce) return;
      94       24439 :         pefree(pce->re, 1);
      95       24439 :         if (pce->extra) {
      96       24434 :                 pcre_free_study(pce->extra);
      97             :         }
      98             : #if HAVE_SETLOCALE
      99       24439 :         if ((void*)pce->tables) pefree((void*)pce->tables, 1);
     100       24439 :         if (pce->locale) {
     101           8 :                 zend_string_release(pce->locale);
     102             :         }
     103             : #endif
     104       24439 :         pefree(pce, 1);
     105             : }
     106             : /* }}} */
     107             : 
     108       20786 : static PHP_GINIT_FUNCTION(pcre) /* {{{ */
     109             : {
     110       20786 :         zend_hash_init(&pcre_globals->pcre_cache, 0, NULL, php_free_pcre_cache, 1);
     111       20786 :         pcre_globals->backtrack_limit = 0;
     112       20786 :         pcre_globals->recursion_limit = 0;
     113       20786 :         pcre_globals->error_code      = PHP_PCRE_NO_ERROR;
     114       20786 : }
     115             : /* }}} */
     116             : 
     117       20820 : static PHP_GSHUTDOWN_FUNCTION(pcre) /* {{{ */
     118             : {
     119       20820 :         zend_hash_destroy(&pcre_globals->pcre_cache);
     120       20820 : }
     121             : /* }}} */
     122             : 
     123             : PHP_INI_BEGIN()
     124             :         STD_PHP_INI_ENTRY("pcre.backtrack_limit", "1000000", PHP_INI_ALL, OnUpdateLong, backtrack_limit, zend_pcre_globals, pcre_globals)
     125             :         STD_PHP_INI_ENTRY("pcre.recursion_limit", "100000",  PHP_INI_ALL, OnUpdateLong, recursion_limit, zend_pcre_globals, pcre_globals)
     126             : #ifdef PCRE_STUDY_JIT_COMPILE
     127             :         STD_PHP_INI_ENTRY("pcre.jit",             "1",       PHP_INI_ALL, OnUpdateBool, jit,             zend_pcre_globals, pcre_globals)
     128             : #endif
     129             : PHP_INI_END()
     130             : 
     131             : 
     132             : /* {{{ PHP_MINFO_FUNCTION(pcre) */
     133         144 : static PHP_MINFO_FUNCTION(pcre)
     134             : {
     135         144 :         php_info_print_table_start();
     136         144 :         php_info_print_table_row(2, "PCRE (Perl Compatible Regular Expressions) Support", "enabled" );
     137         144 :         php_info_print_table_row(2, "PCRE Library Version", pcre_version() );
     138         144 :         php_info_print_table_end();
     139             : 
     140         144 :         DISPLAY_INI_ENTRIES();
     141         144 : }
     142             : /* }}} */
     143             : 
     144             : /* {{{ PHP_MINIT_FUNCTION(pcre) */
     145       20786 : static PHP_MINIT_FUNCTION(pcre)
     146             : {
     147       20786 :         REGISTER_INI_ENTRIES();
     148             : 
     149       20786 :         REGISTER_LONG_CONSTANT("PREG_PATTERN_ORDER", PREG_PATTERN_ORDER, CONST_CS | CONST_PERSISTENT);
     150       20786 :         REGISTER_LONG_CONSTANT("PREG_SET_ORDER", PREG_SET_ORDER, CONST_CS | CONST_PERSISTENT);
     151       20786 :         REGISTER_LONG_CONSTANT("PREG_OFFSET_CAPTURE", PREG_OFFSET_CAPTURE, CONST_CS | CONST_PERSISTENT);
     152       20786 :         REGISTER_LONG_CONSTANT("PREG_SPLIT_NO_EMPTY", PREG_SPLIT_NO_EMPTY, CONST_CS | CONST_PERSISTENT);
     153       20786 :         REGISTER_LONG_CONSTANT("PREG_SPLIT_DELIM_CAPTURE", PREG_SPLIT_DELIM_CAPTURE, CONST_CS | CONST_PERSISTENT);
     154       20786 :         REGISTER_LONG_CONSTANT("PREG_SPLIT_OFFSET_CAPTURE", PREG_SPLIT_OFFSET_CAPTURE, CONST_CS | CONST_PERSISTENT);
     155       20786 :         REGISTER_LONG_CONSTANT("PREG_GREP_INVERT", PREG_GREP_INVERT, CONST_CS | CONST_PERSISTENT);
     156             : 
     157       20786 :         REGISTER_LONG_CONSTANT("PREG_NO_ERROR", PHP_PCRE_NO_ERROR, CONST_CS | CONST_PERSISTENT);
     158       20786 :         REGISTER_LONG_CONSTANT("PREG_INTERNAL_ERROR", PHP_PCRE_INTERNAL_ERROR, CONST_CS | CONST_PERSISTENT);
     159       20786 :         REGISTER_LONG_CONSTANT("PREG_BACKTRACK_LIMIT_ERROR", PHP_PCRE_BACKTRACK_LIMIT_ERROR, CONST_CS | CONST_PERSISTENT);
     160       20786 :         REGISTER_LONG_CONSTANT("PREG_RECURSION_LIMIT_ERROR", PHP_PCRE_RECURSION_LIMIT_ERROR, CONST_CS | CONST_PERSISTENT);
     161       20786 :         REGISTER_LONG_CONSTANT("PREG_BAD_UTF8_ERROR", PHP_PCRE_BAD_UTF8_ERROR, CONST_CS | CONST_PERSISTENT);
     162       20786 :         REGISTER_LONG_CONSTANT("PREG_BAD_UTF8_OFFSET_ERROR", PHP_PCRE_BAD_UTF8_OFFSET_ERROR, CONST_CS | CONST_PERSISTENT);
     163       20786 :         REGISTER_STRING_CONSTANT("PCRE_VERSION", (char *)pcre_version(), CONST_CS | CONST_PERSISTENT);
     164             : 
     165       20786 :         return SUCCESS;
     166             : }
     167             : /* }}} */
     168             : 
     169             : /* {{{ PHP_MSHUTDOWN_FUNCTION(pcre) */
     170       20820 : static PHP_MSHUTDOWN_FUNCTION(pcre)
     171             : {
     172       20820 :         UNREGISTER_INI_ENTRIES();
     173             : 
     174       20820 :         return SUCCESS;
     175             : }
     176             : /* }}} */
     177             : 
     178             : /* {{{ static pcre_clean_cache */
     179       36864 : static int pcre_clean_cache(zval *data, void *arg)
     180             : {
     181       36864 :         int *num_clean = (int *)arg;
     182             : 
     183       36864 :         if (*num_clean > 0) {
     184        4608 :                 (*num_clean)--;
     185        4608 :                 return 1;
     186             :         } else {
     187       32256 :                 return 0;
     188             :         }
     189             : }
     190             : /* }}} */
     191             : 
     192             : /* {{{ static make_subpats_table */
     193          10 : static char **make_subpats_table(int num_subpats, pcre_cache_entry *pce)
     194             : {
     195          10 :         pcre_extra *extra = pce->extra;
     196          10 :         int name_cnt = pce->name_count, name_size, ni = 0;
     197             :         int rc;
     198             :         char *name_table;
     199             :         unsigned short name_idx;
     200             :         char **subpat_names;
     201             :         int rc1, rc2;
     202             : 
     203          10 :         rc1 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMETABLE, &name_table);
     204          10 :         rc2 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMEENTRYSIZE, &name_size);
     205          10 :         rc = rc2 ? rc2 : rc1;
     206          10 :         if (rc < 0) {
     207           0 :                 php_error_docref(NULL, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
     208           0 :                 return NULL;
     209             :         }
     210             : 
     211          10 :         subpat_names = (char **)ecalloc(num_subpats, sizeof(char *));
     212         174 :         while (ni++ < name_cnt) {
     213         154 :                 name_idx = 0xff * (unsigned char)name_table[0] + (unsigned char)name_table[1];
     214         154 :                 subpat_names[name_idx] = name_table + 2;
     215         308 :                 if (is_numeric_string(subpat_names[name_idx], strlen(subpat_names[name_idx]), NULL, NULL, 0) > 0) {
     216           0 :                         php_error_docref(NULL, E_WARNING, "Numeric named subpatterns are not allowed");
     217           0 :                         efree(subpat_names);
     218           0 :                         return NULL;
     219             :                 }
     220         154 :                 name_table += name_size;
     221             :         }
     222          10 :         return subpat_names;
     223             : }
     224             : /* }}} */
     225             : 
     226             : /* {{{ pcre_get_compiled_regex_cache
     227             :  */
     228     2002388 : PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex)
     229             : {
     230     2002388 :         pcre                            *re = NULL;
     231             :         pcre_extra                      *extra;
     232     2002388 :         int                                      coptions = 0;
     233     2002388 :         int                                      soptions = 0;
     234             :         const char                      *error;
     235             :         int                                      erroffset;
     236             :         char                             delimiter;
     237             :         char                             start_delimiter;
     238             :         char                             end_delimiter;
     239             :         char                            *p, *pp;
     240             :         char                            *pattern;
     241     2002388 :         int                                      do_study = 0;
     242     2002388 :         int                                      poptions = 0;
     243     2002388 :         unsigned const char *tables = NULL;
     244             :         pcre_cache_entry        *pce;
     245             :         pcre_cache_entry         new_entry;
     246             :         int                                      rc;
     247             : 
     248             :         /* Try to lookup the cached regex entry, and if successful, just pass
     249             :            back the compiled pattern, otherwise go on and compile it. */
     250     2002388 :         pce = zend_hash_find_ptr(&PCRE_G(pcre_cache), regex);
     251     2002388 :         if (pce) {
     252             : #if HAVE_SETLOCALE
     253     1977985 :                 if (pce->locale == BG(locale_string) ||
     254          40 :                     (pce->locale && BG(locale_string) &&
     255          20 :                      pce->locale->len == BG(locale_string)->len &&
     256          18 :                      !memcmp(pce->locale->val, BG(locale_string)->val, pce->locale->len))) {
     257     1977905 :                         return pce;
     258             :                 }
     259             : #else
     260             :                 return pce;
     261             : #endif
     262             :         }
     263             : 
     264       24483 :         p = regex->val;
     265             : 
     266             :         /* Parse through the leading whitespace, and display a warning if we
     267             :            get to the end without encountering a delimiter. */
     268       24483 :         while (isspace((int)*(unsigned char *)p)) p++;
     269       24483 :         if (*p == 0) {
     270           5 :                 php_error_docref(NULL, E_WARNING,
     271           5 :                                                  p < regex->val + regex->len ? "Null byte in regex" : "Empty regular expression");
     272           5 :                 return NULL;
     273             :         }
     274             : 
     275             :         /* Get the delimiter and display a warning if it is alphanumeric
     276             :            or a backslash. */
     277       24478 :         delimiter = *p++;
     278       24478 :         if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
     279           7 :                 php_error_docref(NULL,E_WARNING, "Delimiter must not be alphanumeric or backslash");
     280           7 :                 return NULL;
     281             :         }
     282             : 
     283       24471 :         start_delimiter = delimiter;
     284       24471 :         if ((pp = strchr("([{< )]}> )]}>", delimiter)))
     285          29 :                 delimiter = pp[5];
     286       24471 :         end_delimiter = delimiter;
     287             : 
     288       24471 :         pp = p;
     289             : 
     290       24471 :         if (start_delimiter == end_delimiter) {
     291             :                 /* We need to iterate through the pattern, searching for the ending delimiter,
     292             :                    but skipping the backslashed delimiters.  If the ending delimiter is not
     293             :                    found, display a warning. */
     294    16328397 :                 while (*pp != 0) {
     295    16303946 :                         if (*pp == '\\' && pp[1] != 0) pp++;
     296    15134773 :                         else if (*pp == delimiter)
     297       24433 :                                 break;
     298    16279513 :                         pp++;
     299             :                 }
     300             :         } else {
     301             :                 /* We iterate through the pattern, searching for the matching ending
     302             :                  * delimiter. For each matching starting delimiter, we increment nesting
     303             :                  * level, and decrement it for each matching ending delimiter. If we
     304             :                  * reach the end of the pattern without matching, display a warning.
     305             :                  */
     306          29 :                 int brackets = 1;       /* brackets nesting level */
     307         281 :                 while (*pp != 0) {
     308         249 :                         if (*pp == '\\' && pp[1] != 0) pp++;
     309         226 :                         else if (*pp == end_delimiter && --brackets <= 0)
     310             :                                 break;
     311         200 :                         else if (*pp == start_delimiter)
     312           1 :                                 brackets++;
     313         223 :                         pp++;
     314             :                 }
     315             :         }
     316             : 
     317       24471 :         if (*pp == 0) {
     318          12 :                 if (pp < regex->val + regex->len) {
     319           4 :                         php_error_docref(NULL,E_WARNING, "Null byte in regex");
     320           8 :                 } else if (start_delimiter == end_delimiter) {
     321           7 :                         php_error_docref(NULL,E_WARNING, "No ending delimiter '%c' found", delimiter);
     322             :                 } else {
     323           1 :                         php_error_docref(NULL,E_WARNING, "No ending matching delimiter '%c' found", delimiter);
     324             :                 }
     325          12 :                 return NULL;
     326             :         }
     327             : 
     328             :         /* Make a copy of the actual pattern. */
     329       24459 :         pattern = estrndup(p, pp-p);
     330             : 
     331             :         /* Move on to the options */
     332       24459 :         pp++;
     333             : 
     334             :         /* Parse through the options, setting appropriate flags.  Display
     335             :            a warning if we encounter an unknown modifier. */
     336       62843 :         while (pp < regex->val + regex->len) {
     337       13943 :                 switch (*pp++) {
     338             :                         /* Perl compatible options */
     339        1923 :                         case 'i':       coptions |= PCRE_CASELESS;              break;
     340        2124 :                         case 'm':       coptions |= PCRE_MULTILINE;             break;
     341        9760 :                         case 's':       coptions |= PCRE_DOTALL;                break;
     342           5 :                         case 'x':       coptions |= PCRE_EXTENDED;              break;
     343             : 
     344             :                         /* PCRE specific options */
     345           2 :                         case 'A':       coptions |= PCRE_ANCHORED;              break;
     346           9 :                         case 'D':       coptions |= PCRE_DOLLAR_ENDONLY;break;
     347          28 :                         case 'S':       do_study  = 1;                                  break;
     348          32 :                         case 'U':       coptions |= PCRE_UNGREEDY;              break;
     349           1 :                         case 'X':       coptions |= PCRE_EXTRA;                 break;
     350          34 :                         case 'u':       coptions |= PCRE_UTF8;
     351             :         /* In  PCRE,  by  default, \d, \D, \s, \S, \w, and \W recognize only ASCII
     352             :        characters, even in UTF-8 mode. However, this can be changed by setting
     353             :        the PCRE_UCP option. */
     354             : #ifdef PCRE_UCP
     355          34 :                                                 coptions |= PCRE_UCP;
     356             : #endif
     357          34 :                                 break;
     358             : 
     359             :                         /* Custom preg options */
     360           5 :                         case 'e':       poptions |= PREG_REPLACE_EVAL;  break;
     361             : 
     362             :                         case ' ':
     363             :                         case '\n':
     364           2 :                                 break;
     365             : 
     366             :                         default:
     367          18 :                                 if (pp[-1]) {
     368          13 :                                         php_error_docref(NULL,E_WARNING, "Unknown modifier '%c'", pp[-1]);
     369             :                                 } else {
     370           5 :                                         php_error_docref(NULL,E_WARNING, "Null byte in regex");
     371             :                                 }
     372          18 :                                 efree(pattern);
     373          18 :                                 return NULL;
     374             :                 }
     375             :         }
     376             : 
     377             : #if HAVE_SETLOCALE
     378       24453 :         if (BG(locale_string) &&
     379          12 :             (BG(locale_string)->len != 1 || BG(locale_string)->val[0] != 'C')) {
     380           4 :                 tables = pcre_maketables();
     381             :         }
     382             : #endif
     383             : 
     384             :         /* Compile pattern and display a warning if compilation failed. */
     385       24441 :         re = pcre_compile(pattern,
     386             :                                           coptions,
     387             :                                           &error,
     388             :                                           &erroffset,
     389             :                                           tables);
     390             : 
     391       24441 :         if (re == NULL) {
     392           6 :                 php_error_docref(NULL,E_WARNING, "Compilation failed: %s at offset %d", error, erroffset);
     393           6 :                 efree(pattern);
     394           6 :                 if (tables) {
     395           0 :                         pefree((void*)tables, 1);
     396             :                 }
     397           6 :                 return NULL;
     398             :         }
     399             : 
     400             : #ifdef PCRE_STUDY_JIT_COMPILE
     401       24435 :         if (PCRE_G(jit)) {
     402             :                 /* Enable PCRE JIT compiler */
     403       24430 :                 do_study = 1;
     404       24430 :                 soptions |= PCRE_STUDY_JIT_COMPILE;
     405             :         }
     406             : #endif
     407             : 
     408             :         /* If study option was specified, study the pattern and
     409             :            store the result in extra for passing to pcre_exec. */
     410       24435 :         if (do_study) {
     411       24430 :                 extra = pcre_study(re, soptions, &error);
     412       24430 :                 if (extra) {
     413       24430 :                         extra->flags |= PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
     414       24430 :                         extra->match_limit = (unsigned long)PCRE_G(backtrack_limit);
     415       24430 :                         extra->match_limit_recursion = (unsigned long)PCRE_G(recursion_limit);
     416             :                 }
     417       24430 :                 if (error != NULL) {
     418           0 :                         php_error_docref(NULL, E_WARNING, "Error while studying pattern");
     419             :                 }
     420             :         } else {
     421           5 :                 extra = NULL;
     422             :         }
     423             : 
     424       24435 :         efree(pattern);
     425             : 
     426             :         /*
     427             :          * If we reached cache limit, clean out the items from the head of the list;
     428             :          * these are supposedly the oldest ones (but not necessarily the least used
     429             :          * ones).
     430             :          */
     431       24435 :         if (zend_hash_num_elements(&PCRE_G(pcre_cache)) == PCRE_CACHE_SIZE) {
     432           9 :                 int num_clean = PCRE_CACHE_SIZE / 8;
     433           9 :                 zend_hash_apply_with_argument(&PCRE_G(pcre_cache), pcre_clean_cache, &num_clean);
     434             :         }
     435             : 
     436             :         /* Store the compiled pattern and extra info in the cache. */
     437       24435 :         new_entry.re = re;
     438       24435 :         new_entry.extra = extra;
     439       24435 :         new_entry.preg_options = poptions;
     440       24435 :         new_entry.compile_options = coptions;
     441             : #if HAVE_SETLOCALE
     442       24443 :         new_entry.locale = BG(locale_string) ?
     443          16 :                 ((GC_FLAGS(BG(locale_string)) & IS_STR_PERSISTENT) ?
     444           0 :                         zend_string_copy(BG(locale_string)) :
     445           8 :                         zend_string_init(BG(locale_string)->val, BG(locale_string)->len, 1)) :
     446             :                 NULL;
     447       24435 :         new_entry.tables = tables;
     448             : #endif
     449             : 
     450       24435 :         rc = pcre_fullinfo(re, extra, PCRE_INFO_CAPTURECOUNT, &new_entry.capture_count);
     451       24435 :         if (rc < 0) {
     452           0 :                 php_error_docref(NULL, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
     453           0 :                 return NULL;
     454             :         }
     455             : 
     456       24435 :         rc = pcre_fullinfo(re, extra, PCRE_INFO_NAMECOUNT, &new_entry.name_count);
     457       24435 :         if (rc < 0) {
     458           0 :                 php_error_docref(NULL, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
     459           0 :                 return NULL;
     460             :         }
     461             : 
     462             :         /*
     463             :          * Interned strings are not duplicated when stored in HashTable,
     464             :          * but all the interned strings created during HTTP request are removed
     465             :          * at end of request. However PCRE_G(pcre_cache) must be consistent
     466             :          * on the next request as well. So we disable usage of interned strings
     467             :          * as hash keys especually for this table.
     468             :          * See bug #63180
     469             :          */
     470       48870 :         pce = zend_hash_str_update_mem(&PCRE_G(pcre_cache), regex->val, regex->len, &new_entry, sizeof(pcre_cache_entry));
     471             : 
     472       24435 :         return pce;
     473             : }
     474             : /* }}} */
     475             : 
     476             : /* {{{ pcre_get_compiled_regex
     477             :  */
     478      127855 : PHPAPI pcre* pcre_get_compiled_regex(zend_string *regex, pcre_extra **extra, int *preg_options)
     479             : {
     480      127855 :         pcre_cache_entry * pce = pcre_get_compiled_regex_cache(regex);
     481             : 
     482      127855 :         if (extra) {
     483      127855 :                 *extra = pce ? pce->extra : NULL;
     484             :         }
     485      127855 :         if (preg_options) {
     486      127855 :                 *preg_options = pce ? pce->preg_options : 0;
     487             :         }
     488             : 
     489      127855 :         return pce ? pce->re : NULL;
     490             : }
     491             : /* }}} */
     492             : 
     493             : /* {{{ pcre_get_compiled_regex_ex
     494             :  */
     495           0 : PHPAPI pcre* pcre_get_compiled_regex_ex(zend_string *regex, pcre_extra **extra, int *preg_options, int *compile_options)
     496             : {
     497           0 :         pcre_cache_entry * pce = pcre_get_compiled_regex_cache(regex);
     498             : 
     499           0 :         if (extra) {
     500           0 :                 *extra = pce ? pce->extra : NULL;
     501             :         }
     502           0 :         if (preg_options) {
     503           0 :                 *preg_options = pce ? pce->preg_options : 0;
     504             :         }
     505           0 :         if (compile_options) {
     506           0 :                 *compile_options = pce ? pce->compile_options : 0;
     507             :         }
     508             : 
     509           0 :         return pce ? pce->re : NULL;
     510             : }
     511             : /* }}} */
     512             : 
     513             : /* {{{ add_offset_pair */
     514          73 : static inline void add_offset_pair(zval *result, char *str, int len, int offset, char *name)
     515             : {
     516             :         zval match_pair, tmp;
     517             : 
     518          73 :         array_init_size(&match_pair, 2);
     519             : 
     520             :         /* Add (match, offset) to the return value */
     521         146 :         ZVAL_STRINGL(&tmp, str, len);
     522          73 :         zend_hash_next_index_insert_new(Z_ARRVAL(match_pair), &tmp);
     523          73 :         ZVAL_LONG(&tmp, offset);
     524          73 :         zend_hash_next_index_insert_new(Z_ARRVAL(match_pair), &tmp);
     525             : 
     526          73 :         if (name) {
     527             :                 Z_ADDREF(match_pair);
     528           2 :                 zend_hash_str_update(Z_ARRVAL_P(result), name, strlen(name), &match_pair);
     529             :         }
     530          73 :         zend_hash_next_index_insert(Z_ARRVAL_P(result), &match_pair);
     531          73 : }
     532             : /* }}} */
     533             : 
     534     1845716 : static void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, int global) /* {{{ */
     535             : {
     536             :         /* parameters */
     537             :         zend_string              *regex;                        /* Regular expression */
     538             :         zend_string              *subject;                      /* String to match against */
     539             :         pcre_cache_entry *pce;                          /* Compiled regular expression */
     540     1845716 :         zval                     *subpats = NULL;       /* Array for subpatterns */
     541     1845716 :         zend_long                 flags = 0;            /* Match control flags */
     542     1845716 :         zend_long                 start_offset = 0;     /* Where the new search starts */
     543             : 
     544             : #ifndef FAST_ZPP
     545             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|z/ll", &regex,
     546             :                                                           &subject, &subpats, &flags, &start_offset) == FAILURE) {
     547             :                 RETURN_FALSE;
     548             :         }
     549             : #else
     550     1845716 :         ZEND_PARSE_PARAMETERS_START(2, 5)
     551     5537124 :                 Z_PARAM_STR(regex)
     552     5537112 :                 Z_PARAM_STR(subject)
     553     1845700 :                 Z_PARAM_OPTIONAL
     554     4061787 :                 Z_PARAM_ZVAL_EX(subpats, 0, 1)
     555     1115942 :                 Z_PARAM_LONG(flags)
     556          56 :                 Z_PARAM_LONG(start_offset)
     557     1845716 :         ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
     558             : #endif
     559             : 
     560             :         /* Compile regex or get it from cache. */
     561     1845700 :         if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
     562          27 :                 RETURN_FALSE;
     563             :         }
     564             : 
     565     1845673 :         php_pcre_match_impl(pce, subject->val, (int)subject->len, return_value, subpats,
     566             :                 global, ZEND_NUM_ARGS() >= 4, flags, start_offset);
     567             : }
     568             : /* }}} */
     569             : 
     570             : /* {{{ php_pcre_match_impl() */
     571     1846519 : PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value,
     572             :         zval *subpats, int global, int use_flags, zend_long flags, zend_long start_offset)
     573             : {
     574             :         zval                     result_set,            /* Holds a set of subpatterns after
     575             :                                                                                    a global match */
     576     1846519 :                                     *match_sets = NULL; /* An array of sets of matches for each
     577             :                                                                                    subpattern after a global match */
     578     1846519 :         pcre_extra              *extra = pce->extra;/* Holds results of studying */
     579             :         pcre_extra               extra_data;            /* Used locally for exec options */
     580     1846519 :         int                              exoptions = 0;         /* Execution options */
     581     1846519 :         int                              count = 0;                     /* Count of matched subpatterns */
     582             :         int                             *offsets;                       /* Array of subpattern offsets */
     583             :         int                              num_subpats;           /* Number of captured subpatterns */
     584             :         int                              size_offsets;          /* Size of the offsets array */
     585             :         int                              matched;                       /* Has anything matched */
     586     1846519 :         int                              g_notempty = 0;        /* If the match should not be empty */
     587             :         const char         **stringlist;                /* Holds list of subpatterns */
     588             :         char               **subpat_names;              /* Array for named subpatterns */
     589             :         int                              i;
     590             :         int                              subpats_order;         /* Order of subpattern matches */
     591             :         int                              offset_capture;    /* Capture match offsets: yes/no */
     592     1846519 :         unsigned char   *mark = NULL;       /* Target for MARK name */
     593             :         zval            marks;                  /* Array of marks for PREG_PATTERN_ORDER */
     594             :         ALLOCA_FLAG(use_heap);
     595             : 
     596     1846519 :         ZVAL_UNDEF(&marks);
     597             : 
     598             :         /* Overwrite the passed-in value for subpatterns with an empty array. */
     599     1846519 :         if (subpats != NULL) {
     600             :                 zval_dtor(subpats);
     601     1116711 :                 array_init(subpats);
     602             :         }
     603             : 
     604     1846519 :         subpats_order = global ? PREG_PATTERN_ORDER : 0;
     605             : 
     606     1846519 :         if (use_flags) {
     607         819 :                 offset_capture = flags & PREG_OFFSET_CAPTURE;
     608             : 
     609             :                 /*
     610             :                  * subpats_order is pre-set to pattern mode so we change it only if
     611             :                  * necessary.
     612             :                  */
     613         819 :                 if (flags & 0xff) {
     614          23 :                         subpats_order = flags & 0xff;
     615             :                 }
     616         819 :                 if ((global && (subpats_order < PREG_PATTERN_ORDER || subpats_order > PREG_SET_ORDER)) ||
     617             :                         (!global && subpats_order != 0)) {
     618           1 :                         php_error_docref(NULL, E_WARNING, "Invalid flags specified");
     619           1 :                         return;
     620             :                 }
     621             :         } else {
     622     1845700 :                 offset_capture = 0;
     623             :         }
     624             : 
     625             :         /* Negative offset counts from the end of the string. */
     626     1846518 :         if (start_offset < 0) {
     627           5 :                 start_offset = subject_len + start_offset;
     628           5 :                 if (start_offset < 0) {
     629           1 :                         start_offset = 0;
     630             :                 }
     631             :         }
     632             : 
     633     1846518 :         if (extra == NULL) {
     634           3 :                 extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
     635           3 :                 extra = &extra_data;
     636             :         }
     637     1846518 :         extra->match_limit = (unsigned long)PCRE_G(backtrack_limit);
     638     1846518 :         extra->match_limit_recursion = (unsigned long)PCRE_G(recursion_limit);
     639             : #ifdef PCRE_EXTRA_MARK
     640     1846518 :         extra->mark = &mark;
     641     1846518 :         extra->flags |= PCRE_EXTRA_MARK;
     642             : #endif
     643             : 
     644             :         /* Calculate the size of the offsets array, and allocate memory for it. */
     645     1846518 :         num_subpats = pce->capture_count + 1;
     646     1846518 :         size_offsets = num_subpats * 3;
     647             : 
     648             :         /*
     649             :          * Build a mapping from subpattern numbers to their names. We will
     650             :          * allocate the table only if there are any named subpatterns.
     651             :          */
     652     1846518 :         subpat_names = NULL;
     653     1846518 :         if (pce->name_count > 0) {
     654           9 :                 subpat_names = make_subpats_table(num_subpats, pce);
     655           9 :                 if (!subpat_names) {
     656           0 :                         RETURN_FALSE;
     657             :                 }
     658             :         }
     659             : 
     660     1846518 :         if (size_offsets <= 32) {
     661     1846507 :                 offsets = (int *)do_alloca(size_offsets * sizeof(int), use_heap);
     662             :         } else {
     663          11 :                 offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
     664             :         }
     665     1846518 :         memset(offsets, 0, size_offsets*sizeof(int));
     666             :         /* Allocate match sets array and initialize the values. */
     667     1846518 :         if (global && subpats && subpats_order == PREG_PATTERN_ORDER) {
     668         895 :                 match_sets = (zval *)safe_emalloc(num_subpats, sizeof(zval), 0);
     669        2023 :                 for (i=0; i<num_subpats; i++) {
     670        1128 :                         array_init(&match_sets[i]);
     671             :                 }
     672             :         }
     673             : 
     674     1846518 :         matched = 0;
     675     1846518 :         PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
     676             : 
     677             :         do {
     678             :                 /* Execute the regular expression. */
     679     1846713 :                 count = pcre_exec(pce->re, extra, subject, (int)subject_len, (int)start_offset,
     680             :                                                   exoptions|g_notempty, offsets, size_offsets);
     681             : 
     682             :                 /* the string was already proved to be valid UTF-8 */
     683     1846713 :                 exoptions |= PCRE_NO_UTF8_CHECK;
     684             : 
     685             :                 /* Check for too many substrings condition. */
     686     1846713 :                 if (count == 0) {
     687           0 :                         php_error_docref(NULL, E_NOTICE, "Matched, but too many substrings");
     688           0 :                         count = size_offsets/3;
     689             :                 }
     690             : 
     691             :                 /* If something has matched */
     692     1846713 :                 if (count > 0) {
     693      206280 :                         matched++;
     694             : 
     695             :                         /* If subpatterns array has been passed, fill it in with values. */
     696      206280 :                         if (subpats != NULL) {
     697             :                                 /* Try to get the list of substrings and display a warning if failed. */
     698       41527 :                                 if (pcre_get_substring_list(subject, offsets, count, &stringlist) < 0) {
     699           0 :                                         if (subpat_names) {
     700           0 :                                                 efree(subpat_names);
     701             :                                         }
     702           0 :                                         if (size_offsets <= 32) {
     703           0 :                                                 free_alloca(offsets, use_heap);
     704             :                                         } else {
     705           0 :                                                 efree(offsets);
     706             :                                         }
     707           0 :                                         if (match_sets) efree(match_sets);
     708           0 :                                         php_error_docref(NULL, E_WARNING, "Get subpatterns list failed");
     709           0 :                                         RETURN_FALSE;
     710             :                                 }
     711             : 
     712       41527 :                                 if (global) {   /* global pattern matching */
     713         324 :                                         if (subpats && subpats_order == PREG_PATTERN_ORDER) {
     714             :                                                 /* For each subpattern, insert it into the appropriate array. */
     715         140 :                                                 if (offset_capture) {
     716          27 :                                                         for (i = 0; i < count; i++) {
     717          30 :                                                                 add_offset_pair(&match_sets[i], (char *)stringlist[i],
     718          30 :                                                                                                 offsets[(i<<1)+1] - offsets[i<<1], offsets[i<<1], NULL);
     719             :                                                         }
     720             :                                                 } else {
     721         337 :                                                         for (i = 0; i < count; i++) {
     722         209 :                                                                 add_next_index_stringl(&match_sets[i], (char *)stringlist[i],
     723         209 :                                                                                                            offsets[(i<<1)+1] - offsets[i<<1]);
     724             :                                                         }
     725             :                                                 }
     726             :                                                 /* Add MARK, if available */
     727         140 :                                                 if (mark) {
     728           2 :                                                         if (Z_TYPE(marks) == IS_UNDEF) {
     729           1 :                                                                 array_init(&marks);
     730             :                                                         }
     731           2 :                                                         add_index_string(&marks, matched - 1, (char *) mark);
     732             :                                                 }
     733             :                                                 /*
     734             :                                                  * If the number of captured subpatterns on this run is
     735             :                                                  * less than the total possible number, pad the result
     736             :                                                  * arrays with empty strings.
     737             :                                                  */
     738         140 :                                                 if (count < num_subpats) {
     739          11 :                                                         for (; i < num_subpats; i++) {
     740           7 :                                                                 add_next_index_string(&match_sets[i], "");
     741             :                                                         }
     742             :                                                 }
     743             :                                         } else {
     744             :                                                 /* Allocate the result set array */
     745          44 :                                                 array_init_size(&result_set, count + (mark ? 1 : 0));
     746             : 
     747             :                                                 /* Add all the subpatterns to it */
     748          44 :                                                 if (subpat_names) {
     749           2 :                                                         if (offset_capture) {
     750           0 :                                                                 for (i = 0; i < count; i++) {
     751           0 :                                                                         add_offset_pair(&result_set, (char *)stringlist[i],
     752           0 :                                                                                                         offsets[(i<<1)+1] - offsets[i<<1], offsets[i<<1], subpat_names[i]);
     753             :                                                                 }
     754             :                                                         } else {
     755          14 :                                                                 for (i = 0; i < count; i++) {
     756          12 :                                                                         if (subpat_names[i]) {
     757           8 :                                                                                 add_assoc_stringl(&result_set, subpat_names[i], (char *)stringlist[i],
     758             :                                                                                                                            offsets[(i<<1)+1] - offsets[i<<1]);
     759             :                                                                         }
     760          12 :                                                                         add_next_index_stringl(&result_set, (char *)stringlist[i],
     761          12 :                                                                                                                    offsets[(i<<1)+1] - offsets[i<<1]);
     762             :                                                                 }
     763             :                                                         }
     764             :                                                 } else {
     765          42 :                                                         if (offset_capture) {
     766          10 :                                                                 for (i = 0; i < count; i++) {
     767          14 :                                                                         add_offset_pair(&result_set, (char *)stringlist[i],
     768          14 :                                                                                                         offsets[(i<<1)+1] - offsets[i<<1], offsets[i<<1], NULL);
     769             :                                                                 }
     770             :                                                         } else {
     771         343 :                                                                 for (i = 0; i < count; i++) {
     772         304 :                                                                         add_next_index_stringl(&result_set, (char *)stringlist[i],
     773         304 :                                                                                                                    offsets[(i<<1)+1] - offsets[i<<1]);
     774             :                                                                 }
     775             :                                                         }
     776             :                                                 }
     777             :                                                 /* Add MARK, if available */
     778          44 :                                                 if (mark) {
     779           2 :                                                         add_assoc_string_ex(&result_set, "MARK", sizeof("MARK") - 1, (char *)mark);
     780             :                                                 }
     781             :                                                 /* And add it to the output array */
     782          44 :                                                 zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &result_set);
     783             :                                         }
     784             :                                 } else {                        /* single pattern matching */
     785             :                                         /* For each subpattern, insert it into the subpatterns array. */
     786       41343 :                                         if (subpat_names) {
     787           5 :                                                 if (offset_capture) {
     788           5 :                                                         for (i = 0; i < count; i++) {
     789          12 :                                                                 add_offset_pair(subpats, (char *)stringlist[i],
     790           4 :                                                                                                 offsets[(i<<1)+1] - offsets[i<<1],
     791           8 :                                                                                                 offsets[i<<1], subpat_names[i]);
     792             :                                                         }
     793             :                                                 } else {
     794          24 :                                                         for (i = 0; i < count; i++) {
     795          20 :                                                                 if (subpat_names[i]) {
     796          13 :                                                                         add_assoc_stringl(subpats, subpat_names[i], (char *)stringlist[i],
     797             :                                                                                                           offsets[(i<<1)+1] - offsets[i<<1]);
     798             :                                                                 }
     799          20 :                                                                 add_next_index_stringl(subpats, (char *)stringlist[i],
     800          20 :                                                                                                            offsets[(i<<1)+1] - offsets[i<<1]);
     801             :                                                         }
     802             :                                                 }
     803             :                                         } else {
     804       41338 :                                                 if (offset_capture) {
     805          10 :                                                         for (i = 0; i < count; i++) {
     806          12 :                                                                 add_offset_pair(subpats, (char *)stringlist[i],
     807           6 :                                                                                                 offsets[(i<<1)+1] - offsets[i<<1],
     808           6 :                                                                                                 offsets[i<<1], NULL);
     809             :                                                         }
     810             :                                                 } else {
     811      126164 :                                                         for (i = 0; i < count; i++) {
     812       84830 :                                                                 add_next_index_stringl(subpats, (char *)stringlist[i],
     813       84830 :                                                                                                            offsets[(i<<1)+1] - offsets[i<<1]);
     814             :                                                         }
     815             :                                                 }
     816             :                                         }
     817             :                                         /* Add MARK, if available */
     818       41343 :                                         if (mark) {
     819           1 :                                                 add_assoc_string_ex(subpats, "MARK", sizeof("MARK") - 1, (char *)mark);
     820             :                                         }
     821             :                                 }
     822             : 
     823       41527 :                                 pcre_free((void *) stringlist);
     824             :                         }
     825     1640433 :                 } else if (count == PCRE_ERROR_NOMATCH) {
     826             :                         /* If we previously set PCRE_NOTEMPTY after a null match,
     827             :                            this is not necessarily the end. We need to advance
     828             :                            the start offset, and continue. Fudge the offset values
     829             :                            to achieve this, unless we're already at the end of the string. */
     830     1640429 :                         if (g_notempty != 0 && start_offset < subject_len) {
     831           2 :                                 offsets[0] = (int)start_offset;
     832           2 :                                 offsets[1] = (int)(start_offset + 1);
     833             :                         } else
     834             :                                 break;
     835             :                 } else {
     836           4 :                         pcre_handle_exec_error(count);
     837           4 :                         break;
     838             :                 }
     839             : 
     840             :                 /* If we have matched an empty string, mimic what Perl's /g options does.
     841             :                    This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
     842             :                    the match again at the same point. If this fails (picked up above) we
     843             :                    advance to the next character. */
     844      206282 :                 g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;
     845             : 
     846             :                 /* Advance to the position right after the last full match */
     847      206282 :                 start_offset = offsets[1];
     848      206282 :         } while (global);
     849             : 
     850             :         /* Add the match sets to the output array and clean up */
     851     1846518 :         if (global && subpats && subpats_order == PREG_PATTERN_ORDER) {
     852         895 :                 if (subpat_names) {
     853          10 :                         for (i = 0; i < num_subpats; i++) {
     854           8 :                                 if (subpat_names[i]) {
     855           5 :                                         zend_hash_str_update(Z_ARRVAL_P(subpats), subpat_names[i],
     856             :                                                                          strlen(subpat_names[i]), &match_sets[i]);
     857           5 :                                         Z_ADDREF(match_sets[i]);
     858             :                                 }
     859           8 :                                 zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &match_sets[i]);
     860             :                         }
     861             :                 } else {
     862        2013 :                         for (i = 0; i < num_subpats; i++) {
     863        1120 :                                 zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &match_sets[i]);
     864             :                         }
     865             :                 }
     866         895 :                 efree(match_sets);
     867             : 
     868         895 :                 if (Z_TYPE(marks) != IS_UNDEF) {
     869           1 :                         add_assoc_zval(subpats, "MARK", &marks);
     870             :                 }
     871             :         }
     872             : 
     873     1846518 :         if (size_offsets <= 32) {
     874     1846507 :                 free_alloca(offsets, use_heap);
     875             :         } else {
     876          11 :                 efree(offsets);
     877             :         }
     878     1846518 :         if (subpat_names) {
     879           9 :                 efree(subpat_names);
     880             :         }
     881             : 
     882             :         /* Did we encounter an error? */
     883     1846518 :         if (PCRE_G(error_code) == PHP_PCRE_NO_ERROR) {
     884     1846514 :                 RETVAL_LONG(matched);
     885             :         } else {
     886           4 :                 RETVAL_FALSE;
     887             :         }
     888             : }
     889             : /* }}} */
     890             : 
     891             : /* {{{ proto int preg_match(string pattern, string subject [, array &subpatterns [, int flags [, int offset]]])
     892             :    Perform a Perl-style regular expression match */
     893     1845609 : static PHP_FUNCTION(preg_match)
     894             : {
     895     1845609 :         php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
     896     1845609 : }
     897             : /* }}} */
     898             : 
     899             : /* {{{ proto int preg_match_all(string pattern, string subject [, array &subpatterns [, int flags [, int offset]]])
     900             :    Perform a Perl-style global regular expression match */
     901         107 : static PHP_FUNCTION(preg_match_all)
     902             : {
     903         107 :         php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
     904         107 : }
     905             : /* }}} */
     906             : 
     907             : /* {{{ preg_get_backref
     908             :  */
     909         146 : static int preg_get_backref(char **str, int *backref)
     910             : {
     911         146 :         register char in_brace = 0;
     912         146 :         register char *walk = *str;
     913             : 
     914         146 :         if (walk[1] == 0)
     915           9 :                 return 0;
     916             : 
     917         137 :         if (*walk == '$' && walk[1] == '{') {
     918          14 :                 in_brace = 1;
     919          14 :                 walk++;
     920             :         }
     921         137 :         walk++;
     922             : 
     923         250 :         if (*walk >= '0' && *walk <= '9') {
     924         113 :                 *backref = *walk - '0';
     925         113 :                 walk++;
     926             :         } else
     927          24 :                 return 0;
     928             : 
     929         113 :         if (*walk && *walk >= '0' && *walk <= '9') {
     930           2 :                 *backref = *backref * 10 + *walk - '0';
     931           2 :                 walk++;
     932             :         }
     933             : 
     934         113 :         if (in_brace) {
     935          14 :                 if (*walk == 0 || *walk != '}')
     936           6 :                         return 0;
     937             :                 else
     938           8 :                         walk++;
     939             :         }
     940             : 
     941         107 :         *str = walk;
     942         107 :         return 1;
     943             : }
     944             : /* }}} */
     945             : 
     946             : /* {{{ preg_do_repl_func
     947             :  */
     948          52 : static zend_string *preg_do_repl_func(zval *function, char *subject, int *offsets, char **subpat_names, int count, unsigned char *mark)
     949             : {
     950             :         zend_string *result_str;
     951             :         zval             retval;                        /* Function return value */
     952             :         zval         args[1];                   /* Argument to pass to function */
     953             :         int                      i;
     954             : 
     955          52 :         array_init_size(&args[0], count + (mark ? 1 : 0));
     956          52 :         if (subpat_names) {
     957           3 :                 for (i = 0; i < count; i++) {
     958           2 :                         if (subpat_names[i]) {
     959           1 :                                 add_assoc_stringl(&args[0], subpat_names[i], &subject[offsets[i<<1]] , offsets[(i<<1)+1] - offsets[i<<1]);
     960             :                         }
     961           2 :                         add_next_index_stringl(&args[0], &subject[offsets[i<<1]], offsets[(i<<1)+1] - offsets[i<<1]);
     962             :                 }
     963             :         } else {
     964         133 :                 for (i = 0; i < count; i++) {
     965          82 :                         add_next_index_stringl(&args[0], &subject[offsets[i<<1]], offsets[(i<<1)+1] - offsets[i<<1]);
     966             :                 }
     967             :         }
     968          52 :         if (mark) {
     969           2 :                 add_assoc_string(&args[0], "MARK", (char *) mark);
     970             :         }
     971             : 
     972         155 :         if (call_user_function_ex(EG(function_table), NULL, function, &retval, 1, args, 0, NULL) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
     973          51 :                 result_str = zval_get_string(&retval);
     974          51 :                 zval_ptr_dtor(&retval);
     975             :         } else {
     976           1 :                 if (!EG(exception)) {
     977           0 :                         php_error_docref(NULL, E_WARNING, "Unable to call custom replacement function");
     978             :                 }
     979             : 
     980           2 :                 result_str = zend_string_init(&subject[offsets[0]], offsets[1] - offsets[0], 0);
     981             :         }
     982             : 
     983          52 :         zval_ptr_dtor(&args[0]);
     984             : 
     985          52 :         return result_str;
     986             : }
     987             : /* }}} */
     988             : 
     989             : /* {{{ preg_do_eval
     990             :  */
     991           4 : static zend_string *preg_do_eval(char *eval_str, int eval_str_len, char *subject,
     992             :                                                 int *offsets, int count)
     993             : {
     994             :         zval             retval;                        /* Return value from evaluation */
     995             :         char            *eval_str_end,          /* End of eval string */
     996             :                                 *match,                         /* Current match for a backref */
     997             :                                 *walk,                          /* Used to walk the code string */
     998             :                                 *segment,                       /* Start of segment to append while walking */
     999             :                                  walk_last;                     /* Last walked character */
    1000             :         int                      match_len;                     /* Length of the match */
    1001             :         int                      backref;                       /* Current backref */
    1002             :         zend_string *esc_match;                 /* Quote-escaped match */
    1003             :         zend_string *result_str;
    1004             :         char        *compiled_string_description;
    1005           4 :         smart_str    code = {0};
    1006             : 
    1007           4 :         eval_str_end = eval_str + eval_str_len;
    1008           4 :         walk = segment = eval_str;
    1009           4 :         walk_last = 0;
    1010             : 
    1011         178 :         while (walk < eval_str_end) {
    1012             :                 /* If found a backreference.. */
    1013         170 :                 if ('\\' == *walk || '$' == *walk) {
    1014          32 :                         smart_str_appendl(&code, segment, walk - segment);
    1015          32 :                         if (walk_last == '\\') {
    1016           0 :                                 code.s->val[code.s->len-1] = *walk++;
    1017           0 :                                 segment = walk;
    1018           0 :                                 walk_last = 0;
    1019           0 :                                 continue;
    1020             :                         }
    1021          32 :                         segment = walk;
    1022          32 :                         if (preg_get_backref(&walk, &backref)) {
    1023           7 :                                 if (backref < count) {
    1024             :                                         /* Find the corresponding string match and substitute it
    1025             :                                            in instead of the backref */
    1026           7 :                                         match = subject + offsets[backref<<1];
    1027           7 :                                         match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
    1028           7 :                                         if (match_len) {
    1029          14 :                                                 esc_match = php_addslashes(zend_string_init(match, match_len, 0), 1);
    1030             :                                         } else {
    1031           0 :                                                 esc_match = zend_string_init(match, match_len, 0);
    1032             :                                         }
    1033             :                                 } else {
    1034           0 :                                         esc_match = STR_EMPTY_ALLOC();
    1035             :                                 }
    1036           7 :                                 smart_str_appendl(&code, esc_match->val, esc_match->len);
    1037             : 
    1038           7 :                                 segment = walk;
    1039             : 
    1040             :                                 /* Clean up and reassign */
    1041             :                                 zend_string_release(esc_match);
    1042           7 :                                 continue;
    1043             :                         }
    1044             :                 }
    1045         163 :                 walk++;
    1046         163 :                 walk_last = walk[-1];
    1047             :         }
    1048           4 :         smart_str_appendl(&code, segment, walk - segment);
    1049             :         smart_str_0(&code);
    1050             : 
    1051           4 :         compiled_string_description = zend_make_compiled_string_description("regexp code");
    1052             :         /* Run the code */
    1053           4 :         if (zend_eval_stringl(code.s->val, code.s->len, &retval, compiled_string_description) == FAILURE) {
    1054           1 :                 efree(compiled_string_description);
    1055           1 :                 php_error_docref(NULL,E_ERROR, "Failed evaluating code: %s%s", PHP_EOL, code.s->val);
    1056             :                 /* zend_error() does not return in this case */
    1057             :         }
    1058           3 :         efree(compiled_string_description);
    1059             : 
    1060             :         /* Save the return string */
    1061           3 :         result_str = zval_get_string(&retval);
    1062             : 
    1063             :         /* Clean up */
    1064             :         zval_dtor(&retval);
    1065             :         smart_str_free(&code);
    1066             : 
    1067           3 :         return result_str;
    1068             : }
    1069             : /* }}} */
    1070             : 
    1071             : /* {{{ php_pcre_replace
    1072             :  */
    1073       25571 : PHPAPI zend_string *php_pcre_replace(zend_string *regex,
    1074             :                                                           char *subject, int subject_len,
    1075             :                                                           zval *replace_val, int is_callable_replace,
    1076             :                                                           int limit, int *replace_count)
    1077             : {
    1078             :         pcre_cache_entry        *pce;                       /* Compiled regular expression */
    1079             : 
    1080             :         /* Compile regex or get it from cache. */
    1081       25571 :         if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
    1082          11 :                 return NULL;
    1083             :         }
    1084             : 
    1085       25560 :         return php_pcre_replace_impl(pce, subject, subject_len, replace_val,
    1086             :                 is_callable_replace, limit, replace_count);
    1087             : }
    1088             : /* }}} */
    1089             : 
    1090             : /* {{{ php_pcre_replace_impl() */
    1091       25572 : PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *replace_val,
    1092             :         int is_callable_replace, int limit, int *replace_count)
    1093             : {
    1094       25572 :         pcre_extra              *extra = pce->extra;/* Holds results of studying */
    1095             :         pcre_extra               extra_data;            /* Used locally for exec options */
    1096       25572 :         int                              exoptions = 0;         /* Execution options */
    1097       25572 :         int                              count = 0;                     /* Count of matched subpatterns */
    1098             :         int                             *offsets;                       /* Array of subpattern offsets */
    1099             :         char                    **subpat_names;         /* Array for named subpatterns */
    1100             :         int                              num_subpats;           /* Number of captured subpatterns */
    1101             :         int                              size_offsets;          /* Size of the offsets array */
    1102             :         int                              new_len;                       /* Length of needed storage */
    1103             :         int                              alloc_len;                     /* Actual allocated length */
    1104             :         int                              match_len;                     /* Length of the current match */
    1105             :         int                              backref;                       /* Backreference number */
    1106             :         int                              eval;                          /* If the replacement string should be eval'ed */
    1107             :         int                              start_offset;          /* Where the new search starts */
    1108       25572 :         int                              g_notempty=0;          /* If the match should not be empty */
    1109       25572 :         int                              replace_len=0;         /* Length of replacement string */
    1110       25572 :         char                    *replace=NULL,          /* Replacement string */
    1111             :                                         *walkbuf,                       /* Location of current replacement in the result */
    1112             :                                         *walk,                          /* Used to walk the replacement string */
    1113             :                                         *match,                         /* The current match */
    1114             :                                         *piece,                         /* The current piece of subject */
    1115       25572 :                                         *replace_end=NULL,      /* End of replacement string */
    1116             :                                          walk_last;                     /* Last walked character */
    1117             :         int                              result_len;            /* Length of result */
    1118       25572 :         unsigned char   *mark = NULL;       /* Target for MARK name */
    1119             :         zend_string             *result;                        /* Result of replacement */
    1120       25572 :         zend_string     *eval_result=NULL;  /* Result of eval or custom function */
    1121             :         ALLOCA_FLAG(use_heap);
    1122             : 
    1123       25572 :         if (extra == NULL) {
    1124           2 :                 extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
    1125           2 :                 extra = &extra_data;
    1126             :         }
    1127       25572 :         extra->match_limit = (unsigned long)PCRE_G(backtrack_limit);
    1128       25572 :         extra->match_limit_recursion = (unsigned long)PCRE_G(recursion_limit);
    1129             : 
    1130       25572 :         eval = pce->preg_options & PREG_REPLACE_EVAL;
    1131       25572 :         if (is_callable_replace) {
    1132          34 :                 if (eval) {
    1133           1 :                         php_error_docref(NULL, E_WARNING, "Modifier /e cannot be used with replacement callback");
    1134           1 :                         return NULL;
    1135             :                 }
    1136             :         } else {
    1137       25538 :                 replace = Z_STRVAL_P(replace_val);
    1138       25538 :                 replace_len = (int)Z_STRLEN_P(replace_val);
    1139       25538 :                 replace_end = replace + replace_len;
    1140             :         }
    1141             : 
    1142       25571 :         if (eval) {
    1143           3 :                 php_error_docref(NULL, E_DEPRECATED, "The /e modifier is deprecated, use preg_replace_callback instead");
    1144             :         }
    1145             : 
    1146             :         /* Calculate the size of the offsets array, and allocate memory for it. */
    1147       25571 :         num_subpats = pce->capture_count + 1;
    1148       25571 :         size_offsets = num_subpats * 3;
    1149       25571 :         if (size_offsets <= 32) {
    1150       25568 :                 offsets = (int *)do_alloca(size_offsets * sizeof(int), use_heap);
    1151             :         } else {
    1152           3 :                 offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
    1153             :         }
    1154             : 
    1155             :         /*
    1156             :          * Build a mapping from subpattern numbers to their names. We will
    1157             :          * allocate the table only if there are any named subpatterns.
    1158             :          */
    1159       25571 :         subpat_names = NULL;
    1160       25571 :         if (pce->name_count > 0) {
    1161           1 :                 subpat_names = make_subpats_table(num_subpats, pce);
    1162           1 :                 if (!subpat_names) {
    1163           0 :                         return NULL;
    1164             :                 }
    1165             :         }
    1166             : 
    1167       25571 :         alloc_len = 2 * subject_len;
    1168       51142 :         result = zend_string_alloc(alloc_len * sizeof(char), 0);
    1169             : 
    1170             :         /* Initialize */
    1171       25571 :         match = NULL;
    1172       25571 :         start_offset = 0;
    1173       25571 :         result_len = 0;
    1174       25571 :         PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
    1175             : 
    1176             :         while (1) {
    1177             : #ifdef PCRE_EXTRA_MARK
    1178       31574 :                 extra->mark = &mark;
    1179       31574 :                 extra->flags |= PCRE_EXTRA_MARK;
    1180             : #endif
    1181             :                 /* Execute the regular expression. */
    1182       31574 :                 count = pcre_exec(pce->re, extra, subject, subject_len, start_offset,
    1183             :                                                   exoptions|g_notempty, offsets, size_offsets);
    1184             : 
    1185             :                 /* the string was already proved to be valid UTF-8 */
    1186       31574 :                 exoptions |= PCRE_NO_UTF8_CHECK;
    1187             : 
    1188             :                 /* Check for too many substrings condition. */
    1189       31574 :                 if (count == 0) {
    1190           0 :                         php_error_docref(NULL,E_NOTICE, "Matched, but too many substrings");
    1191           0 :                         count = size_offsets/3;
    1192             :                 }
    1193             : 
    1194       31574 :                 piece = subject + start_offset;
    1195             : 
    1196       37577 :                 if (count > 0 && (limit == -1 || limit > 0)) {
    1197        6004 :                         if (replace_count) {
    1198        6004 :                                 ++*replace_count;
    1199             :                         }
    1200             :                         /* Set the match location in subject */
    1201        6004 :                         match = subject + offsets[0];
    1202             : 
    1203        6004 :                         new_len = result_len + offsets[0] - start_offset; /* part before the match */
    1204             : 
    1205             :                         /* If evaluating, do it and add the return string's length */
    1206        6004 :                         if (eval) {
    1207           4 :                                 eval_result = preg_do_eval(replace, replace_len, subject,
    1208             :                                                                                            offsets, count);
    1209           3 :                                 new_len += (int)eval_result->len;
    1210        6000 :                         } else if (is_callable_replace) {
    1211             :                                 /* Use custom function to get replacement string and its length. */
    1212          52 :                                 eval_result = preg_do_repl_func(replace_val, subject, offsets, subpat_names, count, mark);
    1213          52 :                                 new_len += (int)eval_result->len;
    1214             :                         } else { /* do regular substitution */
    1215        5948 :                                 walk = replace;
    1216        5948 :                                 walk_last = 0;
    1217       17927 :                                 while (walk < replace_end) {
    1218        6031 :                                         if ('\\' == *walk || '$' == *walk) {
    1219          57 :                                                 if (walk_last == '\\') {
    1220           0 :                                                         walk++;
    1221           0 :                                                         walk_last = 0;
    1222           0 :                                                         continue;
    1223             :                                                 }
    1224          57 :                                                 if (preg_get_backref(&walk, &backref)) {
    1225          50 :                                                         if (backref < count)
    1226          49 :                                                                 new_len += offsets[(backref<<1)+1] - offsets[backref<<1];
    1227          50 :                                                         continue;
    1228             :                                                 }
    1229             :                                         }
    1230        5981 :                                         new_len++;
    1231        5981 :                                         walk++;
    1232        5981 :                                         walk_last = walk[-1];
    1233             :                                 }
    1234             :                         }
    1235             : 
    1236        6003 :                         if (new_len > alloc_len) {
    1237          12 :                                 alloc_len = alloc_len + 2 * new_len;
    1238          24 :                                 result = zend_string_realloc(result, alloc_len, 0);
    1239             :                         }
    1240             :                         /* copy the part of the string before the match */
    1241        6003 :                         memcpy(&result->val[result_len], piece, match-piece);
    1242        6003 :                         result_len += (int)(match-piece);
    1243             : 
    1244             :                         /* copy replacement and backrefs */
    1245        6003 :                         walkbuf = result->val + result_len;
    1246             : 
    1247             :                         /* If evaluating or using custom function, copy result to the buffer
    1248             :                          * and clean up. */
    1249        6058 :                         if (eval || is_callable_replace) {
    1250          55 :                                 memcpy(walkbuf, eval_result->val, eval_result->len);
    1251          55 :                                 result_len += (int)eval_result->len;
    1252          55 :                                 if (eval_result) zend_string_release(eval_result);
    1253             :                         } else { /* do regular backreference copying */
    1254        5948 :                                 walk = replace;
    1255        5948 :                                 walk_last = 0;
    1256       17927 :                                 while (walk < replace_end) {
    1257        6031 :                                         if ('\\' == *walk || '$' == *walk) {
    1258          57 :                                                 if (walk_last == '\\') {
    1259           0 :                                                         *(walkbuf-1) = *walk++;
    1260           0 :                                                         walk_last = 0;
    1261           0 :                                                         continue;
    1262             :                                                 }
    1263          57 :                                                 if (preg_get_backref(&walk, &backref)) {
    1264          50 :                                                         if (backref < count) {
    1265          49 :                                                                 match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
    1266          49 :                                                                 memcpy(walkbuf, subject + offsets[backref<<1], match_len);
    1267          49 :                                                                 walkbuf += match_len;
    1268             :                                                         }
    1269          50 :                                                         continue;
    1270             :                                                 }
    1271             :                                         }
    1272        5981 :                                         *walkbuf++ = *walk++;
    1273        5981 :                                         walk_last = walk[-1];
    1274             :                                 }
    1275        5948 :                                 *walkbuf = '\0';
    1276             :                                 /* increment the result length by how much we've added to the string */
    1277        5948 :                                 result_len += (int)(walkbuf - (result->val + result_len));
    1278             :                         }
    1279             : 
    1280        6003 :                         if (limit != -1)
    1281          32 :                                 limit--;
    1282             : 
    1283       25570 :                 } else if (count == PCRE_ERROR_NOMATCH || limit == 0) {
    1284             :                         /* If we previously set PCRE_NOTEMPTY after a null match,
    1285             :                            this is not necessarily the end. We need to advance
    1286             :                            the start offset, and continue. Fudge the offset values
    1287             :                            to achieve this, unless we're already at the end of the string. */
    1288       25566 :                         if (g_notempty != 0 && start_offset < subject_len) {
    1289           0 :                                 offsets[0] = start_offset;
    1290           0 :                                 offsets[1] = start_offset + 1;
    1291           0 :                                 memcpy(&result->val[result_len], piece, 1);
    1292           0 :                                 result_len++;
    1293             :                         } else {
    1294       25566 :                                 new_len = result_len + subject_len - start_offset;
    1295       25566 :                                 if (new_len > alloc_len) {
    1296           1 :                                         alloc_len = new_len; /* now we know exactly how long it is */
    1297           2 :                                         result = zend_string_realloc(result, alloc_len, 0);
    1298             :                                 }
    1299             :                                 /* stick that last bit of string on our output */
    1300       25566 :                                 memcpy(&result->val[result_len], piece, subject_len - start_offset);
    1301       25566 :                                 result_len += subject_len - start_offset;
    1302       25566 :                                 result->val[result_len] = '\0';
    1303       25566 :                                 break;
    1304             :                         }
    1305             :                 } else {
    1306           4 :                         pcre_handle_exec_error(count);
    1307             :                         zend_string_free(result);
    1308           4 :                         result = NULL;
    1309           4 :                         break;
    1310             :                 }
    1311             : 
    1312             :                 /* If we have matched an empty string, mimic what Perl's /g options does.
    1313             :                    This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
    1314             :                    the match again at the same point. If this fails (picked up above) we
    1315             :                    advance to the next character. */
    1316        6003 :                 g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;
    1317             : 
    1318             :                 /* Advance to the next piece. */
    1319        6003 :                 start_offset = offsets[1];
    1320        6003 :         }
    1321             : 
    1322       25570 :         if (result) {
    1323       25566 :                 result->len = result_len;
    1324             :         }
    1325       25570 :         if (size_offsets <= 32) {
    1326       25567 :                 free_alloca(offsets, use_heap);
    1327             :         } else {
    1328           3 :                 efree(offsets);
    1329             :         }
    1330       25570 :         if (subpat_names) {
    1331           1 :                 efree(subpat_names);
    1332             :         }
    1333             : 
    1334       25570 :         return result;
    1335             : }
    1336             : /* }}} */
    1337             : 
    1338             : /* {{{ php_replace_in_subject
    1339             :  */
    1340       25528 : static zend_string *php_replace_in_subject(zval *regex, zval *replace, zval *subject, int limit, int is_callable_replace, int *replace_count)
    1341             : {
    1342             :         zval            *regex_entry,
    1343       25528 :                                 *replace_entry = NULL,
    1344             :                                 *replace_value,
    1345             :                                  empty_replace;
    1346             :         zend_string *result;
    1347       25528 :         zend_string     *subject_str = zval_get_string(subject);
    1348             :         uint32_t replace_idx;
    1349             : 
    1350             :         /* FIXME: This might need to be changed to STR_EMPTY_ALLOC(). Check if this zval could be dtor()'ed somehow */
    1351       25528 :         ZVAL_EMPTY_STRING(&empty_replace);
    1352             : 
    1353             :         /* If regex is an array */
    1354       25528 :         if (Z_TYPE_P(regex) == IS_ARRAY) {
    1355          22 :                 replace_value = replace;
    1356          22 :                 replace_idx = 0;
    1357             : 
    1358             :                 /* For each entry in the regex array, get the entry */
    1359         151 :                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(regex), regex_entry) {
    1360             :                         /* Make sure we're dealing with strings. */
    1361          65 :                         zend_string *regex_str = zval_get_string(regex_entry);
    1362             : 
    1363             :                         /* If replace is an array and not a callable construct */
    1364          65 :                         if (Z_TYPE_P(replace) == IS_ARRAY && !is_callable_replace) {
    1365             :                                 /* Get current entry */
    1366          50 :                                 replace_entry = NULL;
    1367         100 :                                 while (replace_idx < Z_ARRVAL_P(replace)->nNumUsed) {
    1368          96 :                                         if (Z_TYPE(Z_ARRVAL_P(replace)->arData[replace_idx].val) != IS_UNUSED) {
    1369          48 :                                                 replace_entry = &Z_ARRVAL_P(replace)->arData[replace_idx].val;
    1370          48 :                                                 break;
    1371             :                                         }
    1372           0 :                                         replace_idx++;
    1373             :                                 }
    1374          50 :                                 if (replace_entry != NULL) {
    1375          48 :                                         if (!is_callable_replace) {
    1376          48 :                                                 convert_to_string_ex(replace_entry);
    1377             :                                         }
    1378          48 :                                         replace_value = replace_entry;
    1379          48 :                                         replace_idx++;
    1380             :                                 } else {
    1381             :                                         /* We've run out of replacement strings, so use an empty one */
    1382           2 :                                         replace_value = &empty_replace;
    1383             :                                 }
    1384             :                         }
    1385             : 
    1386             :                         /* Do the actual replacement and put the result back into subject_str
    1387             :                            for further replacements. */
    1388          65 :                         if ((result = php_pcre_replace(regex_str,
    1389             :                                                                                    subject_str->val,
    1390             :                                                                                    (int)subject_str->len,
    1391             :                                                                                    replace_value,
    1392             :                                                                                    is_callable_replace,
    1393             :                                                                                    limit,
    1394             :                                                                                    replace_count)) != NULL) {
    1395             :                                 zend_string_release(subject_str);
    1396          64 :                                 subject_str = result;
    1397             :                         } else {
    1398             :                                 zend_string_release(subject_str);
    1399             :                                 zend_string_release(regex_str);
    1400           1 :                                 return NULL;
    1401             :                         }
    1402             : 
    1403             :                         zend_string_release(regex_str);
    1404             :                 } ZEND_HASH_FOREACH_END();
    1405             : 
    1406          21 :                 return subject_str;
    1407             :         } else {
    1408       25506 :                 result = php_pcre_replace(Z_STR_P(regex),
    1409             :                                                                   subject_str->val,
    1410             :                                                                   (int)subject_str->len,
    1411             :                                                                   replace,
    1412             :                                                                   is_callable_replace,
    1413             :                                                                   limit,
    1414             :                                                                   replace_count);
    1415             :                 zend_string_release(subject_str);
    1416       25505 :                 return result;
    1417             :         }
    1418             : }
    1419             : /* }}} */
    1420             : 
    1421             : /* {{{ preg_replace_impl
    1422             :  */
    1423       25536 : static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, int is_callable_replace, int is_filter)
    1424             : {
    1425             :         zval                *regex,
    1426             :                                     *replace,
    1427             :                                     *subject,
    1428             :                                     *subject_entry,
    1429       25536 :                                     *zcount = NULL;
    1430       25536 :         int                              limit_val = -1;
    1431       25536 :         zend_long                limit = -1;
    1432             :         zend_string             *result;
    1433             :         zend_string             *string_key;
    1434             :         zend_ulong               num_key;
    1435             :         zend_string             *callback_name;
    1436       25536 :         int                              replace_count=0, old_replace_count;
    1437             : 
    1438             : #ifndef FAST_ZPP
    1439             :         /* Get function parameters and do error-checking. */
    1440             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|lz/", &regex, &replace, &subject, &limit, &zcount) == FAILURE) {
    1441             :                 return;
    1442             :         }
    1443             : #else
    1444       25536 :         ZEND_PARSE_PARAMETERS_START(3, 5)
    1445       25527 :                 Z_PARAM_ZVAL(regex)
    1446       25527 :                 Z_PARAM_ZVAL(replace)
    1447       25527 :                 Z_PARAM_ZVAL(subject)
    1448       25527 :                 Z_PARAM_OPTIONAL
    1449       25579 :                 Z_PARAM_LONG(limit)
    1450          33 :                 Z_PARAM_ZVAL_EX(zcount, 0, 1)
    1451       25536 :         ZEND_PARSE_PARAMETERS_END();
    1452             : #endif
    1453             : 
    1454       51020 :         if (!is_callable_replace && Z_TYPE_P(replace) == IS_ARRAY && Z_TYPE_P(regex) != IS_ARRAY) {
    1455           3 :                 php_error_docref(NULL, E_WARNING, "Parameter mismatch, pattern is a string while replacement is an array");
    1456           3 :                 RETURN_FALSE;
    1457             :         }
    1458             : 
    1459       76558 :         if (Z_TYPE_P(replace) != IS_ARRAY && (Z_TYPE_P(replace) != IS_OBJECT || !is_callable_replace)) {
    1460       25535 :                 SEPARATE_ZVAL(replace);
    1461       51020 :                 convert_to_string_ex(replace);
    1462             :         }
    1463       25520 :         if (is_callable_replace) {
    1464          39 :                 if (!zend_is_callable(replace, 0, &callback_name)) {
    1465           4 :                         php_error_docref(NULL, E_WARNING, "Requires argument 2, '%s', to be a valid callback", callback_name->val);
    1466           4 :                         zend_string_release(callback_name);
    1467           4 :                         ZVAL_DUP(return_value, subject);
    1468           4 :                         return;
    1469             :                 }
    1470          35 :                 zend_string_release(callback_name);
    1471             :         }
    1472             : 
    1473       25516 :         if (ZEND_NUM_ARGS() > 3) {
    1474          22 :                 limit_val = (int)limit;
    1475             :         }
    1476             : 
    1477       51032 :         if (Z_TYPE_P(regex) != IS_ARRAY) {
    1478       25539 :                 SEPARATE_ZVAL(regex);
    1479       51011 :                 convert_to_string_ex(regex);
    1480             :         }
    1481             : 
    1482             :         /* if subject is an array */
    1483       51030 :         if (Z_TYPE_P(subject) == IS_ARRAY) {
    1484           6 :                 array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(subject)));
    1485             : 
    1486             :                 /* For each subject entry, convert it to string, then perform replacement
    1487             :                    and add the result to the return_value array. */
    1488          44 :                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(subject), num_key, string_key, subject_entry) {
    1489          19 :                         old_replace_count = replace_count;
    1490          19 :                         if ((result = php_replace_in_subject(regex, replace, subject_entry, limit_val, is_callable_replace, &replace_count)) != NULL) {
    1491          36 :                                 if (!is_filter || replace_count > old_replace_count) {
    1492             :                                         /* Add to return array */
    1493          17 :                                         if (string_key) {
    1494           1 :                                                 add_assoc_str_ex(return_value, string_key->val, string_key->len, result);
    1495             :                                         } else {
    1496          16 :                                                 add_index_str(return_value, num_key, result);
    1497             :                                         }
    1498             :                                 } else {
    1499             :                                         zend_string_release(result);
    1500             :                                 }
    1501             :                         }
    1502             :                 } ZEND_HASH_FOREACH_END();
    1503             :         } else {        /* if subject is not an array */
    1504       25509 :                 old_replace_count = replace_count;
    1505       25509 :                 if ((result = php_replace_in_subject(regex, replace, subject, limit_val, is_callable_replace, &replace_count)) != NULL) {
    1506       50984 :                         if (!is_filter || replace_count > old_replace_count) {
    1507       25492 :                                 RETVAL_STR(result);
    1508             :                         } else {
    1509             :                                 zend_string_release(result);
    1510             :                         }
    1511             :                 }
    1512             :         }
    1513       25514 :         if (ZEND_NUM_ARGS() > 4) {
    1514           8 :                 zval_dtor(zcount);
    1515           8 :                 ZVAL_LONG(zcount, replace_count);
    1516             :         }
    1517             : 
    1518             : }
    1519             : /* }}} */
    1520             : 
    1521             : /* {{{ proto mixed preg_replace(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])
    1522             :    Perform Perl-style regular expression replacement. */
    1523       25488 : static PHP_FUNCTION(preg_replace)
    1524             : {
    1525       25488 :         preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
    1526       25484 : }
    1527             : /* }}} */
    1528             : 
    1529             : /* {{{ proto mixed preg_replace_callback(mixed regex, mixed callback, mixed subject [, int limit [, int &count]])
    1530             :    Perform Perl-style regular expression replacement using replacement callback. */
    1531          47 : static PHP_FUNCTION(preg_replace_callback)
    1532             : {
    1533          47 :         preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
    1534          47 : }
    1535             : /* }}} */
    1536             : 
    1537             : /* {{{ proto mixed preg_filter(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])
    1538             :    Perform Perl-style regular expression replacement and only return matches. */
    1539           1 : static PHP_FUNCTION(preg_filter)
    1540             : {
    1541           1 :         preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
    1542           1 : }
    1543             : /* }}} */
    1544             : 
    1545             : /* {{{ proto array preg_split(string pattern, string subject [, int limit [, int flags]])
    1546             :    Split string into an array using a perl-style regular expression as a delimiter */
    1547        2423 : static PHP_FUNCTION(preg_split)
    1548             : {
    1549             :         zend_string                     *regex;                 /* Regular expression */
    1550             :         zend_string                     *subject;               /* String to match against */
    1551        2423 :         zend_long                        limit_val = -1;/* Integer value of limit */
    1552        2423 :         zend_long                        flags = 0;             /* Match control flags */
    1553             :         pcre_cache_entry        *pce;                   /* Compiled regular expression */
    1554             : 
    1555             :         /* Get function parameters and do error checking */
    1556             : #ifndef FAST_ZPP
    1557             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|ll", &regex,
    1558             :                                                           &subject, &limit_val, &flags) == FAILURE) {
    1559             :                 RETURN_FALSE;
    1560             :         }
    1561             : #else
    1562        2423 :         ZEND_PARSE_PARAMETERS_START(2, 4)
    1563        7257 :                 Z_PARAM_STR(regex)
    1564        7251 :                 Z_PARAM_STR(subject)
    1565        2415 :                 Z_PARAM_OPTIONAL
    1566        2455 :                 Z_PARAM_LONG(limit_val)
    1567          52 :                 Z_PARAM_LONG(flags)
    1568        2423 :         ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
    1569             : #endif
    1570             : 
    1571             :         /* Compile regex or get it from cache. */
    1572        2415 :         if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
    1573           5 :                 RETURN_FALSE;
    1574             :         }
    1575             : 
    1576        2410 :         php_pcre_split_impl(pce, subject->val, (int)subject->len, return_value, (int)limit_val, flags);
    1577             : }
    1578             : /* }}} */
    1579             : 
    1580             : /* {{{ php_pcre_split
    1581             :  */
    1582        2425 : PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value,
    1583             :         zend_long limit_val, zend_long flags)
    1584             : {
    1585        2425 :         pcre_extra              *extra = pce->extra;/* Holds results of studying */
    1586        2425 :         pcre                    *re_bump = NULL;        /* Regex instance for empty matches */
    1587        2425 :         pcre_extra              *extra_bump = NULL;     /* Almost dummy */
    1588             :         pcre_extra               extra_data;            /* Used locally for exec options */
    1589             :         int                             *offsets;                       /* Array of subpattern offsets */
    1590             :         int                              size_offsets;          /* Size of the offsets array */
    1591        2425 :         int                              exoptions = 0;         /* Execution options */
    1592        2425 :         int                              count = 0;                     /* Count of matched subpatterns */
    1593             :         int                              start_offset;          /* Where the new search starts */
    1594             :         int                              next_offset;           /* End of the last delimiter match + 1 */
    1595        2425 :         int                              g_notempty = 0;        /* If the match should not be empty */
    1596             :         char                    *last_match;            /* Location of last match */
    1597             :         int                              no_empty;                      /* If NO_EMPTY flag is set */
    1598             :         int                              delim_capture;         /* If delimiters should be captured */
    1599             :         int                              offset_capture;        /* If offsets should be captured */
    1600             :         zval                     tmp;
    1601             :         ALLOCA_FLAG(use_heap);
    1602             : 
    1603        2425 :         no_empty = flags & PREG_SPLIT_NO_EMPTY;
    1604        2425 :         delim_capture = flags & PREG_SPLIT_DELIM_CAPTURE;
    1605        2425 :         offset_capture = flags & PREG_SPLIT_OFFSET_CAPTURE;
    1606             : 
    1607        2425 :         if (limit_val == 0) {
    1608           1 :                 limit_val = -1;
    1609             :         }
    1610             : 
    1611        2425 :         if (extra == NULL) {
    1612           7 :                 extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
    1613           7 :                 extra = &extra_data;
    1614             :         }
    1615        2425 :         extra->match_limit = (unsigned long)PCRE_G(backtrack_limit);
    1616        2425 :         extra->match_limit_recursion = (unsigned long)PCRE_G(recursion_limit);
    1617             : #ifdef PCRE_EXTRA_MARK
    1618        2425 :         extra->flags &= ~PCRE_EXTRA_MARK;
    1619             : #endif
    1620             : 
    1621             :         /* Initialize return value */
    1622        2425 :         array_init(return_value);
    1623             : 
    1624             :         /* Calculate the size of the offsets array, and allocate memory for it. */
    1625        2425 :         size_offsets = (pce->capture_count + 1) * 3;
    1626        2425 :         if (size_offsets <= 32) {
    1627        2425 :                 offsets = (int *)do_alloca(size_offsets * sizeof(int), use_heap);
    1628             :         } else {
    1629           0 :                 offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
    1630             :         }
    1631             : 
    1632             :         /* Start at the beginning of the string */
    1633        2425 :         start_offset = 0;
    1634        2425 :         next_offset = 0;
    1635        2425 :         last_match = subject;
    1636        2425 :         PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
    1637             : 
    1638             :         /* Get next piece if no limit or limit not yet reached and something matched*/
    1639        9009 :         while ((limit_val == -1 || limit_val > 1)) {
    1640        6582 :                 count = pcre_exec(pce->re, extra, subject,
    1641             :                                                   subject_len, start_offset,
    1642             :                                                   exoptions|g_notempty, offsets, size_offsets);
    1643             : 
    1644             :                 /* the string was already proved to be valid UTF-8 */
    1645        6582 :                 exoptions |= PCRE_NO_UTF8_CHECK;
    1646             : 
    1647             :                 /* Check for too many substrings condition. */
    1648        6582 :                 if (count == 0) {
    1649           0 :                         php_error_docref(NULL,E_NOTICE, "Matched, but too many substrings");
    1650           0 :                         count = size_offsets/3;
    1651             :                 }
    1652             : 
    1653             :                 /* If something matched */
    1654        6582 :                 if (count > 0) {
    1655        4101 :                         if (!no_empty || &subject[offsets[0]] != last_match) {
    1656             : 
    1657        4042 :                                 if (offset_capture) {
    1658             :                                         /* Add (match, offset) pair to the return value */
    1659          26 :                                         add_offset_pair(return_value, last_match, (int)(&subject[offsets[0]]-last_match), next_offset, NULL);
    1660             :                                 } else {
    1661             :                                         /* Add the piece to the return value */
    1662        8032 :                                         ZVAL_STRINGL(&tmp, last_match, &subject[offsets[0]]-last_match);
    1663        4016 :                                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
    1664             :                                 }
    1665             : 
    1666             :                                 /* One less left to do */
    1667        4042 :                                 if (limit_val != -1)
    1668           1 :                                         limit_val--;
    1669             :                         }
    1670             : 
    1671        4101 :                         last_match = &subject[offsets[1]];
    1672        4101 :                         next_offset = offsets[1];
    1673             : 
    1674        4101 :                         if (delim_capture) {
    1675             :                                 int i, match_len;
    1676          62 :                                 for (i = 1; i < count; i++) {
    1677          31 :                                         match_len = offsets[(i<<1)+1] - offsets[i<<1];
    1678             :                                         /* If we have matched a delimiter */
    1679          31 :                                         if (!no_empty || match_len > 0) {
    1680          21 :                                                 if (offset_capture) {
    1681          10 :                                                         add_offset_pair(return_value, &subject[offsets[i<<1]], match_len, offsets[i<<1], NULL);
    1682             :                                                 } else {
    1683          22 :                                                         ZVAL_STRINGL(&tmp, &subject[offsets[i<<1]], match_len);
    1684          11 :                                                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
    1685             :                                                 }
    1686             :                                         }
    1687             :                                 }
    1688             :                         }
    1689        2481 :                 } else if (count == PCRE_ERROR_NOMATCH) {
    1690             :                         /* If we previously set PCRE_NOTEMPTY after a null match,
    1691             :                            this is not necessarily the end. We need to advance
    1692             :                            the start offset, and continue. Fudge the offset values
    1693             :                            to achieve this, unless we're already at the end of the string. */
    1694        2480 :                         if (g_notempty != 0 && start_offset < subject_len) {
    1695          58 :                                 if (pce->compile_options & PCRE_UTF8) {
    1696          12 :                                         if (re_bump == NULL) {
    1697             :                                                 int dummy;
    1698           2 :                                                 zend_string *regex = zend_string_init("/./us", sizeof("/./us")-1, 0);
    1699           2 :                                                 re_bump = pcre_get_compiled_regex(regex, &extra_bump, &dummy);
    1700             :                                                 zend_string_release(regex);
    1701           2 :                                                 if (re_bump == NULL) {
    1702           0 :                                                         RETURN_FALSE;
    1703             :                                                 }
    1704             :                                         }
    1705          12 :                                         count = pcre_exec(re_bump, extra_bump, subject,
    1706             :                                                           subject_len, start_offset,
    1707             :                                                           exoptions, offsets, size_offsets);
    1708          12 :                                         if (count < 1) {
    1709           0 :                                                 php_error_docref(NULL, E_WARNING, "Unknown error");
    1710           0 :                                                 RETURN_FALSE;
    1711             :                                         }
    1712             :                                 } else {
    1713          46 :                                         offsets[0] = start_offset;
    1714          46 :                                         offsets[1] = start_offset + 1;
    1715             :                                 }
    1716             :                         } else
    1717             :                                 break;
    1718             :                 } else {
    1719           1 :                         pcre_handle_exec_error(count);
    1720           1 :                         break;
    1721             :                 }
    1722             : 
    1723             :                 /* If we have matched an empty string, mimic what Perl's /g options does.
    1724             :                    This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
    1725             :                    the match again at the same point. If this fails (picked up above) we
    1726             :                    advance to the next character. */
    1727        4159 :                 g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;
    1728             : 
    1729             :                 /* Advance to the position right after the last full match */
    1730        4159 :                 start_offset = offsets[1];
    1731             :         }
    1732             : 
    1733             : 
    1734        2425 :         start_offset = (int)(last_match - subject); /* the offset might have been incremented, but without further successful matches */
    1735             : 
    1736        2425 :         if (!no_empty || start_offset < subject_len)
    1737             :         {
    1738        2417 :                 if (offset_capture) {
    1739             :                         /* Add the last (match, offset) pair to the return value */
    1740           5 :                         add_offset_pair(return_value, &subject[start_offset], subject_len - start_offset, start_offset, NULL);
    1741             :                 } else {
    1742             :                         /* Add the last piece to the return value */
    1743        4824 :                         ZVAL_STRINGL(&tmp, last_match, subject + subject_len - last_match);
    1744        2412 :                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
    1745             :                 }
    1746             :         }
    1747             : 
    1748             : 
    1749             :         /* Clean up */
    1750        2425 :         if (size_offsets <= 32) {
    1751        2425 :                 free_alloca(offsets, use_heap);
    1752             :         } else {
    1753           0 :                 efree(offsets);
    1754             :         }
    1755             : }
    1756             : /* }}} */
    1757             : 
    1758             : /* {{{ proto string preg_quote(string str [, string delim_char])
    1759             :    Quote regular expression characters plus an optional character */
    1760        8193 : static PHP_FUNCTION(preg_quote)
    1761             : {
    1762             :         size_t           in_str_len;
    1763             :         char    *in_str;                /* Input string argument */
    1764             :         char    *in_str_end;    /* End of the input string */
    1765        8193 :         size_t           delim_len = 0;
    1766        8193 :         char    *delim = NULL;  /* Additional delimiter argument */
    1767             :         zend_string     *out_str;       /* Output string with quoted characters */
    1768             :         char    *p,                             /* Iterator for input string */
    1769             :                         *q,                             /* Iterator for output string */
    1770        8193 :                          delim_char=0,  /* Delimiter character to be quoted */
    1771             :                          c;                             /* Current character */
    1772        8193 :         zend_bool quote_delim = 0; /* Whether to quote additional delim char */
    1773             : 
    1774             :         /* Get the arguments and check for errors */
    1775             : #ifndef FAST_ZPP
    1776             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &in_str, &in_str_len,
    1777             :                                                           &delim, &delim_len) == FAILURE) {
    1778             :                 return;
    1779             :         }
    1780             : #else
    1781        8193 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1782       24570 :                 Z_PARAM_STRING(in_str, in_str_len)
    1783        8188 :                 Z_PARAM_OPTIONAL
    1784       24556 :                 Z_PARAM_STRING(delim, delim_len)
    1785        8193 :         ZEND_PARSE_PARAMETERS_END();
    1786             : #endif
    1787             : 
    1788        8188 :         in_str_end = in_str + in_str_len;
    1789             : 
    1790             :         /* Nothing to do if we got an empty string */
    1791        8188 :         if (in_str == in_str_end) {
    1792           5 :                 RETURN_EMPTY_STRING();
    1793             :         }
    1794             : 
    1795        8183 :         if (delim && *delim) {
    1796        8180 :                 delim_char = delim[0];
    1797        8180 :                 quote_delim = 1;
    1798             :         }
    1799             : 
    1800             :         /* Allocate enough memory so that even if each character
    1801             :            is quoted, we won't run out of room */
    1802       16366 :         out_str = zend_string_safe_alloc(4, in_str_len, 0, 0);
    1803             : 
    1804             :         /* Go through the string and quote necessary characters */
    1805     7537236 :         for (p = in_str, q = out_str->val; p != in_str_end; p++) {
    1806     7529053 :                 c = *p;
    1807     7529053 :                 switch(c) {
    1808             :                         case '.':
    1809             :                         case '\\':
    1810             :                         case '+':
    1811             :                         case '*':
    1812             :                         case '?':
    1813             :                         case '[':
    1814             :                         case '^':
    1815             :                         case ']':
    1816             :                         case '$':
    1817             :                         case '(':
    1818             :                         case ')':
    1819             :                         case '{':
    1820             :                         case '}':
    1821             :                         case '=':
    1822             :                         case '!':
    1823             :                         case '>':
    1824             :                         case '<':
    1825             :                         case '|':
    1826             :                         case ':':
    1827             :                         case '-':
    1828     1069099 :                                 *q++ = '\\';
    1829     1069099 :                                 *q++ = c;
    1830     1069099 :                                 break;
    1831             : 
    1832             :                         case '\0':
    1833        1031 :                                 *q++ = '\\';
    1834        1031 :                                 *q++ = '0';
    1835        1031 :                                 *q++ = '0';
    1836        1031 :                                 *q++ = '0';
    1837        1031 :                                 break;
    1838             : 
    1839             :                         default:
    1840     6458923 :                                 if (quote_delim && c == delim_char)
    1841       15022 :                                         *q++ = '\\';
    1842     6458923 :                                 *q++ = c;
    1843             :                                 break;
    1844             :                 }
    1845             :         }
    1846        8183 :         *q = '\0';
    1847             : 
    1848             :         /* Reallocate string and return it */
    1849       16366 :         out_str = zend_string_realloc(out_str, q - out_str->val, 0);
    1850        8183 :         RETURN_STR(out_str);
    1851             : }
    1852             : /* }}} */
    1853             : 
    1854             : /* {{{ proto array preg_grep(string regex, array input [, int flags])
    1855             :    Searches array and returns entries which match regex */
    1856          29 : static PHP_FUNCTION(preg_grep)
    1857             : {
    1858             :         zend_string                     *regex;                 /* Regular expression */
    1859             :         zval                            *input;                 /* Input array */
    1860          29 :         zend_long                        flags = 0;             /* Match control flags */
    1861             :         pcre_cache_entry        *pce;                   /* Compiled regular expression */
    1862             : 
    1863             :         /* Get arguments and do error checking */
    1864             : #ifndef FAST_ZPP
    1865             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa|l", &regex,
    1866             :                                                           &input, &flags) == FAILURE) {
    1867             :                 return;
    1868             :         }
    1869             : #else
    1870          29 :         ZEND_PARSE_PARAMETERS_START(2, 3)
    1871          75 :                 Z_PARAM_STR(regex)
    1872          69 :                 Z_PARAM_ARRAY(input)
    1873          20 :                 Z_PARAM_OPTIONAL
    1874          26 :                 Z_PARAM_LONG(flags)
    1875          29 :         ZEND_PARSE_PARAMETERS_END();
    1876             : #endif
    1877             : 
    1878             :         /* Compile regex or get it from cache. */
    1879          20 :         if ((pce = pcre_get_compiled_regex_cache(regex)) == NULL) {
    1880           5 :                 RETURN_FALSE;
    1881             :         }
    1882             : 
    1883          15 :         php_pcre_grep_impl(pce, input, return_value, flags);
    1884             : }
    1885             : /* }}} */
    1886             : 
    1887          15 : PHPAPI void  php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return_value, zend_long flags) /* {{{ */
    1888             : {
    1889             :         zval                *entry;                             /* An entry in the input array */
    1890          15 :         pcre_extra              *extra = pce->extra;/* Holds results of studying */
    1891             :         pcre_extra               extra_data;            /* Used locally for exec options */
    1892             :         int                             *offsets;                       /* Array of subpattern offsets */
    1893             :         int                              size_offsets;          /* Size of the offsets array */
    1894          15 :         int                              count = 0;                     /* Count of matched subpatterns */
    1895             :         zend_string             *string_key;
    1896             :         zend_ulong               num_key;
    1897             :         zend_bool                invert;                        /* Whether to return non-matching
    1898             :                                                                                    entries */
    1899             :         ALLOCA_FLAG(use_heap);
    1900             : 
    1901          15 :         invert = flags & PREG_GREP_INVERT ? 1 : 0;
    1902             : 
    1903          15 :         if (extra == NULL) {
    1904           0 :                 extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
    1905           0 :                 extra = &extra_data;
    1906             :         }
    1907          15 :         extra->match_limit = (unsigned long)PCRE_G(backtrack_limit);
    1908          15 :         extra->match_limit_recursion = (unsigned long)PCRE_G(recursion_limit);
    1909             : #ifdef PCRE_EXTRA_MARK
    1910          15 :         extra->flags &= ~PCRE_EXTRA_MARK;
    1911             : #endif
    1912             : 
    1913             :         /* Calculate the size of the offsets array, and allocate memory for it. */
    1914          15 :         size_offsets = (pce->capture_count + 1) * 3;
    1915          15 :         if (size_offsets <= 32) {
    1916          15 :                 offsets = (int *)do_alloca(size_offsets * sizeof(int), use_heap);
    1917             :         } else {
    1918           0 :                 offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
    1919             :         }
    1920             : 
    1921             :         /* Initialize return array */
    1922          15 :         array_init(return_value);
    1923             : 
    1924          15 :         PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
    1925             : 
    1926             :         /* Go through the input array */
    1927         171 :         ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
    1928          78 :                 zend_string *subject_str = zval_get_string(entry);
    1929             : 
    1930             :                 /* Perform the match */
    1931          78 :                 count = pcre_exec(pce->re, extra, subject_str->val,
    1932             :                                                   (int)subject_str->len, 0,
    1933             :                                                   0, offsets, size_offsets);
    1934             : 
    1935             :                 /* Check for too many substrings condition. */
    1936          78 :                 if (count == 0) {
    1937           0 :                         php_error_docref(NULL, E_NOTICE, "Matched, but too many substrings");
    1938           0 :                         count = size_offsets/3;
    1939          78 :                 } else if (count < 0 && count != PCRE_ERROR_NOMATCH) {
    1940           0 :                         pcre_handle_exec_error(count);
    1941             :                         zend_string_release(subject_str);
    1942           0 :                         break;
    1943             :                 }
    1944             : 
    1945             :                 /* If the entry fits our requirements */
    1946          78 :                 if ((count > 0 && !invert) || (count == PCRE_ERROR_NOMATCH && invert)) {
    1947          33 :                         if (Z_REFCOUNTED_P(entry)) {
    1948             :                                 Z_ADDREF_P(entry);
    1949             :                         }
    1950             : 
    1951             :                         /* Add to return array */
    1952          33 :                         if (string_key) {
    1953           3 :                                 zend_hash_update(Z_ARRVAL_P(return_value), string_key, entry);
    1954             :                         } else {
    1955          30 :                                 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
    1956             :                         }
    1957             :                 }
    1958             : 
    1959             :                 zend_string_release(subject_str);
    1960             :         } ZEND_HASH_FOREACH_END();
    1961             : 
    1962             :         /* Clean up */
    1963          15 :         if (size_offsets <= 32) {
    1964          15 :                 free_alloca(offsets, use_heap);
    1965             :         } else {
    1966           0 :                 efree(offsets);
    1967             :         }
    1968          15 : }
    1969             : /* }}} */
    1970             : 
    1971             : /* {{{ proto int preg_last_error()
    1972             :    Returns the error code of the last regexp execution. */
    1973          17 : static PHP_FUNCTION(preg_last_error)
    1974             : {
    1975             : #ifndef FAST_ZPP
    1976             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
    1977             :                 return;
    1978             :         }
    1979             : #else
    1980          17 :         ZEND_PARSE_PARAMETERS_START(0, 0)
    1981          17 :         ZEND_PARSE_PARAMETERS_END();
    1982             : #endif
    1983             : 
    1984          15 :         RETURN_LONG(PCRE_G(error_code));
    1985             : }
    1986             : /* }}} */
    1987             : 
    1988             : /* {{{ module definition structures */
    1989             : 
    1990             : /* {{{ arginfo */
    1991             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_match, 0, 0, 2)
    1992             :     ZEND_ARG_INFO(0, pattern)
    1993             :     ZEND_ARG_INFO(0, subject)
    1994             :     ZEND_ARG_INFO(1, subpatterns) /* array */
    1995             :     ZEND_ARG_INFO(0, flags)
    1996             :     ZEND_ARG_INFO(0, offset)
    1997             : ZEND_END_ARG_INFO()
    1998             : 
    1999             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_match_all, 0, 0, 2)
    2000             :     ZEND_ARG_INFO(0, pattern)
    2001             :     ZEND_ARG_INFO(0, subject)
    2002             :     ZEND_ARG_INFO(1, subpatterns) /* array */
    2003             :     ZEND_ARG_INFO(0, flags)
    2004             :     ZEND_ARG_INFO(0, offset)
    2005             : ZEND_END_ARG_INFO()
    2006             : 
    2007             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_replace, 0, 0, 3)
    2008             :     ZEND_ARG_INFO(0, regex)
    2009             :     ZEND_ARG_INFO(0, replace)
    2010             :     ZEND_ARG_INFO(0, subject)
    2011             :     ZEND_ARG_INFO(0, limit)
    2012             :     ZEND_ARG_INFO(1, count)
    2013             : ZEND_END_ARG_INFO()
    2014             : 
    2015             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_replace_callback, 0, 0, 3)
    2016             :     ZEND_ARG_INFO(0, regex)
    2017             :     ZEND_ARG_INFO(0, callback)
    2018             :     ZEND_ARG_INFO(0, subject)
    2019             :     ZEND_ARG_INFO(0, limit)
    2020             :     ZEND_ARG_INFO(1, count)
    2021             : ZEND_END_ARG_INFO()
    2022             : 
    2023             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_split, 0, 0, 2)
    2024             :     ZEND_ARG_INFO(0, pattern)
    2025             :     ZEND_ARG_INFO(0, subject)
    2026             :     ZEND_ARG_INFO(0, limit)
    2027             :     ZEND_ARG_INFO(0, flags)
    2028             : ZEND_END_ARG_INFO()
    2029             : 
    2030             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_quote, 0, 0, 1)
    2031             :     ZEND_ARG_INFO(0, str)
    2032             :     ZEND_ARG_INFO(0, delim_char)
    2033             : ZEND_END_ARG_INFO()
    2034             : 
    2035             : ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_grep, 0, 0, 2)
    2036             :     ZEND_ARG_INFO(0, regex)
    2037             :     ZEND_ARG_INFO(0, input) /* array */
    2038             :     ZEND_ARG_INFO(0, flags)
    2039             : ZEND_END_ARG_INFO()
    2040             : 
    2041             : ZEND_BEGIN_ARG_INFO(arginfo_preg_last_error, 0)
    2042             : ZEND_END_ARG_INFO()
    2043             : /* }}} */
    2044             : 
    2045             : static const zend_function_entry pcre_functions[] = {
    2046             :         PHP_FE(preg_match,                              arginfo_preg_match)
    2047             :         PHP_FE(preg_match_all,                  arginfo_preg_match_all)
    2048             :         PHP_FE(preg_replace,                    arginfo_preg_replace)
    2049             :         PHP_FE(preg_replace_callback,   arginfo_preg_replace_callback)
    2050             :         PHP_FE(preg_filter,                             arginfo_preg_replace)
    2051             :         PHP_FE(preg_split,                              arginfo_preg_split)
    2052             :         PHP_FE(preg_quote,                              arginfo_preg_quote)
    2053             :         PHP_FE(preg_grep,                               arginfo_preg_grep)
    2054             :         PHP_FE(preg_last_error,                 arginfo_preg_last_error)
    2055             :         PHP_FE_END
    2056             : };
    2057             : 
    2058             : zend_module_entry pcre_module_entry = {
    2059             :         STANDARD_MODULE_HEADER,
    2060             :    "pcre",
    2061             :         pcre_functions,
    2062             :         PHP_MINIT(pcre),
    2063             :         PHP_MSHUTDOWN(pcre),
    2064             :         NULL,
    2065             :         NULL,
    2066             :         PHP_MINFO(pcre),
    2067             :         NO_VERSION_YET,
    2068             :         PHP_MODULE_GLOBALS(pcre),
    2069             :         PHP_GINIT(pcre),
    2070             :         PHP_GSHUTDOWN(pcre),
    2071             :         NULL,
    2072             :         STANDARD_MODULE_PROPERTIES_EX
    2073             : };
    2074             : 
    2075             : #ifdef COMPILE_DL_PCRE
    2076             : ZEND_GET_MODULE(pcre)
    2077             : #endif
    2078             : 
    2079             : /* }}} */
    2080             : 
    2081             : #endif /* HAVE_PCRE || HAVE_BUNDLED_PCRE */
    2082             : 
    2083             : /*
    2084             :  * Local variables:
    2085             :  * tab-width: 4
    2086             :  * c-basic-offset: 4
    2087             :  * End:
    2088             :  * vim600: sw=4 ts=4 fdm=marker
    2089             :  * vim<600: sw=4 ts=4
    2090             :  */

Generated by: LCOV version 1.10

Generated at Mon, 26 Jan 2015 14:46:49 +0000 (2 days ago)

Copyright © 2005-2015 The PHP Group
All rights reserved.