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博客