How to get the extension implementation of the variable name of PHP variable

Source: Internet
Author: User
Tags php json php source code vars

I haven't updated my blog for a long time. One of the recent work is busy, there is no time to study the problem, the second is that there is no good material can be written. There are some problems that have not been thoroughly researched, and they are written with no clue, they are thrown in the draft box. This incidentally also to update the template of the blog, now the template body is a bit narrow, not suitable for reading. I am this blog now, the main thing is to write some technical things. Let's change to an eye-friendly theme.

This article addresses a PHP question that has been being considered since last year: How to get the variable name of a PHP variable. There has been no good research. Recently intermittent start to look at the PHP source code. And try to solve it. It was not until two weeks ago that the problem was solved that it began to be written down.

If you are interested in first see how this feature is implemented. You can click here to download the code first.

1. Question: Can I get the name of the PHP variable itself in PHP?

More than a year ago, when we made a template engine. When there is a requirement: Gets the variable name of the variable. Like what:

1

2

3

4

5

$some _variable_name = "Blahblah";

//...

Echo get_var_name ($some _variable_name); The output "Some_variable_name" is expected here;

?>

If you also have such a demand. Your understanding of the need is absolutely problematic. But then I thought it was unreasonable. But if I have such unreasonable demand, do I have a way to really be satisfied?

2. What are the workarounds

Before encountering this problem, there is no system to see the C implementation of PHP. From the point of the question to date, I think of the following methods:

  • Write a PHP function directly to get it. For example:

    1

    2

    3

    4

    Function Get_var_name ($var) {

    //But ... How do I get to the name of the variable ...

    //echo?  how to?

    }

    The person who used the $globals variable should know that the value of the variable $var can be obtained by $GLOBALS [\ ' Var\ ']. In that case, I should have done it.

    1

    2

    3

    4

    5

    6

    7

    Function Get_var_name ($var) {

    foreach ($GLOBALS as $var _name  => $var _value) {

    if ($v AR = = = $var _value) {

    return $var _name;

    }

    }

    }

    This is not possible. First, this method can only return variables that are within the global scope. If you call this function in the function body, there is a problem. All unreliable.

  • Then I began to look at the internal implementation of PHP. Know that all the variables in PHP execution are stored in the symbol table (symbol_table), similar to the $globals variable, stored in the same way as the variable name = = value. And there are different active_symbol_table in different scopes, so there is no scope problem, then we can compare it from the current symbol table according to the value of the variable passed in. The values in the symbol table are stored in a pointer to the ZVAL structure. Is it possible to find the name of the variable that holds the value by comparing the pointer address? In fact, this is not feasible. Because there may be multiple variables within PHP that point to the same internal value. That is, the reference count. It seems that the symbol table doesn't solve the problem.
  • Further learning through the internal implementation of PHP found that there are many other rich internal information available when the script is running. For example, the following script runs a global variable. This is also the key to solve this problem, this article will be based on these run-time information to write an extension to implement the function.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    Ten

    One

    struct _zend_executor_globals {

    Zval **return_value_ptr_ptr;

    Zval Uninitialized_zval;

    Zval *uninitialized_zval_ptr;

    Zval Error_zval;

    Zval *error_zval_ptr;

    Zend_ptr_stack Arg_types_stack;

    /* Symbol table cache */

    HashTable *symtable_cache[symtable_cache_size];

    HashTable **symtable_cache_limit;

    HashTable **symtable_cache_ptr;

    Zend_op **opline_ptr;

    HashTable *active_symbol_table;  //variable symbol table for current scope

    HashTable symbol_table;    /* main symbol Table */ //global symbol table

    HashTable included_files;  /* files already included */

    //..

    };

  • -One final sure way to do this is to add an echo-like syntax structure to PHP. This approach is the most intrusive and will not be discussed in this log, and I'll explain in the next log how to support the opening question by modifying PHP syntax.
  • 3. Extended implementations

    For example, the module provides a function called Get_var_name () to get the variable name. If you have any experience with PHP extensions, you should see a function implementation similar to the following (taken from PHP json extension $PHP_SRC/EXT/JSON/JSON.C):

    1

    2

    3

    4

    5

    6

    7

    8

    9

    Ten

    One

    /* {{{proto string Json_encode (mixed data [, int options])

    Returns the JSON representation of a value */

    Static php_function (Json_encode)

    {

    Zval *parameter;

    Smart_str buf = {0};

    Long options = 0;

    If (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "Z|l", ¶meter, &options) = = FAILURE) { //This takes the incoming parameter out. Reference Document http://www.php.net/manual/en/internals2.funcs.php

    return;

    }

    Php_json_encode (&buf, parameter, Options tsrmls_cc);

    Zval_stringl (Return_value, BUF.C, Buf.len, 1);

    Smart_str_free (&BUF);

    }

    /*}}} */

    This implementation in the body of the function can be zend_parse_parameter way to get passed in the variable, but this can only get the value of the variable. But we can't get any more information. Let's go down to the bottom to see how the function is called in PHP, and how the arguments are passed.

    3.1 The invocation of functions in PHP

    Before we look at how the function is called, we need to see how the PHP code executes.

    It can be roughly divided into 2 steps:

    -Lexical analysis, parsing and compiling into opcode

    -Executive opcode

    The execution of PHP functions can only be performed at the opcode execution stage.

    Here's a great tool to see opcode VLD (HTTP://PECL.PHP.NET/PACKAGE/VLD)

    Install this extension. You can view the php script compiled opcode from the command line

    Let's see what the following php script opcode after it was compiled.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    $str = "http://reeze.cn";

    $len = strlen ($STR);

    $len 2 = strlen ($str 2=10);

    Echo $len;

    echo $len 2;

    ?>

    $ php-dvld.active=1 func_call.php

    You can also add a parameter-dvld.verbosity=3, which will show more information.

    It is compiled into the above 10 opcode commands.

    The name of OP can tell what it means. .. Where to "!" The number at the beginning represents the compiled variable, and the variable that begins with "~" represents the zero-hour variable.

    It can be seen that if a function call has parameters, the Send_var or send_var_no_ref instructions are executed before do_fcall. And these instructions are followed by a compiled variable or a temporary variable.

    When called in PHP, we are able to access the opcode information for this do_fcall operation. The current instruction can be obtained through EG (ACTIVE_OPLINE_PTR)

    There is a series of *g macros in PHP, and EG is a global variable when executing opcode.

    See document: $PHP _src/zend/zend_globals.h

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21st

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    struct _zend_executor_globals {

    // ...

    Zend_op **opline_ptr; Points to the currently executing Zend_op object

    HashTable *active_symbol_table;

    HashTable symbol_table; /* Main symbol Table */

    HashTable Included_files; /* files already included */

    Jmp_buf *bailout;

    int error_reporting;

    int orig_error_reporting;

    int exit_status;

    Zend_op_array *active_op_array;

    // ...

    };

    struct _zend_op_array {

    /* Common Elements */

    Zend_uchar type;

    Char *function_name;

    Zend_class_entry *scope;

    Zend_uint Fn_flags;

    Union _zend_function *prototype;

    Zend_uint Num_args;

    Zend_uint Required_num_args;

    Zend_arg_info *arg_info;

    Zend_bool pass_rest_by_reference;

    unsigned char return_reference;

    /* END of common Elements */

    Zend_bool Done_pass_two;

    Zend_uint *refcount;

    Zend_op *opcodes; An array of zend_op.

    Zend_uint last, size;

    Zend_compiled_variable *vars; All compiled variable information since PHP5.1 this is an array

    int Last_var, Size_var; Last_var the index of the last compiled variable

    // ...

    };

    Save the information for all compiled variables in the currently executing Op_array, and then look at the structure of zend_compiled_variable.

    1

    2

    3

    4

    5

    typedef struct _ZEND_COMPILED_VARIABLE {

    Char *name;

    int Name_len;

    ULONG Hash_value;

    } zend_compiled_variable;

    This is the name of the variable I want to get.

    We can get to the currently executing ZEND_OP by using the global variable eg (opline_ptr) pointer, the structure of the ZEND_OP is as follows:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    struct _ZEND_OP {

    opcode_handler_t handler; Processing function for handling this opcode

    Znode result; The results of the opcode execution

    Znode OP1; Some opcode require 1, and some require two operands.

    Znode OP2;

    ULONG Extended_value;

    UINT Lineno;

    Zend_uchar opcode; The value of the opcode is shown in $php_src/zend/zend_vm_opcodes.h

    };

    This is the opcode that we execute when we call the function. We can now get to do_fcall when the opcode, through the vld view opcode tool is easy to know before the function call, if the function has parameters, before Do_fcall must have Send_var or SEND_VAR_NO_REF instruction, the pointer back one must be pointing to the Send_var or send_var_no_ref instructions. In this way we can get the Send_var instructions in a short time according to the ZEND_OP command Do_fcall obtained. The send_var instruction will manipulate the compiled_var so that we can get the information about the variable.

    See what information Znode has:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    typedef struct _ZNODE {

    int op_type;

    Union {

    ZVAL constant;

    Zend_uint var; This var is the index of the current variable in the compiled_variable array in Zend_op_array.vars. But this request is not literal. For details, see the final code implementation.

    Zend_uint Opline_num; /* Needs to be signed */

    Zend_op_array *op_array;

    Zend_op *jmp_addr;

    struct {

    Zend_uint var; /* Dummy */

    Zend_uint type;

    } EA;

    } u;

    } Znode;

    As in the comments above. You can get information about a variable by getting the value of Znode.u.var.

    In this case, the implementation of the program is simple.

    Here is the implementation:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21st

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    /* {{{get_var_name

    *

    * This extension requires PHP >= 5.1

    * Due to PHP 5.1 introduced compiled variable

    *

    * Export a get_var_name function in PHP space.

    * Echo get_var_name ($var _name); Expect:var_name

    * Echo get_var_name ($lineno =100); Expect:lineno

    */

    Php_function (Get_var_name)

    {

    int Len;

    Char *STRG = "";

    if (Zend_num_args () < 1) {

    Return

    }

    /* Show all compilation variables

    int i;

    Zend_compiled_variable *vars = EG (Active_op_array)->vars;

    for (i=0; i < EG (active_op_array)->last_var; ++i) {//Last_var index of the last compiled variable

    spprintf (&STRG, 0, "%s\\nvar:%s\\n", STRG, EG (Active_op_array)->vars[i].name);

    ++vars;

    }

    */

    Zend_op *pre_opline_ptr = *eg (opline_ptr);

    pre_opline_ptr--;

    This type of invocation is supported: get_var_name ($a = "VALUE"); Expect:a

    This adds the ability to correctly return the name of the variable in the case of assignment, and if the method parameter is assigned, the compiled opcode will be send_var before the

    There is a zend_assign operation, and the return value of the zend_assign operation is used. For example: $c = $d + 1; The return value of $d + 1 is used. You'll be able to confirm

    Is the previous method of invocation

    Zend_op *pre_pre_online_ptr = pre_opline_ptr-1;

    if (pre_pre_online_ptr && pre_pre_online_ptr->opcode = = Zend_assign &&! Pre_pre_online_ptr->result.u.ea.type & ext_type_unused)) {

    Get variable information by assigning a value before the Zend_op

    Pre_opline_ptr = pre_pre_online_ptr;

    }

    int index;

    such as Get_var_name ($name); At this time Send_var opcode op1 operand type is IS_CV that is Compiled Variable

    Only compiled variable is the direct storage index. PHP >= 5.1

    if (Pre_opline_ptr->op1.op_type = = IS_CV) {

    index = pre_opline_ptr->op1.u.var;

    }

    else {

    Please refer to VLD source code $VLD _src/srm_oparray.c line:320 vld_dump_znode function

    index = pre_opline_ptr->op1.u.var/sizeof (temp_variable);

    }

    zend_compiled_variable var = EG (Active_op_array)->vars[index];

    Len = spprintf (&STRG, 0, "%s", STRG, Var.name);

    Return_stringl (STRG, Len, 0);

    }

    /* }}} */

    Click here to download the code

    • This article is from: Linux Learning Tutorial Network

How to get the extension implementation of the variable name of PHP variable

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.