【问题标题】:Where's the php-src/PHP-Internals Main Entry Pointphp-src/PHP-Internals 主要入口点在哪里
【发布时间】:2018-08-02 14:47:55
【问题描述】:

source of PHP itself 中执行/解释 PHP 程序的主要入口点是什么函数或代码?根据我在谷歌上搜索或在书中阅读的内容,我知道 PHP 旨在与某种服务器一起工作(甚至 CLI 命令也通过启动“命令行 SAPI”来工作,它充当设计的迷你服务器处理单个请求),并且服务器将要求 PHP 执行程序。

我知道minitrinit 生命周期函数,它们是PHP 扩展 的入口点。

我不知道的是PHP源代码在哪里与自己有这种对话

你看,这个文件/字符串中有一个 PHP 程序。我应该运行它

我不想在这里完成任何特定的任务。我试图了解 PHP 的内部结构是如何工作的,并找到一个可以开始跟踪其执行的主要入口点。

【问题讨论】:

  • This 是 CLI 开始的位置,this 是开始处理 PHP 文件的位置,该文​​件的名称在命令行中提供。以此类推。
  • @axiac 谢谢!如果您将其作为官方回复而不仅仅是评论,我很高兴将其标记为最佳答案。

标签: php php-internals


【解决方案1】:

一些SAPI的代码入口点在哪里?

CLI 是一个独立的应用程序。与任何其他用 C 编写的应用程序一样,它的入口点是函数main()(文件sapi/cli/php_cli.c,行1200):

int main(int argc, char *argv[])

Windows 的 CLI 有两个版本,一个是控制台应用程序,以上述main() 函数启动,另一个是 Windows GUI 应用程序(它在启动时不创建控制台,并且使用以WinMain() 函数开头的消息框进行输出(文件sapi/cli/php_cli.c,行1198)。
main()WinMain() 在此处使用相同的代码。通过检查符号 PHP_CLI_WIN32_NO_CONSOLE 是否已定义,它们在这里和那里有不同的名称和不同的代码片段。它在文件sapi/cli/cli_win32.c 中定义,用于生成Windows GUI 应用程序。
</Windows>

CGI 版本也是一个独立的控制台应用程序。它的入口点也是文件sapi/cgi/cgi_main.c中的main()函数,行1792

类似地,FPM 版本在文件sapi/fpm/fpm/fpm_main.c1570 行中以main() 开头。

Apache2 处理程序 是一个可动态加载的模块(.dll 在 Windows 上,.so 在类 Unix 系统上)。它将一些函数注册为 Web 服务器发布的事件的事件处理程序(服务器启动、加载的前/后配置、处理请求等)。这些处理程序由sapi/apache2handler/sapi_apache2.c 文件738 中的php_ap2_register_hook() 函数注册。
(您可以在Apache documentation 中找到有关可加载模块如何与 Apache 集成的详细信息。 )

我们感兴趣的处理程序是函数php_handler(),它被调用来处理 HTTP 请求。

以类似的方式,每个 SAPI 都有一个入口点(main() 或 Web 服务器调用的函数)。

所有这些入口点都做类似的处理:

  • 自己初始化;
  • 解析命令行参数(仅当它是 CLICGI 或其他类型的独立应用程序时);
  • 读取php.ini 和/或他们拥有的其他配置(可以在.htaccess 中覆盖Apache 模块配置);
  • 使用输入文件创建一个流并将其传递给在文件main/main.c,行2496中定义的函数php_execute_script()
  • 清理并向调用进程(shell 或 Web 服务器)返回退出代码。

实际执行 PHP 脚本的代码在哪里?

函数php_execute_script()是一个包装器;它解释php.ini 配置条目auto_prepend_fileauto_append_file,准备文件列表(自动前置文件、主脚本、自动附加文件)并将列表传递给处理它们的zend_execute_scripts()

php_execute_script() 并不总是被调用,某些 SAPI 和 CLI 的命令行参数会直接调用 zend_execute_scripts()

zend_execute_scripts() 是有趣的事情发生的地方。

compiles PHP 文件(并返回op_array 中的OP codes 列表,然后,如果编译成功(返回的op_array 不是NULL)它executes 操作码。还有异常处理和清理;无聊的工作,但与解析和执行一样重要。

编译是一个乏味的过程。它由文件Zend/zend_language_parser.c 中定义的函数zendparse() 完成。 zendparse() 函数的定义和文件 Zend/zend_language_parser.c 在 Git repo 中无处可见;解析器是使用bisonre2c 生成的,它们从Zend/zend_language_parser.yZend/zend_language_scanner.l 中读取语言语法规则和词法标记的定义,并在文件Zend/zend_language_parser.c 中生成实际的编译器。

但是,即使在 repo 中看不到艰苦的工作,编译过程中有趣的部分在上述文件中也是可见的。

编译脚本(OP 代码列表)的执行由文件Zend/zend_vm_execute.h 中定义的函数zend_execute() 完成。这也是一个生成的文件,有趣的是它是由 PHP 脚本生成的。

生成器脚本 (Zend/zend_vm_gen.php) 使用 zend_vm_def.hzend_vm_execute.skl 生成 zend_vm_execute.hzend_vm_opcodes.h

zend_vm_def.h 包含执行以处理每个 OP 代码的实际解释器代码。

PHP 核心或其捆绑扩展之一提供的某些功能的代码在哪里?

PHP 函数和扩展提供的函数的代码在某种程度上更容易理解。 PHP核心包含的函数位于ext/standard目录下的文件中,其他扩展提供的函数位于对应的ext子目录下的文件中。

在这些文件中,实现 PHP 函数的 C 函数是使用 PHP_FUNCTION() 宏声明的。比如PHP函数strpos()的实现 开始于文件ext/standard/string.c,行1948。函数strchr()strstr() 的别名,是使用ext/standard/basic_functions.c2833 上文件ext/standard/basic_functions.c 中的PHP_FALIAS() 宏声明的。

等等,等等。

【讨论】:

  • 这是一个了不起的答案,远远超出了我的预期。非常感谢!
  • 我希望我能带你去开会。反应很好!
猜你喜欢
  • 1970-01-01
  • 2012-03-03
  • 2022-01-06
  • 1970-01-01
  • 2013-02-17
  • 2013-04-18
  • 1970-01-01
  • 2023-04-02
  • 2020-02-26
相关资源
最近更新 更多