├── .travis.yml ├── README.md ├── build.sh ├── course1-how-to-export-a-module ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course10-how-to-work-with-resource ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course11-how-to-include-php-file ├── Makefile ├── myext.c ├── myext.h ├── other.php └── test.php ├── course12-how-to-throw-error-and-exception ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course2-how-to-define-deps-and-ini ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course3-how-to-define-functions ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course4-how-the-zval-works ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course5-how-to-define-class ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course6-how-to-call-php-functions ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course7-how-to-create-object ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course8-how-to-visit-global-vars ├── Makefile ├── myext.c ├── myext.h └── test.php ├── course9-how-to-work-with-zend-array ├── Makefile ├── myext.c ├── myext.h └── test.php └── index.php /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1 5 | 6 | sudo: required 7 | 8 | script: 9 | sh build.sh 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP7扩展开发系列教程 2 | [![Build Status](https://travis-ci.org/owenliang/php7-extension-explore.svg?branch=master)](https://travis-ci.org/owenliang/php7-extension-explore) 3 | 4 | ## 点赞 5 | 如果我帮到了你,请右上角点1个star! 6 | 7 | ## 环境要求 8 | * PHP7.1+ 9 | * Linux 10 | 11 | ## 常见问题 12 | * 示例代码大量使用带副作用的assert断言, 请确保gcc工作在debug模式 13 | 14 | ## 提问与反馈 15 | * 请提交issue 16 | 17 | ## 教程清单 18 | * [course1-how-to-export-a-module](https://yuerblog.cc/2017/08/07/course1-how-to-export-a-module/) 19 | * [course2-how-to-define-deps-and-ini](https://yuerblog.cc/2017/08/07/course2-how-to-define-deps-and-ini/) 20 | * [course3-how-to-define-functions](https://yuerblog.cc/2017/08/08/course3-how-to-define-functions/) 21 | * [course4-how-the-zval-works](https://yuerblog.cc/2017/08/09/course4-how-the-zval-works/) 22 | * [course5-how-to-define-class](https://yuerblog.cc/2017/08/09/course5-how-to-define-class/) 23 | * [course6-how-to-call-php-functions](https://yuerblog.cc/2017/08/10/course6-how-to-call-php-functions/) 24 | * [course7-how-to-create-object](https://yuerblog.cc/2017/08/10/course7-how-to-create-object/) 25 | * [course8-how-to-visit-global-vars](https://yuerblog.cc/2017/08/11/course8-how-to-visit-global-vars/) 26 | * [course9-how-to-work-with-zend-array](https://yuerblog.cc/2017/08/11/course9-how-to-work-with-zend-array/) 27 | * [course10-how-to-work-with-resource](https://yuerblog.cc/2017/08/14/course10-how-to-work-with-resource/) 28 | * [course11-how-to-include-php-file](http://yuerblog.cc/2017/08/14/course11-how-to-include-php-file/) 29 | * [course12-how-to-throw-error-and-exception](https://yuerblog.cc/2017/09/08/course12-how-to-throw-error-and-exception/) 30 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 只编译不安装, 若需安装某个course需自己执行make install 4 | 5 | set -e 6 | 7 | for c in course*;do 8 | cd $c && make && cd - 9 | done 10 | 11 | sudo echo "extension=myext.so" >> `php --ini|grep Loaded| awk '{print $NF}'` 12 | -------------------------------------------------------------------------------- /course1-how-to-export-a-module/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course1-how-to-export-a-module/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | int extension_startup(int type, int module_number) { 4 | TRACE("extension_startup"); 5 | return SUCCESS; 6 | } 7 | 8 | int extension_shutdown(int type, int module_number) { 9 | TRACE("extension_shutdown"); 10 | return SUCCESS; 11 | } 12 | 13 | int extension_before_request(int type, int module_number) { 14 | TRACE("extension_before_request"); 15 | return SUCCESS; 16 | } 17 | 18 | int extension_after_request(int type, int module_number) { 19 | TRACE("extension_after_request"); 20 | return SUCCESS; 21 | } 22 | 23 | void extension_info(zend_module_entry *zend_module) { 24 | php_info_print_table_start(); 25 | php_info_print_table_header(2, "myext support", "enabled"); 26 | php_info_print_table_row(2, "author", "owenliang"); 27 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 28 | php_info_print_table_end(); 29 | } 30 | 31 | zend_module_entry module = { 32 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 33 | NULL, // ini_entry 34 | NULL, // deps 35 | "myext", //name 36 | NULL, // functions 37 | extension_startup, // module_startup_func 38 | extension_shutdown, // module_shutdown_func 39 | extension_before_request, // request_startup_func 40 | extension_after_request, // request_shutdown_func 41 | extension_info, // info_func 42 | "1.0", // version 43 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 44 | // handle,module_number,build_id 45 | STANDARD_MODULE_PROPERTIES, 46 | }; 47 | 48 | ZEND_DLEXPORT zend_module_entry *get_module() { 49 | return &module; 50 | } 51 | -------------------------------------------------------------------------------- /course1-how-to-export-a-module/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | 7 | #include "php.h" 8 | #include "ext/standard/info.h" 9 | 10 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 11 | 12 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 13 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 14 | va_list args; 15 | va_start(args, fmt); 16 | vfprintf(stderr, fmt, args); 17 | fprintf(stderr, "\n"); 18 | va_end(args); 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /course1-how-to-export-a-module/test.php: -------------------------------------------------------------------------------- 1 | type == myext_string_resource_id); 84 | free(res->ptr); 85 | } 86 | 87 | int extension_startup(int type, int module_number) { 88 | TRACE("extension_startup"); 89 | zend_register_ini_entries(ini_defs, module_number); 90 | 91 | // 92 | // Interface myext_interface 93 | // 94 | 95 | // interface defination 96 | zend_class_entry myext_interface_def; 97 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 98 | 99 | // get interface handle 100 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 101 | 102 | // 103 | // Class myext 104 | // 105 | 106 | // class defination 107 | zend_class_entry myext_class_def; 108 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 109 | 110 | // get class handle 111 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 112 | 113 | // implements interface 114 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 115 | 116 | // add property to handle 117 | zval version_zval; 118 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 119 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 120 | 121 | // add static property to handle 122 | zval author_zval; 123 | ZVAL_PSTRING(&author_zval, "owenliang"); 124 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 125 | 126 | // add constant to handle 127 | zval build_date_zval; 128 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 129 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 130 | 131 | // 132 | // Class myext_child (inherit from Class myext) 133 | // 134 | zend_class_entry myext_child_class_def; 135 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 136 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 137 | 138 | // final class, no more child class 139 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 140 | 141 | // register constant 142 | zend_constant c; 143 | c.name = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 1); // persistant memory 144 | ZVAL_STR(&c.value, zend_string_init("https://github.com/owenliang/php7-extension-explore", 145 | sizeof("https://github.com/owenliang/php7-extension-explore"), 1)); // persistant memory 146 | c.flags = CONST_CS | CONST_PERSISTENT; 147 | c.module_number = module_number; 148 | assert(zend_register_constant(&c) == SUCCESS); 149 | 150 | // register resource type 151 | myext_string_resource_id = zend_register_list_destructors_ex(myext_string_resource_dtor, NULL, MYEXT_STRING_RESOURCE_DTOR, module_number); 152 | assert(myext_string_resource_id != FAILURE); 153 | 154 | return SUCCESS; 155 | } 156 | 157 | int extension_shutdown(int type, int module_number) { 158 | TRACE("extension_shutdown"); 159 | return SUCCESS; 160 | } 161 | 162 | int extension_before_request(int type, int module_number) { 163 | TRACE("extension_before_request"); 164 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 165 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 166 | 167 | // try active jit super globals 168 | zend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1); 169 | 170 | // find it in global symbol table 171 | zval *server = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1); 172 | assert(server != NULL); 173 | assert(Z_TYPE_P(server) == IS_ARRAY); 174 | 175 | // var_dump($_SERVER) 176 | zval func_name; 177 | ZVAL_STR(&func_name, zend_string_init("var_dump", sizeof("var_dump") - 1, 0)); 178 | zval retval; 179 | assert(call_user_function(&EG(function_table), NULL, &func_name, &retval, 1, server) == SUCCESS); 180 | zval_ptr_dtor(&func_name); 181 | zval_ptr_dtor(&retval); 182 | return SUCCESS; 183 | } 184 | 185 | int extension_after_request(int type, int module_number) { 186 | TRACE("extension_after_request"); 187 | return SUCCESS; 188 | } 189 | 190 | void extension_info(zend_module_entry *zend_module) { 191 | php_info_print_table_start(); 192 | php_info_print_table_header(2, "myext support", "enabled"); 193 | php_info_print_table_row(2, "author", "owenliang"); 194 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 195 | php_info_print_table_end(); 196 | } 197 | 198 | // Myext's static method 199 | void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { 200 | TRACE("zim_myext_print_author"); 201 | php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); 202 | ZVAL_BOOL(return_value, 1); 203 | } 204 | 205 | 206 | // global function 207 | void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { 208 | TRACE("zif_myext_test_object"); 209 | 210 | // call myext's static method: print_author 211 | zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); 212 | zend_class_entry *myext_handle = zend_lookup_class(myext_classname); 213 | zend_string_release(myext_classname); 214 | assert(myext_handle == myext_class_handle); 215 | 216 | zval retval; 217 | zend_fcall_info fci = { 218 | size: sizeof(zend_fcall_info), 219 | retval: &retval, 220 | params: NULL, 221 | object: NULL, 222 | no_separation: 1, 223 | param_count: 0, 224 | }; 225 | ZVAL_UNDEF(&fci.function_name); 226 | 227 | zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1); 228 | 229 | zend_fcall_info_cache fcic = { 230 | initialized: 1, 231 | function_handler: print_author_func->value.func, 232 | calling_scope: myext_handle, 233 | called_scope: NULL, 234 | object: NULL, 235 | }; 236 | 237 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 238 | assert(Z_TYPE_P(&retval) == IS_TRUE); 239 | 240 | // new a myext object 241 | zval myext_obj; 242 | assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS); 243 | 244 | // call object's __construct 245 | zval ctor_name; 246 | zval ctor_retval; 247 | ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); 248 | assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); 249 | zval_ptr_dtor(&ctor_name); 250 | 251 | // call object's method 252 | zval func_name; 253 | ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); 254 | zval param; 255 | ZVAL_STR(¶m, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); 256 | zval retval2; 257 | assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, ¶m) == SUCCESS); 258 | TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); 259 | zval_ptr_dtor(&func_name); 260 | zval_ptr_dtor(¶m); 261 | zval_ptr_dtor(&retval2); 262 | 263 | // free object 264 | zval_ptr_dtor(&myext_obj); 265 | } 266 | 267 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 268 | TRACE("zim_myext_child_version"); 269 | 270 | zval *this = &(execute_data->This); 271 | 272 | // call parent's version() 273 | zval retval; 274 | zend_fcall_info fci = { 275 | size: sizeof(zend_fcall_info), 276 | retval: &retval, 277 | params: NULL, 278 | object: this->value.obj, 279 | no_separation: 1, 280 | param_count: 0, 281 | }; 282 | ZVAL_UNDEF(&fci.function_name); 283 | 284 | // find parent's version method 285 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 286 | 287 | zend_fcall_info_cache fcic = { 288 | initialized: 1, 289 | function_handler: parent_version_func->value.func, 290 | calling_scope: this->value.obj->ce->parent, 291 | called_scope: this->value.obj->ce, 292 | object: this->value.obj, 293 | }; 294 | 295 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 296 | assert(Z_TYPE_P(&retval) == IS_STRING); 297 | 298 | int len = retval.value.str->len + sizeof(".child") - 1; 299 | char *child_version = emalloc(len); 300 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 301 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 302 | 303 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 304 | efree(child_version); 305 | zval_ptr_dtor(&retval); 306 | } 307 | 308 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 309 | TRACE("zim_myext_strcase_convert"); 310 | 311 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 312 | zval *args = ZEND_CALL_ARG(execute_data, 1); 313 | 314 | TRACE("num_args=%d", num_args); 315 | 316 | zval *zv = &args[0]; 317 | zval *lowercase_zval = &args[1]; 318 | 319 | convert_to_string(zv); 320 | convert_to_boolean(lowercase_zval); 321 | 322 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 323 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 324 | size_t i; 325 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 326 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 327 | dup->val[i] = tolower(dup->val[i]); 328 | } else { 329 | dup->val[i] = toupper(dup->val[i]); 330 | } 331 | } 332 | ZVAL_STR(return_value, dup); 333 | } 334 | 335 | // Equals to PHP_METHOD(myext, strtolwer) 336 | // 337 | // zim_ means Zend Internal Method 338 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 339 | TRACE("zim_myext_constructor"); 340 | 341 | zval *this = &(execute_data->This); 342 | 343 | // class handle of this object 344 | zend_class_entry *class_handle = this->value.obj->ce; 345 | 346 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 347 | 348 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 349 | zval ver_zval; 350 | ZVAL_STR(&ver_zval, new_ver_prop); 351 | 352 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 353 | 354 | zend_string_release(ver_prop_name); 355 | zval_ptr_dtor(&ver_zval); 356 | } 357 | 358 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 359 | TRACE("zim_myext_version"); 360 | 361 | // same as $this 362 | zval *this = &(execute_data->This); 363 | 364 | // class handle of this object 365 | zend_class_entry *class_handle = this->value.obj->ce; 366 | 367 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 368 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 369 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 370 | ZVAL_STR(return_value, dup); 371 | } else { 372 | ZVAL_BOOL(return_value, 0); 373 | } 374 | 375 | // read constant 376 | zend_string *cname = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 0); 377 | zval *c_github = zend_get_constant(cname); 378 | assert(Z_TYPE_P(c_github) == IS_STRING); 379 | TRACE("zend_get_constant(GITHUB)=%.*s", Z_STRLEN_P(c_github), Z_STRVAL_P(c_github)); 380 | zend_string_release(cname); 381 | } 382 | 383 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 384 | TRACE("zim_myext_strtolower"); 385 | 386 | zval *this = &(execute_data->This); 387 | 388 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 389 | zval *args = ZEND_CALL_ARG(execute_data, 1); 390 | 391 | TRACE("num_args=%d", num_args); 392 | 393 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 394 | zval func_name_zval; 395 | ZVAL_STR(&func_name_zval, func_name); 396 | 397 | zval params[2]; 398 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 399 | ZVAL_BOOL(¶ms[1], 1); 400 | 401 | // call method 402 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 403 | zval_ptr_dtor(&func_name_zval); 404 | } 405 | 406 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 407 | TRACE("zim_myext_strtoupper"); 408 | 409 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 410 | zval *args = ZEND_CALL_ARG(execute_data, 1); 411 | 412 | TRACE("num_args=%d", num_args); 413 | 414 | // call global function 415 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 416 | zval func_name_zval; 417 | ZVAL_STR(&func_name_zval, func_name); 418 | 419 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 420 | zval_ptr_dtor(&func_name_zval); 421 | } 422 | 423 | void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value) { 424 | // init arr 425 | zval arr_zval; 426 | assert(array_init(&arr_zval) == SUCCESS); 427 | 428 | // add k-v 429 | add_assoc_long(&arr_zval, "date", 20170811); 430 | assert(zend_hash_str_exists(arr_zval.value.arr, "date", sizeof("date") - 1)); 431 | 432 | // add v 433 | assert(add_next_index_string(&arr_zval, "hahaha") == SUCCESS); 434 | 435 | // arr count 436 | assert(zend_hash_num_elements(arr_zval.value.arr) == 2); 437 | 438 | // traversal arr 439 | zend_array *arr = arr_zval.value.arr; 440 | int i; 441 | for (i = 0; i < arr->nNumUsed; ++i) { 442 | zval *val = &(arr->arData[i].val); 443 | // handle indirect zval 444 | if (Z_TYPE_P(val) == IS_INDIRECT) { 445 | val = Z_INDIRECT_P(val); 446 | } 447 | // empty slots 448 | if (Z_TYPE_P(val) == IS_UNDEF) { 449 | continue; 450 | } 451 | 452 | if (arr->arData[i].key) { // must be array["date"] 453 | TRACE("arr['%.*s']=%ld", arr->arData[i].key->len, arr->arData[i].key->val, val->value.lval); 454 | } else { // must be array[0] 455 | TRACE("arr[%ld]=%.*s", arr->arData[i].h, val->value.str->len, val->value.str->val); 456 | } 457 | } 458 | 459 | // find key 460 | zval *zv_in_arr = zend_hash_str_find_ind(arr_zval.value.arr, "date", sizeof("date") - 1); 461 | assert(zv_in_arr->value.lval == 20170811); 462 | 463 | // del string key 464 | assert(zend_hash_str_del(arr_zval.value.arr, "date", sizeof("date") - 1) == SUCCESS); 465 | 466 | // del index key 467 | assert(zend_hash_index_del(arr_zval.value.arr, 0) == SUCCESS); 468 | 469 | // release arr 470 | zval_ptr_dtor(&arr_zval); 471 | } 472 | 473 | void zif_myext_test_resource(zend_execute_data *execute_data, zval *return_value) { 474 | char *string = strdup("i am a string resource"); 475 | zend_resource *res = zend_register_resource(string, myext_string_resource_id); 476 | assert(GC_REFCOUNT(res) == 1); 477 | 478 | // wrappped with zval, refcount=2 479 | zval res_zval; 480 | ZVAL_RES(&res_zval, res); 481 | zval_addref_p(&res_zval); 482 | assert(GC_REFCOUNT(res) == 2); 483 | 484 | // release resource directly, left refcount=1 485 | zend_list_delete(res); 486 | assert(GC_REFCOUNT(res) == 1); 487 | 488 | // validate and get resource ptr 489 | char *s = zend_fetch_resource_ex(&res_zval, MYEXT_STRING_RESOURCE_DTOR, myext_string_resource_id); 490 | assert(strcmp(s, "i am a string resource") == 0); 491 | 492 | // release resource through zval, left refcount=0, zend_list_free is called 493 | zval_ptr_dtor(&res_zval); 494 | } 495 | 496 | zend_module_entry module = { 497 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 498 | NULL, // ini_entry 499 | NULL, // deps 500 | "myext", //name 501 | global_funcs, // functions 502 | extension_startup, // module_startup_func 503 | extension_shutdown, // module_shutdown_func 504 | extension_before_request, // request_startup_func 505 | extension_after_request, // request_shutdown_func 506 | extension_info, // info_func 507 | "1.0", // version 508 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 509 | // handle,module_number,build_id 510 | STANDARD_MODULE_PROPERTIES, 511 | }; 512 | 513 | zend_module_dep deps[] = { 514 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 515 | { NULL, NULL, NULL, 0}, 516 | }; 517 | 518 | ZEND_DLEXPORT zend_module_entry *get_module() { 519 | module.deps = deps; 520 | 521 | return &module; 522 | } 523 | -------------------------------------------------------------------------------- /course10-how-to-work-with-resource/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course10-how-to-work-with-resource/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; 20 | 21 | // global function 22 | test_object(); 23 | 24 | test_array(); 25 | 26 | test_resource(); 27 | 28 | echo GITHUB . PHP_EOL; 29 | -------------------------------------------------------------------------------- /course11-how-to-include-php-file/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -gdwarf-2 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course11-how-to-include-php-file/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | #define MYEXT_INI_NAME_GITHUB "myext.github" 4 | #define MYEXT_INI_VALUE_GITHUB "https://github.com/owenliang/php7-extension-explore" 5 | 6 | // overwrite in php.ini like this: 7 | // 8 | // myext.github = "some value" 9 | // 10 | zend_ini_entry_def ini_defs[] = { 11 | // name,on_modify,mh_arg1,mh_arg2,mh_arg3,value,displayer,modifiable,name_length,value_length 12 | {MYEXT_INI_NAME_GITHUB, NULL, NULL, NULL, NULL, MYEXT_INI_VALUE_GITHUB, NULL, ZEND_INI_ALL, sizeof(MYEXT_INI_NAME_GITHUB) - 1, sizeof(MYEXT_INI_VALUE_GITHUB) - 1}, 13 | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, 14 | }; 15 | 16 | extern void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value); 17 | extern void zim_myext_version(zend_execute_data *execute_data, zval *return_value); 18 | extern void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value); 19 | extern void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value); 20 | extern void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value); 21 | extern void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value); 22 | extern void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value); 23 | 24 | extern void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value); 25 | extern void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value); 26 | extern void zif_myext_test_resource(zend_execute_data *execute_data, zval *return_value); 27 | extern void zif_myext_test_include(zend_execute_data *execute_data, zval *return_value); 28 | 29 | // zif_strtolower's params defination 30 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 31 | // required_num_args(interger stored in pointer) 32 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 33 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 34 | {"string", NULL, IS_STRING, 0, 0, 0}, 35 | }; 36 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 37 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 38 | {"string", NULL, IS_STRING, 0, 0, 0}, 39 | }; 40 | 41 | zend_function_entry funcs[] = { 42 | // fname,handler,arg_info,,num_args,flags 43 | {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, 44 | {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, 45 | {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, 46 | {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, 47 | {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, 48 | {"print_author", zim_myext_print_author, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC}, 49 | {NULL, NULL, NULL, 0, 0}, 50 | }; 51 | 52 | zend_function_entry interface_funcs[] = { 53 | // fname,handler,arg_info,,num_args,flags 54 | {"version", NULL, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT}, 55 | {NULL, NULL, NULL, 0, 0}, 56 | }; 57 | 58 | zend_function_entry final_funcs[] = { 59 | // fname,handler,arg_info,,num_args,flags 60 | {"version", zim_myext_child_version, NULL, 0, ZEND_ACC_PUBLIC}, 61 | {NULL, NULL, NULL, 0, 0}, 62 | }; 63 | 64 | zend_function_entry global_funcs[] = { 65 | // fname,handler,arg_info,,num_args,flags 66 | {"test_object", zif_myext_test_object, NULL, 0, 0}, 67 | {"test_array", zif_myext_test_array, NULL, 0, 0}, 68 | {"test_resource", zif_myext_test_resource, 0, 0}, 69 | {"test_include", zif_myext_test_include, 0, 0}, 70 | {NULL, NULL, NULL, 0, 0}, 71 | }; 72 | 73 | zend_class_entry *myext_interface_handle = NULL; // interface handle 74 | zend_class_entry *myext_class_handle = NULL; // base class handle 75 | zend_class_entry *myext_child_class_handle = NULL; // child class handle 76 | 77 | // resource id 78 | int myext_string_resource_id = 0; 79 | 80 | // resource type description 81 | #define MYEXT_STRING_RESOURCE_DTOR "myext_string_resource" 82 | 83 | // resource destructor callback 84 | void myext_string_resource_dtor(zend_resource *res) { 85 | assert(res->type == myext_string_resource_id); 86 | free(res->ptr); 87 | } 88 | 89 | int extension_startup(int type, int module_number) { 90 | TRACE("extension_startup"); 91 | zend_register_ini_entries(ini_defs, module_number); 92 | 93 | // 94 | // Interface myext_interface 95 | // 96 | 97 | // interface defination 98 | zend_class_entry myext_interface_def; 99 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 100 | 101 | // get interface handle 102 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 103 | 104 | // 105 | // Class myext 106 | // 107 | 108 | // class defination 109 | zend_class_entry myext_class_def; 110 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 111 | 112 | // get class handle 113 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 114 | 115 | // implements interface 116 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 117 | 118 | // add property to handle 119 | zval version_zval; 120 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 121 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 122 | 123 | // add static property to handle 124 | zval author_zval; 125 | ZVAL_PSTRING(&author_zval, "owenliang"); 126 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 127 | 128 | // add constant to handle 129 | zval build_date_zval; 130 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 131 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 132 | 133 | // 134 | // Class myext_child (inherit from Class myext) 135 | // 136 | zend_class_entry myext_child_class_def; 137 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 138 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 139 | 140 | // final class, no more child class 141 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 142 | 143 | // register constant 144 | zend_constant c; 145 | c.name = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 1); // persistant memory 146 | ZVAL_STR(&c.value, zend_string_init("https://github.com/owenliang/php7-extension-explore", 147 | sizeof("https://github.com/owenliang/php7-extension-explore"), 1)); // persistant memory 148 | c.flags = CONST_CS | CONST_PERSISTENT; 149 | c.module_number = module_number; 150 | assert(zend_register_constant(&c) == SUCCESS); 151 | 152 | // register resource type 153 | myext_string_resource_id = zend_register_list_destructors_ex(myext_string_resource_dtor, NULL, MYEXT_STRING_RESOURCE_DTOR, module_number); 154 | assert(myext_string_resource_id != FAILURE); 155 | 156 | return SUCCESS; 157 | } 158 | 159 | int extension_shutdown(int type, int module_number) { 160 | TRACE("extension_shutdown"); 161 | return SUCCESS; 162 | } 163 | 164 | int extension_before_request(int type, int module_number) { 165 | TRACE("extension_before_request"); 166 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 167 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 168 | 169 | // try active jit super globals 170 | zend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1); 171 | 172 | // find it in global symbol table 173 | zval *server = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1); 174 | assert(server != NULL); 175 | assert(Z_TYPE_P(server) == IS_ARRAY); 176 | 177 | // var_dump($_SERVER) 178 | zval func_name; 179 | ZVAL_STR(&func_name, zend_string_init("var_dump", sizeof("var_dump") - 1, 0)); 180 | zval retval; 181 | assert(call_user_function(&EG(function_table), NULL, &func_name, &retval, 1, server) == SUCCESS); 182 | zval_ptr_dtor(&func_name); 183 | zval_ptr_dtor(&retval); 184 | return SUCCESS; 185 | } 186 | 187 | int extension_after_request(int type, int module_number) { 188 | TRACE("extension_after_request"); 189 | return SUCCESS; 190 | } 191 | 192 | void extension_info(zend_module_entry *zend_module) { 193 | php_info_print_table_start(); 194 | php_info_print_table_header(2, "myext support", "enabled"); 195 | php_info_print_table_row(2, "author", "owenliang"); 196 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 197 | php_info_print_table_end(); 198 | } 199 | 200 | // Myext's static method 201 | void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { 202 | TRACE("zim_myext_print_author"); 203 | php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); 204 | ZVAL_BOOL(return_value, 1); 205 | } 206 | 207 | 208 | // global function 209 | void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { 210 | TRACE("zif_myext_test_object"); 211 | 212 | // call myext's static method: print_author 213 | zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); 214 | zend_class_entry *myext_handle = zend_lookup_class(myext_classname); 215 | zend_string_release(myext_classname); 216 | assert(myext_handle == myext_class_handle); 217 | 218 | zval retval; 219 | zend_fcall_info fci = { 220 | size: sizeof(zend_fcall_info), 221 | retval: &retval, 222 | params: NULL, 223 | object: NULL, 224 | no_separation: 1, 225 | param_count: 0, 226 | }; 227 | ZVAL_UNDEF(&fci.function_name); 228 | 229 | zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1); 230 | 231 | zend_fcall_info_cache fcic = { 232 | initialized: 1, 233 | function_handler: print_author_func->value.func, 234 | calling_scope: myext_handle, 235 | called_scope: NULL, 236 | object: NULL, 237 | }; 238 | 239 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 240 | assert(Z_TYPE_P(&retval) == IS_TRUE); 241 | 242 | // new a myext object 243 | zval myext_obj; 244 | assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS); 245 | 246 | // call object's __construct 247 | zval ctor_name; 248 | zval ctor_retval; 249 | ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); 250 | assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); 251 | zval_ptr_dtor(&ctor_name); 252 | 253 | // call object's method 254 | zval func_name; 255 | ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); 256 | zval param; 257 | ZVAL_STR(¶m, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); 258 | zval retval2; 259 | assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, ¶m) == SUCCESS); 260 | TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); 261 | zval_ptr_dtor(&func_name); 262 | zval_ptr_dtor(¶m); 263 | zval_ptr_dtor(&retval2); 264 | 265 | // free object 266 | zval_ptr_dtor(&myext_obj); 267 | } 268 | 269 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 270 | TRACE("zim_myext_child_version"); 271 | 272 | zval *this = &(execute_data->This); 273 | 274 | // call parent's version() 275 | zval retval; 276 | zend_fcall_info fci = { 277 | size: sizeof(zend_fcall_info), 278 | retval: &retval, 279 | params: NULL, 280 | object: this->value.obj, 281 | no_separation: 1, 282 | param_count: 0, 283 | }; 284 | ZVAL_UNDEF(&fci.function_name); 285 | 286 | // find parent's version method 287 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 288 | 289 | zend_fcall_info_cache fcic = { 290 | initialized: 1, 291 | function_handler: parent_version_func->value.func, 292 | calling_scope: this->value.obj->ce->parent, 293 | called_scope: this->value.obj->ce, 294 | object: this->value.obj, 295 | }; 296 | 297 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 298 | assert(Z_TYPE_P(&retval) == IS_STRING); 299 | 300 | int len = retval.value.str->len + sizeof(".child") - 1; 301 | char *child_version = emalloc(len); 302 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 303 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 304 | 305 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 306 | efree(child_version); 307 | zval_ptr_dtor(&retval); 308 | } 309 | 310 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 311 | TRACE("zim_myext_strcase_convert"); 312 | 313 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 314 | zval *args = ZEND_CALL_ARG(execute_data, 1); 315 | 316 | TRACE("num_args=%d", num_args); 317 | 318 | zval *zv = &args[0]; 319 | zval *lowercase_zval = &args[1]; 320 | 321 | convert_to_string(zv); 322 | convert_to_boolean(lowercase_zval); 323 | 324 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 325 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 326 | size_t i; 327 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 328 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 329 | dup->val[i] = tolower(dup->val[i]); 330 | } else { 331 | dup->val[i] = toupper(dup->val[i]); 332 | } 333 | } 334 | ZVAL_STR(return_value, dup); 335 | } 336 | 337 | // Equals to PHP_METHOD(myext, strtolwer) 338 | // 339 | // zim_ means Zend Internal Method 340 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 341 | TRACE("zim_myext_constructor"); 342 | 343 | zval *this = &(execute_data->This); 344 | 345 | // class handle of this object 346 | zend_class_entry *class_handle = this->value.obj->ce; 347 | 348 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 349 | 350 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 351 | zval ver_zval; 352 | ZVAL_STR(&ver_zval, new_ver_prop); 353 | 354 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 355 | 356 | zend_string_release(ver_prop_name); 357 | zval_ptr_dtor(&ver_zval); 358 | } 359 | 360 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 361 | TRACE("zim_myext_version"); 362 | 363 | // same as $this 364 | zval *this = &(execute_data->This); 365 | 366 | // class handle of this object 367 | zend_class_entry *class_handle = this->value.obj->ce; 368 | 369 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 370 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 371 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 372 | ZVAL_STR(return_value, dup); 373 | } else { 374 | ZVAL_BOOL(return_value, 0); 375 | } 376 | 377 | // read constant 378 | zend_string *cname = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 0); 379 | zval *c_github = zend_get_constant(cname); 380 | assert(Z_TYPE_P(c_github) == IS_STRING); 381 | TRACE("zend_get_constant(GITHUB)=%.*s", Z_STRLEN_P(c_github), Z_STRVAL_P(c_github)); 382 | zend_string_release(cname); 383 | } 384 | 385 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 386 | TRACE("zim_myext_strtolower"); 387 | 388 | zval *this = &(execute_data->This); 389 | 390 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 391 | zval *args = ZEND_CALL_ARG(execute_data, 1); 392 | 393 | TRACE("num_args=%d", num_args); 394 | 395 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 396 | zval func_name_zval; 397 | ZVAL_STR(&func_name_zval, func_name); 398 | 399 | zval params[2]; 400 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 401 | ZVAL_BOOL(¶ms[1], 1); 402 | 403 | // call method 404 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 405 | zval_ptr_dtor(&func_name_zval); 406 | } 407 | 408 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 409 | TRACE("zim_myext_strtoupper"); 410 | 411 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 412 | zval *args = ZEND_CALL_ARG(execute_data, 1); 413 | 414 | TRACE("num_args=%d", num_args); 415 | 416 | // call global function 417 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 418 | zval func_name_zval; 419 | ZVAL_STR(&func_name_zval, func_name); 420 | 421 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 422 | zval_ptr_dtor(&func_name_zval); 423 | } 424 | 425 | void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value) { 426 | // init arr 427 | zval arr_zval; 428 | assert(array_init(&arr_zval) == SUCCESS); 429 | 430 | // add k-v 431 | add_assoc_long(&arr_zval, "date", 20170811); 432 | assert(zend_hash_str_exists(arr_zval.value.arr, "date", sizeof("date") - 1)); 433 | 434 | // add v 435 | assert(add_next_index_string(&arr_zval, "hahaha") == SUCCESS); 436 | 437 | // arr count 438 | assert(zend_hash_num_elements(arr_zval.value.arr) == 2); 439 | 440 | // traversal arr 441 | zend_array *arr = arr_zval.value.arr; 442 | int i; 443 | for (i = 0; i < arr->nNumUsed; ++i) { 444 | zval *val = &(arr->arData[i].val); 445 | // handle indirect zval 446 | if (Z_TYPE_P(val) == IS_INDIRECT) { 447 | val = Z_INDIRECT_P(val); 448 | } 449 | // empty slots 450 | if (Z_TYPE_P(val) == IS_UNDEF) { 451 | continue; 452 | } 453 | 454 | if (arr->arData[i].key) { // must be array["date"] 455 | TRACE("arr['%.*s']=%ld", arr->arData[i].key->len, arr->arData[i].key->val, val->value.lval); 456 | } else { // must be array[0] 457 | TRACE("arr[%ld]=%.*s", arr->arData[i].h, val->value.str->len, val->value.str->val); 458 | } 459 | } 460 | 461 | // find key 462 | zval *zv_in_arr = zend_hash_str_find_ind(arr_zval.value.arr, "date", sizeof("date") - 1); 463 | assert(zv_in_arr->value.lval == 20170811); 464 | 465 | // del string key 466 | assert(zend_hash_str_del(arr_zval.value.arr, "date", sizeof("date") - 1) == SUCCESS); 467 | 468 | // del index key 469 | assert(zend_hash_index_del(arr_zval.value.arr, 0) == SUCCESS); 470 | 471 | // release arr 472 | zval_ptr_dtor(&arr_zval); 473 | } 474 | 475 | void zif_myext_test_resource(zend_execute_data *execute_data, zval *return_value) { 476 | TRACE("zif_myext_test_resource"); 477 | char *string = strdup("i am a string resource"); 478 | zend_resource *res = zend_register_resource(string, myext_string_resource_id); 479 | assert(GC_REFCOUNT(res) == 1); 480 | 481 | // wrappped with zval, refcount=2 482 | zval res_zval; 483 | ZVAL_RES(&res_zval, res); 484 | zval_addref_p(&res_zval); 485 | assert(GC_REFCOUNT(res) == 2); 486 | 487 | // release resource directly, left refcount=1 488 | zend_list_delete(res); 489 | assert(GC_REFCOUNT(res) == 1); 490 | 491 | // validate and get resource ptr 492 | char *s = zend_fetch_resource_ex(&res_zval, MYEXT_STRING_RESOURCE_DTOR, myext_string_resource_id); 493 | assert(strcmp(s, "i am a string resource") == 0); 494 | 495 | // release resource through zval, left refcount=0, zend_list_free is called 496 | zval_ptr_dtor(&res_zval); 497 | } 498 | 499 | void zif_myext_test_include(zend_execute_data *execute_data, zval *return_value) { 500 | TRACE("zif_myext_test_include"); 501 | 502 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 503 | zval *args = ZEND_CALL_ARG(execute_data, 1); 504 | assert(num_args == 1); 505 | 506 | // relative path to absolute full path 507 | char realpath[MAXPATHLEN]; 508 | if (!virtual_realpath(Z_STRVAL(args[0]), realpath)) { 509 | ZVAL_BOOL(return_value, 0); 510 | return; 511 | } 512 | 513 | TRACE("realpath=%s", realpath); 514 | 515 | zend_string *filename = zend_string_init(realpath, strlen(realpath), 0); 516 | 517 | // already loaded before 518 | zval *existed = zend_hash_find(&EG(included_files), filename); 519 | if (existed) { 520 | zend_string_release(filename); 521 | ZVAL_BOOL(return_value, 0); 522 | return; 523 | } 524 | 525 | // not opened file handle 526 | zend_file_handle file_handle = { 527 | filename: filename->val, 528 | free_filename: 0, 529 | type: ZEND_HANDLE_FILENAME, 530 | opened_path: NULL, 531 | handle: {fp: NULL}, 532 | }; 533 | 534 | // compile file into opcode 535 | zend_op_array *op_array = zend_compile_file(&file_handle, ZEND_INCLUDE); 536 | assert(op_array); 537 | 538 | // mark file is included 539 | zval empty_zv; 540 | ZVAL_NULL(&empty_zv); 541 | assert(zend_hash_add_new(&EG(included_files), filename, &empty_zv)); // 1 ref added inner 542 | zend_string_release(filename); 543 | 544 | // close file handle 545 | zend_destroy_file_handle(&file_handle); 546 | 547 | // execute opcode 548 | zval result; 549 | ZVAL_UNDEF(&result); 550 | zend_execute(op_array, &result); 551 | 552 | // free opcode 553 | destroy_op_array(op_array); 554 | efree(op_array); 555 | 556 | // array included from php file 557 | assert(Z_TYPE(result) == IS_ARRAY); 558 | *return_value = result; 559 | } 560 | 561 | zend_module_entry module = { 562 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 563 | NULL, // ini_entry 564 | NULL, // deps 565 | "myext", //name 566 | global_funcs, // functions 567 | extension_startup, // module_startup_func 568 | extension_shutdown, // module_shutdown_func 569 | extension_before_request, // request_startup_func 570 | extension_after_request, // request_shutdown_func 571 | extension_info, // info_func 572 | "1.0", // version 573 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 574 | // handle,module_number,build_id 575 | STANDARD_MODULE_PROPERTIES, 576 | }; 577 | 578 | zend_module_dep deps[] = { 579 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 580 | { NULL, NULL, NULL, 0}, 581 | }; 582 | 583 | ZEND_DLEXPORT zend_module_entry *get_module() { 584 | module.deps = deps; 585 | 586 | return &module; 587 | } 588 | -------------------------------------------------------------------------------- /course11-how-to-include-php-file/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course11-how-to-include-php-file/other.php: -------------------------------------------------------------------------------- 1 | "owenliang", 7 | ]; 8 | 9 | ?> -------------------------------------------------------------------------------- /course11-how-to-include-php-file/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; 20 | 21 | // global function 22 | test_object(); 23 | 24 | test_array(); 25 | 26 | test_resource(); 27 | 28 | $file = __DIR__ . "/other.php"; 29 | var_dump(test_include("other.php")); // array 30 | var_dump(test_include($file)); // false 31 | 32 | echo SOME_DEF . PHP_EOL; 33 | 34 | echo GITHUB . PHP_EOL; 35 | -------------------------------------------------------------------------------- /course12-how-to-throw-error-and-exception/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -gdwarf-2 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course12-how-to-throw-error-and-exception/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | #define MYEXT_INI_NAME_GITHUB "myext.github" 4 | #define MYEXT_INI_VALUE_GITHUB "https://github.com/owenliang/php7-extension-explore" 5 | 6 | // overwrite in php.ini like this: 7 | // 8 | // myext.github = "some value" 9 | // 10 | zend_ini_entry_def ini_defs[] = { 11 | // name,on_modify,mh_arg1,mh_arg2,mh_arg3,value,displayer,modifiable,name_length,value_length 12 | {MYEXT_INI_NAME_GITHUB, NULL, NULL, NULL, NULL, MYEXT_INI_VALUE_GITHUB, NULL, ZEND_INI_ALL, sizeof(MYEXT_INI_NAME_GITHUB) - 1, sizeof(MYEXT_INI_VALUE_GITHUB) - 1}, 13 | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, 14 | }; 15 | 16 | extern void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value); 17 | extern void zim_myext_version(zend_execute_data *execute_data, zval *return_value); 18 | extern void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value); 19 | extern void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value); 20 | extern void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value); 21 | extern void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value); 22 | extern void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value); 23 | 24 | extern void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value); 25 | extern void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value); 26 | extern void zif_myext_test_resource(zend_execute_data *execute_data, zval *return_value); 27 | extern void zif_myext_test_include(zend_execute_data *execute_data, zval *return_value); 28 | extern void zif_myext_test_exception(zend_execute_data *execute_data, zval *return_value); 29 | extern void zif_myext_test_error(zend_execute_data *execute_data, zval *return_value); 30 | 31 | // zif_strtolower's params defination 32 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 33 | // required_num_args(interger stored in pointer) 34 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 35 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 36 | {"string", NULL, IS_STRING, 0, 0, 0}, 37 | }; 38 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 39 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 40 | {"string", NULL, IS_STRING, 0, 0, 0}, 41 | }; 42 | 43 | zend_function_entry funcs[] = { 44 | // fname,handler,arg_info,,num_args,flags 45 | {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, 46 | {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, 47 | {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, 48 | {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, 49 | {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, 50 | {"print_author", zim_myext_print_author, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC}, 51 | {NULL, NULL, NULL, 0, 0}, 52 | }; 53 | 54 | zend_function_entry interface_funcs[] = { 55 | // fname,handler,arg_info,,num_args,flags 56 | {"version", NULL, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT}, 57 | {NULL, NULL, NULL, 0, 0}, 58 | }; 59 | 60 | zend_function_entry final_funcs[] = { 61 | // fname,handler,arg_info,,num_args,flags 62 | {"version", zim_myext_child_version, NULL, 0, ZEND_ACC_PUBLIC}, 63 | {NULL, NULL, NULL, 0, 0}, 64 | }; 65 | 66 | zend_function_entry global_funcs[] = { 67 | // fname,handler,arg_info,,num_args,flags 68 | {"test_object", zif_myext_test_object, NULL, 0, 0}, 69 | {"test_array", zif_myext_test_array, NULL, 0, 0}, 70 | {"test_resource", zif_myext_test_resource, 0, 0}, 71 | {"test_include", zif_myext_test_include, 0, 0}, 72 | {"test_exception", zif_myext_test_exception, 0, 0}, 73 | {"test_error", zif_myext_test_error, 0, 0}, 74 | {NULL, NULL, NULL, 0, 0}, 75 | }; 76 | 77 | zend_class_entry *myext_interface_handle = NULL; // interface handle 78 | zend_class_entry *myext_class_handle = NULL; // base class handle 79 | zend_class_entry *myext_child_class_handle = NULL; // child class handle 80 | 81 | // resource id 82 | int myext_string_resource_id = 0; 83 | 84 | // resource type description 85 | #define MYEXT_STRING_RESOURCE_DTOR "myext_string_resource" 86 | 87 | // resource destructor callback 88 | void myext_string_resource_dtor(zend_resource *res) { 89 | assert(res->type == myext_string_resource_id); 90 | free(res->ptr); 91 | } 92 | 93 | int extension_startup(int type, int module_number) { 94 | TRACE("extension_startup"); 95 | zend_register_ini_entries(ini_defs, module_number); 96 | 97 | // 98 | // Interface myext_interface 99 | // 100 | 101 | // interface defination 102 | zend_class_entry myext_interface_def; 103 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 104 | 105 | // get interface handle 106 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 107 | 108 | // 109 | // Class myext 110 | // 111 | 112 | // class defination 113 | zend_class_entry myext_class_def; 114 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 115 | 116 | // get class handle 117 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 118 | 119 | // implements interface 120 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 121 | 122 | // add property to handle 123 | zval version_zval; 124 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 125 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 126 | 127 | // add static property to handle 128 | zval author_zval; 129 | ZVAL_PSTRING(&author_zval, "owenliang"); 130 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 131 | 132 | // add constant to handle 133 | zval build_date_zval; 134 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 135 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 136 | 137 | // 138 | // Class myext_child (inherit from Class myext) 139 | // 140 | zend_class_entry myext_child_class_def; 141 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 142 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 143 | 144 | // final class, no more child class 145 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 146 | 147 | // register constant 148 | zend_constant c; 149 | c.name = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 1); // persistant memory 150 | ZVAL_STR(&c.value, zend_string_init("https://github.com/owenliang/php7-extension-explore", 151 | sizeof("https://github.com/owenliang/php7-extension-explore"), 1)); // persistant memory 152 | c.flags = CONST_CS | CONST_PERSISTENT; 153 | c.module_number = module_number; 154 | assert(zend_register_constant(&c) == SUCCESS); 155 | 156 | // register resource type 157 | myext_string_resource_id = zend_register_list_destructors_ex(myext_string_resource_dtor, NULL, MYEXT_STRING_RESOURCE_DTOR, module_number); 158 | assert(myext_string_resource_id != FAILURE); 159 | 160 | return SUCCESS; 161 | } 162 | 163 | int extension_shutdown(int type, int module_number) { 164 | TRACE("extension_shutdown"); 165 | return SUCCESS; 166 | } 167 | 168 | int extension_before_request(int type, int module_number) { 169 | TRACE("extension_before_request"); 170 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 171 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 172 | 173 | // try active jit super globals 174 | zend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1); 175 | 176 | // find it in global symbol table 177 | zval *server = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1); 178 | assert(server != NULL); 179 | assert(Z_TYPE_P(server) == IS_ARRAY); 180 | 181 | // var_dump($_SERVER) 182 | zval func_name; 183 | ZVAL_STR(&func_name, zend_string_init("var_dump", sizeof("var_dump") - 1, 0)); 184 | zval retval; 185 | assert(call_user_function(&EG(function_table), NULL, &func_name, &retval, 1, server) == SUCCESS); 186 | zval_ptr_dtor(&func_name); 187 | zval_ptr_dtor(&retval); 188 | return SUCCESS; 189 | } 190 | 191 | int extension_after_request(int type, int module_number) { 192 | TRACE("extension_after_request"); 193 | return SUCCESS; 194 | } 195 | 196 | void extension_info(zend_module_entry *zend_module) { 197 | php_info_print_table_start(); 198 | php_info_print_table_header(2, "myext support", "enabled"); 199 | php_info_print_table_row(2, "author", "owenliang"); 200 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 201 | php_info_print_table_end(); 202 | } 203 | 204 | // Myext's static method 205 | void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { 206 | TRACE("zim_myext_print_author"); 207 | php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); 208 | ZVAL_BOOL(return_value, 1); 209 | } 210 | 211 | 212 | // global function 213 | void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { 214 | TRACE("zif_myext_test_object"); 215 | 216 | // call myext's static method: print_author 217 | zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); 218 | zend_class_entry *myext_handle = zend_lookup_class(myext_classname); 219 | zend_string_release(myext_classname); 220 | assert(myext_handle == myext_class_handle); 221 | 222 | zval retval; 223 | zend_fcall_info fci = { 224 | size: sizeof(zend_fcall_info), 225 | retval: &retval, 226 | params: NULL, 227 | object: NULL, 228 | no_separation: 1, 229 | param_count: 0, 230 | }; 231 | ZVAL_UNDEF(&fci.function_name); 232 | 233 | zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1); 234 | 235 | zend_fcall_info_cache fcic = { 236 | initialized: 1, 237 | function_handler: print_author_func->value.func, 238 | calling_scope: myext_handle, 239 | called_scope: NULL, 240 | object: NULL, 241 | }; 242 | 243 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 244 | assert(Z_TYPE_P(&retval) == IS_TRUE); 245 | 246 | // new a myext object 247 | zval myext_obj; 248 | assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS); 249 | 250 | // call object's __construct 251 | zval ctor_name; 252 | zval ctor_retval; 253 | ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); 254 | assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); 255 | zval_ptr_dtor(&ctor_name); 256 | 257 | // call object's method 258 | zval func_name; 259 | ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); 260 | zval param; 261 | ZVAL_STR(¶m, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); 262 | zval retval2; 263 | assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, ¶m) == SUCCESS); 264 | TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); 265 | zval_ptr_dtor(&func_name); 266 | zval_ptr_dtor(¶m); 267 | zval_ptr_dtor(&retval2); 268 | 269 | // free object 270 | zval_ptr_dtor(&myext_obj); 271 | } 272 | 273 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 274 | TRACE("zim_myext_child_version"); 275 | 276 | zval *this = &(execute_data->This); 277 | 278 | // call parent's version() 279 | zval retval; 280 | zend_fcall_info fci = { 281 | size: sizeof(zend_fcall_info), 282 | retval: &retval, 283 | params: NULL, 284 | object: this->value.obj, 285 | no_separation: 1, 286 | param_count: 0, 287 | }; 288 | ZVAL_UNDEF(&fci.function_name); 289 | 290 | // find parent's version method 291 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 292 | 293 | zend_fcall_info_cache fcic = { 294 | initialized: 1, 295 | function_handler: parent_version_func->value.func, 296 | calling_scope: this->value.obj->ce->parent, 297 | called_scope: this->value.obj->ce, 298 | object: this->value.obj, 299 | }; 300 | 301 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 302 | assert(Z_TYPE_P(&retval) == IS_STRING); 303 | 304 | int len = retval.value.str->len + sizeof(".child") - 1; 305 | char *child_version = emalloc(len); 306 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 307 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 308 | 309 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 310 | efree(child_version); 311 | zval_ptr_dtor(&retval); 312 | } 313 | 314 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 315 | TRACE("zim_myext_strcase_convert"); 316 | 317 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 318 | zval *args = ZEND_CALL_ARG(execute_data, 1); 319 | 320 | TRACE("num_args=%d", num_args); 321 | 322 | zval *zv = &args[0]; 323 | zval *lowercase_zval = &args[1]; 324 | 325 | convert_to_string(zv); 326 | convert_to_boolean(lowercase_zval); 327 | 328 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 329 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 330 | size_t i; 331 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 332 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 333 | dup->val[i] = tolower(dup->val[i]); 334 | } else { 335 | dup->val[i] = toupper(dup->val[i]); 336 | } 337 | } 338 | ZVAL_STR(return_value, dup); 339 | } 340 | 341 | // Equals to PHP_METHOD(myext, strtolwer) 342 | // 343 | // zim_ means Zend Internal Method 344 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 345 | TRACE("zim_myext_constructor"); 346 | 347 | zval *this = &(execute_data->This); 348 | 349 | // class handle of this object 350 | zend_class_entry *class_handle = this->value.obj->ce; 351 | 352 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 353 | 354 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 355 | zval ver_zval; 356 | ZVAL_STR(&ver_zval, new_ver_prop); 357 | 358 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 359 | 360 | zend_string_release(ver_prop_name); 361 | zval_ptr_dtor(&ver_zval); 362 | } 363 | 364 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 365 | TRACE("zim_myext_version"); 366 | 367 | // same as $this 368 | zval *this = &(execute_data->This); 369 | 370 | // class handle of this object 371 | zend_class_entry *class_handle = this->value.obj->ce; 372 | 373 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 374 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 375 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 376 | ZVAL_STR(return_value, dup); 377 | } else { 378 | ZVAL_BOOL(return_value, 0); 379 | } 380 | 381 | // read constant 382 | zend_string *cname = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 0); 383 | zval *c_github = zend_get_constant(cname); 384 | assert(Z_TYPE_P(c_github) == IS_STRING); 385 | TRACE("zend_get_constant(GITHUB)=%.*s", Z_STRLEN_P(c_github), Z_STRVAL_P(c_github)); 386 | zend_string_release(cname); 387 | } 388 | 389 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 390 | TRACE("zim_myext_strtolower"); 391 | 392 | zval *this = &(execute_data->This); 393 | 394 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 395 | zval *args = ZEND_CALL_ARG(execute_data, 1); 396 | 397 | TRACE("num_args=%d", num_args); 398 | 399 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 400 | zval func_name_zval; 401 | ZVAL_STR(&func_name_zval, func_name); 402 | 403 | zval params[2]; 404 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 405 | ZVAL_BOOL(¶ms[1], 1); 406 | 407 | // call method 408 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 409 | zval_ptr_dtor(&func_name_zval); 410 | } 411 | 412 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 413 | TRACE("zim_myext_strtoupper"); 414 | 415 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 416 | zval *args = ZEND_CALL_ARG(execute_data, 1); 417 | 418 | TRACE("num_args=%d", num_args); 419 | 420 | // call global function 421 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 422 | zval func_name_zval; 423 | ZVAL_STR(&func_name_zval, func_name); 424 | 425 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 426 | zval_ptr_dtor(&func_name_zval); 427 | } 428 | 429 | void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value) { 430 | // init arr 431 | zval arr_zval; 432 | assert(array_init(&arr_zval) == SUCCESS); 433 | 434 | // add k-v 435 | add_assoc_long(&arr_zval, "date", 20170811); 436 | assert(zend_hash_str_exists(arr_zval.value.arr, "date", sizeof("date") - 1)); 437 | 438 | // add v 439 | assert(add_next_index_string(&arr_zval, "hahaha") == SUCCESS); 440 | 441 | // arr count 442 | assert(zend_hash_num_elements(arr_zval.value.arr) == 2); 443 | 444 | // traversal arr 445 | zend_array *arr = arr_zval.value.arr; 446 | int i; 447 | for (i = 0; i < arr->nNumUsed; ++i) { 448 | zval *val = &(arr->arData[i].val); 449 | // handle indirect zval 450 | if (Z_TYPE_P(val) == IS_INDIRECT) { 451 | val = Z_INDIRECT_P(val); 452 | } 453 | // empty slots 454 | if (Z_TYPE_P(val) == IS_UNDEF) { 455 | continue; 456 | } 457 | 458 | if (arr->arData[i].key) { // must be array["date"] 459 | TRACE("arr['%.*s']=%ld", arr->arData[i].key->len, arr->arData[i].key->val, val->value.lval); 460 | } else { // must be array[0] 461 | TRACE("arr[%ld]=%.*s", arr->arData[i].h, val->value.str->len, val->value.str->val); 462 | } 463 | } 464 | 465 | // find key 466 | zval *zv_in_arr = zend_hash_str_find_ind(arr_zval.value.arr, "date", sizeof("date") - 1); 467 | assert(zv_in_arr->value.lval == 20170811); 468 | 469 | // del string key 470 | assert(zend_hash_str_del(arr_zval.value.arr, "date", sizeof("date") - 1) == SUCCESS); 471 | 472 | // del index key 473 | assert(zend_hash_index_del(arr_zval.value.arr, 0) == SUCCESS); 474 | 475 | // release arr 476 | zval_ptr_dtor(&arr_zval); 477 | } 478 | 479 | void zif_myext_test_resource(zend_execute_data *execute_data, zval *return_value) { 480 | TRACE("zif_myext_test_resource"); 481 | char *string = strdup("i am a string resource"); 482 | zend_resource *res = zend_register_resource(string, myext_string_resource_id); 483 | assert(GC_REFCOUNT(res) == 1); 484 | 485 | // wrappped with zval, refcount=2 486 | zval res_zval; 487 | ZVAL_RES(&res_zval, res); 488 | zval_addref_p(&res_zval); 489 | assert(GC_REFCOUNT(res) == 2); 490 | 491 | // release resource directly, left refcount=1 492 | zend_list_delete(res); 493 | assert(GC_REFCOUNT(res) == 1); 494 | 495 | // validate and get resource ptr 496 | char *s = zend_fetch_resource_ex(&res_zval, MYEXT_STRING_RESOURCE_DTOR, myext_string_resource_id); 497 | assert(strcmp(s, "i am a string resource") == 0); 498 | 499 | // release resource through zval, left refcount=0, zend_list_free is called 500 | zval_ptr_dtor(&res_zval); 501 | } 502 | 503 | void zif_myext_test_include(zend_execute_data *execute_data, zval *return_value) { 504 | TRACE("zif_myext_test_include"); 505 | 506 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 507 | zval *args = ZEND_CALL_ARG(execute_data, 1); 508 | assert(num_args == 1); 509 | 510 | // relative path to absolute full path 511 | char realpath[MAXPATHLEN]; 512 | if (!virtual_realpath(Z_STRVAL(args[0]), realpath)) { 513 | ZVAL_BOOL(return_value, 0); 514 | return; 515 | } 516 | 517 | TRACE("realpath=%s", realpath); 518 | 519 | zend_string *filename = zend_string_init(realpath, strlen(realpath), 0); 520 | 521 | // already loaded before 522 | zval *existed = zend_hash_find(&EG(included_files), filename); 523 | if (existed) { 524 | zend_string_release(filename); 525 | ZVAL_BOOL(return_value, 0); 526 | return; 527 | } 528 | 529 | // not opened file handle 530 | zend_file_handle file_handle = { 531 | filename: filename->val, 532 | free_filename: 0, 533 | type: ZEND_HANDLE_FILENAME, 534 | opened_path: NULL, 535 | handle: {fp: NULL}, 536 | }; 537 | 538 | // compile file into opcode 539 | zend_op_array *op_array = zend_compile_file(&file_handle, ZEND_INCLUDE); 540 | assert(op_array); 541 | 542 | // close file handle 543 | zend_destroy_file_handle(&file_handle); 544 | 545 | // mark file is included 546 | zval empty_zv; 547 | ZVAL_NULL(&empty_zv); 548 | assert(zend_hash_add_new(&EG(included_files), filename, &empty_zv)); // 1 ref added inner 549 | zend_string_release(filename); 550 | 551 | // execute opcode 552 | zval result; 553 | ZVAL_UNDEF(&result); 554 | zend_execute(op_array, &result); 555 | 556 | // free opcode 557 | destroy_op_array(op_array); 558 | efree(op_array); 559 | 560 | // array included from php file 561 | assert(Z_TYPE(result) == IS_ARRAY); 562 | *return_value = result; 563 | } 564 | 565 | void zif_myext_test_exception(zend_execute_data *execute_data, zval *return_value) { 566 | TRACE("zif_myext_test_exception"); 567 | 568 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 569 | zval *args = ZEND_CALL_ARG(execute_data, 1); 570 | assert(num_args == 1 && Z_TYPE_P(args) == IS_LONG); 571 | 572 | // throw self-defined exception 573 | if (Z_LVAL_P(args) == 1) { 574 | // inherit from base exception 575 | zend_function_entry nofuncs[] = { 576 | // fname,handler,arg_info,,num_args,flags 577 | {NULL, NULL, NULL, 0, 0}, 578 | }; 579 | zend_class_entry my_exception_def; 580 | INIT_CLASS_ENTRY_EX(my_exception_def, "MyException", sizeof("MyException") - 1, nofuncs); 581 | zend_class_entry *my_exception_handle = zend_register_internal_class_ex(&my_exception_def, zend_ce_exception); 582 | assert(my_exception_handle); 583 | // throw myexception 584 | zend_throw_exception(my_exception_handle, "I AM MY EXCEPTION", 10086); 585 | } else { 586 | // throw base exception directly 587 | zend_throw_exception(NULL, "I AM DEFAULT EXCEPTION", 10087); 588 | } 589 | } 590 | 591 | void zif_myext_test_error(zend_execute_data *execute_data, zval *return_value) { 592 | TRACE("zif_myext_test_error"); 593 | 594 | php_error_docref(NULL, E_WARNING, "I AM %s", "WARNING"); 595 | 596 | php_error_docref2(NULL, "string $parmas1", "integer $params2", E_NOTICE, "I AM %s", "JUST A NOTICE"); 597 | 598 | // fatal error will stop executing script 599 | php_error_docref1(NULL, "string $params1", E_ERROR, "I AM %s", "FATAL ERROR"); 600 | } 601 | 602 | zend_module_entry module = { 603 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 604 | NULL, // ini_entry 605 | NULL, // deps 606 | "myext", //name 607 | global_funcs, // functions 608 | extension_startup, // module_startup_func 609 | extension_shutdown, // module_shutdown_func 610 | extension_before_request, // request_startup_func 611 | extension_after_request, // request_shutdown_func 612 | extension_info, // info_func 613 | "1.0", // version 614 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 615 | // handle,module_number,build_id 616 | STANDARD_MODULE_PROPERTIES, 617 | }; 618 | 619 | zend_module_dep deps[] = { 620 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 621 | { NULL, NULL, NULL, 0}, 622 | }; 623 | 624 | ZEND_DLEXPORT zend_module_entry *get_module() { 625 | module.deps = deps; 626 | 627 | return &module; 628 | } 629 | -------------------------------------------------------------------------------- /course12-how-to-throw-error-and-exception/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "zend_exceptions.h" 12 | #include "ext/standard/info.h" 13 | 14 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 15 | 16 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 17 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 18 | va_list args; 19 | va_start(args, fmt); 20 | vfprintf(stderr, fmt, args); 21 | fprintf(stderr, "\n"); 22 | va_end(args); 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /course12-how-to-throw-error-and-exception/test.php: -------------------------------------------------------------------------------- 1 | 5 | #include 6 | 7 | #include "php.h" 8 | #include "ext/standard/info.h" 9 | 10 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 11 | 12 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 13 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 14 | va_list args; 15 | va_start(args, fmt); 16 | vfprintf(stderr, fmt, args); 17 | fprintf(stderr, "\n"); 18 | va_end(args); 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /course2-how-to-define-deps-and-ini/test.php: -------------------------------------------------------------------------------- 1 | value.str; // Z_STR_P(zv) 53 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 54 | size_t i; 55 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 56 | if (lowercase) { 57 | dup->val[i] = tolower(dup->val[i]); 58 | } else { 59 | dup->val[i] = toupper(dup->val[i]); 60 | } 61 | } 62 | ZVAL_STR(&retval, dup); 63 | } else { 64 | ZVAL_BOOL(&retval, 0); 65 | } 66 | return retval; 67 | } 68 | 69 | // Equals to PHP_FUNCTION(strtolwer) 70 | // 71 | // zif_ means Zend Internal Function 72 | void zif_strtolower(zend_execute_data *execute_data, zval *return_value) { 73 | TRACE("zif_strtolower"); 74 | 75 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 76 | zval *args = ZEND_CALL_ARG(execute_data, 1); 77 | 78 | TRACE("num_args=%d", num_args); 79 | *return_value = strcase_convert(&args[0], 1); 80 | } 81 | 82 | void zif_strtoupper(zend_execute_data *execute_data, zval *return_value) { 83 | TRACE("zif_strtoupper"); 84 | 85 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 86 | zval *args = ZEND_CALL_ARG(execute_data, 1); 87 | 88 | TRACE("num_args=%d", num_args); 89 | *return_value = strcase_convert(&args[0], 0); 90 | } 91 | 92 | // zif_strtolower's params defination 93 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 94 | // required_num_args(interger stored in pointer) 95 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 96 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 97 | {"string", NULL, IS_STRING, 0, 0, 0}, 98 | }; 99 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 100 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 101 | {"string", NULL, IS_STRING, 0, 0, 0}, 102 | }; 103 | 104 | zend_function_entry funcs[] = { 105 | // fname,handler,arg_info,,num_args,flags 106 | {"my_strtolower", zif_strtolower, myext_strtolwer_arginfo, 1, 0}, 107 | {"my_strtoupper", zif_strtoupper, myext_strtoupper_arginfo, 1, 0}, 108 | {NULL, NULL, NULL, 0, 0}, 109 | }; 110 | 111 | zend_module_entry module = { 112 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 113 | NULL, // ini_entry 114 | NULL, // deps 115 | "myext", //name 116 | funcs, // functions 117 | extension_startup, // module_startup_func 118 | extension_shutdown, // module_shutdown_func 119 | extension_before_request, // request_startup_func 120 | extension_after_request, // request_shutdown_func 121 | extension_info, // info_func 122 | "1.0", // version 123 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 124 | // handle,module_number,build_id 125 | STANDARD_MODULE_PROPERTIES, 126 | }; 127 | 128 | zend_module_dep deps[] = { 129 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 130 | { NULL, NULL, NULL, 0}, 131 | }; 132 | 133 | ZEND_DLEXPORT zend_module_entry *get_module() { 134 | module.deps = deps; 135 | return &module; 136 | } 137 | -------------------------------------------------------------------------------- /course3-how-to-define-functions/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "php.h" 9 | #include "ext/standard/info.h" 10 | 11 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 12 | 13 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 14 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 15 | va_list args; 16 | va_start(args, fmt); 17 | vfprintf(stderr, fmt, args); 18 | fprintf(stderr, "\n"); 19 | va_end(args); 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /course3-how-to-define-functions/test.php: -------------------------------------------------------------------------------- 1 | value.str; // Z_STR_P(zv) 53 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 54 | size_t i; 55 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 56 | if (lowercase) { 57 | dup->val[i] = tolower(dup->val[i]); 58 | } else { 59 | dup->val[i] = toupper(dup->val[i]); 60 | } 61 | } 62 | ZVAL_STR(&retval, dup); 63 | } else { 64 | ZVAL_BOOL(&retval, 0); 65 | } 66 | return retval; 67 | } 68 | 69 | // Equals to PHP_FUNCTION(strtolwer) 70 | // 71 | // zif_ means Zend Internal Function 72 | void zif_strtolower(zend_execute_data *execute_data, zval *return_value) { 73 | TRACE("zif_strtolower"); 74 | 75 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 76 | zval *args = ZEND_CALL_ARG(execute_data, 1); 77 | 78 | TRACE("num_args=%d", num_args); 79 | *return_value = strcase_convert(&args[0], 1); 80 | } 81 | 82 | void zif_strtoupper(zend_execute_data *execute_data, zval *return_value) { 83 | TRACE("zif_strtoupper"); 84 | 85 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 86 | zval *args = ZEND_CALL_ARG(execute_data, 1); 87 | 88 | TRACE("num_args=%d", num_args); 89 | *return_value = strcase_convert(&args[0], 0); 90 | } 91 | 92 | void zif_testzval(zend_execute_data *execute_data, zval *return_value) { 93 | TRACE("zif_testzval"); 94 | 95 | // undef 96 | zval undef_zval; 97 | ZVAL_UNDEF(&undef_zval); 98 | assert(Z_TYPE(undef_zval) == IS_UNDEF); 99 | 100 | // null 101 | zval null_zval; 102 | ZVAL_NULL(&null_zval); 103 | assert(Z_TYPE(null_zval) == IS_NULL); 104 | 105 | // bool 106 | zval bool_zval; 107 | ZVAL_BOOL(&bool_zval, 1); 108 | assert(Z_TYPE(bool_zval) == IS_TRUE); 109 | 110 | // long 111 | zval long_zval; 112 | ZVAL_LONG(&long_zval, 1024); 113 | assert(Z_TYPE(long_zval) == IS_LONG); 114 | assert(Z_LVAL_P(&long_zval) == 1024); 115 | 116 | // string 117 | zval str_zval; 118 | zend_string *str = zend_string_init("IS_STRING", sizeof("IS_STRING") - 1, 0); 119 | ZVAL_STR(&str_zval, str); 120 | assert(Z_TYPE(str_zval) == IS_STRING); 121 | assert(strncmp(Z_STRVAL_P(&str_zval), "IS_STRING", Z_STRLEN_P(&str_zval)) == 0); 122 | zval_addref_p(&str_zval); // add 1 ref for arr_zval below 123 | zval_addref_p(&str_zval); // add 1 ref for ref_zval below 124 | zval_ptr_dtor(&str_zval); 125 | 126 | // array 127 | zval arr_zval; 128 | zend_array *arr = emalloc(sizeof(*arr)); 129 | zend_hash_init(arr, 0, NULL, ZVAL_PTR_DTOR, 0); 130 | 131 | ZVAL_ARR(&arr_zval, arr); 132 | assert(Z_TYPE(arr_zval) == IS_ARRAY); 133 | assert((void*)arr_zval.value.counted == (void*)&(arr_zval.value.arr->gc)); 134 | zend_symtable_str_update(arr, "str_zval", sizeof("str_zval") - 1, &str_zval); 135 | zval *zval_in_arr = zend_symtable_str_find(arr, "str_zval", sizeof("str_zval") - 1); 136 | assert(zval_in_arr); 137 | assert(Z_TYPE_P(zval_in_arr) == IS_STRING); 138 | assert(GC_REFCOUNT(Z_COUNTED_P(zval_in_arr)) == 2); 139 | uint32_t num_elems = zend_hash_num_elements(Z_ARRVAL_P(&arr_zval)); 140 | assert(num_elems == 1); 141 | zval_ptr_dtor(&arr_zval); 142 | 143 | // reference 144 | zval ref_zval; 145 | zend_reference *ref = emalloc(sizeof(*ref)); 146 | GC_REFCOUNT(ref) = 1; 147 | GC_TYPE_INFO(ref) = IS_REFERENCE; 148 | memcpy(&(ref->val), &str_zval, sizeof(str_zval)); 149 | ZVAL_REF(&ref_zval, ref); 150 | assert(Z_TYPE(ref_zval) == IS_REFERENCE); 151 | zval_addref_p(&ref_zval); // add 1 ref for ref_ref_zval 152 | zval_ptr_dtor(&ref_zval); 153 | 154 | // zval ref to reference 155 | zval ref_ref_zval; 156 | memcpy(&ref_ref_zval, &ref_zval, sizeof(ref_zval)); 157 | zval *real_zval = Z_REFVAL_P(&ref_ref_zval); 158 | zend_string *real_str = Z_STR_P(real_zval); 159 | assert(real_str == str); 160 | zend_string_release(real_str); 161 | ZVAL_STR(real_zval, zend_string_init("IS_STRING_TOO", sizeof("IS_STRING_TOO") - 1, 0)); 162 | zval_ptr_dtor(&ref_ref_zval); 163 | 164 | // tips: 165 | // zval_addref_p/zval_delref_p can only be applied to zval with ref count 166 | zval double_zval; 167 | ZVAL_DOUBLE(&double_zval, 13.14); 168 | assert(!Z_REFCOUNTED_P(&double_zval)); 169 | // the following line will trigger assert-coredump 170 | // zval_delref_p(&double_zval); 171 | 172 | // zval copy 173 | zval copy_from; 174 | ZVAL_STR(©_from, zend_string_init("test ZVAL_COPY", sizeof("test ZVAL_COPY") - 1, 0)); 175 | 176 | zval copy_to1; 177 | ZVAL_COPY(©_to1, ©_from); 178 | 179 | // same as ZVAL_COPY above 180 | zval copy_to2; 181 | memcpy(©_to2, ©_from, sizeof(copy_from)); // same as ZVAL_COPY_VALUE 182 | if (Z_REFCOUNTED_P(©_from)) { 183 | zval_addref_p(©_to2); 184 | } 185 | 186 | // release all reference count 187 | zval_ptr_dtor(©_from); 188 | zval_ptr_dtor(©_from); 189 | zval_ptr_dtor(©_from); 190 | } 191 | 192 | // zif_strtolower's params defination 193 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 194 | // required_num_args(interger stored in pointer) 195 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 196 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 197 | {"string", NULL, IS_STRING, 0, 0, 0}, 198 | }; 199 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 200 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 201 | {"string", NULL, IS_STRING, 0, 0, 0}, 202 | }; 203 | 204 | zend_function_entry funcs[] = { 205 | // fname,handler,arg_info,,num_args,flags 206 | {"my_strtolower", zif_strtolower, myext_strtolwer_arginfo, 1, 0}, 207 | {"my_strtoupper", zif_strtoupper, myext_strtoupper_arginfo, 1, 0}, 208 | {"my_testzval", zif_testzval, NULL, 0, 0}, 209 | {NULL, NULL, NULL, 0, 0}, 210 | }; 211 | 212 | zend_module_entry module = { 213 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 214 | NULL, // ini_entry 215 | NULL, // deps 216 | "myext", //name 217 | funcs, // functions 218 | extension_startup, // module_startup_func 219 | extension_shutdown, // module_shutdown_func 220 | extension_before_request, // request_startup_func 221 | extension_after_request, // request_shutdown_func 222 | extension_info, // info_func 223 | "1.0", // version 224 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 225 | // handle,module_number,build_id 226 | STANDARD_MODULE_PROPERTIES, 227 | }; 228 | 229 | zend_module_dep deps[] = { 230 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 231 | { NULL, NULL, NULL, 0}, 232 | }; 233 | 234 | ZEND_DLEXPORT zend_module_entry *get_module() { 235 | module.deps = deps; 236 | return &module; 237 | } 238 | -------------------------------------------------------------------------------- /course4-how-the-zval-works/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course4-how-the-zval-works/test.php: -------------------------------------------------------------------------------- 1 | ce_flags |= ZEND_ACC_FINAL; 105 | 106 | return SUCCESS; 107 | } 108 | 109 | int extension_shutdown(int type, int module_number) { 110 | TRACE("extension_shutdown"); 111 | return SUCCESS; 112 | } 113 | 114 | int extension_before_request(int type, int module_number) { 115 | TRACE("extension_before_request"); 116 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 117 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 118 | return SUCCESS; 119 | } 120 | 121 | int extension_after_request(int type, int module_number) { 122 | TRACE("extension_after_request"); 123 | return SUCCESS; 124 | } 125 | 126 | void extension_info(zend_module_entry *zend_module) { 127 | php_info_print_table_start(); 128 | php_info_print_table_header(2, "myext support", "enabled"); 129 | php_info_print_table_row(2, "author", "owenliang"); 130 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 131 | php_info_print_table_end(); 132 | } 133 | 134 | zval strcase_convert(const zval *zv, int lowercase) { 135 | zval retval; 136 | 137 | zend_uchar zv_type = zval_get_type(zv); // equals to Z_TYPE_P(zv) 138 | if (zv_type == IS_STRING) { 139 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 140 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 141 | size_t i; 142 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 143 | if (lowercase) { 144 | dup->val[i] = tolower(dup->val[i]); 145 | } else { 146 | dup->val[i] = toupper(dup->val[i]); 147 | } 148 | } 149 | ZVAL_STR(&retval, dup); 150 | } else { 151 | ZVAL_BOOL(&retval, 0); 152 | } 153 | return retval; 154 | } 155 | 156 | // Equals to PHP_METHOD(myext, strtolwer) 157 | // 158 | // zim_ means Zend Internal Method 159 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 160 | TRACE("zim_myext_constructor"); 161 | 162 | zval *this = &(execute_data->This); 163 | 164 | // class handle of this object 165 | zend_class_entry *class_handle = this->value.obj->ce; 166 | 167 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 168 | 169 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 170 | zval ver_zval; 171 | ZVAL_STR(&ver_zval, new_ver_prop); 172 | 173 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 174 | 175 | zend_string_release(ver_prop_name); 176 | zval_ptr_dtor(&ver_zval); 177 | } 178 | 179 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 180 | TRACE("zim_myext_version"); 181 | 182 | // same as $this 183 | zval *this = &(execute_data->This); 184 | 185 | // class handle of this object 186 | zend_class_entry *class_handle = this->value.obj->ce; 187 | 188 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 189 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 190 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 191 | ZVAL_STR(return_value, dup); 192 | } else { 193 | ZVAL_BOOL(return_value, 0); 194 | } 195 | } 196 | 197 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 198 | TRACE("zim_myext_strtolower"); 199 | 200 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 201 | zval *args = ZEND_CALL_ARG(execute_data, 1); 202 | 203 | TRACE("num_args=%d", num_args); 204 | *return_value = strcase_convert(&args[0], 1); 205 | } 206 | 207 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 208 | TRACE("zim_myext_strtoupper"); 209 | 210 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 211 | zval *args = ZEND_CALL_ARG(execute_data, 1); 212 | 213 | TRACE("num_args=%d", num_args); 214 | *return_value = strcase_convert(&args[0], 0); 215 | } 216 | 217 | zend_module_entry module = { 218 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 219 | NULL, // ini_entry 220 | NULL, // deps 221 | "myext", //name 222 | NULL, // functions 223 | extension_startup, // module_startup_func 224 | extension_shutdown, // module_shutdown_func 225 | extension_before_request, // request_startup_func 226 | extension_after_request, // request_shutdown_func 227 | extension_info, // info_func 228 | "1.0", // version 229 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 230 | // handle,module_number,build_id 231 | STANDARD_MODULE_PROPERTIES, 232 | }; 233 | 234 | zend_module_dep deps[] = { 235 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 236 | { NULL, NULL, NULL, 0}, 237 | }; 238 | 239 | ZEND_DLEXPORT zend_module_entry *get_module() { 240 | module.deps = deps; 241 | 242 | return &module; 243 | } 244 | -------------------------------------------------------------------------------- /course5-how-to-define-class/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course5-how-to-define-class/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; -------------------------------------------------------------------------------- /course6-how-to-call-php-functions/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -gdwarf-2 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course6-how-to-call-php-functions/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | #define MYEXT_INI_NAME_GITHUB "myext.github" 4 | #define MYEXT_INI_VALUE_GITHUB "https://github.com/owenliang/php7-extension-explore" 5 | 6 | // overwrite in php.ini like this: 7 | // 8 | // myext.github = "some value" 9 | // 10 | zend_ini_entry_def ini_defs[] = { 11 | // name,on_modify,mh_arg1,mh_arg2,mh_arg3,value,displayer,modifiable,name_length,value_length 12 | {MYEXT_INI_NAME_GITHUB, NULL, NULL, NULL, NULL, MYEXT_INI_VALUE_GITHUB, NULL, ZEND_INI_ALL, sizeof(MYEXT_INI_NAME_GITHUB) - 1, sizeof(MYEXT_INI_VALUE_GITHUB) - 1}, 13 | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, 14 | }; 15 | 16 | extern void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value); 17 | extern void zim_myext_version(zend_execute_data *execute_data, zval *return_value); 18 | extern void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value); 19 | extern void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value); 20 | extern void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value); 21 | extern void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value); 22 | 23 | // zif_strtolower's params defination 24 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 25 | // required_num_args(interger stored in pointer) 26 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 27 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 28 | {"string", NULL, IS_STRING, 0, 0, 0}, 29 | }; 30 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 31 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 32 | {"string", NULL, IS_STRING, 0, 0, 0}, 33 | }; 34 | 35 | zend_function_entry funcs[] = { 36 | // fname,handler,arg_info,,num_args,flags 37 | {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, 38 | {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, 39 | {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, 40 | {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, 41 | {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, 42 | {NULL, NULL, NULL, 0, 0}, 43 | }; 44 | 45 | zend_function_entry interface_funcs[] = { 46 | // fname,handler,arg_info,,num_args,flags 47 | {"version", NULL, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT}, 48 | {NULL, NULL, NULL, 0, 0}, 49 | }; 50 | 51 | zend_function_entry final_funcs[] = { 52 | // fname,handler,arg_info,,num_args,flags 53 | {"version", zim_myext_child_version, NULL, 0, ZEND_ACC_PUBLIC}, 54 | {NULL, NULL, NULL, 0, 0}, 55 | }; 56 | 57 | zend_class_entry *myext_interface_handle = NULL; // interface handle 58 | zend_class_entry *myext_class_handle = NULL; // base class handle 59 | zend_class_entry *myext_child_class_handle = NULL; // child class handle 60 | 61 | int extension_startup(int type, int module_number) { 62 | TRACE("extension_startup"); 63 | zend_register_ini_entries(ini_defs, module_number); 64 | 65 | // 66 | // Interface myext_interface 67 | // 68 | 69 | // interface defination 70 | zend_class_entry myext_interface_def; 71 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 72 | 73 | // get interface handle 74 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 75 | 76 | // 77 | // Class myext 78 | // 79 | 80 | // class defination 81 | zend_class_entry myext_class_def; 82 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 83 | 84 | // get class handle 85 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 86 | 87 | // implements interface 88 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 89 | 90 | // add property to handle 91 | zval version_zval; 92 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 93 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 94 | 95 | // add static property to handle 96 | zval author_zval; 97 | ZVAL_PSTRING(&author_zval, "owenliang"); 98 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 99 | 100 | // add constant to handle 101 | zval build_date_zval; 102 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 103 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 104 | 105 | // 106 | // Class myext_child (inherit from Class myext) 107 | // 108 | zend_class_entry myext_child_class_def; 109 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 110 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 111 | 112 | // final class, no more child class 113 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 114 | 115 | return SUCCESS; 116 | } 117 | 118 | int extension_shutdown(int type, int module_number) { 119 | TRACE("extension_shutdown"); 120 | return SUCCESS; 121 | } 122 | 123 | int extension_before_request(int type, int module_number) { 124 | TRACE("extension_before_request"); 125 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 126 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 127 | return SUCCESS; 128 | } 129 | 130 | int extension_after_request(int type, int module_number) { 131 | TRACE("extension_after_request"); 132 | return SUCCESS; 133 | } 134 | 135 | void extension_info(zend_module_entry *zend_module) { 136 | php_info_print_table_start(); 137 | php_info_print_table_header(2, "myext support", "enabled"); 138 | php_info_print_table_row(2, "author", "owenliang"); 139 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 140 | php_info_print_table_end(); 141 | } 142 | 143 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 144 | TRACE("zim_myext_child_version"); 145 | 146 | zval *this = &(execute_data->This); 147 | 148 | // call parent's version() 149 | zval retval; 150 | zend_fcall_info fci = { 151 | size: sizeof(zend_fcall_info), 152 | retval: &retval, 153 | params: NULL, 154 | object: this->value.obj, 155 | no_separation: 1, 156 | param_count: 0, 157 | }; 158 | ZVAL_UNDEF(&fci.function_name); 159 | 160 | // find parent's version method 161 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 162 | 163 | zend_fcall_info_cache fcic = { 164 | initialized: 1, 165 | function_handler: parent_version_func->value.func, 166 | calling_scope: this->value.obj->ce->parent, 167 | called_scope: this->value.obj->ce, 168 | object: this->value.obj, 169 | }; 170 | 171 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 172 | assert(Z_TYPE_P(&retval) == IS_STRING); 173 | 174 | int len = retval.value.str->len + sizeof(".child") - 1; 175 | char *child_version = emalloc(len); 176 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 177 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 178 | 179 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 180 | efree(child_version); 181 | zval_ptr_dtor(&retval); 182 | } 183 | 184 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 185 | TRACE("zim_myext_strcase_convert"); 186 | 187 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 188 | zval *args = ZEND_CALL_ARG(execute_data, 1); 189 | 190 | TRACE("num_args=%d", num_args); 191 | 192 | zval *zv = &args[0]; 193 | zval *lowercase_zval = &args[1]; 194 | 195 | convert_to_string(zv); 196 | convert_to_boolean(lowercase_zval); 197 | 198 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 199 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 200 | size_t i; 201 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 202 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 203 | dup->val[i] = tolower(dup->val[i]); 204 | } else { 205 | dup->val[i] = toupper(dup->val[i]); 206 | } 207 | } 208 | ZVAL_STR(return_value, dup); 209 | } 210 | 211 | // Equals to PHP_METHOD(myext, strtolwer) 212 | // 213 | // zim_ means Zend Internal Method 214 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 215 | TRACE("zim_myext_constructor"); 216 | 217 | zval *this = &(execute_data->This); 218 | 219 | // class handle of this object 220 | zend_class_entry *class_handle = this->value.obj->ce; 221 | 222 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 223 | 224 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 225 | zval ver_zval; 226 | ZVAL_STR(&ver_zval, new_ver_prop); 227 | 228 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 229 | 230 | zend_string_release(ver_prop_name); 231 | zval_ptr_dtor(&ver_zval); 232 | } 233 | 234 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 235 | TRACE("zim_myext_version"); 236 | 237 | // same as $this 238 | zval *this = &(execute_data->This); 239 | 240 | // class handle of this object 241 | zend_class_entry *class_handle = this->value.obj->ce; 242 | 243 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 244 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 245 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 246 | ZVAL_STR(return_value, dup); 247 | } else { 248 | ZVAL_BOOL(return_value, 0); 249 | } 250 | } 251 | 252 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 253 | TRACE("zim_myext_strtolower"); 254 | 255 | zval *this = &(execute_data->This); 256 | 257 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 258 | zval *args = ZEND_CALL_ARG(execute_data, 1); 259 | 260 | TRACE("num_args=%d", num_args); 261 | 262 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 263 | zval func_name_zval; 264 | ZVAL_STR(&func_name_zval, func_name); 265 | 266 | zval params[2]; 267 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 268 | ZVAL_BOOL(¶ms[1], 1); 269 | 270 | // call method 271 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 272 | zval_ptr_dtor(&func_name_zval); 273 | } 274 | 275 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 276 | TRACE("zim_myext_strtoupper"); 277 | 278 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 279 | zval *args = ZEND_CALL_ARG(execute_data, 1); 280 | 281 | TRACE("num_args=%d", num_args); 282 | 283 | // call global function 284 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 285 | zval func_name_zval; 286 | ZVAL_STR(&func_name_zval, func_name); 287 | 288 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 289 | zval_ptr_dtor(&func_name_zval); 290 | } 291 | 292 | zend_module_entry module = { 293 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 294 | NULL, // ini_entry 295 | NULL, // deps 296 | "myext", //name 297 | NULL, // functions 298 | extension_startup, // module_startup_func 299 | extension_shutdown, // module_shutdown_func 300 | extension_before_request, // request_startup_func 301 | extension_after_request, // request_shutdown_func 302 | extension_info, // info_func 303 | "1.0", // version 304 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 305 | // handle,module_number,build_id 306 | STANDARD_MODULE_PROPERTIES, 307 | }; 308 | 309 | zend_module_dep deps[] = { 310 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 311 | { NULL, NULL, NULL, 0}, 312 | }; 313 | 314 | ZEND_DLEXPORT zend_module_entry *get_module() { 315 | module.deps = deps; 316 | 317 | return &module; 318 | } 319 | -------------------------------------------------------------------------------- /course6-how-to-call-php-functions/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course6-how-to-call-php-functions/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; -------------------------------------------------------------------------------- /course7-how-to-create-object/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -gdwarf-2 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course7-how-to-create-object/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | #define MYEXT_INI_NAME_GITHUB "myext.github" 4 | #define MYEXT_INI_VALUE_GITHUB "https://github.com/owenliang/php7-extension-explore" 5 | 6 | // overwrite in php.ini like this: 7 | // 8 | // myext.github = "some value" 9 | // 10 | zend_ini_entry_def ini_defs[] = { 11 | // name,on_modify,mh_arg1,mh_arg2,mh_arg3,value,displayer,modifiable,name_length,value_length 12 | {MYEXT_INI_NAME_GITHUB, NULL, NULL, NULL, NULL, MYEXT_INI_VALUE_GITHUB, NULL, ZEND_INI_ALL, sizeof(MYEXT_INI_NAME_GITHUB) - 1, sizeof(MYEXT_INI_VALUE_GITHUB) - 1}, 13 | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, 14 | }; 15 | 16 | extern void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value); 17 | extern void zim_myext_version(zend_execute_data *execute_data, zval *return_value); 18 | extern void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value); 19 | extern void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value); 20 | extern void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value); 21 | extern void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value); 22 | extern void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value); 23 | 24 | extern void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value); 25 | 26 | // zif_strtolower's params defination 27 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 28 | // required_num_args(interger stored in pointer) 29 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 30 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 31 | {"string", NULL, IS_STRING, 0, 0, 0}, 32 | }; 33 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 34 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 35 | {"string", NULL, IS_STRING, 0, 0, 0}, 36 | }; 37 | 38 | zend_function_entry funcs[] = { 39 | // fname,handler,arg_info,,num_args,flags 40 | {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, 41 | {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, 42 | {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, 43 | {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, 44 | {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, 45 | {"print_author", zim_myext_print_author, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC}, 46 | {NULL, NULL, NULL, 0, 0}, 47 | }; 48 | 49 | zend_function_entry interface_funcs[] = { 50 | // fname,handler,arg_info,,num_args,flags 51 | {"version", NULL, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT}, 52 | {NULL, NULL, NULL, 0, 0}, 53 | }; 54 | 55 | zend_function_entry final_funcs[] = { 56 | // fname,handler,arg_info,,num_args,flags 57 | {"version", zim_myext_child_version, NULL, 0, ZEND_ACC_PUBLIC}, 58 | {NULL, NULL, NULL, 0, 0}, 59 | }; 60 | 61 | zend_function_entry global_funcs[] = { 62 | // fname,handler,arg_info,,num_args,flags 63 | {"test_object", zif_myext_test_object, NULL, 0, 0}, 64 | {NULL, NULL, NULL, 0, 0}, 65 | }; 66 | 67 | zend_class_entry *myext_interface_handle = NULL; // interface handle 68 | zend_class_entry *myext_class_handle = NULL; // base class handle 69 | zend_class_entry *myext_child_class_handle = NULL; // child class handle 70 | 71 | int extension_startup(int type, int module_number) { 72 | TRACE("extension_startup"); 73 | zend_register_ini_entries(ini_defs, module_number); 74 | 75 | // 76 | // Interface myext_interface 77 | // 78 | 79 | // interface defination 80 | zend_class_entry myext_interface_def; 81 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 82 | 83 | // get interface handle 84 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 85 | 86 | // 87 | // Class myext 88 | // 89 | 90 | // class defination 91 | zend_class_entry myext_class_def; 92 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 93 | 94 | // get class handle 95 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 96 | 97 | // implements interface 98 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 99 | 100 | // add property to handle 101 | zval version_zval; 102 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 103 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 104 | 105 | // add static property to handle 106 | zval author_zval; 107 | ZVAL_PSTRING(&author_zval, "owenliang"); 108 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 109 | 110 | // add constant to handle 111 | zval build_date_zval; 112 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 113 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 114 | 115 | // 116 | // Class myext_child (inherit from Class myext) 117 | // 118 | zend_class_entry myext_child_class_def; 119 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 120 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 121 | 122 | // final class, no more child class 123 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 124 | 125 | return SUCCESS; 126 | } 127 | 128 | int extension_shutdown(int type, int module_number) { 129 | TRACE("extension_shutdown"); 130 | return SUCCESS; 131 | } 132 | 133 | int extension_before_request(int type, int module_number) { 134 | TRACE("extension_before_request"); 135 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 136 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 137 | return SUCCESS; 138 | } 139 | 140 | int extension_after_request(int type, int module_number) { 141 | TRACE("extension_after_request"); 142 | return SUCCESS; 143 | } 144 | 145 | void extension_info(zend_module_entry *zend_module) { 146 | php_info_print_table_start(); 147 | php_info_print_table_header(2, "myext support", "enabled"); 148 | php_info_print_table_row(2, "author", "owenliang"); 149 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 150 | php_info_print_table_end(); 151 | } 152 | 153 | // Myext's static method 154 | void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { 155 | TRACE("zim_myext_print_author"); 156 | php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); 157 | ZVAL_BOOL(return_value, 1); 158 | } 159 | 160 | 161 | // global function 162 | void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { 163 | TRACE("zif_myext_test_object"); 164 | 165 | // call myext's static method: print_author 166 | zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); 167 | zend_class_entry *myext_handle = zend_lookup_class(myext_classname); 168 | zend_string_release(myext_classname); 169 | assert(myext_handle == myext_class_handle); 170 | 171 | zval retval; 172 | zend_fcall_info fci = { 173 | size: sizeof(zend_fcall_info), 174 | retval: &retval, 175 | params: NULL, 176 | object: NULL, 177 | no_separation: 1, 178 | param_count: 0, 179 | }; 180 | ZVAL_UNDEF(&fci.function_name); 181 | 182 | zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1); 183 | 184 | zend_fcall_info_cache fcic = { 185 | initialized: 1, 186 | function_handler: print_author_func->value.func, 187 | calling_scope: myext_handle, 188 | called_scope: NULL, 189 | object: NULL, 190 | }; 191 | 192 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 193 | assert(Z_TYPE_P(&retval) == IS_TRUE); 194 | 195 | // new a myext object 196 | zval myext_obj; 197 | assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS); 198 | 199 | // call object's __construct 200 | zval ctor_name; 201 | zval ctor_retval; 202 | ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); 203 | assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); 204 | zval_ptr_dtor(&ctor_name); 205 | 206 | // call object's method 207 | zval func_name; 208 | ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); 209 | zval param; 210 | ZVAL_STR(¶m, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); 211 | zval retval2; 212 | assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, ¶m) == SUCCESS); 213 | TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); 214 | zval_ptr_dtor(&func_name); 215 | zval_ptr_dtor(¶m); 216 | zval_ptr_dtor(&retval2); 217 | 218 | // free object 219 | zval_ptr_dtor(&myext_obj); 220 | } 221 | 222 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 223 | TRACE("zim_myext_child_version"); 224 | 225 | zval *this = &(execute_data->This); 226 | 227 | // call parent's version() 228 | zval retval; 229 | zend_fcall_info fci = { 230 | size: sizeof(zend_fcall_info), 231 | retval: &retval, 232 | params: NULL, 233 | object: this->value.obj, 234 | no_separation: 1, 235 | param_count: 0, 236 | }; 237 | ZVAL_UNDEF(&fci.function_name); 238 | 239 | // find parent's version method 240 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 241 | 242 | zend_fcall_info_cache fcic = { 243 | initialized: 1, 244 | function_handler: parent_version_func->value.func, 245 | calling_scope: this->value.obj->ce->parent, 246 | called_scope: this->value.obj->ce, 247 | object: this->value.obj, 248 | }; 249 | 250 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 251 | assert(Z_TYPE_P(&retval) == IS_STRING); 252 | 253 | int len = retval.value.str->len + sizeof(".child") - 1; 254 | char *child_version = emalloc(len); 255 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 256 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 257 | 258 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 259 | efree(child_version); 260 | zval_ptr_dtor(&retval); 261 | } 262 | 263 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 264 | TRACE("zim_myext_strcase_convert"); 265 | 266 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 267 | zval *args = ZEND_CALL_ARG(execute_data, 1); 268 | 269 | TRACE("num_args=%d", num_args); 270 | 271 | zval *zv = &args[0]; 272 | zval *lowercase_zval = &args[1]; 273 | 274 | convert_to_string(zv); 275 | convert_to_boolean(lowercase_zval); 276 | 277 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 278 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 279 | size_t i; 280 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 281 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 282 | dup->val[i] = tolower(dup->val[i]); 283 | } else { 284 | dup->val[i] = toupper(dup->val[i]); 285 | } 286 | } 287 | ZVAL_STR(return_value, dup); 288 | } 289 | 290 | // Equals to PHP_METHOD(myext, strtolwer) 291 | // 292 | // zim_ means Zend Internal Method 293 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 294 | TRACE("zim_myext_constructor"); 295 | 296 | zval *this = &(execute_data->This); 297 | 298 | // class handle of this object 299 | zend_class_entry *class_handle = this->value.obj->ce; 300 | 301 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 302 | 303 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 304 | zval ver_zval; 305 | ZVAL_STR(&ver_zval, new_ver_prop); 306 | 307 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 308 | 309 | zend_string_release(ver_prop_name); 310 | zval_ptr_dtor(&ver_zval); 311 | } 312 | 313 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 314 | TRACE("zim_myext_version"); 315 | 316 | // same as $this 317 | zval *this = &(execute_data->This); 318 | 319 | // class handle of this object 320 | zend_class_entry *class_handle = this->value.obj->ce; 321 | 322 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 323 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 324 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 325 | ZVAL_STR(return_value, dup); 326 | } else { 327 | ZVAL_BOOL(return_value, 0); 328 | } 329 | } 330 | 331 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 332 | TRACE("zim_myext_strtolower"); 333 | 334 | zval *this = &(execute_data->This); 335 | 336 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 337 | zval *args = ZEND_CALL_ARG(execute_data, 1); 338 | 339 | TRACE("num_args=%d", num_args); 340 | 341 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 342 | zval func_name_zval; 343 | ZVAL_STR(&func_name_zval, func_name); 344 | 345 | zval params[2]; 346 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 347 | ZVAL_BOOL(¶ms[1], 1); 348 | 349 | // call method 350 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 351 | zval_ptr_dtor(&func_name_zval); 352 | } 353 | 354 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 355 | TRACE("zim_myext_strtoupper"); 356 | 357 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 358 | zval *args = ZEND_CALL_ARG(execute_data, 1); 359 | 360 | TRACE("num_args=%d", num_args); 361 | 362 | // call global function 363 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 364 | zval func_name_zval; 365 | ZVAL_STR(&func_name_zval, func_name); 366 | 367 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 368 | zval_ptr_dtor(&func_name_zval); 369 | } 370 | 371 | zend_module_entry module = { 372 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 373 | NULL, // ini_entry 374 | NULL, // deps 375 | "myext", //name 376 | global_funcs, // functions 377 | extension_startup, // module_startup_func 378 | extension_shutdown, // module_shutdown_func 379 | extension_before_request, // request_startup_func 380 | extension_after_request, // request_shutdown_func 381 | extension_info, // info_func 382 | "1.0", // version 383 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 384 | // handle,module_number,build_id 385 | STANDARD_MODULE_PROPERTIES, 386 | }; 387 | 388 | zend_module_dep deps[] = { 389 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 390 | { NULL, NULL, NULL, 0}, 391 | }; 392 | 393 | ZEND_DLEXPORT zend_module_entry *get_module() { 394 | module.deps = deps; 395 | 396 | return &module; 397 | } 398 | -------------------------------------------------------------------------------- /course7-how-to-create-object/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course7-how-to-create-object/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; 20 | 21 | // global function 22 | test_object(); -------------------------------------------------------------------------------- /course8-how-to-visit-global-vars/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -gdwarf-2 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course8-how-to-visit-global-vars/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | #define MYEXT_INI_NAME_GITHUB "myext.github" 4 | #define MYEXT_INI_VALUE_GITHUB "https://github.com/owenliang/php7-extension-explore" 5 | 6 | // overwrite in php.ini like this: 7 | // 8 | // myext.github = "some value" 9 | // 10 | zend_ini_entry_def ini_defs[] = { 11 | // name,on_modify,mh_arg1,mh_arg2,mh_arg3,value,displayer,modifiable,name_length,value_length 12 | {MYEXT_INI_NAME_GITHUB, NULL, NULL, NULL, NULL, MYEXT_INI_VALUE_GITHUB, NULL, ZEND_INI_ALL, sizeof(MYEXT_INI_NAME_GITHUB) - 1, sizeof(MYEXT_INI_VALUE_GITHUB) - 1}, 13 | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, 14 | }; 15 | 16 | extern void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value); 17 | extern void zim_myext_version(zend_execute_data *execute_data, zval *return_value); 18 | extern void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value); 19 | extern void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value); 20 | extern void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value); 21 | extern void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value); 22 | extern void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value); 23 | 24 | extern void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value); 25 | 26 | // zif_strtolower's params defination 27 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 28 | // required_num_args(interger stored in pointer) 29 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 30 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 31 | {"string", NULL, IS_STRING, 0, 0, 0}, 32 | }; 33 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 34 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 35 | {"string", NULL, IS_STRING, 0, 0, 0}, 36 | }; 37 | 38 | zend_function_entry funcs[] = { 39 | // fname,handler,arg_info,,num_args,flags 40 | {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, 41 | {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, 42 | {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, 43 | {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, 44 | {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, 45 | {"print_author", zim_myext_print_author, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC}, 46 | {NULL, NULL, NULL, 0, 0}, 47 | }; 48 | 49 | zend_function_entry interface_funcs[] = { 50 | // fname,handler,arg_info,,num_args,flags 51 | {"version", NULL, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT}, 52 | {NULL, NULL, NULL, 0, 0}, 53 | }; 54 | 55 | zend_function_entry final_funcs[] = { 56 | // fname,handler,arg_info,,num_args,flags 57 | {"version", zim_myext_child_version, NULL, 0, ZEND_ACC_PUBLIC}, 58 | {NULL, NULL, NULL, 0, 0}, 59 | }; 60 | 61 | zend_function_entry global_funcs[] = { 62 | // fname,handler,arg_info,,num_args,flags 63 | {"test_object", zif_myext_test_object, NULL, 0, 0}, 64 | {NULL, NULL, NULL, 0, 0}, 65 | }; 66 | 67 | zend_class_entry *myext_interface_handle = NULL; // interface handle 68 | zend_class_entry *myext_class_handle = NULL; // base class handle 69 | zend_class_entry *myext_child_class_handle = NULL; // child class handle 70 | 71 | int extension_startup(int type, int module_number) { 72 | TRACE("extension_startup"); 73 | zend_register_ini_entries(ini_defs, module_number); 74 | 75 | // 76 | // Interface myext_interface 77 | // 78 | 79 | // interface defination 80 | zend_class_entry myext_interface_def; 81 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 82 | 83 | // get interface handle 84 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 85 | 86 | // 87 | // Class myext 88 | // 89 | 90 | // class defination 91 | zend_class_entry myext_class_def; 92 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 93 | 94 | // get class handle 95 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 96 | 97 | // implements interface 98 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 99 | 100 | // add property to handle 101 | zval version_zval; 102 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 103 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 104 | 105 | // add static property to handle 106 | zval author_zval; 107 | ZVAL_PSTRING(&author_zval, "owenliang"); 108 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 109 | 110 | // add constant to handle 111 | zval build_date_zval; 112 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 113 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 114 | 115 | // 116 | // Class myext_child (inherit from Class myext) 117 | // 118 | zend_class_entry myext_child_class_def; 119 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 120 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 121 | 122 | // final class, no more child class 123 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 124 | 125 | // register constant 126 | zend_constant c; 127 | c.name = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 1); // persistant memory 128 | ZVAL_STR(&c.value, zend_string_init("https://github.com/owenliang/php7-extension-explore", 129 | sizeof("https://github.com/owenliang/php7-extension-explore"), 1)); // persistant memory 130 | c.flags = CONST_CS | CONST_PERSISTENT; 131 | c.module_number = module_number; 132 | assert(zend_register_constant(&c) == SUCCESS); 133 | 134 | return SUCCESS; 135 | } 136 | 137 | int extension_shutdown(int type, int module_number) { 138 | TRACE("extension_shutdown"); 139 | return SUCCESS; 140 | } 141 | 142 | int extension_before_request(int type, int module_number) { 143 | TRACE("extension_before_request"); 144 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 145 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 146 | 147 | // try active jit super globals 148 | zend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1); 149 | 150 | // find it in global symbol table 151 | zval *server = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1); 152 | assert(server != NULL); 153 | assert(Z_TYPE_P(server) == IS_ARRAY); 154 | 155 | // var_dump($_SERVER) 156 | zval func_name; 157 | ZVAL_STR(&func_name, zend_string_init("var_dump", sizeof("var_dump") - 1, 0)); 158 | zval retval; 159 | assert(call_user_function(&EG(function_table), NULL, &func_name, &retval, 1, server) == SUCCESS); 160 | zval_ptr_dtor(&func_name); 161 | zval_ptr_dtor(&retval); 162 | return SUCCESS; 163 | } 164 | 165 | int extension_after_request(int type, int module_number) { 166 | TRACE("extension_after_request"); 167 | return SUCCESS; 168 | } 169 | 170 | void extension_info(zend_module_entry *zend_module) { 171 | php_info_print_table_start(); 172 | php_info_print_table_header(2, "myext support", "enabled"); 173 | php_info_print_table_row(2, "author", "owenliang"); 174 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 175 | php_info_print_table_end(); 176 | } 177 | 178 | // Myext's static method 179 | void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { 180 | TRACE("zim_myext_print_author"); 181 | php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); 182 | ZVAL_BOOL(return_value, 1); 183 | } 184 | 185 | 186 | // global function 187 | void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { 188 | TRACE("zif_myext_test_object"); 189 | 190 | // call myext's static method: print_author 191 | zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); 192 | zend_class_entry *myext_handle = zend_lookup_class(myext_classname); 193 | zend_string_release(myext_classname); 194 | assert(myext_handle == myext_class_handle); 195 | 196 | zval retval; 197 | zend_fcall_info fci = { 198 | size: sizeof(zend_fcall_info), 199 | retval: &retval, 200 | params: NULL, 201 | object: NULL, 202 | no_separation: 1, 203 | param_count: 0, 204 | }; 205 | ZVAL_UNDEF(&fci.function_name); 206 | 207 | zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1); 208 | 209 | zend_fcall_info_cache fcic = { 210 | initialized: 1, 211 | function_handler: print_author_func->value.func, 212 | calling_scope: myext_handle, 213 | called_scope: NULL, 214 | object: NULL, 215 | }; 216 | 217 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 218 | assert(Z_TYPE_P(&retval) == IS_TRUE); 219 | 220 | // new a myext object 221 | zval myext_obj; 222 | assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS); 223 | 224 | // call object's __construct 225 | zval ctor_name; 226 | zval ctor_retval; 227 | ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); 228 | assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); 229 | zval_ptr_dtor(&ctor_name); 230 | 231 | // call object's method 232 | zval func_name; 233 | ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); 234 | zval param; 235 | ZVAL_STR(¶m, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); 236 | zval retval2; 237 | assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, ¶m) == SUCCESS); 238 | TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); 239 | zval_ptr_dtor(&func_name); 240 | zval_ptr_dtor(¶m); 241 | zval_ptr_dtor(&retval2); 242 | 243 | // free object 244 | zval_ptr_dtor(&myext_obj); 245 | } 246 | 247 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 248 | TRACE("zim_myext_child_version"); 249 | 250 | zval *this = &(execute_data->This); 251 | 252 | // call parent's version() 253 | zval retval; 254 | zend_fcall_info fci = { 255 | size: sizeof(zend_fcall_info), 256 | retval: &retval, 257 | params: NULL, 258 | object: this->value.obj, 259 | no_separation: 1, 260 | param_count: 0, 261 | }; 262 | ZVAL_UNDEF(&fci.function_name); 263 | 264 | // find parent's version method 265 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 266 | 267 | zend_fcall_info_cache fcic = { 268 | initialized: 1, 269 | function_handler: parent_version_func->value.func, 270 | calling_scope: this->value.obj->ce->parent, 271 | called_scope: this->value.obj->ce, 272 | object: this->value.obj, 273 | }; 274 | 275 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 276 | assert(Z_TYPE_P(&retval) == IS_STRING); 277 | 278 | int len = retval.value.str->len + sizeof(".child") - 1; 279 | char *child_version = emalloc(len); 280 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 281 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 282 | 283 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 284 | efree(child_version); 285 | zval_ptr_dtor(&retval); 286 | } 287 | 288 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 289 | TRACE("zim_myext_strcase_convert"); 290 | 291 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 292 | zval *args = ZEND_CALL_ARG(execute_data, 1); 293 | 294 | TRACE("num_args=%d", num_args); 295 | 296 | zval *zv = &args[0]; 297 | zval *lowercase_zval = &args[1]; 298 | 299 | convert_to_string(zv); 300 | convert_to_boolean(lowercase_zval); 301 | 302 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 303 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 304 | size_t i; 305 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 306 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 307 | dup->val[i] = tolower(dup->val[i]); 308 | } else { 309 | dup->val[i] = toupper(dup->val[i]); 310 | } 311 | } 312 | ZVAL_STR(return_value, dup); 313 | } 314 | 315 | // Equals to PHP_METHOD(myext, strtolwer) 316 | // 317 | // zim_ means Zend Internal Method 318 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 319 | TRACE("zim_myext_constructor"); 320 | 321 | zval *this = &(execute_data->This); 322 | 323 | // class handle of this object 324 | zend_class_entry *class_handle = this->value.obj->ce; 325 | 326 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 327 | 328 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 329 | zval ver_zval; 330 | ZVAL_STR(&ver_zval, new_ver_prop); 331 | 332 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 333 | 334 | zend_string_release(ver_prop_name); 335 | zval_ptr_dtor(&ver_zval); 336 | } 337 | 338 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 339 | TRACE("zim_myext_version"); 340 | 341 | // same as $this 342 | zval *this = &(execute_data->This); 343 | 344 | // class handle of this object 345 | zend_class_entry *class_handle = this->value.obj->ce; 346 | 347 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 348 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 349 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 350 | ZVAL_STR(return_value, dup); 351 | } else { 352 | ZVAL_BOOL(return_value, 0); 353 | } 354 | 355 | // read constant 356 | zend_string *cname = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 0); 357 | zval *c_github = zend_get_constant(cname); 358 | assert(Z_TYPE_P(c_github) == IS_STRING); 359 | TRACE("zend_get_constant(GITHUB)=%.*s", Z_STRLEN_P(c_github), Z_STRVAL_P(c_github)); 360 | zend_string_release(cname); 361 | } 362 | 363 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 364 | TRACE("zim_myext_strtolower"); 365 | 366 | zval *this = &(execute_data->This); 367 | 368 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 369 | zval *args = ZEND_CALL_ARG(execute_data, 1); 370 | 371 | TRACE("num_args=%d", num_args); 372 | 373 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 374 | zval func_name_zval; 375 | ZVAL_STR(&func_name_zval, func_name); 376 | 377 | zval params[2]; 378 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 379 | ZVAL_BOOL(¶ms[1], 1); 380 | 381 | // call method 382 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 383 | zval_ptr_dtor(&func_name_zval); 384 | } 385 | 386 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 387 | TRACE("zim_myext_strtoupper"); 388 | 389 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 390 | zval *args = ZEND_CALL_ARG(execute_data, 1); 391 | 392 | TRACE("num_args=%d", num_args); 393 | 394 | // call global function 395 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 396 | zval func_name_zval; 397 | ZVAL_STR(&func_name_zval, func_name); 398 | 399 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 400 | zval_ptr_dtor(&func_name_zval); 401 | } 402 | 403 | zend_module_entry module = { 404 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 405 | NULL, // ini_entry 406 | NULL, // deps 407 | "myext", //name 408 | global_funcs, // functions 409 | extension_startup, // module_startup_func 410 | extension_shutdown, // module_shutdown_func 411 | extension_before_request, // request_startup_func 412 | extension_after_request, // request_shutdown_func 413 | extension_info, // info_func 414 | "1.0", // version 415 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 416 | // handle,module_number,build_id 417 | STANDARD_MODULE_PROPERTIES, 418 | }; 419 | 420 | zend_module_dep deps[] = { 421 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 422 | { NULL, NULL, NULL, 0}, 423 | }; 424 | 425 | ZEND_DLEXPORT zend_module_entry *get_module() { 426 | module.deps = deps; 427 | 428 | return &module; 429 | } 430 | -------------------------------------------------------------------------------- /course8-how-to-visit-global-vars/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course8-how-to-visit-global-vars/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; 20 | 21 | // global function 22 | test_object(); 23 | 24 | echo GITHUB . PHP_EOL; 25 | -------------------------------------------------------------------------------- /course9-how-to-work-with-zend-array/Makefile: -------------------------------------------------------------------------------- 1 | # PHP环境变量 2 | PHP_INCLUDE = `php-config --includes` 3 | PHP_LIBS = `php-config --libs` 4 | PHP_LDFLAGS = `php-config --ldflags` 5 | PHP_INCLUDE_DIR = `php-config --include-dir` 6 | PHP_EXTENSION_DIR = `php-config --extension-dir` 7 | 8 | # 编译器 9 | CC = gcc 10 | 11 | # 编译参数 12 | CFLAGS = -g -O0 -gdwarf-2 -fPIC -I. ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} 13 | 14 | # 链接参数 15 | LDFLAGS = -shared -L${PHP_EXTENSION_DIR} ${PHP_LIBS} ${PHP_LDFLAGS} 16 | 17 | # 源代码 18 | SRC = myext.c 19 | # 头文件 20 | HEADER = myext.h 21 | 22 | # .o目标文件 23 | OBJECTS = $(SRC:.c=.o) 24 | 25 | .PHONY: all test clean 26 | 27 | all: myext.so 28 | @echo "done" 29 | 30 | myext.so: $(OBJECTS) 31 | $(CC) -o $@ $^ $(LDFLAGS) 32 | 33 | %.o:%.cpp $(HEADER) 34 | $(CC) -o $@ -c $< $(CFLAGS) 35 | 36 | install: myext.so 37 | cp myext.so ${PHP_EXTENSION_DIR}/ 38 | 39 | test: 40 | php test.php 41 | 42 | clean: 43 | rm -f *.so 44 | rm -f *.o 45 | -------------------------------------------------------------------------------- /course9-how-to-work-with-zend-array/myext.c: -------------------------------------------------------------------------------- 1 | #include "myext.h" 2 | 3 | #define MYEXT_INI_NAME_GITHUB "myext.github" 4 | #define MYEXT_INI_VALUE_GITHUB "https://github.com/owenliang/php7-extension-explore" 5 | 6 | // overwrite in php.ini like this: 7 | // 8 | // myext.github = "some value" 9 | // 10 | zend_ini_entry_def ini_defs[] = { 11 | // name,on_modify,mh_arg1,mh_arg2,mh_arg3,value,displayer,modifiable,name_length,value_length 12 | {MYEXT_INI_NAME_GITHUB, NULL, NULL, NULL, NULL, MYEXT_INI_VALUE_GITHUB, NULL, ZEND_INI_ALL, sizeof(MYEXT_INI_NAME_GITHUB) - 1, sizeof(MYEXT_INI_VALUE_GITHUB) - 1}, 13 | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, 14 | }; 15 | 16 | extern void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value); 17 | extern void zim_myext_version(zend_execute_data *execute_data, zval *return_value); 18 | extern void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value); 19 | extern void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value); 20 | extern void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value); 21 | extern void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value); 22 | extern void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value); 23 | 24 | extern void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value); 25 | extern void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value); 26 | 27 | // zif_strtolower's params defination 28 | zend_internal_arg_info myext_strtolwer_arginfo[] = { 29 | // required_num_args(interger stored in pointer) 30 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 31 | // name, class_name, type_hint, pass_by_reference, allow_null, is_variadic 32 | {"string", NULL, IS_STRING, 0, 0, 0}, 33 | }; 34 | zend_internal_arg_info myext_strtoupper_arginfo[] = { 35 | {(const char *)(zend_uintptr_t)1, NULL, 0, 0, 0, 0}, 36 | {"string", NULL, IS_STRING, 0, 0, 0}, 37 | }; 38 | 39 | zend_function_entry funcs[] = { 40 | // fname,handler,arg_info,,num_args,flags 41 | {"__construct", zim_myext_constructor, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR}, 42 | {"version", zim_myext_version, NULL, 0, ZEND_ACC_PUBLIC}, 43 | {"strtolower", zim_myext_strtolower, myext_strtolwer_arginfo, 1, ZEND_ACC_PUBLIC/*method flag*/}, 44 | {"strtoupper", zim_myext_strtoupper, myext_strtoupper_arginfo, 1, ZEND_ACC_PUBLIC}, 45 | {"strcase_convert", zim_myext_strcase_convert, NULL, 2, ZEND_ACC_PRIVATE}, 46 | {"print_author", zim_myext_print_author, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC}, 47 | {NULL, NULL, NULL, 0, 0}, 48 | }; 49 | 50 | zend_function_entry interface_funcs[] = { 51 | // fname,handler,arg_info,,num_args,flags 52 | {"version", NULL, NULL, 0, ZEND_ACC_PUBLIC | ZEND_ACC_ABSTRACT}, 53 | {NULL, NULL, NULL, 0, 0}, 54 | }; 55 | 56 | zend_function_entry final_funcs[] = { 57 | // fname,handler,arg_info,,num_args,flags 58 | {"version", zim_myext_child_version, NULL, 0, ZEND_ACC_PUBLIC}, 59 | {NULL, NULL, NULL, 0, 0}, 60 | }; 61 | 62 | zend_function_entry global_funcs[] = { 63 | // fname,handler,arg_info,,num_args,flags 64 | {"test_object", zif_myext_test_object, NULL, 0, 0}, 65 | {"test_array", zif_myext_test_array, NULL, 0, 0}, 66 | {NULL, NULL, NULL, 0, 0}, 67 | }; 68 | 69 | zend_class_entry *myext_interface_handle = NULL; // interface handle 70 | zend_class_entry *myext_class_handle = NULL; // base class handle 71 | zend_class_entry *myext_child_class_handle = NULL; // child class handle 72 | 73 | int extension_startup(int type, int module_number) { 74 | TRACE("extension_startup"); 75 | zend_register_ini_entries(ini_defs, module_number); 76 | 77 | // 78 | // Interface myext_interface 79 | // 80 | 81 | // interface defination 82 | zend_class_entry myext_interface_def; 83 | INIT_CLASS_ENTRY_EX(myext_interface_def, "myext_interface", sizeof("myext_interface") - 1, interface_funcs); 84 | 85 | // get interface handle 86 | assert(myext_interface_handle = zend_register_internal_interface(&myext_interface_def)); 87 | 88 | // 89 | // Class myext 90 | // 91 | 92 | // class defination 93 | zend_class_entry myext_class_def; 94 | INIT_CLASS_ENTRY_EX(myext_class_def, "myext", sizeof("myext") - 1, funcs); 95 | 96 | // get class handle 97 | assert(myext_class_handle = zend_register_internal_class(&myext_class_def)); 98 | 99 | // implements interface 100 | assert(zend_do_implement_interface(myext_class_handle, myext_interface_handle) == SUCCESS); 101 | 102 | // add property to handle 103 | zval version_zval; 104 | ZVAL_PSTRING(&version_zval, "1.0.0"); // must be allocted from persistant memory 105 | assert(zend_declare_property(myext_class_handle, "version", sizeof("version") - 1, &version_zval, ZEND_ACC_PROTECTED) == SUCCESS); 106 | 107 | // add static property to handle 108 | zval author_zval; 109 | ZVAL_PSTRING(&author_zval, "owenliang"); 110 | assert(zend_declare_property(myext_class_handle, "author", sizeof("author") - 1, &author_zval, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) == SUCCESS); 111 | 112 | // add constant to handle 113 | zval build_date_zval; 114 | ZVAL_PSTRING(&build_date_zval, "2017-08-09 14:48"); 115 | assert(zend_declare_class_constant(myext_class_handle, "BUILD_DATE", sizeof("build_date") - 1, &build_date_zval) == SUCCESS); 116 | 117 | // 118 | // Class myext_child (inherit from Class myext) 119 | // 120 | zend_class_entry myext_child_class_def; 121 | INIT_CLASS_ENTRY_EX(myext_child_class_def, "myext_child", sizeof("myext_child") - 1, final_funcs); 122 | assert(myext_child_class_handle = zend_register_internal_class_ex(&myext_child_class_def, myext_class_handle)); 123 | 124 | // final class, no more child class 125 | myext_child_class_handle->ce_flags |= ZEND_ACC_FINAL; 126 | 127 | // register constant 128 | zend_constant c; 129 | c.name = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 1); // persistant memory 130 | ZVAL_STR(&c.value, zend_string_init("https://github.com/owenliang/php7-extension-explore", 131 | sizeof("https://github.com/owenliang/php7-extension-explore"), 1)); // persistant memory 132 | c.flags = CONST_CS | CONST_PERSISTENT; 133 | c.module_number = module_number; 134 | assert(zend_register_constant(&c) == SUCCESS); 135 | 136 | return SUCCESS; 137 | } 138 | 139 | int extension_shutdown(int type, int module_number) { 140 | TRACE("extension_shutdown"); 141 | return SUCCESS; 142 | } 143 | 144 | int extension_before_request(int type, int module_number) { 145 | TRACE("extension_before_request"); 146 | const char* value = zend_ini_string(MYEXT_INI_NAME_GITHUB, sizeof(MYEXT_INI_NAME_GITHUB) - 1, 0); 147 | TRACE("ini: %s=%s", MYEXT_INI_NAME_GITHUB, value); 148 | 149 | // try active jit super globals 150 | zend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1); 151 | 152 | // find it in global symbol table 153 | zval *server = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER") - 1); 154 | assert(server != NULL); 155 | assert(Z_TYPE_P(server) == IS_ARRAY); 156 | 157 | // var_dump($_SERVER) 158 | zval func_name; 159 | ZVAL_STR(&func_name, zend_string_init("var_dump", sizeof("var_dump") - 1, 0)); 160 | zval retval; 161 | assert(call_user_function(&EG(function_table), NULL, &func_name, &retval, 1, server) == SUCCESS); 162 | zval_ptr_dtor(&func_name); 163 | zval_ptr_dtor(&retval); 164 | return SUCCESS; 165 | } 166 | 167 | int extension_after_request(int type, int module_number) { 168 | TRACE("extension_after_request"); 169 | return SUCCESS; 170 | } 171 | 172 | void extension_info(zend_module_entry *zend_module) { 173 | php_info_print_table_start(); 174 | php_info_print_table_header(2, "myext support", "enabled"); 175 | php_info_print_table_row(2, "author", "owenliang"); 176 | php_info_print_table_row(2, "course name", "course1-how-to-export-a-module"); 177 | php_info_print_table_end(); 178 | } 179 | 180 | // Myext's static method 181 | void zim_myext_print_author(zend_execute_data *execute_data, zval *return_value) { 182 | TRACE("zim_myext_print_author"); 183 | php_output_write("author=owenliang\n", sizeof("author=owenliang\n") - 1); 184 | ZVAL_BOOL(return_value, 1); 185 | } 186 | 187 | 188 | // global function 189 | void zif_myext_test_object(zend_execute_data *execute_data, zval *return_value) { 190 | TRACE("zif_myext_test_object"); 191 | 192 | // call myext's static method: print_author 193 | zend_string *myext_classname = zend_string_init("myext", sizeof("myext") - 1, 0); 194 | zend_class_entry *myext_handle = zend_lookup_class(myext_classname); 195 | zend_string_release(myext_classname); 196 | assert(myext_handle == myext_class_handle); 197 | 198 | zval retval; 199 | zend_fcall_info fci = { 200 | size: sizeof(zend_fcall_info), 201 | retval: &retval, 202 | params: NULL, 203 | object: NULL, 204 | no_separation: 1, 205 | param_count: 0, 206 | }; 207 | ZVAL_UNDEF(&fci.function_name); 208 | 209 | zval *print_author_func = zend_hash_str_find(&(myext_handle->function_table), "print_author", sizeof("print_author") - 1); 210 | 211 | zend_fcall_info_cache fcic = { 212 | initialized: 1, 213 | function_handler: print_author_func->value.func, 214 | calling_scope: myext_handle, 215 | called_scope: NULL, 216 | object: NULL, 217 | }; 218 | 219 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 220 | assert(Z_TYPE_P(&retval) == IS_TRUE); 221 | 222 | // new a myext object 223 | zval myext_obj; 224 | assert(object_init_ex(&myext_obj, myext_handle) == SUCCESS); 225 | 226 | // call object's __construct 227 | zval ctor_name; 228 | zval ctor_retval; 229 | ZVAL_STR(&ctor_name, zend_string_init("__construct", sizeof("__construct") - 1, 0)); 230 | assert(call_user_function(&EG(function_table), &myext_obj, &ctor_name, &ctor_retval, 0, NULL) == SUCCESS); 231 | zval_ptr_dtor(&ctor_name); 232 | 233 | // call object's method 234 | zval func_name; 235 | ZVAL_STR(&func_name, zend_string_init("strtolower", sizeof("strtolower") - 1, 0)); 236 | zval param; 237 | ZVAL_STR(¶m, zend_string_init("OWENLIANG", sizeof("OWENLIANG") - 1, 0)); 238 | zval retval2; 239 | assert(call_user_function(&EG(function_table), &myext_obj, &func_name, &retval2, 1, ¶m) == SUCCESS); 240 | TRACE("$myext_obj->strtolower(OWENLIANG)=%.*s", retval2.value.str->len, retval2.value.str->val); 241 | zval_ptr_dtor(&func_name); 242 | zval_ptr_dtor(¶m); 243 | zval_ptr_dtor(&retval2); 244 | 245 | // free object 246 | zval_ptr_dtor(&myext_obj); 247 | } 248 | 249 | void zim_myext_child_version(zend_execute_data *execute_data, zval *return_value) { 250 | TRACE("zim_myext_child_version"); 251 | 252 | zval *this = &(execute_data->This); 253 | 254 | // call parent's version() 255 | zval retval; 256 | zend_fcall_info fci = { 257 | size: sizeof(zend_fcall_info), 258 | retval: &retval, 259 | params: NULL, 260 | object: this->value.obj, 261 | no_separation: 1, 262 | param_count: 0, 263 | }; 264 | ZVAL_UNDEF(&fci.function_name); 265 | 266 | // find parent's version method 267 | zval *parent_version_func = zend_hash_str_find(&(this->value.obj->ce->parent->function_table), "version", sizeof("version") - 1); 268 | 269 | zend_fcall_info_cache fcic = { 270 | initialized: 1, 271 | function_handler: parent_version_func->value.func, 272 | calling_scope: this->value.obj->ce->parent, 273 | called_scope: this->value.obj->ce, 274 | object: this->value.obj, 275 | }; 276 | 277 | assert(zend_call_function(&fci, &fcic) == SUCCESS); 278 | assert(Z_TYPE_P(&retval) == IS_STRING); 279 | 280 | int len = retval.value.str->len + sizeof(".child") - 1; 281 | char *child_version = emalloc(len); 282 | memcpy(child_version, retval.value.str->val, retval.value.str->len); 283 | memcpy(child_version + retval.value.str->len, ".child", sizeof(".child") - 1); 284 | 285 | ZVAL_STR(return_value, zend_string_init(child_version, len, 0)); 286 | efree(child_version); 287 | zval_ptr_dtor(&retval); 288 | } 289 | 290 | void zim_myext_strcase_convert(zend_execute_data *execute_data, zval *return_value) { 291 | TRACE("zim_myext_strcase_convert"); 292 | 293 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 294 | zval *args = ZEND_CALL_ARG(execute_data, 1); 295 | 296 | TRACE("num_args=%d", num_args); 297 | 298 | zval *zv = &args[0]; 299 | zval *lowercase_zval = &args[1]; 300 | 301 | convert_to_string(zv); 302 | convert_to_boolean(lowercase_zval); 303 | 304 | zend_string *raw = zv->value.str; // Z_STR_P(zv) 305 | zend_string *dup = zend_string_init(raw->val, raw->len, 0); 306 | size_t i; 307 | for (i = 0; i < dup->len/*ZSTR_LEN*/; ++i) { 308 | if (Z_TYPE_P(lowercase_zval) == IS_TRUE) { 309 | dup->val[i] = tolower(dup->val[i]); 310 | } else { 311 | dup->val[i] = toupper(dup->val[i]); 312 | } 313 | } 314 | ZVAL_STR(return_value, dup); 315 | } 316 | 317 | // Equals to PHP_METHOD(myext, strtolwer) 318 | // 319 | // zim_ means Zend Internal Method 320 | void zim_myext_constructor(zend_execute_data *execute_data, zval *return_value) { 321 | TRACE("zim_myext_constructor"); 322 | 323 | zval *this = &(execute_data->This); 324 | 325 | // class handle of this object 326 | zend_class_entry *class_handle = this->value.obj->ce; 327 | 328 | zend_string *ver_prop_name = zend_string_init("version", sizeof("version") - 1, 0); 329 | 330 | zend_string *new_ver_prop = zend_string_init("1.0.1", sizeof("1.0.1") - 1, 0); 331 | zval ver_zval; 332 | ZVAL_STR(&ver_zval, new_ver_prop); 333 | 334 | zend_update_property_ex(class_handle, this, ver_prop_name, &ver_zval); 335 | 336 | zend_string_release(ver_prop_name); 337 | zval_ptr_dtor(&ver_zval); 338 | } 339 | 340 | void zim_myext_version(zend_execute_data *execute_data, zval *return_value) { 341 | TRACE("zim_myext_version"); 342 | 343 | // same as $this 344 | zval *this = &(execute_data->This); 345 | 346 | // class handle of this object 347 | zend_class_entry *class_handle = this->value.obj->ce; 348 | 349 | zval *ver_prop = zend_read_property(class_handle, this, "version", sizeof("version") - 1, 0, NULL/*always pass null*/); 350 | if (Z_TYPE_P(ver_prop) == IS_STRING) { 351 | zend_string *dup = zend_string_init(ver_prop->value.str->val, ver_prop->value.str->len, 0); 352 | ZVAL_STR(return_value, dup); 353 | } else { 354 | ZVAL_BOOL(return_value, 0); 355 | } 356 | 357 | // read constant 358 | zend_string *cname = zend_string_init("GITHUB", sizeof("GITHUB") - 1, 0); 359 | zval *c_github = zend_get_constant(cname); 360 | assert(Z_TYPE_P(c_github) == IS_STRING); 361 | TRACE("zend_get_constant(GITHUB)=%.*s", Z_STRLEN_P(c_github), Z_STRVAL_P(c_github)); 362 | zend_string_release(cname); 363 | } 364 | 365 | void zim_myext_strtolower(zend_execute_data *execute_data, zval *return_value) { 366 | TRACE("zim_myext_strtolower"); 367 | 368 | zval *this = &(execute_data->This); 369 | 370 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 371 | zval *args = ZEND_CALL_ARG(execute_data, 1); 372 | 373 | TRACE("num_args=%d", num_args); 374 | 375 | zend_string *func_name = zend_string_init("strcase_convert", sizeof("strcase_convert") - 1, 0); 376 | zval func_name_zval; 377 | ZVAL_STR(&func_name_zval, func_name); 378 | 379 | zval params[2]; 380 | memcpy(¶ms[0], &args[0], sizeof(args[0])); 381 | ZVAL_BOOL(¶ms[1], 1); 382 | 383 | // call method 384 | assert(call_user_function(&EG(function_table), this, &func_name_zval, return_value, 2, params) == SUCCESS); 385 | zval_ptr_dtor(&func_name_zval); 386 | } 387 | 388 | void zim_myext_strtoupper(zend_execute_data *execute_data, zval *return_value) { 389 | TRACE("zim_myext_strtoupper"); 390 | 391 | int num_args = ZEND_CALL_NUM_ARGS(execute_data); 392 | zval *args = ZEND_CALL_ARG(execute_data, 1); 393 | 394 | TRACE("num_args=%d", num_args); 395 | 396 | // call global function 397 | zend_string *func_name = zend_string_init("strtoupper", sizeof("strtoupper") - 1, 0); 398 | zval func_name_zval; 399 | ZVAL_STR(&func_name_zval, func_name); 400 | 401 | call_user_function(&EG(function_table), NULL, &func_name_zval, return_value, 1, &args[0]); 402 | zval_ptr_dtor(&func_name_zval); 403 | } 404 | 405 | void zif_myext_test_array(zend_execute_data *execute_data, zval *return_value) { 406 | // init arr 407 | zval arr_zval; 408 | assert(array_init(&arr_zval) == SUCCESS); 409 | 410 | // add k-v 411 | add_assoc_long(&arr_zval, "date", 20170811); 412 | assert(zend_hash_str_exists(arr_zval.value.arr, "date", sizeof("date") - 1)); 413 | 414 | // add v 415 | assert(add_next_index_string(&arr_zval, "hahaha") == SUCCESS); 416 | 417 | // arr count 418 | assert(zend_hash_num_elements(arr_zval.value.arr) == 2); 419 | 420 | // traversal arr 421 | zend_array *arr = arr_zval.value.arr; 422 | int i; 423 | for (i = 0; i < arr->nNumUsed; ++i) { 424 | zval *val = &(arr->arData[i].val); 425 | // handle indirect zval 426 | if (Z_TYPE_P(val) == IS_INDIRECT) { 427 | val = Z_INDIRECT_P(val); 428 | } 429 | // empty slots 430 | if (Z_TYPE_P(val) == IS_UNDEF) { 431 | continue; 432 | } 433 | 434 | if (arr->arData[i].key) { // must be array["date"] 435 | TRACE("arr['%.*s']=%ld", arr->arData[i].key->len, arr->arData[i].key->val, val->value.lval); 436 | } else { // must be array[0] 437 | TRACE("arr[%ld]=%.*s", arr->arData[i].h, val->value.str->len, val->value.str->val); 438 | } 439 | } 440 | 441 | // find key 442 | zval *zv_in_arr = zend_hash_str_find_ind(arr_zval.value.arr, "date", sizeof("date") - 1); 443 | assert(zv_in_arr->value.lval == 20170811); 444 | 445 | // del string key 446 | assert(zend_hash_str_del(arr_zval.value.arr, "date", sizeof("date") - 1) == SUCCESS); 447 | 448 | // del index key 449 | assert(zend_hash_index_del(arr_zval.value.arr, 0) == SUCCESS); 450 | 451 | // release arr 452 | zval_ptr_dtor(&arr_zval); 453 | } 454 | 455 | zend_module_entry module = { 456 | STANDARD_MODULE_HEADER_EX, // size,zend_api,zend_debug,zts 457 | NULL, // ini_entry 458 | NULL, // deps 459 | "myext", //name 460 | global_funcs, // functions 461 | extension_startup, // module_startup_func 462 | extension_shutdown, // module_shutdown_func 463 | extension_before_request, // request_startup_func 464 | extension_after_request, // request_shutdown_func 465 | extension_info, // info_func 466 | "1.0", // version 467 | // globals_size,globals_ptr,globals_ctor,globals_dtor,post_deactivate_func,module_started,type, 468 | // handle,module_number,build_id 469 | STANDARD_MODULE_PROPERTIES, 470 | }; 471 | 472 | zend_module_dep deps[] = { 473 | {"standard", NULL, NULL, MODULE_DEP_REQUIRED}, 474 | { NULL, NULL, NULL, 0}, 475 | }; 476 | 477 | ZEND_DLEXPORT zend_module_entry *get_module() { 478 | module.deps = deps; 479 | 480 | return &module; 481 | } 482 | -------------------------------------------------------------------------------- /course9-how-to-work-with-zend-array/myext.h: -------------------------------------------------------------------------------- 1 | #ifndef MYEXT_H 2 | #define MYEXT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "php.h" 11 | #include "ext/standard/info.h" 12 | 13 | #define TRACE(fmt, ...) do { trace(__FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__); } while (0) 14 | 15 | static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) { 16 | fprintf(stderr, "%s(%s:%d) - ", function, file, line); 17 | va_list args; 18 | va_start(args, fmt); 19 | vfprintf(stderr, fmt, args); 20 | fprintf(stderr, "\n"); 21 | va_end(args); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /course9-how-to-work-with-zend-array/test.php: -------------------------------------------------------------------------------- 1 | strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 7 | echo $obj->version() . PHP_EOL; 8 | echo MyExt::$author . PHP_EOL; 9 | echo MyExt::BUILD_DATE . PHP_EOL; 10 | 11 | echo "====================" . PHP_EOL; 12 | 13 | // child 14 | $obj = new MyExt_Child(); 15 | 16 | echo $obj->strtolower("HELLO") . $obj->strtoupper("php") . $obj->strtolower(2017) . PHP_EOL; 17 | echo $obj->version() . PHP_EOL; 18 | echo MyExt_Child::$author . PHP_EOL; 19 | echo MyExt_Child::BUILD_DATE . PHP_EOL; 20 | 21 | // global function 22 | test_object(); 23 | 24 | test_array(); 25 | 26 | echo GITHUB . PHP_EOL; 27 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 |