整理目录

This commit is contained in:
2024-03-22 17:42:41 +08:00
parent 5a4efad893
commit fab9914f39
158 changed files with 158 additions and 84 deletions

View File

@@ -0,0 +1,144 @@
---
title: PHP 扩展开发(一)-骨架
---
> 学习了这么久的 php,还一直停留在 CURD 也太捞了,来接触一下扩展开发
> 官方的文档:[http://php.net/manual/zh/internals2.php](http://php.net/manual/zh/internals2.php) 可以 mark 一下
## 环境
- php7.2
- ubuntu18.04
- gcc 7.3.0
- make 4.1
## 开始
### ext_skel
> [http://php.net/manual/zh/internals2.buildsys.skeleton.php](http://php.net/manual/zh/internals2.buildsys.skeleton.php)
首先我们要利用 php 给我们提供的 ext_skel 脚本工具生成我们扩展的骨架,这个文件一般在 php 的源码的 ext 目录下面
这里我弄一个扩展名字为**study**的扩展 --extname 这个参数为扩展名字,还有其他的参数可以在上面的链接文档中看到
```shell
huanl@huanl-CN15S:/www/server/php/72/src/ext$ sudo ./ext_skel --extname=study
Creating directory study
Creating basic files: config.m4 config.w32 .gitignore study.c php_study.h CREDITS EXPERIMENTAL tests/001.phpt study.php [done].
To use your new extension, you will have to execute the following steps:
1. $ cd ..
2. $ vi ext/study/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-study
5. $ make
6. $ ./sapi/cli/php -f ext/study/study.php
7. $ vi ext/study/study.c
8. $ make
Repeat steps 3-6 until you are satisfied with ext/study/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.
```
这里创建完成之后,就多了一个 study 的目录,我们可以进入这个扩展目录,进行一些操作,这里的话,因为我的用户没有权限,所以我直接的给了 777 权限
```shell
huanl@huanl-CN15S:/www/server/php/72/src/ext$ sudo chmod -R 777 study/
```
### 编译安装
#### 目录结构
之后我们进入这个扩展目录,有这些文件,官方的文档:[http://php.net/manual/zh/internals2.structure.files.php](http://php.net/manual/zh/internals2.structure.files.php)
```shell
huanl@huanl-CN15S:/www/server/php/72/src/ext$ cd study/
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ ls
config.m4 CREDITS php_study.h study.php
config.w32 EXPERIMENTAL study.c tests
```
等下我们需要修改**config.m4**文件,这相当于是一个编译配置的文档,是 Unix 下的,还有一个**config.w32**看名字就知道是 Windows 下的
**study.c**和**php_study.h**这是依照我们的扩展名称来帮我们生成的两个源文件,包括了一些宏的定义和函数声明等等
**study.php**可以用 php cli 来测试我们的扩展是否安装成功
#### config.m4
> [http://php.net/manual/zh/internals2.buildsys.configunix.php](http://php.net/manual/zh/internals2.buildsys.configunix.php)
要进行一下修改,是动态编译成 so 库还是静态编译进 php 里面,这里我们扩展开发自然是动态编译成 so 库,不然还得重新编译 php
去掉前面的 dnl,例如下面这个
```
dnl If your extension references something external, use with:
# 编译成so库
PHP_ARG_WITH(study, for study support,
Make sure that the comment is aligned:
[ --with-study Include study support])
dnl Otherwise use enable:
# 静态编译
dnl PHP_ARG_ENABLE(study, whether to enable study support,
dnl Make sure that the comment is aligned:
dnl [ --enable-study Enable study support])
```
### 编译
我们需要用**phpize**生成编译的配置的文件**configure**,然后**make**,**make install**就完成了,make install 的时候注意用 sudo
```shell
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ phpize
Configuring for:
PHP Api Version: 20170718
Zend Module Api No: 20170718
Zend Extension Api No: 320170718
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ ./configure --with-php-config=/www/server/php/72/bin/php-config
# 这里注意配置php的配置文件
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ make
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ sudo make install
Installing shared extensions: /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/
```
### 配置
完成之后我们要在**php.ini**文件中加载扩展,不然我们运行`php study.php`的时候不会成功
这样可以看 ini 文件在哪里
```shell
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ php --ini
Configuration File (php.ini) Path: /www/server/php/72/etc
Loaded Configuration File: /www/server/php/72/etc/php.ini
Scan for additional .ini files in: (none)
Additional .ini files parsed: (none)
```
增加一行
```
extension=study.so
```
## 完成
这时候我们执行目录下的那个`study.php`
```shell
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ php study.php
Functions available in the test extension:
confirm_study_compiled
Congratulations! You have successfully modified ext/study/config.m4. Module study is now compiled into PHP.
```

View File

@@ -0,0 +1,237 @@
---
title: PHP扩展开发(二)-函数
---
> 弄好骨架之后,我们得给我们的扩展增加些 php 能够调用的函数,这里我们使用 vscode 进行开发
## 开发环境
> 给我们的 vscode 装好扩展,然后配置一下 include 路径
![](img/02-%E5%87%BD%E6%95%B0.assets/2018-09-21-14-20-32-%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE-300x132.png)
点击生成一个配置文件我的配置如下php 的路径看自己的来定,我这里是宝塔安装的,路径为:**/www/server/php/72/include/php**,主要是自动提示
```json
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/www/server/php/72/include/php",
"/www/server/php/72/include/php/main",
"/www/server/php/72/include/php/Zend",
"/www/server/php/72/include/php/TSRM"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}
```
## 开发
### 返回和输出
> 假设我这里想添加一个输出一些东西的函数
这里用到了三个宏,**PHP_FUNCTION**,**RETURN\_\***,**PHP_FE**
php_printf 是 php 提供的一个输出函数
```c
PHP_FUNCTION(study_ext_print) {
php_printf("我是输出到页面的内容\n");
RETURN_STRING("学习PHP扩展开发~~");
}
/* }}} */
/* {{{ study_functions[]
*
* Every user visible function must have an entry in study_functions[].
*/
const zend_function_entry study_functions[] = {
PHP_FE(confirm_study_compiled, NULL) /* For testing, remove later. */
PHP_FE(study_ext_print, NULL) /* 学习插件输出 */
PHP_FE_END /* Must be the last line in study_functions[] */
};
/* }}} */
```
然后再 makesudo make install
#### 调试跑一次
修改我们目录下的`study.php`,在最后写一行,我们刚刚编写的这个函数
```php
echo study_ext_print()."\n";
```
输出,成功
```
huanl@huanl-CN15S:/www/server/php/72/src/ext/study$ php study.php
Functions available in the test extension:
confirm_study_compiled
study_ext_print
Congratulations! You have successfully modified ext/study/config.m4. Module study is now compiled into PHP.
我是输出到页面的内容
学习PHP扩展开发~~
```
#### 这里再说下这三个宏
##### PHP_FUNCTION
使用这个宏会将我们的函数最终定义成如下的形式
```cpp
void zif_study_ext_print(zend_execute_data *execute_data, zval *return_value)
```
~~官网上的是 php5.3 的版本,我这里是 php7所以是这样如果有什么错误还望指出~~
然后因为这里的返回值是 void所以在我们写函数的时候`return`不能带值
##### RETURN\_\*
这个宏一看就知道是 php 给我们的返回值,除了我上面所写的`RETURN_STR`外还有其他的类型
```c
#define RETURN_BOOL(b) { RETVAL_BOOL(b); return; }
#define RETURN_NULL() { RETVAL_NULL(); return;}
#define RETURN_LONG(l) { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; }
#define RETURN_STR(s) { RETVAL_STR(s); return; }
#define RETURN_INTERNED_STR(s) { RETVAL_INTERNED_STR(s); return; }
#define RETURN_NEW_STR(s) { RETVAL_NEW_STR(s); return; }
#define RETURN_STR_COPY(s) { RETVAL_STR_COPY(s); return; }
#define RETURN_STRING(s) { RETVAL_STRING(s); return; }
#define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; }
#define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; }
#define RETURN_RES(r) { RETVAL_RES(r); return; }
#define RETURN_ARR(r) { RETVAL_ARR(r); return; }
#define RETURN_OBJ(r) { RETVAL_OBJ(r); return; }
#define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE { RETVAL_FALSE; return; }
#define RETURN_TRUE { RETVAL_TRUE; return; }
```
##### PHP_FE
这个宏帮助我们生成一个和 php 函数相关的结构体
```c
//宏如下PHP_替换成了ZEND_
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags },
#define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)
//感觉就是帮我们省事了,不需要我们去重写结构体,上面那些结构体也是
typedef struct _zend_function_entry {
const char *fname;//我们的php函数名
zif_handler handler;//相当于再调用一次PHP_FUNCTIONc中函数的指针
const struct _zend_internal_arg_info *arg_info;//参数的信息就上一个函数来说我们是NULL
uint32_t num_args;//参数个数
uint32_t flags;//flags这里是0
} zend_function_entry;
```
### 参数
> 假设来一个两数相加的函数,这时候我们就需要传送参数了
> 参考:[https://wiki.php.net/rfc/fast_zpp](https://wiki.php.net/rfc/fast_zpp)
我的 c 代码如下
```cpp
//定义参数结构
ZEND_BEGIN_ARG_INFO(add_param,0)
ZEND_ARG_INFO(0,num1)
ZEND_ARG_INFO(0,num2)
ZEND_END_ARG_INFO()
//函数
PHP_FUNCTION(study_add)
{
long long num1=0,num2=0;
if(zend_parse_parameters(ZEND_NUM_ARGS() , "ll", &num1,&num2)==FAILURE){
RETURN_LONG(-1)
}
RETURN_LONG(num1+num2)
}
/* }}} */
/* {{{ study_functions[]
*
* Every user visible function must have an entry in study_functions[].
*/
const zend_function_entry study_functions[] = {
PHP_FE(confirm_study_compiled, NULL) /* For testing, remove later. */
PHP_FE(study_ext_print, NULL) /* 学习插件输出 */
PHP_FE(study_add, add_param) /* 两数相加 */
PHP_FE_END /* Must be the last line in study_functions[] */
};
```
```php
echo "study_add:".study_add(10,123456);
```
#### 宏
##### ZEND_BEGIN_ARG_INFOZEND_ARG_INFOZEND_END_ARG_INFO
定义参数,我把源码贴上来
```cpp
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
static const zend_internal_arg_info name[] = { \
{ (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO() };
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0},
```
那么通过这些宏的转换,变成了这样
```cpp
static const zend_internal_arg_info add_param[] = {
{ (const char*)(zend_uintptr_t)(-1), 0, 0, 0 },
{ "num1", 0, 0, 0},
{ "num2", 0, 0, 0},
};
```
#### zend_parse_parameters
获取参数
```cpp
//声明
ZEND_API int zend_parse_parameters(int num_args, const char *type_spec, ...);
```
第一个参数是我们要获取的参数的个数,第二个是 参数的格式化字符串,后面的是变量指针
| 数据类型 | 字符 | c 对应类型 |
| -------- | ---- | ----------- |
| Boolean | b | zend_bool |
| Long | l | long |
| Double | d | double |
| String | s | char\*, int |
| Resource | r | zval\* |
| Array | a | zval\* |
| Object | o | zval\* |
| zval | z | zval\* |
#### ZEND_NUM_ARGS
参数个数,一般这样填就好了

View File

@@ -0,0 +1,385 @@
---
title: PHP扩展开发(三)---类
---
> 前面已经了解了函数和参数,今天来了解一下类
## 例子
定义了一个 **study_ext_class** 类,里面只有一个 **print** 方法
类使用 **PHP_ME**和**PHP_METHOD** 宏,与方法最大的不同的地方是类需要注册
这里我写了一个 **init_class** 方法,**PHP_MINIT_FUNCTION**中调用,主要是需要注册类
```c
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(study)
{
/* If you have INI entries, uncomment these lines
REGISTER_INI_ENTRIES();
*/
init_class();
return SUCCESS;
}
/* }}} */
PHP_METHOD(study_ext_class,print)
{
php_printf("你调用了study_ext_class的print方法\n");
}
/* }}} */
const zend_function_entry study_class_method[]={
PHP_ME(study_ext_class,print,NULL,ZEND_ACC_PUBLIC)/* study_ext_class的print方法 */
PHP_FE_END
};
zend_class_entry *study_ce;
void init_class(){
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "study_ext_class" , study_class_method);
study_ce = zend_register_internal_class(&ce);
}
```
### PHP_MINIT_FUNCTION
这是我们扩展启动时会执行的一个函数,所以在这里注册类
```c
#define ZEND_MODULE_STARTUP_N(module) zm_startup_##module
#define INIT_FUNC_ARGS int type, int module_number
#define ZEND_MODULE_STARTUP_D(module) int ZEND_MODULE_STARTUP_N(module)(INIT_FUNC_ARGS)
//之后
int zm_startup_study(int type, int module_number);
```
### PHP_METHOD
这两个宏和我们前面函数哪里的 **PHP_FE** **PHP_FUNCTION** 差不多
```c
#define ZEND_MN(name) zim_##name
#define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))
//PHP_METHOD 最终定义成了这样
void zim_study_ext_class_print(zend_execute_data *execute_data, zval *return_value);
```
### PHP_ME
**PHP_ME** 相比原来的 **PHP_FE** 多了几个参数,主要是方法的属性和类名
```c
#define ZEND_ME(classname, name, arg_info, flags) ZEND_FENTRY(name, ZEND_MN(classname##_##name), arg_info, flags)
```
flags 是方法的属性,我们可以用 **|** 连接它们
```c
/* method flags (types)方法类型 */
#define ZEND_ACC_STATIC 0x01
#define ZEND_ACC_ABSTRACT 0x02
#define ZEND_ACC_FINAL 0x04
#define ZEND_ACC_IMPLEMENTED_ABSTRACT 0x08
/* method flags (visibility)访问属性 */
/* The order of those must be kept - public < protected < private */
#define ZEND_ACC_PUBLIC 0x100
#define ZEND_ACC_PROTECTED 0x200
#define ZEND_ACC_PRIVATE 0x400
#define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
#define ZEND_ACC_CHANGED 0x800
#define ZEND_ACC_IMPLICIT_PUBLIC 0x1000
/* method flags (special method detection)构造函数和析构 */
#define ZEND_ACC_CTOR 0x2000
#define ZEND_ACC_DTOR 0x4000
/* method flag used by Closure::__invoke() */
#define ZEND_ACC_USER_ARG_INFO 0x80
/* method flag (bc only), any method that has this flag can be used statically and non statically. */
#define ZEND_ACC_ALLOW_STATIC 0x10000
```
### 类注册
完成了上面的,编译&安装,实例化我们的类是是会报错的,因为 php 不知道你有那些类
方法的话,在最后面通过 **ZEND_GET_MODULE** 生成了一个 **get_module** 的接口将方法进行了返回
```c
zend_class_entry *study_ce;
void init_class(){
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "study_ext_class" , study_class_method);
study_ce = zend_register_internal_class(&ce);
}
```
#### INIT_CLASS_ENTRY
这一个宏的作用是生成这个类的结构,包括类的名称,方法,然后返回在 ce 这个变量中
```c
//太长了,就只贴了一部分
#define INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset) \
INIT_OVERLOADED_CLASS_ENTRY_EX(class_container, class_name, sizeof(class_name)-1, functions, handle_fcall, handle_propget, handle_propset, NULL, NULL)
#define INIT_CLASS_ENTRY(class_container, class_name, functions) \
INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, NULL, NULL, NULL)
```
#### zend_register_internal_class
显而易见的是将我们的类的信息告诉 php注册进去还有一个 **zend_register_internal_class_ex** 的函数,可以指定父类,然后这个函数返回我们的这个类的指针
## 构造和析构
函数列表哪里标记一下 **ZEND_ACC_CTOR** 或者 **ZEND_ACC_DTOR** 就好了
```c
PHP_METHOD(study_ext_class,__construct)
{
php_printf("study_ext_class构造函数\n");
}
PHP_METHOD(study_ext_class,__destruct)
{
php_printf("study_ext_class析构函数\n");
}
const zend_function_entry study_class_method[]={
PHP_ME(study_ext_class,__construct,NULL,ZEND_ACC_CTOR)/* 构造 */
PHP_ME(study_ext_class,__destruct,NULL,ZEND_ACC_DTOR)/* 析构 */
PHP_ME(study_ext_class,print,NULL,ZEND_ACC_PUBLIC)/* study_ext_class的print方法 */
PHP_FE_END
};
```
## 类属性
在初始化注册类的时候,使用 **zend*declare_property*\*** 给我们的类添加属性,还可以给他们赋予默认值
```c
void init_class(){
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "study_ext_class" , study_class_method);
study_ce = zend_register_internal_class(&ce);
zend_declare_property_null(study_ce,"attr",sizeof("attr")-1,ZEND_ACC_PUBLIC);
zend_declare_property_long(study_ce,"num",sizeof("num")-1,100,ZEND_ACC_STATIC|ZEND_ACC_PUBLIC);
}
```
```php
$a->attr="2333s";
echo '静态属性:study_ext_class::$num:'.study_ext_class::$num."\n";
echo '属性:study_ext_class::$attr:'.$a->attr."\n";
```
输出
```
静态属性:study_ext_class::$num:100
属性:study_ext_class::$attr:2333s
```
### 类指针和属性读取
上面写了怎么去定义属性,但是如果是在类里面要怎么使用属性呢?我们需要用一个 **getThis** 的宏来获取当前这个类的指针,我就偷懒直接在原来的 **print** 中添加了
```c
zval *attr;
attr=zend_read_property(Z_OBJCE_P(getThis()),getThis(),"attr",sizeof("attr")-1,0,NULL);
php_var_dump(attr, 1);
if(Z_TYPE_P(attr)==IS_STRING){
php_printf("attr的值为:%s\n",attr->value.str->val);
}
```
#### zend_read_property
这个函数用于获取属性,还有`zend_read_static_property`,用法相同,不过这个是获取静态的属性,关于更新属性可以使用`zend_update_property`
```c
ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv);
```
第一个参数 **scope** 是这个类的指针,在之前的`study_ce = zend_register_internal_class(&ce);`获取,不过也可以这样获取`Z_OBJCE_P(getThis())`
第二个参数 **object** 是当前的对象,我们可以用`getThis`这个宏获取
第三个参数和第四个参数分别是 属性的名称和属性的长度
第五个参数 **silent** 用于是假设属性不存在的情况下是否报错
最后一个参数 **rv** 为魔术方法所返回的,如果不是魔术方法所返回的是一个**NULL**值,可以看我下面这个例子
```c
zval *attr,*rv=NULL;
attr=zend_read_property(study_ce,getThis(),"attr",sizeof("attr")-1,0,rv);
if(Z_TYPE_P(attr)==IS_STRING){
php_printf("attr的值为:%s,%d\n",attr->value.str->val,rv);
}
```
#### getThis
获取对象指针,不多说了
```c
#define EX(element) ((execute_data)->element)
#define getThis() ((Z_TYPE(EX(This)) == IS_OBJECT) ? &EX(This) : NULL)
```
### 类参数
其实和函数的参数一样,还有一个类似的`zend_parse_method_parameters`我用的时候总是错误,还没明白这个函数是干什么的,而且找不到说明的资料=\_=,后面附上两个源码的区别再看看
```c
PHP_METHOD(study_ext_class,sum)
{
zend_long parma_num=0;
zval* this=getThis();
zval* static_num=zend_read_static_property(Z_OBJCE_P(this),"num",sizeof("num")-1,0);
if(zend_parse_parameters(ZEND_NUM_ARGS(),"l",&parma_num)==FAILURE){
RETURN_LONG(-1)
}
if(Z_TYPE_P(static_num)==IS_LONG){
RETURN_LONG(static_num->value.lval+parma_num)
}
RETURN_LONG(-1)
}
ZEND_BEGIN_ARG_INFO(sum_arg,0)
ZEND_ARG_INFO(0,num)
ZEND_END_ARG_INFO()
```
### 探究
如果我们的第二个参数**this_ptr**为 NULL 或者不是**OBJECT**类型的话,那么效果和**zend_parse_parameters**一样,我之前填的是 this 指针,所以跳到了 else 分支
else 分之第一句就是`p++;`表示字符串往后面移动一位,我填的参数是是一个单独的 **l** 然后一移动....没啦,后面还有两个 va_arg
通过后面这两个得知,我们的两个参数,一个是 **zval** 的,一个是 **zend_class_entry\*** 我们传入的 **this_ptr** 参数会赋值给 **object** 也就是我们后面的第四个参数,第五个是我们类的指针
```c
object = va_arg(va, zval **);
ce = va_arg(va, zend_class_entry *);
*object = this_ptr;
```
看后面这一段,好像是校验类的,所以我觉得这个`zend_parse_method_parameters``zend_parse_parameters`的区别就在这里method 能够对类进行校验
```c
if (ce && !instanceof_function(Z_OBJCE_P(this_ptr), ce)) {
zend_error_noreturn(E_CORE_ERROR, "%s::%s() must be derived from %s::%s",
ZSTR_VAL(Z_OBJCE_P(this_ptr)->name), get_active_function_name(), ZSTR_VAL(ce->name), get_active_function_name());
}
ZEND_API zend_bool ZEND_FASTCALL instanceof_function(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */
{
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
return instanceof_interface(instance_ce, ce);
} else {
return instanceof_class(instance_ce, ce);
}
}
static zend_always_inline zend_bool instanceof_class(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */
{
while (instance_ce) {
if (instance_ce == ce) {//会循环校验父类是否相等
return 1;
}
instance_ce = instance_ce->parent;
}
return 0;
}
```
```c
ZEND_API int zend_parse_parameters(int num_args, const char *type_spec, ...) /* {{{ */
{
va_list va;
int retval;
int flags = 0;
va_start(va, type_spec);
retval = zend_parse_va_args(num_args, type_spec, &va, flags);
va_end(va);
return retval;
}
/* }}} */
ZEND_API int zend_parse_method_parameters(int num_args, zval *this_ptr, const char *type_spec, ...) /* {{{ */
{
va_list va;
int retval;
int flags = 0;
const char *p = type_spec;
zval **object;
zend_class_entry *ce;
/* Just checking this_ptr is not enough, because fcall_common_helper does not set
* Z_OBJ(EG(This)) to NULL when calling an internal function with common.scope == NULL.
* In that case EG(This) would still be the $this from the calling code and we'd take the
* wrong branch here. */
zend_bool is_method = EG(current_execute_data)->func->common.scope != NULL;
if (!is_method || !this_ptr || Z_TYPE_P(this_ptr) != IS_OBJECT) {
va_start(va, type_spec);
retval = zend_parse_va_args(num_args, type_spec, &va, flags);
va_end(va);
} else {
p++;
va_start(va, type_spec);
object = va_arg(va, zval **);
ce = va_arg(va, zend_class_entry *);
*object = this_ptr;
if (ce && !instanceof_function(Z_OBJCE_P(this_ptr), ce)) {
zend_error_noreturn(E_CORE_ERROR, "%s::%s() must be derived from %s::%s",
ZSTR_VAL(Z_OBJCE_P(this_ptr)->name), get_active_function_name(), ZSTR_VAL(ce->name), get_active_function_name());
}
retval = zend_parse_va_args(num_args, p, &va, flags);
va_end(va);
}
return retval;
}
/* }}} */
```
#### 使用
这里的**type_spec**我还加了一个 O因为在源码中`p++;`这里跳过了一个字符,那么我们后面`retval = zend_parse_va_args(num_args, p, &va, flags);`的时候传入的就是 **l 了, O** 这里应该是可以乱填一个字符的
**&this** 又传回来了- -
```c
PHP_METHOD(study_ext_class,sum)
{
zend_long parma_num=0;
zval* this=getThis();
zval* static_num=zend_read_static_property(Z_OBJCE_P(this),"num",sizeof("num")-1,0);
// zval
if(zend_parse_method_parameters(ZEND_NUM_ARGS(),this,"Ol",&this,study_ce,&parma_num)==FAILURE){
RETURN_LONG(-1)
}
if(Z_TYPE_P(static_num)==IS_LONG){
RETURN_LONG(static_num->value.lval+parma_num)
}
RETURN_LONG(-1)
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB