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

Generated by: LCOV version 1.10

Generated at Fri, 31 Jul 2015 08:58:53 +0000 (47 hours ago)

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