在上一篇文章中,它说明了您可以在哪里找到PHP的源代码,其基本目录结构以及对某些C语言的简要介绍(因为PHP是用C语言编写的)。如果您错过了该帖子,也许您应该在开始阅读之前阅读它。
在本文中,我们谈论的是定位PHP内部功能的定义并了解其原理。
如何找到函数的定义
首先,让我们尝试找出功能的定义。
尝试的第一步是转到PHP 5.4根目录,然后在页面顶部的搜索框中输入。搜索结果是一个大列表,显示了它们在PHP源代码中出现的位置。
由于这个结果对我们没有太大帮助,因此我们使用了一个小技巧:我们搜索“”(不要错过双引号,它们很重要),而是搜索。
现在我们获得了两个条目链接:
/PHP_5_4/ext/standard/
php_string.h 48 PHP_FUNCTION(strpos);
string.c 1789 PHP_FUNCTION(strpos)
要注意的第一件事是两个位置都在EXT/文件夹中。这就是我们想要找到的,因为功能(像大多数数组和文件功能一样)是扩展程序的一部分。
现在,在“新标签”中打开两个链接,然后查看隐藏在它们后面的代码。
您将看到第一个链接将您带到.H文件,其中包含以下代码:
// ...PHP_FUNCTION(strpos);
PHP_FUNCTION(stripos);
PHP_FUNCTION(strrpos);
PHP_FUNCTION(strripos);
PHP_FUNCTION(strrchr);
PHP_FUNCTION(substr);// ...
这是一个典型的标头文件(以.H后缀结尾的文件)看起来像:函数的简单列表,并且在其他地方定义了功能。实际上,我们对这些不感兴趣,因为我们已经知道我们在寻找什么。
第二个链接更有趣:它将我们带到.c文件,其中包含该函数的真实源代码。
在我带您逐步查找此功能之前,我建议您尝试自己了解此功能。这是一个非常简单的功能,尽管您不知道真实的细节,但大多数代码看起来很清楚。
PHP功能的骨骼
所有PHP功能都使用相同的基本结构。每个变量在函数的顶部定义,然后调用s函数,然后调用主逻辑。
因此,让我们从函数的定义开始:
zval *needle;char *haystack;char *found = NULL;char needle_char[2];long offset = 0;int haystack_len;
第一行定义了指向Zval的指针。 ZVAL是代表PHP内部任何PHP变量的定义。真正的内容将在下一篇文章中讨论。
第二行定义了指向单个字符的指针。目前,您需要记住,在C语言中,阵列代表了其第一个元素的指示。例如,该变量将指向您正在传递的$字符串变量的第一个字符。 + 1将指向第二个字符, + 2将指向第三个字符,依此类推。因此,通过将指针逐一增加,可以读取整个字符串。
然后,问题是,PHP需要知道字符串在哪里结束。否则,它将不停地增加指针。为了解决此问题,PHP还节省了清晰的长度,即变量。
现在,在上面的定义中,我们对变量感兴趣,该变量用于保存函数的第三个参数:开始搜索的偏移。它是使用long定义的,就像int一样,也是整数数据类型。现在,两者之间的区别并不重要,但是您需要知道的是,在PHP中,整数值是使用长长和字符串长度存储的,并使用int存储。
现在让我们看一下以下三行:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) { return;
}
这三行代码的作用是将参数传递到函数并将其存储在上面声明的变量中。
传递给该函数的第一个参数是传递的参数数。该数字由()宏提供。
下一个功能是宏,这是PHP的功能。您会发现这个怪异的宏散布在PHP代码库中的许多地方。是线程安全资源管理器(TSRM)的一部分,可确保PHP不会弄乱多个线程之间的变量。这对我们来说不是很重要,当您在代码中看到(或)时,只会忽略它。 (您需要注意的一件奇怪的事情是,“”之前没有逗号。这是因为宏将被解释为空的,或者,无论您是使用线程安全创建函数。因此,逗号是宏的一部分。)
现在,我们提出一些重要的东西:“ SZ \ | L”字符串标记了该函数收到的参数。 :
s // 第一个参数是字符串z // 第二个参数是一个zval结构体,任意的变量| // 标识接下来的参数是可选的l // 第三个参数是long类型(整型)
除S,Z和L外,还有更多的识别类型,但是大多数可以从字符中明确定义。例如,b is,d is(浮点数),a是数组,f is ()和o is。
以下参数&&&&,并指定需要分配的参数的变量。如您所见,它们都是使用参考(&)传递的,这意味着它们不是通过变量本身,而是向它们传递。
此功能调用后,它将包含一个字符串,即字符串的长度,字符串的值以及开始时的偏移。
另外,使用此功能(当您尝试将无效参数传递给该功能)(例如将数组分配给字符串)传递给字符串时,会发生此功能。在这种情况下,S函数将发出警告,此功能将立即返回(它将返回NULL到PHP的用户层代码)。
参数解析完成后,主函数主体开始:
if (offset < 0 || offset > haystack_len) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}
该代码的作用是显而易见的,如果超过边界,函数将丢弃级别的错误,并且该函数使用宏返回false。
是您可以在扩展目录中找到的错误函数(例如,Ext文件夹)。它的名称是根据错误页面中的返回文档引用定义的(即无法正常工作的函数)。还有一个主要由Zend使用的函数,但通常也以扩展代码出现。
这两个功能都使用函数,例如格式化信息,因此错误消息可以包含占位符,其中将填充以下参数。这是一个示例:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write %d bytes to %s", Z_STRLEN_PP(tmp), filename);// %d is filled with Z_STRLEN_PP(tmp)// %s is filled with filename
让我们继续解析代码:
if (Z_TYPE_P(needle) == IS_STRING) { if (!Z_STRLEN_P(needle)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
RETURN_FALSE;
}
found = php_memnstr(haystack + offset,
Z_STRVAL_P(needle),
Z_STRLEN_P(needle),
haystack + haystack_len);
}
前5行非常清楚:此分支只能作为字符串执行,如果它为空,则会丢弃错误。然后我们到达了一个更有趣的部分:被称为,此功能完成了主要工作。像往常一样,您可以单击函数名称并查看其源代码。
将指针返回到首次出现的位置的指针(这就是为什么发现的变量定义为char*的原因,例如,指向字符的指针)。从这里我们知道可以简单地通过减法来计算偏移(),这可以在函数末尾看到:
RETURN_LONG(found - haystack);
最后,让我们看一下不是字符串的分支:
else { if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
RETURN_FALSE;
}
needle_char[1] = 0;
found = php_memnstr(haystack + offset,
needle_char, 1,
haystack + haystack_len);
}
我仅引用“如果不是字符串,则将其转换为整数并将其视为字符订单值。”这基本上意味着除了写作($ str,'a')之外,您还可以写($ str,65),因为A字符的编码为65。
如果您再次查看变量定义,则可以看到它被定义为char [2],一个带有两个字符的字符串,它将将真实字符(此处为a')传递到[0]。然后,该函数将设置[1]至0。其原因是因为在C中,字符串以'\ 0'结束,即,最后一个字符设置为nul(一个字符编码为0)。在PHP语法环境中,这种情况不存在,因为PHP存储了所有字符串的长度(因此它不需要0来帮助找到字符串的末端),但是为了确保与C函数的兼容性,它仍然在PHP内实现。
Zend
我对此功能感到非常厌倦,让我们找到另一个功能:。我们使用以前的方法:
开始从php5.4源代码根目录搜索。
您将看到一堆无关的功能,因此请搜索“”。当您这样的搜索时,您会发现发生了一些奇怪的事情:没有结果。
原因是它是Zend而不是PHP扩展定义的一些函数。在这种情况下,该函数不是使用()而定义的,而是()。因此,我们还想搜索“”。
我们都知道,我们需要单击链接,而无需半隆结束,以跳到源代码的定义。此链接将我们带到下面的函数定义:
ZEND_FUNCTION(strlen)
{ char *s1; int s1_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s1, &s1_len) == FAILURE) { return;
}
RETVAL_LONG(s1_len);
}
此功能实现太简单了,我认为我不需要进一步的解释。
方法
我们将讨论有关类和对象在其他文章中如何工作的更多详细信息,但是作为一个小的扰流板:您可以通过在搜索框中搜索搜索对象方法::来搜索对象方法。例如,尝试搜索::。
下一部分
下一节将讨论什么是Zval,它们的工作方式以及如何在源代码(所有Z_*宏)中使用。
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。


客服1