📄 php_pcre.c
字号:
*/PHPAPI char *php_pcre_replace(char *regex, int regex_len, char *subject, int subject_len, zval *replace_val, int is_callable_replace, int *result_len, int limit TSRMLS_DC){ pcre *re = NULL; /* Compiled regular expression */ pcre_extra *extra = NULL; /* Holds results of studying */ int exoptions = 0; /* Execution options */ int preg_options = 0; /* Custom preg options */ int count = 0; /* Count of matched subpatterns */ int *offsets; /* Array of subpattern offsets */ int size_offsets; /* Size of the offsets array */ int new_len; /* Length of needed storage */ int alloc_len; /* Actual allocated length */ int eval_result_len=0; /* Length of the eval'ed or function-returned string */ int match_len; /* Length of the current match */ int backref; /* Backreference number */ int eval; /* If the replacement string should be eval'ed */ int start_offset; /* Where the new search starts */ int g_notempty=0; /* If the match should not be empty */ int replace_len=0; /* Length of replacement string */ char *result, /* Result of replacement */ *replace=NULL, /* Replacement string */ *new_buf, /* Temporary buffer for re-allocation */ *walkbuf, /* Location of current replacement in the result */ *walk, /* Used to walk the replacement string */ *match, /* The current match */ *piece, /* The current piece of subject */ *replace_end=NULL, /* End of replacement string */ *eval_result, /* Result of eval or custom function */ walk_last; /* Last walked character */ int rc; /* Compile regex or get it from cache. */ if ((re = pcre_get_compiled_regex(regex, &extra, &preg_options)) == NULL) { return NULL; } eval = preg_options & PREG_REPLACE_EVAL; if (is_callable_replace) { if (eval) { php_error(E_WARNING, "/e modifier cannot be used with replacement callback"); return NULL; } } else { replace = Z_STRVAL_P(replace_val); replace_len = Z_STRLEN_P(replace_val); replace_end = replace + replace_len; } /* Calculate the size of the offsets array, and allocate memory for it. */ rc = pcre_fullinfo(re, extra, PCRE_INFO_CAPTURECOUNT, &size_offsets); if (rc < 0) { php_error(E_WARNING, "%s: internal pcre_fullinfo() error %d", get_active_function_name(TSRMLS_C), rc); return NULL; } size_offsets = (size_offsets + 1) * 3; offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0); alloc_len = 2 * subject_len + 1; result = safe_emalloc(alloc_len, sizeof(char), 0); /* Initialize */ match = NULL; *result_len = 0; start_offset = 0; while (1) { /* Execute the regular expression. */ count = pcre_exec(re, extra, subject, subject_len, start_offset, exoptions|g_notempty, offsets, size_offsets); /* Check for too many substrings condition. */ if (count == 0) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Matched, but too many substrings"); count = size_offsets/3; } piece = subject + start_offset; if (count > 0 && (limit == -1 || limit > 0)) { /* Set the match location in subject */ match = subject + offsets[0]; new_len = *result_len + offsets[0] - start_offset; /* part before the match */ /* If evaluating, do it and add the return string's length */ if (eval) { eval_result_len = preg_do_eval(replace, replace_len, subject, offsets, count, &eval_result TSRMLS_CC); new_len += eval_result_len; } else if (is_callable_replace) { /* Use custom function to get replacement string and its length. */ eval_result_len = preg_do_repl_func(replace_val, subject, offsets, count, &eval_result); new_len += eval_result_len; } else { /* do regular substitution */ walk = replace; walk_last = 0; while (walk < replace_end) { if ('\\' == *walk || '$' == *walk) { if (walk_last == '\\') { walk++; walk_last = 0; continue; } if (preg_get_backref(&walk, &backref)) { if (backref < count) new_len += offsets[(backref<<1)+1] - offsets[backref<<1]; continue; } } new_len++; walk++; walk_last = walk[-1]; } } if (new_len + 1 > alloc_len) { alloc_len = 1 + alloc_len + 2 * new_len; new_buf = emalloc(alloc_len); memcpy(new_buf, result, *result_len); efree(result); result = new_buf; } /* copy the part of the string before the match */ memcpy(&result[*result_len], piece, match-piece); *result_len += match-piece; /* copy replacement and backrefs */ walkbuf = result + *result_len; /* If evaluating or using custom function, copy result to the buffer * and clean up. */ if (eval || is_callable_replace) { memcpy(walkbuf, eval_result, eval_result_len); *result_len += eval_result_len; STR_FREE(eval_result); } else { /* do regular backreference copying */ walk = replace; walk_last = 0; while (walk < replace_end) { if ('\\' == *walk || '$' == *walk) { if (walk_last == '\\') { *(walkbuf-1) = *walk++; walk_last = 0; continue; } if (preg_get_backref(&walk, &backref)) { if (backref < count) { match_len = offsets[(backref<<1)+1] - offsets[backref<<1]; memcpy(walkbuf, subject + offsets[backref<<1], match_len); walkbuf += match_len; } continue; } } *walkbuf++ = *walk++; walk_last = walk[-1]; } *walkbuf = '\0'; /* increment the result length by how much we've added to the string */ *result_len += walkbuf - (result + *result_len); } if (limit != -1) limit--; } else { /* Failed to match */ /* If we previously set PCRE_NOTEMPTY after a null match, this is not necessarily the end. We need to advance the start offset, and continue. Fudge the offset values to achieve this, unless we're already at the end of the string. */ if (g_notempty != 0 && start_offset < subject_len) { offsets[0] = start_offset; offsets[1] = start_offset + 1; memcpy(&result[*result_len], piece, 1); (*result_len)++; } else { new_len = *result_len + subject_len - start_offset; if (new_len + 1 > alloc_len) { alloc_len = new_len + 1; /* now we know exactly how long it is */ new_buf = safe_emalloc(alloc_len, sizeof(char), 0); memcpy(new_buf, result, *result_len); efree(result); result = new_buf; } /* stick that last bit of string on our output */ memcpy(&result[*result_len], piece, subject_len - start_offset); *result_len += subject_len - start_offset; result[*result_len] = '\0'; break; } } /* If we have matched an empty string, mimic what Perl's /g options does. This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try the match again at the same point. If this fails (picked up above) we advance to the next character. */ g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0; /* Advance to the next piece. */ start_offset = offsets[1]; } efree(offsets); return result;}/* }}} *//* {{{ php_replace_in_subject */static char *php_replace_in_subject(zval *regex, zval *replace, zval **subject, int *result_len, int limit, zend_bool is_callable_replace TSRMLS_DC){ zval **regex_entry, **replace_entry = NULL, *replace_value, empty_replace; char *subject_value, *result; int subject_len; /* Make sure we're dealing with strings. */ convert_to_string_ex(subject); ZVAL_STRINGL(&empty_replace, empty_string, 0, 0); /* If regex is an array */ if (Z_TYPE_P(regex) == IS_ARRAY) { /* Duplicate subject string for repeated replacement */ subject_value = estrndup(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject)); subject_len = Z_STRLEN_PP(subject); *result_len = subject_len; zend_hash_internal_pointer_reset(Z_ARRVAL_P(regex)); replace_value = replace; if (Z_TYPE_P(replace) == IS_ARRAY && !is_callable_replace) zend_hash_internal_pointer_reset(Z_ARRVAL_P(replace)); /* For each entry in the regex array, get the entry */ while (zend_hash_get_current_data(Z_ARRVAL_P(regex), (void **)®ex_entry) == SUCCESS) { /* Make sure we're dealing with strings. */ convert_to_string_ex(regex_entry); /* If replace is an array and not a callable construct */ if (Z_TYPE_P(replace) == IS_ARRAY && !is_callable_replace) { /* Get current entry */ if (zend_hash_get_current_data(Z_ARRVAL_P(replace), (void **)&replace_entry) == SUCCESS) { if (!is_callable_replace) { convert_to_string_ex(replace_entry); } replace_value = *replace_entry; zend_hash_move_forward(Z_ARRVAL_P(replace)); } else { /* We've run out of replacement strings, so use an empty one */ replace_value = &empty_replace; } } /* Do the actual replacement and put the result back into subject_value for further replacements. */ if ((result = php_pcre_replace(Z_STRVAL_PP(regex_entry), Z_STRLEN_PP(regex_entry), subject_value, subject_len, replace_value, is_callable_replace, result_len, limit TSRMLS_CC)) != NULL) { efree(subject_value); subject_value = result; subject_len = *result_len; } zend_hash_move_forward(Z_ARRVAL_P(regex)); } return subject_value; } else { result = php_pcre_replace(Z_STRVAL_P(regex), Z_STRLEN_P(regex), Z_STRVAL_PP(subject), Z_STRLEN_PP(subject), replace, is_callable_replace, result_len, limit TSRMLS_CC); return result; }}/* }}} *//* {{{ preg_replace_impl */static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_callable_replace){ zval **regex, **replace, **subject, **limit, **subject_entry; char *result; int result_len; int limit_val = -1; char *string_key; ulong num_key; char *callback_name = NULL; /* Get function parameters and do error-checking. */ if (ZEND_NUM_ARGS() < 3 || ZEND_NUM_ARGS() > 4 || zend_get_parameters_ex(ZEND_NUM_ARGS(), ®ex, &replace, &subject, &limit) == FAILURE) { WRONG_PARAM_COUNT; } if (!is_callable_replace && Z_TYPE_PP(replace) == IS_ARRAY && Z_TYPE_PP(regex) != IS_ARRAY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter mismatch, pattern is a string while replacement in an array."); RETURN_FALSE; } SEPARATE_ZVAL(replace); if (Z_TYPE_PP(replace) != IS_ARRAY) convert_to_string_ex(replace); if (is_callable_replace) { if (!zend_is_callable(*replace, 0, &callback_name)) { php_error(E_WARNING, "%s() requires argument 2, '%s', to be a valid callback", get_active_function_name(TSRMLS_C), callback_name); efree(callback_name); *return_value = **subject; zval_copy_ctor(return_value); return; } efree(callback_name); } SEPARATE_ZVAL(regex); SEPARATE_ZVAL(subject); if (ZEND_NUM_ARGS() > 3) { convert_to_long_ex(limit); limit_val = Z_LVAL_PP(limit); } if (Z_TYPE_PP(regex) != IS_ARRAY) convert_to_string_ex(regex); /* if subject is an array */ if (Z_TYPE_PP(subject) == IS_ARRAY) { array_init(return_value); zend_hash_internal_pointer_reset(Z_ARRVAL_PP(subject)); /* For each subject entry, convert it to string, then perform replacement and add the result to the return_value array. */ while (zend_hash_get_current_data(Z_ARRVAL_PP(subject), (void **)&subject_entry) == SUCCESS) { SEPARATE_ZVAL(subject_entry); if ((result = php_replace_in_subject(*regex, *replace, subject_entry, &result_len, limit_val, is_callable_replace TSRMLS_CC)) != NULL) { /* Add to return array */ switch(zend_hash_get_current_key(Z_ARRVAL_PP(subject), &string_key, &num_key, 0)) { case HASH_KEY_IS_STRING: add_assoc_stringl(return_value, string_key, result, result_len, 0); break; case HASH_KEY_IS_LONG: add_index_stringl(return_value, num_key, result, result_len, 0); break; } } zend_hash_move_forward(Z_ARRVAL_PP(subject)); } } else { /* if subject is not an array */ if ((result = php_replace_in_subject(*regex, *replace, subject, &result_len, limit_val, is_callable_replace TSRMLS_CC)) != NULL) { RETVAL_STRINGL(result, result_len, 0); } } }/* }}} *//* {{{ proto string preg_replace(mixed regex, mixed replace, mixed subject [, int limit]) Perform Perl-style regular expression replacement. */PHP_FUNCTION(preg_replace){ preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);}/* }}} *//* {{{ proto string preg_replace_callback(mixed regex, mixed callback, mixed subject [, int limit]) Perform Perl-style regular expression replacement using replacement callback. */PHP_FUNCTION(preg_replace_callback){ preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);}/* }}} *//* {{{ proto array preg_split(string pattern, string subject [, int limit [, int flags]]) Split string into an array using a perl-style regular expression as a delimiter */PHP_FUNCTION(preg_split){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -