65279 php,php函数之子字符串替换 str_replace

str_replace—子字符串替换 [mixedstr_replace(mixed$search,mixed$replace,mixed$subject[,int&$count] )

php函数str_replace: 返回一个字符串或者数组。该字符串或数组是将subject中全部的search都被replace替换之后的结果。

现在我们所能知道的一些这个函数的用法,如:str_replace("#", "-", "dizaz#7#final"),str_replace(array('#', '$'), "-", "dizaz#7$final") 等,就这些调用方式,php内部是如何实现的呢,鉴于[深入理解PHP内核],在这里小做分析。

测试代码:

$object = "dizaz#7#final";

$res = str_replace("#", "-", $object);

echo $res;

如上,先从字符“#”替换为字符“-”开始。

预备工作:

下载PHP源代码,http://www.php.net下载即可

打造自己的阅读代码的工具[本人使用VIM+CSCOPE] 另:Linux用户也推荐图形化查看源代码工具kscope [google之]

编译工具[gcc],调试工具[gdb],另:GDB图形化端口DDD也很不错,推荐

编译PHP源码,记得使用--enable-debug [当然也希望通过./configure --help 看看PHP提供的一些编译选项,会有很多收获的]

开始分析:

通过[深入理解PHP内核]阅读,我们不难发现其PHP提供标准函数所在目录为PHP-SOURCE-DIR/ext/standard目录下,由于是字符串函数,很容易我们就可以在此目录下找到str_replace函数实现的文件 string.c,接下来就围绕着这个文件进行分析。[当然用CScope很容易就可以锁定,用:cs find s str_replace]

查询得知其定义实现:

/* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])

Replaces all occurrences of search in haystack with replace */

PHP_FUNCTION(str_replace)

{

php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);

}

/* }}} */

现在需要查看函数php_str_replace_common函数

/* {{{ php_str_replace_common

*/

static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)

{

/**

* TODO

* typedef struct _zval_struct zval;

* typedef struct _zend_class_entry zend_class_entry

*

* struct _zval_struct {

* zvalue_value value;

* zend_uint refcount__gc;

* zend_uchar type;

* zend_uchar is_ref__gc;

* };

*

* typedef union _zvalue_value {

* long lval;

* double dval;

* struct {

* char *val;

* int len;

* } str;

* HashTable *ht;

* zend_object_value obj;

* } zvalue_value;

*

* typedef struct _zend_object {

* zend_class_entry *ce;

* HashTable *properties;

* HashTable *guards;

* } zend_object;

*

*/

zval **subject, **search, **replace, **subject_entry, **zcount = NULL;

zval *result;

char *string_key;

uint string_key_len;

ulong num_key;

int count = 0;

int argc = ZEND_NUM_ARGS();

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE) {

return;

}

SEPARATE_ZVAL(search);

SEPARATE_ZVAL(replace);

SEPARATE_ZVAL(subject);

/* Make sure we're dealing with strings and do the replacement. */

if (Z_TYPE_PP(search) != IS_ARRAY) {

....//代码省滤

} else { /* if subject is not an array */

php_str_replace_in_subject(*search, *replace, subject, return_value, case_sensitivity, (argc > 3) ? &count : NULL);

}

if (argc > 3) {

zval_dtor(*zcount);

ZVAL_LONG(*zcount, count);

}

}

/* }}} */

继续跟踪php_str_replace_in_subject

/* {{{ php_str_replace_in_subject

*/

static void php_str_replace_in_subject(zval *search, zval *replace, zval **subject, zval *result, int case_sensitivity, int *replace_count)

{

zval **search_entry,

**replace_entry = NULL,

temp_result;

char *replace_value = NULL;

int replace_len = 0;

/* Make sure we're dealing with strings. */

convert_to_string_ex(subject);

Z_TYPE_P(result) = IS_STRING;

if (Z_STRLEN_PP(subject) == 0) {

ZVAL_STRINGL(result, "", 0, 1);

return;

}

/* If search is an array */

if (Z_TYPE_P(search) == IS_ARRAY) {

...//不走这步

} else {

if (Z_STRLEN_P(search) == 1) { //例子中只有”#“所以,执行这一步。

php_char_to_str_ex(Z_STRVAL_PP(subject),//subject的值,也就是dizaz#7#final

Z_STRLEN_PP(subject), //获取subject的长度

Z_STRVAL_P(search)[0], //由于只有1个”#”,所以只需要第一个字符

Z_STRVAL_P(replace), //所要替换成的字符,现在是“-”

Z_STRLEN_P(replace), //目标字符的长度,现在为1

result, //替换结果

case_sensitivity, //大小写是否敏感,默认是1

replace_count); //替换次数

} else if (Z_STRLEN_P(search) > 1) {

Z_STRVAL_P(result) = php_str_to_str_ex(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject),

Z_STRVAL_P(search), Z_STRLEN_P(search),

Z_STRVAL_P(replace), Z_STRLEN_P(replace), &Z_STRLEN_P(result), case_sensitivity, replace_count);

} else {

MAKE_COPY_ZVAL(subject, result);

}

}

}

到现在为止,我们的目标最终锁定到了php_char_to_str_ex 函数,现在只需要分析这个函数就OK了。其实现为:

/* {{{ php_char_to_str_ex

*/

PHPAPI int php_char_to_str_ex(char *str, uint len, char from, char *to, int to_len, zval *result, int case_sensitivity, int *replace_count)

{

int char_count = 0;

int replaced = 0;

char *source, *target, *tmp, *source_end=str+len, *tmp_end = NULL;

if (case_sensitivity) { //现在case_sensitivity = 1

char *p = str, *e = p + len;

//计算需要替换几次

while ((p = memchr(p, from, (e - p)))) {

char_count++;

p++;

}

} else {

for (source = str; source < source_end; source++) {

if (tolower(*source) == tolower(from)) {

char_count++;

}

}

}

if (char_count == 0 && case_sensitivity) {

ZVAL_STRINGL(result, str, len, 1);

return 0;

}

//计算替换以后的长度,并且存储到result中。

Z_STRLEN_P(result) = len + (char_count * (to_len - 1));

//申请内存,存放替换后的数据

Z_STRVAL_P(result) = target = safe_emalloc(char_count, to_len, len + 1);

//设定结果是一个字符串

Z_TYPE_P(result) = IS_STRING;

//target跟result的值都指向统一块内存,所以只需要处理target

if (case_sensitivity) {

char *p = str, *e = p + len, *s = str;

while ((p = memchr(p, from, (e - p)))) { //判断在第几个字符出现#

memcpy(target, s, (p - s)); //把#以前的数据拷贝给target

target += p - s;

memcpy(target, to, to_len); //把目标字符拷贝给target[当然此时的target是开始target+p-s的]

target += to_len;

p++;

s = p;

if (replace_count) {

*replace_count += 1; //设定替换次数

}

}

//如果后面还有,继续添加到target后,这样target所指向的内存块已经是替换好的数据了。

if (s < e) {

memcpy(target, s, (e - s));

target += e - s;

}

} else {

for (source = str; source < source_end; source++) {

if (tolower(*source) == tolower(from)) {

replaced = 1;

if (replace_count) {

*replace_count += 1;

}

for (tmp = to, tmp_end = tmp+to_len; tmp < tmp_end; tmp++) {

*target = *tmp;

target++;

}

} else {

*target = *source;

target++;

}

}

}

*target = 0;

return replaced;

}

/* }}} */

如上注释,其就这样完成了对于字符到字符串的替换。至于其中怎么return,怎么一个详细的过程,需要再对PHP执行过程有个相对的了解。

当然可以用gdb下断点到php_char_to_str_ex函数,来了解其详细执行过程。

下一篇来做对于字符串替换成字符串的分析。

小结:

其结果是存在zval中

其对替换的实现比较巧妙,可以学习

需要继续查看源码,学习更多编写技巧以及设计技巧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值