我以前对于 C 语言的印象是有很强的确定性,而 PHP 在执行的时候会被翻译为 C 语言执行,所以一直很好奇 PHP 怎么调用底层函数。

换句话说就是已知函数名字的情况下如何调用 C 语言中对应名字的函数?

解决这个问题前,首先根据过往的经验做出假设,然后再去验证。

之前在写《用 C 语言实现面向对象》的时候,就意识到使用 void 指针实现很多功能,包括指向任意的函数。接着在写《PHP 数组底层实现》的时候,了解了 HashTable 的实现,即在 C 语言层面通过字符串 key 找到任意类型值。

现在把两者结合起来,是否就能解决以上问题了?比如说把函数名作为 HashTable 的 key,函数指针作为 HashTable 的 value,这样就可以通过函数名获取函数指针来调用函数了。

接下来通过查看 PHP 的源码来看这个假设与真实情况有多少差距。

总体分为三个步骤:

  1. 从 PHP 层进入 C 语言层
  2. 找到字符串函数名与函数的关系
  3. 函数的调用

注:这篇博客的源码对应的版本是 PHP 7.4.4 。

https://github.com/php/php-src/tree/php-7.4.4

从 PHP 层进入 C 语言层

首先要找到 C 语言层调用函数的地方。怎么找?

经常使用 PHP 的同学看到前面的问题描述很容易联想到 PHP 中的一个传入函数名及其参数就可以调用函数的函数 call_user_func() 。可以从这里入手。

怎么找到 call_user_func() 在 PHP 源码中的位置?这就要根据 PHP 源码的规律来找了。

当然也可以直接全代码搜索,只是比较慢。

PHP 源码里面在定义一个 PHP 函数的时候会用 PHP_FUNCTION(函数名) ,所以只要找到 PHP_FUNCTION(call_user_func) 就可以了。

另外 call_user_func() 不像 array_column() 这种函数有特定前缀 array_ ,所以属于比较基础的函数,而 PHP 的基础函数会放在两个地方:

  • 内置函数,放在 Zend/zend_buildin_functions.c
  • 标准库函数,放在 ext/standard/ 。
    举个例子: ext/standard/array.c 里有 array_column() 之类的函数。

在这两个地方搜索就能找到 PHP_FUNCTION(call_user_func) ,如下:

ext/standard/basic_functions.c

PHP_FUNCTION(call_user_func)
{// ...if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {// ...}
}

现在我们已经从 PHP 层面进入到 C 语言层面,接下去就是在 C 语言代码里面探索了。

找到字符串函数名与函数的关系

从上文展示位于 ext/standard/basic_functions.c 的 call_user_func() 函数定义可以找到关键点 zend_call_function() ,现在要找到这个函数。

这种以 zend_ 开头的函数都在 Zend/ 文件夹底下,所以我们要换个目录了。

在 Zend/ 文件夹里面随便搜索 zend_call_function ,从搜索结果里面随便挑一个跳转,然后通过 IDE 的功能(ctrl + 鼠标左键)跳转到它定义的地方就可以了。

如果 IDE 能直接跳转就不用在 Zend/ 文件夹搜索了,这里是因为 VS Code 没法直接跳转。

注:以下代码中的 // ... 都表示我省略了一部分代码,但我会尽量保持代码结构。

第一遍看代码的时候不需要掌握所有细节,只需要了解整体概念或者前后关系,否则会陷入细节无法自拔。

Zend/zend_execute_API.c

int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /* {{{ */
{// ...if (!fci_cache || !fci_cache->function_handler) {// ...if (!zend_is_callable_ex(&fci->function_name, fci->object, IS_CALLABLE_CHECK_SILENT, NULL, fci_cache, &error)) {// ...}// ...}func = fci_cache->function_handler;// ...call = zend_vm_stack_push_call_frame(call_info,func, fci->param_count, object_or_called_scope);// ...if (func->type == ZEND_USER_FUNCTION) {// ...} else if (func->type == ZEND_INTERNAL_FUNCTION) {// ...func->internal_function.handler(call, fci->retval);// ...} else {// ...}// ...return SUCCESS;
}
/* }}} */

这里的关键点在于和函数名以及函数调用相关的词。关键词有:

  • function name
  • call
  • return value

上面的代码片段中,我把几个有可能的点抽出来了。从这几个点出发,往前追溯参数来源或者查看后面使用它的地方就行了。

如果被这个函数里面大量的 EG(...) 吸引而想知道其内部结构的话,就离结果非常近了。如果没有被其吸引,那也没关系,继续看。

优先深入看哪个呢?根据以前看数组源码的经验, “查找” 这个行为更容易获得信息

更多更全干货 请点击这里

让我来教你 PHP 函数调用相关推荐

  1. 独家 | 手把手教TensorFlow(附代码)

    上一期我们发布了"一文读懂TensorFlow(附代码.学习资料)",带领大家对TensorFlow进行了全面了解,并分享了入门所需的网站.图书.视频等资料,本期文章就来带你一步步 ...

  2. iOS系类教程之用instruments来检验你的app

    2019独角兽企业重金招聘Python工程师标准>>> 发布于:2014-01-14 10:23阅读数:22668 比较了好多关于instruments 还是发现老外写的比较牛逼.于 ...

  3. 三天竟然爆发两起大漏洞事件!我们来教你如何跳过以太坊的坑

    三天竟然爆发两起大漏洞事件!我们来教你如何跳过以太坊的坑 2018年04月26日 00:00:00 阅读数:1314 "现在进入你还是先行者,最后观望者进场才是韭菜."美图董事长蔡 ...

  4. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  5. 你已经是个成熟的985大学了,请不要在大一教 C 语言!

    昨天晚上回家后突然在朋友圈发了个问卷,看下国内大学第一门语言到底有多少是用的 C 语言. 结果也是很符合预期,使用 C 语言做第一门编程语言课的大学达到了 90% 以上. 之前在知乎看见一个问题,问为 ...

  6. 三国志、英雄无敌玩腻了?没关系,我教你开发个战旗游戏玩玩

    喜欢回合制战棋游戏的玩家,肯定对<三国志曹操传>和<英雄无敌>这两款经典战旗游戏不陌生吧. 在<三国志曹操传>中,镇压黄巾军.群雄讨伐董卓.灭吕布等历史事件与游戏中 ...

  7. 手把手教你玩转SOCKET模型:完成例程(Completion Routine)篇

    本文假设你已经对重叠I/O的机制已有了解,否则请先参考本系列的前一篇<手把手教你玩转SOCKET模型之重叠I/O篇>: 目录: 1.完成例程的优点 2.完成例程的基本原理 3.关于完成例程 ...

  8. 手把手教你玩转SOCKET模型:完成端口(Completion Port)详解

    这篇文档我非常详细并且图文并茂的介绍了关于网络编程模型中完成端口的方方面面的信息,从API的用法到使用的步骤,从完成端口的实现机理到实际使用的注意事项,都有所涉及,并且为了让朋友们更直观的体会完成端口 ...

  9. 手把手教你玩转网络编程模型之完成例程(Completion Routine)

    前  言 记得写这个系列的上一篇文章的时候已经是四年前了,准确的说是四年半以前了,翻开我尘封已久的IO模型里面的"完成例程"的实现方式及示例代码. 本文凝聚着笔者心血,如要转载,请 ...

最新文章

  1. 阿里全资收购一家核心技术公司,中科院大牛带队加盟
  2. c语言i o编程,C 语言输入输出 (I/O)
  3. vue创建脚手架 cil
  4. (素材源代码) 猫猫学iOS 之UIDynamic重力、弹性碰撞吸附等现象牛逼Demo
  5. 文档管理系统_云脉纸质文档管理系统怎么玩?
  6. 判断一个男人穷还是富,只看这几点!
  7. tar命令核心应用案列及多重参数和find组合应用
  8. 第二百四十八节,Bootstrap轮播插件
  9. rplidar 启动马达 c++_【玩码】刘作虎:一加7 Pro的横向线性马达,为安卓手机最大...
  10. linux内核 panic,linux 内核 panic
  11. docker安装(2018-03-14版本)
  12. linux桥接命令virsh,CentOS6.3 KVM如何设置网卡为桥接模式
  13. 基于持续集成的轻量级接口自动化测试 【持续更新...】
  14. L1-034 点赞 (20 分)
  15. atitit.重装系统需要备份的资料总结 o84..
  16. 直播开发中关于三级分销功能源码的实现介绍
  17. 常用字典代码推荐标准
  18. 使用WinRadius服务器软件 搭建 radius 认证
  19. pimple idiom
  20. ctf php沙箱,详谈CTF中常出现的PHP反序列化漏洞

热门文章

  1. 2003配置php环境,2003配置PHP环境(有利于升级)
  2. 手绘线条一直画不直_我学素描,线条画不直怎么办啊?
  3. opencv中的Rodrigues()函数
  4. python数据分析实况_机器学习竞赛分享:通用的团队竞技类的数据分析挖掘方法...
  5. 关于python面向对象编程中、下列说法中_关于Python面向对象编程的知识点总结
  6. day 01 ————立志运维的第一天开始,承若书!
  7. 使用navicat premium将数据库从Oracle迁移到SQL Server,或从Oracle迁移到MySQL
  8. SuperSocket 服务管理器 (ServerManager)
  9. Ember.js如何与后端服务交互?adapter、store、ember data关系揭秘
  10. 编写Linux内核模块——第三部分:按键和发光二极管