php is an interpreted language,For users,Our carefully controlled memory means easy prototyping and fewer crashes! When we dive into the kernel,All security lines have been crossed,In the end, we must rely on truly responsible software engineers to ensure the stable operation of the system.

1.Thread-safe macro definition

There are the following definitions in the tsrm/tsrm.h file

#define tsrmls_fetch () void *** tsrm_ls=(void ***) ts_resource_ex (0, null)

#define tsrmls_fetch_from_ctx (ctx) void *** tsrm_ls=(void ***) ctx

#define tsrmls_set_ctx (ctx) ctx=(void ***) tsrm_ls

#define tsrmg (id, type, element) (((type) (* ((void ***) tsrm_ls)) [tsrm_unshuffle_rsrc_id (id)])->element)

#define tsrmls_d void *** tsrm_ls

#define tsrmls_dc, tsrmls_d

#define tsrmls_c tsrm_ls

#define tsrmls_cc, tsrmls_c

There is such a paragraph in ext/xsl/php_xsl.h

/* in every utility function you add that needs to use variables.

in php_xsl_globals, call tsrm_fetch ();after declaring other.

variables used by that function, or better yet, pass in tsrmls_cc

after the last function argument and declare your utility function

with tsrmls_dc after the last declared argument. always refer to

the globals in your function as xsl_g (variable). you are.

encouraged to rename these macros something shorter, see

examples in any other php module directory.

* /

1. Add tsrmls_d (if the method has no parameters) or tsrmls_dc (with more than 1 parameter) to the method definition

2. Use tsrmls_c (if the method has no parameters) or tsrmls_cc (with more than one parameter) in the method call

Should be understood like this

The first suffix letter d means definition,That is d=define, the first suffix letter c means call,That is c=call, and does the second suffix letter c mean a comma?c=comma (comma)

tsrmls_d is defined,So it is void *** tsrm_ls

tsrmls_dc is a definition with a comma,So yes, void *** tsrm_ls

tsrmls_c is the call, ie tsrm_ls

tsrmls_cc is called with a comma,That is, tsrm_ls

So one is a formal parameter and one is an actual parameter

Can be used like this

int php_myext_action (int action_id, char * message tsrmls_dc);

php_myext_action (42, "the meaning of life" tsrmls_cc);

It is generally recommended to use the tsrm_ls pointer definition to ensure thread safety

The tsrmls_fetch call requires some processing time.This is not obvious in a single iteration,But as your number of threads increases,As the number of points you call tsrmls_fetch () increases,Your expansion will show this bottleneck.Therefore, use it with caution. Note:For compatibility with the C++ compiler,Make sure to put tsrmls_fetch () and all variable definitions on top of the scope of a given block (before any other statements). Because the tsrmls_fetch () macro itself has many different parsing methods,So it's best to use it as the last line of the variable definition

2, PHP's life cycle

The two most common operating modes of PHP are web mode and cli mode, no matter which mode,php works the same,Run as a sapi.

1. When we type the command php in the terminal,It uses cli.

It works like a web server to support php to complete this request,After the request is completed, control is returned to the terminal.

2. When using apache as the host,When a request comes,PHP will come to support this request

php_minit_function runs when the module is initialized

php_mshutdown_function runs when the module is uninstalled

php_rinit_function runs when a request is initialized

php_rshutdown_function runs when a request ends

php_minfo_function This is the information for setting this module in phpinfo

php_ginit_function when initializing global variables

When php_gshutdown_function releases global variables

E.g. php_ginit_function

php_ginit_function (test)
  /** Initialize global variables * /
//corresponding c code
void zm_globals_ctor_test (zend_test_globals * test_globals tsrmls_dc)
  /** Initialize global variables * /
//When the thread exits,When you need to release the resources you applied for before,Destructors can be registered using php_gshutdown_function.
php_gshutdown_function (test)
  /** Clear global variables * /
//corresponding c code
void zm_globals_dtor_test (zend_test_globals * test_globals tsrmls_dc)
  /** Clear global variables * /

Here is a piece of code,Can test

int minit_time;
php_minit_function (test)
  minit_time=time (null);
  return success;
php_mshutdown_function (test)
  file * fp=fopen ("mshutdown.txt", "a +");
  fprintf (fp, "%ld \ n", time (null));
  fclose (fp);
  return success;
int rinit_time;
php_rinit_function (test)
  rinit_time=time (null);
  return success;
php_rshutdown_function (test)
  file * fp=fopen ("rshutdown.txt", "a +");
  fprintf (fp, "%ld \ n", time (null));
  fclose (fp);
  return success;
php_minfo_function (test)
  php_info_print_table_start ();
  php_info_print_table_header (, "module info", "enabled");
  php_info_print_table_end ();
  /* remove comments if you have entries in php.ini
  display_ini_entries ();
  * /
php_function (test)
  php_printf ("%d", time_of_minit);
  php_printf ("%d", time_of_rinit);

3. Segment fault debugging

C programs under Linux often cause segment faults due to memory access errors and other reasons. At this time, if the core dump function is turned onThen there will be a memory image dump to the hard disk,After that, you can use gdb to analyze the core file.Restore the stack situation at the time of the system fault.This is very helpful for us to find program bugs.

Use ulimit -a to view the size limit of the system core file;Use ulimit -c [kbytes] to set the core file size allowed by the system.

ulimit -c 0 does not generate core files

ulimit -c 100 sets the maximum core file to 100k

ulimit -c unlimited does not limit the core file size


1. When a segmentation fault occurs,We check that ulimit -a (core file size (blocks, -c) 0) has no files,

2, set:ulimit -c unlimited does not limit the core file size

3. Run the program and it will be automatically recorded in the core when a segmentation fault occurs (php -f workwitharray.php)

4. ls -al core. * Under that file (-rw ------- 1 leconte leconte 139264 01-06 22:3 1 core.2065)

5. Use gdb to run the program and the file recorded by the segmentation fault.(Gdb ./test core.2065)

6. Which line is wrong?

Many systems have a default core file size of 0. We can specify the core file size by adding the ulimit -c command in the shell startup script/etc/bashrc or ~/.bashrc, etc.So as to ensure that the core file can be generated.

In addition,You can also set the file name template of the core file in/proc/sys/kernel/core_pattern.For details, see the official man page of core.

4.Common variable manipulation macros

cg->complier global compile-time information,Including function tables, etc. (zend_globals_macros.h:32)

eg->executor global runtime information (zend_globals_macros.h:43)

pg->php core global mainly stores information in php.ini

sg->sapi global sapi information

1, sg for sapi information in main/sapi.h file

typedef struct _sapi_globals_struct {
  void * server_context;
  sapi_request_info request_info;
  sapi_headers_struct sapi_headers;
  int read_post_bytes;
  unsigned char headers_sent;
  struct stat global_stat;
  char * default_mimetype;
  char * default_charset;
  hashtable * rfc1867_uploaded_files;
  long post_max_size;
  int options;
  zend_bool sapi_started;
  double global_request_time;
  hashtable known_post_content_types;
  zval * callback_func;
  zend_fcall_info_cache fci_cache;
  zend_bool callback_run;
} sapi_globals_struct;

Look at the definition of sg

begin_extern_c ()

#ifdef zts

#define sg (v) tsrmg (sapi_globals_id, sapi_globals_struct *, v)

sapi_api extern int sapi_globals_id;


#define sg (v) (sapi_globals.v)

extern sapi_api sapi_globals_struct sapi_globals;


sapi_api void sapi_startup (sapi_module_struct * sf);

sapi_api void sapi_shutdown (void);

sapi_api void sapi_activate (tsrmls_d);

sapi_api void sapi_deactivate (tsrmls_d);

sapi_api void sapi_initialize_empty_request (tsrmls_d);

end_extern_c ()

Members are here sapi_globals_struct

So how can we call it like this

sg (default_mimetype)

sg (request_info) .request_uri

Can feel this piece of code

static int sapi_cgi_send_headers (sapi_headers_struct * sapi_headers tsrmls_dc)
  char buf [sapi_cgi_max_header_length];
  sapi_header_struct * h;
  zend_llist_position pos;
  long rfc2616_headers=0;
  if (sg (request_info) .no_headers == 1) {
    return sapi_header_sent_successfully;
  if (sg (sapi_headers) .http_response_code!=200) {
    int len;
    len=sprintf (buf, "status:%d \ r \ n", sg (sapi_headers) .http_response_code);
    phpwrite_h (buf, len);
  if (sg (sapi_headers) .send_default_content_type) {
    char * hd;
    hd=sapi_get_default_content_type (tsrmls_c);
    phpwrite_h ("content-type:", sizeof ("content-type:") -1);
    phpwrite_h (hd, strlen (hd));
    phpwrite_h ("\ r \ n", 2);
    efree (hd);
  h=zend_llist_get_first_ex (&sapi_headers->headers,&pos);
  while (h) {
    phpwrite_h (h->header, h->header_len);
    phpwrite_h ("\ r \ n", 2);
    h=zend_llist_get_next_ex (&sapi_headers->headers,&pos);
  phpwrite_h ("\ r \ n", 2);
  return sapi_header_sent_successfully;

2.eg executor globals

eg get the data in struct _zend_execution_globals structure

struct _zend_execution_globals {
 hashtable symbol_table;/* global scope,If you do not enter the function,Global=Active * /
 hashtable * active_symbol_table;/* active scope,Current scope * /

Usually, using eg (symbol_table) to get the symbol table in the global scope,Use eg (active_symbol_table) to get the symbol table under the current scope

For example, to define $foo="bar"

zval * fooval;

make_std_zval (fooval);

zval_string (fooval, "bar", 1);

zend_set_symbol (eg (active_symbol_table), "foo", fooval);

Or look up $foo from the symbol table

zval ** fooval;

if (zend_hash_find (&eg (symbol_table), "foo", sizeof ("foo"), (void **)&fooval) == success) {

return_stringl (z_strval_pp (fooval), z_strlen_pp (fooval));

} else {



In the above code,eg (active_symbol_table) ==&eg (symbol_table)

Cg () is used to access core global variables.(zend/zend_globals_macros.h)

4, pg () php global variables.We know that php.ini will map one or more php global structures.(main/php_globals.h)

5, fg () file global variables.The data stream for most file i/o or related global variables is stuffed into the standard extended exit structure.(ext/standard/file.h)

5.Get the type and value of a variable

#define z_type (zval) (zval) .type

#define z_type_p (zval_p) z_type (* zval_p)

#define z_type_pp (zval_pp) z_type (** zval_pp)

Such as getting the type of a variable

void describe_zval (zval * foo)
  if (z_type_p (foo) == is_null)
    php_printf ("The data type of this variable is:null");
    php_printf ("The data type of this variable is not null, the corresponding number of this data type is:%d", z_type_p (foo));

There are so many types

#define is_null 0

#define is_long 1

#define is_double 2

#define is_bool 3

#define is_array 4

#define is_object 5

#define is_string 6

#define is_resource 7

#define is_constant 8

#define is_constant_array 9

#define is_callable 10

The php_printf () function is a layer of the kernel's encapsulation of the printf () function.We can use it just like the printf () function,The parameters of a macro ending in p are mostly * zval variables. In addition, there are two macros for obtaining variable types.They are z_type and z_type_pp. The former parameter is zval type, while the latter parameter is ** zval.

For example, the implementation of gettype function

//Start defining the function gettype in php language
php_function (gettype)
  //arg indirectly points to the parameters passed when calling the gettype function.
Is a zval ** structure
  //So we want to use a macro with the __pp suffix for it.
  zval ** arg;
  //The operation of this if is to make arg point to the parameter ~
  if (zend_parse_parameters (zend_num_args () tsrmls_cc, "z",&arg) == failure) {
  //Call the z_type_pp macro to get the type of arg pointing to zval.
  //Then a switch structure, the retval_string macro represents the value of the string type returned by this gettype function
  switch (z_type_pp (arg)) {
    case is_null:
      retval_string ("null", 1);
    case is_bool:
      retval_string ("boolean", 1);
    case is_long:
      retval_string ("integer", 1);
    case is_double:
      retval_string ("double", 1);
    case is_string:
      retval_string ("string", 1);
    case is_array:
      retval_string ("array", 1);
    case is_object:
      retval_string ("object", 1);
    case is_resource:
        char * type_name;
        type_name=zend_rsrc_list_get_rsrc_type (z_lval_pp (arg) tsrmls_cc);
        if (type_name) {
          retval_string ("resource", 1);
      retval_string ("unknown type", 1);

Get the value of a variable,There are so many macros to get




string value

string length

z_lval ()
z_bval ()
z_dval ()
z_strval ()
z_strlen ()
z_lval_p ()
z_bval_p ()
z_dval_p ()
z_strval_p ()
z_strlen_p ()
z_lval_pp ()
z_bval_pp ()
z_dval_pp ()
z_strval_pp ()
z_strlen_pp ()



object properties

object class entry

resource value

z_arrval ()
z_obj ()
z_objprop ()
z_objce ()
z_resval ()
z_arrval_p ()
z_obj_p ()
z_objprop_p ()
z_objce_p ()
z_resval_p ()
z_arrval_pp ()
z_obj_pp ()
z_objprop_pp ()
z_objce_pp ()
z_resval_pp ()

Implementation of rot13 function

php_function (rot13)
 zval ** arg;
 char * ch, cap;
 int i;
 if (zend_num_args ()!=1 || zend_get_parameters_ex (1,&arg) == failure) {
 * return_value=** arg;
 zval_copy_ctor (return_value);
 convert_to_string (return_value);
 for (i=0, ch=return_value->value.str.val;
   i<return_value->value.str.len;i ++, ch ++) {
    cap=* ch&32;
    * ch&= ~ cap;
    * ch=((* ch>= "a")&&(* ch<= "z")?((* ch- "a" +13)%26 + "a"):* ch) | cap;

To get the value of a variable,It should also be accessed using zend-defined macros.For simple scalar data types, boolean, long, double, use z_bval, z_lval, z_dval

void display_values ​​(zval boolzv, zval * longpzv, zval ** doubleppzv)
 if (z_type (boolzv) == is_bool) {
  php_printf ("the value of the boolean is:%s \ n", z_bval (boolzv)?"true":"false");
 if (z_type_p (longpzv) == is_long) {
  php_printf ("the value of the long is:%ld \ n", z_lval_p (longpzv));
 if (z_type_pp (doubleppzv) == is_double) {
  php_printf ("the value of the double is:%f \ n", z_dval_pp (doubleppzv));

For string types,Because it contains two fields char * (z_strval) and int (z_strlen), two macros are needed to take the value,Because you need binary safe output for this string

void display_string (zval * zstr)
 if (z_type_p (zstr)!=is_string) {
  php_printf ("the wronng datatype was passed! \ n");
 phpwrite (z_strval_p (zstr), z_strlen_p (zstr));

Because arrays exist as hashtables in zval,So use z_arrval () for access

void display_zval (zval * value)
  switch (z_type_p (value)) {
    case is_null:
      /* If it is null, nothing is output * /
    case is_bool:
      /* If it is a bool type, and true, it outputs 1, otherwise it does nothing * /
      if (z_bval_p (value)) {
        php_printf ("1");
    case is_long:
      /* If it is a long integer, output the numeric form * /
      php_printf ("%ld", z_lval_p (value));
    case is_double:
      /* If it is double, output floating point number * /
      php_printf ("%f", z_dval_p (value));
    case is_string:
      /* If it is a string, the binary safe output of this string * /
      phpwrite (z_strval_p (value), z_strlen_p (value));
    case is_resource:
      /* If it is a resource,The output is in the format of resource #10 * /
      php_printf ("resource #%ld", z_resval_p (value));
    case is_array:
      /* If it is an array, output 5 letters of array! * /
      php_printf ("array");
    case is_object:
      php_printf ("object");
      /* should never happen in practice,       * but it "s dangerous to make assumptions
       * /
       php_printf ("unknown");

Some type conversion functions

zend_api void convert_to_long (zval * op);

zend_api void convert_to_double (zval * op);

zend_api void convert_to_null (zval * op);

zend_api void convert_to_boolean (zval * op);

zend_api void convert_to_array (zval * op);

zend_api void convert_to_object (zval * op);

zend_api void _convert_to_string (zval * op zend_file_line_dc);

Instantiation of constants

We can instantiate it like this

php_minit_function (consts)
  register_long_constant ("consts_meaning_of_life", 42, const_cs | const_persistent);
  register_double_constant ("consts_pi", 3.1415926, const_persistent);
  register_string_constant ("consts_name", "leon", const_cs | const_persistent);
php_rinit_function (consts) //Define constants on every request
  char buffer [40];
  srand ((int) time (null));
  snprintf (buffer, sizeof (buffer), "%d", rand ());
  register_string_constant ("consts_rand", estrdup (buffer), const_cs);
  return success;

Common macros

/* Register long constant * /

#define register_long_constant (name, lval, flags) zend_register_long_constant ((name), sizeof (name), (lval), (flags), module_number tsrmls_cc)

/* Register double constant * /

#define register_double_constant (name, dval, flags) zend_register_double_constant ((name), sizeof (name), (dval), (flags), module_number tsrmls_cc)

/* Register string constant * /

#define register_string_constant (name, str, flags) zend_register_string_constant ((name), sizeof (name), (str), (flags), module_number tsrmls_cc)

/* Register string constant * /

#define register_stringl_constant (name, str, len, flags) zend_register_stringl_constant ((name), sizeof (name), (str), (len), (flags), module_number tsrmls_cc)

7.Global variables

#php-fpm Process of generating post | get | cookie | server | env | request | files global variables
php_cgi_startup ()->php_module_startup ()->php_startup_auto_globals ()->save variables to symbol_table symbol table
php_cgi_startup () is defined in fpm/fpm/fpm_main.c
php_module_startup () is defined in main/main.c
php_startup_auto_globals () is defined in main/php_variables.h
zend_hash_update (&eg (symbol_table), "_get", sizeof ("_ get") + 1,&vars, sizeof (zval *), null);
/* Read $_server variable * /
static php_function (print_server_vars) {
  zval ** val;
  if (zend_hash_find (&eg (symbol_table), "_server", sizeof ("_ server"), (void **)&val) == success) {
    return_zval (* val, 1, 0);
  } else {
/* Read $_server [$name] * /
zend_begin_arg_info (print_server_var_arginfo, 0)
  zend_arg_info (0, "name")
zend_end_arg_info ()
static php_function (print_server_var) {
  char * name;
  int name_len;
  zval ** val;
  hashtable * ht_vars=null;
  hashposition pos;
  zval ** ret_val;
  if (zend_parse_parameters (zend_num_args () tsrmls_cc, "| s!",&name,&name_len) == failure) {
    return_null ();
  if (zend_hash_find (&eg (symbol_table), "_server", sizeof ("_ server"), (void **)&val) == success) {
    ht_vars=z_arrval_pp (val);
    //You need to pass in a value greater than name length +1, because "\ 0" is needed after the string value
    if (zend_hash_find (ht_vars, name, name_len + 1, (void **)&ret_val) == success) {return_string (z_strval_pp (ret_val), 0);
    } else {
      return_null ();
  } else {
    return_null ();

Packaging third-party libraries

Configuration (config.m4)

search_path="/usr/local/usr" #lib searched directories
search_for="/include/curl/curl.h" #path to lib header files
if test -r $php_libs/$search_for;then
else #search default path list
  ac_msg_checking ([for libs files in default path])
  for i in $search_path;do
    if test -r $i/$search_for;then
      libs_dir=$i #path to the libs found
      ac_msg_result (found in $i)
/* Verify the existence of lib * /
if test -z "$libs_dir";then
  ac_msg_result ([not found])
  ac_msg_error ([please reinstall the libs distribution])
/* Add lib include directory when compiling, -i/usr/include * /
php_add_include ($libs_dir/include)
libname=curl #lib name
libsymbol=curl_version #lib a function,Used by php_check_library to verify lib
/* Verify lib * /
php_check_library ($libname, $libsymbol,[
  php_add_library_with_path ($libname, $libs_dir/$php_libdir, libs_shared_libadd) #Link lib when compiling, -llibcurl
  ac_define (have_libslib, 1, [])
], [
  ac_msg_error ([wrong libs lib version or lib not found])
], [
  -l $libs_dir/$php_libdir -lm
php_subst (libs_shared_libadd)

9.Macros for return

//These macros are defined in the zend/zend_api.h file

#define retval_resource (l) zval_resource (return_value, l)

#define retval_bool (b) zval_bool (return_value, b)

#define retval_null () zval_null (return_value)

#define retval_long (l) zval_long (return_value, l)

#define retval_double (d) zval_double (return_value, d)

#define retval_string (s, duplicate) zval_string (return_value, s, duplicate)

#define retval_stringl (s, l, duplicate) zval_stringl (return_value, s, l, duplicate)

#define retval_empty_string () zval_empty_string (return_value)

#define retval_zval (zv, copy, dtor) zval_zval (return_value, zv, copy, dtor)

#define retval_false zval_bool (return_value, 0)

#define retval_true zval_bool (return_value, 1)

#define return_resource (l) {retval_resource (l);return;}

#define return_bool (b) {retval_bool (b);return;}

#define return_null () {retval_null ();return;}

#define return_long (l) {retval_long (l);return;}

#define return_double (d) {retval_double (d);return;}

#define return_string (s, duplicate) {retval_string (s, duplicate);return;}

#define return_stringl (s, l, duplicate) {retval_stringl (s, l, duplicate);return;}

#define return_empty_string () {retval_empty_string ();return;}

#define return_zval (zv, copy, dtor) {retval_zval (zv, copy, dtor);return;}

#define return_false {retval_false;return;}

#define return_true {retval_true;return;}

Actually, in addition to these scalar types,There are also many composite types in PHP. We need to return them in functions.Such as arrays and objects,We can manipulate them with retval_zval and return_zval

10.Hashtable traversal function

//operation function based on long key

zval * v3;

make_std_zval (v3);

zval_string (v3, "value3", 1);

zend_hash_index_update (names, 0,&v3, sizeof (zval *), null);//Update the value of the hashtable element by the numeric index key

zval ** v4;

zend_hash_index_find (names, 1,&v4);//Get the value of the hashtable element by numerical index

php_printf ("v4:");

phpwrite (z_strval_pp (v4), z_strlen_pp (v4));

php_printf ("\ n");

ulong idx;

idx=zend_hash_index_exists (names, 10);//Find the hashtable according to the numerical index, if it is found, it returns 1, otherwise it returns 0

zend_hash_index_del (names, 2);//Remove hashtable element by numerical index

//hashtable traversal function

zend_hash_internal_pointer_reset (names);//Initialize the hash pointer

zend_hash_internal_pointer_reset_ex (names,&pos);//initialize the hash pointer and pay the value

zend_hash_get_current_data (names, (void **)&val);//Get the current hash storage value, data should be cast to void **, ie:(void **)&data

zend_hash_get_current_data_ex (names, (void **)&val,&pos) == success;//Get the current hash storage value

zend_hash_get_current_key (names,&key,&klen,&index, 0) == hash_key_is_long

zend_hash_get_current_key_ex (names,&key,&klen,&index, 0,&pos) == hash_key_is_long;//Read the current key of hashtable, there will be two kinds of hash_key_is_long | hash_key_is_string, corresponding to array ("value"), array ("key "=>" value ") Two hashtables

zend_hash_move_forward (names);

zend_hash_move_forward_ex (names,&pos);//hash pointer moves to the next bit

//hashtable length

php_printf ("%* carray (%d) {\ n", depth * 2, "", zend_hash_num_elements (z_arrval_p (zv))

A simple function

function hello_array_strings ($arr) {
  if (! is_array ($arr)) return null;
  printf ("the array passed contains%d elements", count ($arr));
  foreach ($arr as $data) {
    if (is_string ($data)) echo "$data";

php kernel implementation

php_function (hello_array_strings)
  zval * arr, ** data;
  hashtable * arr_hash;
  hashposition pointer;
  int array_count;
  if (zend_parse_parameters (zend_num_args () tsrmls_cc, "a",&arr) == failure) {
    return_null ();
  arr_hash=z_arrval_p (arr);
  array_count=zend_hash_num_elements (arr_hash);
  php_printf ("the array passed contains%d elements", array_count);
  for (zend_hash_internal_pointer_reset_ex (arr_hash,&pointer);zend_hash_get_current_data_ex (arr_hash, (void **)&data,&pointer) == success;zend_hash_move_forward_ex (arr_hash,&pointer)) {
    if (z_type_pp (data) == is_string) {
      phpwrite (z_strval_pp (data), z_strlen_pp (data));
      php_printf ("");

The above is the PHP extension development tutorial introduced in this article.Hope you like it.

  • Previous What are the differences between several applications of PHP explode () function and implode () function
  • Next Function learning tutorial in Swift language