PHP strpos 函数源码分析

PHP version 7.4

头文件 ext/standard/php_string.h(此文件声明了字符串函数) 中声明了strpos函数

PHP_FUNCTION(strpos);

头文件 ext/standard/string.c 找到函数实现

PHP_FUNCTION(strpos)
{
	zval *needle;
	zend_string *haystack;
	const char *found = NULL;
	char  needle_char[2];
	zend_long  offset = 0;
    // 解析参数 2 表示2个必传参数;3 表示共3个参数
	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;

		php_error_docref(NULL, E_DEPRECATED,
			"Non-string needles will be interpreted as strings in the future. " \
			"Use an explicit chr() call to preserve the current behavior");

		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;
	}
}

阅读代码可发现,逻辑由 php_memnstr函数实现

在 main/php.h 中可发现 php_memnstr 是宏

#define php_memnstr zend_memnstr

在 Zend/ zend_operators.h 文件中可找到 zend_memnstr 函数定义

static 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;

    // 若needle字符串长度为1,直接调用memchr进行查询
	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;
	}
    
    // 字符串长度小于1024或者needle字符串长度小于9 则调用memchr
	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+1, p+1, needle_len-2)) {
					return p;
				}
			}

			if (p == NULL) {
				return NULL;
			}

			p++;
		}

		return NULL;
	} else {
        // 不易查找时调用zend_memnstr_ex
		return zend_memnstr_ex(haystack, needle, needle_len, end);
	}
}

// 采用Sunday算法,在匹配过程中,模式串发现不匹配时,算法能跳过尽可能多的字符以进行下一步的匹配,从而提高匹配效率
ZEND_API const char* ZEND_FASTCALL zend_memnstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end) /* {{{ */
{
	unsigned int td[256]; // 存储每一种字符最后出现的位置
	register size_t i;
	register const char *p;

	if (needle_len == 0 || (end - haystack) < needle_len) {
		return NULL;
	}

	zend_memnstr_ex_pre(td, needle, needle_len, 0);

	p = haystack;
	end -= needle_len;

	while (p <= end) {
		for (i = 0; i < needle_len; i++) {
			if (needle[i] != p[i]) {
				break;
			}
		}
		if (i == needle_len) {
			return p;
		}
		if (UNEXPECTED(p == end)) {
			return NULL;
		}
		p += td[(unsigned char)(p[needle_len])];
	}

	return NULL;
}

/*
 * String matching - Sunday algorithm
 * http://www.iti.fh-flensburg.de/lang/algorithmen/pattern/sundayen.htm
 * 记录每一种字符最后出现的位置
 */
static zend_always_inline void zend_memnstr_ex_pre(unsigned int td[], const char *needle, size_t needle_len, int reverse) /* {{{ */ {
	int i;

	for (i = 0; i < 256; i++) {
		td[i] = needle_len + 1;
	}

	if (reverse) {
		for (i = needle_len - 1; i >= 0; i--) {
			td[(unsigned char)needle[i]] = i + 1;
		}
	} else {
		size_t i;

		for (i = 0; i < needle_len; i++) {
			td[(unsigned char)needle[i]] = (int)needle_len - i;
		}
	}
}

memchr与memcmp函数声明位于标准库string.h文件

// 在参数 s 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置
// 返回一个指向匹配字节的指针,如果在给定的内存区域未出现字符,则返回 NULL
void	*memchr(const void *__s, int __c, size_t __n);

// 把存储区 s1 和存储区 s2 的前 n 个字节进行比较
// 返回值 < 0,则表示 str1 小于 str2
// 返回值 > 0,则表示 str1 大于 str2
// 返回值 = 0,则表示 str1 等于 str2
int	 memcmp(const void *__s1, const void *__s2, size_t __n);

Sunday 算法 算法之Sunday算法_程序员的暴击的博客-CSDN博客

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值