400string(99) php,php 源码 (string)

寻根究底

最近看到一些 php 的函数时,一直在想,它背后是怎么实现的呢?不仔细的挖一遍它的底细,压根就是经常会用错。而且感觉用的是一个黑箱子。指不定里面啥时候窜出一条蛇来。所以,找来源码,推敲一下底层的实现逻辑。

第一节: 伸展运动 strpos

它的整个实现风貌先展现出来, 长的是貌美如花:

strpos 源码1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51PHP_FUNCTION(strpos)

{

zval *needle;

zend_string *haystack;

char *found = NULL;

char needle_char[2];

zend_long offset = 0;

ZEND_PARSE_PARAMETERS_START(2, 3)

Z_PARAM_STR(haystack)

Z_PARAM_ZVAL(needle)

Z_PARAM_OPTIONAL

Z_PARAM_LONG(offset)

ZEND_PARSE_PARAMETERS_END();

if (offset < 0) {

offset += (zend_long)ZSTR_LEN(haystack);

}

if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {

php_error_docref(NULL, E_WARNING, "Offset not contained in string");

RETURN_FALSE;

}

if (Z_TYPE_P(needle) == IS_STRING) {

if (!Z_STRLEN_P(needle)) {

php_error_docref(NULL, E_WARNING, "Empty needle");

RETURN_FALSE;

}

found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,

Z_STRVAL_P(needle),

Z_STRLEN_P(needle),

ZSTR_VAL(haystack) + ZSTR_LEN(haystack));

} else {

if (php_needle_char(needle, needle_char) != SUCCESS) {

RETURN_FALSE;

}

needle_char[1] = 0;

found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,

needle_char,

1,

ZSTR_VAL(haystack) + ZSTR_LEN(haystack));

}

if (found) {

RETURN_LONG(found - ZSTR_VAL(haystack));

} else {

RETURN_FALSE;

}

}

初探

咋看,果然没有让我失望, 有点蒙: 这都啥啥啥? 第一眼只看懂了 if, else。 没系统的学习过 c 语言,阅读起来真的很费眼力劲, 呵呵。

那就先过一遍:1

2

3

4

5zval *needle

zend_string *haystack

char *found = NULL

char needle_char[2]

zend_long offset = 0

这一堆,是定义了一些变量。

然后1

2

3

4

5

6ZEND_PARSE_PARAMETERS_START(2, 3)

Z_PARAM_STR(haystack)

Z_PARAM_ZVAL(needle)

Z_PARAM_OPTIONAL

Z_PARAM_LONG(offset)

ZEND_PARSE_PARAMETERS_END()

没看懂,应该是初始化参数, (2, 3)说明是这个 strpos 函数的参数在 2 - 3 个之间。

接着,进行至第三个参数 offset 的判断了。1

2

3

4

5

6

7if (offset < 0) {

offset += (zend_long)ZSTR_LEN(haystack);

}

if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {

php_error_docref(NULL, E_WARNING, "Offset not contained in string");

RETURN_FALSE;

}

猜测当第三个参数 offset 是负数的时候,是反向的查找的。如果负数太小了,比如反向查找 ‘test’ 中的 ‘s’ 来说, 就是 strpos(‘test’, ‘t’, -10), 就会报错了。1

2

3

4➜ php -r "echo strpos('test', 's', -10);"

PHP Warning: strpos(): Offset not contained in string in Command line code on line 1

Warning: strpos(): Offset not contained in string in Command line code on line 1

果然。

先不追究细节, 走着。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21if (Z_TYPE_P(needle) == IS_STRING) {

if (!Z_STRLEN_P(needle)) {

php_error_docref(NULL, E_WARNING, "Empty needle");

RETURN_FALSE;

}

found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,

Z_STRVAL_P(needle),

Z_STRLEN_P(needle),

ZSTR_VAL(haystack) + ZSTR_LEN(haystack));

} else {

if (php_needle_char(needle, needle_char) != SUCCESS) {

RETURN_FALSE;

}

needle_char[1] = 0;

found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,

needle_char,

1,

ZSTR_VAL(haystack) + ZSTR_LEN(haystack));

}

一个 if(){} else{}, 哎呀,我滴亲娘啊,好像很简单啊,就判断一下要查找的参数 needle 是不是字符串类型, 然后如此如此, 最后返回一个 found。

而 found 最核心的就是一个真正查找这个字符的位置的函数 php_memnstr, 这个函数的实体实际是 zend_memnstr 这个函数。

继续追下去看看1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41static zend_always_inline const char *

zend_memnstr(const char *haystack, const char *needle, size_t needle_len, const char *end)

{

const char *p = haystack;

const char ne = needle[needle_len-1];

ptrdiff_t off_p;

size_t off_s;

if (needle_len == 1) {

return (const char *)memchr(p, *needle, (end-p));

}

off_p = end - haystack;

off_s = (off_p > 0) ? (size_t)off_p : 0;

if (needle_len > off_s) {

return NULL;

}

if (EXPECTED(off_s < 1024 || needle_len < 9)) {/* glibc memchr is faster when needle is too short */

end -= needle_len;

while (p <= end) {

if ((p = (const char *)memchr(p, *needle, (end-p+1))) && ne == p[needle_len-1]) {

if (!memcmp(needle, p, needle_len-1)) {

return p;

}

}

if (p == NULL) {

return NULL;

}

p++;

}

return NULL;

} else {

return zend_memnstr_ex(haystack, needle, needle_len, end);

}

}

这里 zend_memnstr() , 接收四个传进来的参数。 这里可以看到, 当判断出查找的字符串很短,查找的区间也很短时,(为啥是off_s < 1024 或者 needle_len < 9 这两个阀值, 不得而知) 调用的是 glibc 库,这个库是 linux 最底层的 api, 否则就跑去调用 zend_operators.h 文件下面的 ZEND_FASTCALL 类型的 zend_memnstr_ex, 注释里说 glibc 更快。

ptrdiff_t 这个其实是一个 zend_long 在/intl/collator/collator_sort.c文件下面有这个定义。1

2

3typedef zend_long ptrdiff_t;

#endif

又跳去搞事情了

/ext/standard/file.c1#define FPUTCSV_FLD_CHK(c) memchr(ZSTR_VAL(field_str), c, ZSTR_LEN(field_str))

其他看不太明白。

在非字符串类型搜索分支判断里面,还进行了详细的判断。总之: 要搜索的字符应该是字符串或者数值类型。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24static int php_needle_char(zval *needle, char *target)

{

switch (Z_TYPE_P(needle)) {

case IS_LONG:

*target = (char)Z_LVAL_P(needle);

return SUCCESS;

case IS_NULL:

case IS_FALSE:

*target = '

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值