Translation [PHP extension development and Embedded] Chapter 6th-return value

Source: Internet
Author: User
return value

The user-space function uses the return keyword to send back information to its calling space, which is the same as the C language syntax.

For example:

function Sample_long () {    return;  }  $bar = Sample_long ();

When Sample_long () is called, it returns 42 and is set to the $bar variable. The equivalent code in the C language is as follows:

int Sample_long (void) {    return;  }  void Main (void) {    int bar = Sample_long ();  }

Of course, in C you always know what the function is called and return based on the function prototype, so you define the variable that returns the result store. In PHP User space processing, the variable type is dynamic, relying instead on the type of Zval described in chapter 2nd, "Inside and outside of variables".

Return_value variable

You might think that your intrinsic function should return a zval directly, or allocate a zval memory space and return Zval * as follows.

Php_function (Sample_long_wrong)  {      zval *retval;        Make_std_zval (retval);      Zval_long (retval);        return retval;  }

Unfortunately, this is not the right thing to do. It is not mandatory for each function implementation to allocate zval and return it. Instead, the Zend engine allocates this space beforehand before the function call. The type of zval is then initialized to Is_null and the value is passed as the parameter name Return_value. Here's the right approach:

Php_function (Sample_long)  {      zval_long (return_value);      return;  }

Note that the php_function () implementation does not return any values directly. Instead, the appropriate data is ejected directly into the Return_value parameter, and the Zend engine processes it after the internal function has finished executing.

Friendly tip: Zval_long () macros are an encapsulation of multiple assignment operations:

Z_type_p (return_value) = Is_long;  Z_lval_p (Return_value) = 42;

or more directly:

Return_value->type = Is_long;  Return_value->value.lval = 42;

The Is_ref and RefCount properties of Return_value should not be modified directly by the intrinsic function. These values are initialized and processed by the Zend engine when you call your function.

Now let's take a look at this special function and add it to the sample extension created in Chapter 5th, "Your first extension." Just add the function below the Sample_hello_world () function and include Sample_long () in the php_sample_functions structure:

Static Function_entry php_sample_functions[] = {      Php_fe (sample_hello_world, NULL)      Php_fe (Sample_long, NULL)      {null, NULL, NULL}  };

Now we can execute make to rebuild the extension.

If everything is OK, you can run PHP and test the new function:

$ Php-r ' Var_dump (Sample_long ());

Packaging more compact macros

In terms of code readability and maintainability, there is a duplicate part of the zval_* () macro: the Return_value variable. In this case, replace the zval of the macro with retval, and we can omit return_value at the time of the call.

In the previous example, the implementation code for Sample_long () can be reduced to the following:

Php_function (Sample_long)  {      retval_long);      return;  }

The following table lists the macros for the RetVal family in the Zend engine. In addition to the two special, the RETVAL macro is the same as the corresponding ZVAL family macro except that the Return_value parameter is removed.


Be aware that true and false macros have no parentheses. This is due to the deviation of the zend/php code standard and retains the primary one to maintain backwards compatibility. If you fail to build the extension, you receive the error message undefined macro retval_true (), make sure that you write the two macros in code with the parentheses incorrectly.

Normally, when your function processes the return value, it is ready to exit and return control to the calling scope. For this reason, additional macros have been designed for internal functions to return: return_* () family macros.

Php_function (Sample_long)  {      return_long;  }

Although not visible, this function does return after the Return_long () macro is called. We can test by adding php_printf () at the end of the function:

Php_function (Sample_long)  {      return_long);      php_printf ("I'll Never Be reached.\n");  }

Php_printf (), as the content describes, because the Return_long () call implicitly ends the function.

As with the RetVal series, each simple type listed in the preceding table has a corresponding return macro. Similarly, as with the RetVal series, return_true and Return_false macros do not use parentheses.

More complex types, such as objects and arrays, are also returned by the Return_value parameter; However, they are inherently not created by simple macros. Even though the resource type has a retval macro, the actual return of the resource type requires additional work. You will see how to return these types in chapters 8th through 11th.

Is it worth the trouble?

An unused Zend intrinsic function attribute is the return_value_used parameter. Consider the following user space code:

function Sample_array_range () {      $ret = array ();      for ($i = 0; $i < $i + +) {          $ret [] = $i;      }      return $ret;  }  Sample_array_range ();

Because the Sample_array_range () call does not store the result in a variable, creating an array space of the 1000 elements used here will be completely wasted. Of course, it's foolish to call Sample_array_range (), but there's no good way to predict the future.

Although the user-space function cannot be accessed, the intrinsic function can rely on all the intrinsic functions of the public return_value_used parameter settings, conditional skipping such meaningless behavior.

Php_function (Sample_array_range)  {      if (return_value_used) {          int i;          /* Returns an array from 0 to 999 *          /Array_init (return_value);          for (i = 0; i < i++) {              Add_next_index_long (return_value, i);          }          return;      } else {          /* hint error *          /php_error_docref (NULL tsrmls_cc, E_notice,                 "Static return-only function called without Processing output ");          Return_null ();      }  }

To see the operation of this function, simply add the function to your sample.c source file and expose it to the php_sample_functions structure:

Php_fe (Sample_array_range, NULL)

How to handle the return value of a function that is not used in user space, you can read the Zend_do_fcall_common_helper_spec function in Zend/zend_vm_execute.h, after it finishes processing the internal function call, Checks whether the return value of the function is used and, if not used, releases it accordingly.

Return reference value

You may already know from the PHP work of user space that PHP functions can also return values in a reference way. Because of the implementation problem, you should avoid returning the reference in the intrinsic function before PHP 5.1 because it does not work. Consider the following user space code snippet:

function &sample_reference_a () {/      * If the global space does not have a variable $ A, it is created with an initial value of NULL *      /if (!isset ($GLOBALS [' a '])) {          $ Globals[' a '] = NULL;      }      return $GLOBALS [' a '];  }  $a = ' Foo ';  $b = Sample_reference_a ();  $b = ' Bar ';

In this code snippet, it is like using $b = & $GLOBALS [' A], or because in the global space, use $b = & $a; Create the $b as a reference to $ A.

Recalling the 3rd Chapter, "Memory Management", when the last line is reached, $a and $b actually contain the same value ' Bar '. Now let's look at the internal implementation of this function:

#if (Php_major_version > 5) | |  (Php_major_version = = 5 && \ php_minor_version > 0)          Php_function (sample_reference_a) {zval **a_ptr, *a;                                            /* Find the variable $ A/if (Zend_hash_find (&eg (symbol_table), "a", sizeof ("a") from the global symbol table,      (void**) &a_ptr) = = SUCCESS) {a = *a_ptr;          } else {/* If there is no $globals[' a '] then create it */Alloc_init_zval (a); Zend_hash_add (&eg (symbol_table), "a", sizeof ("a"), &a, sizeof (zval*      ), NULL);      }/* Discard the old return value */Zval_ptr_dtor (RETURN_VALUE_PTR);          if (!a->is_ref && a->refcount > 1) {/* $a need to be copied on write, before use, must first isolate */Zval *newa;          Make_std_zval (Newa);          *newa = *a;          Zval_copy_ctor (Newa);          Newa->is_ref = 0;          Newa->refcount = 1; Zend_hash_update (&eg (symbol_table), "a", sizeof ("a"), &newa,                                                   sizeof (zval*), NULL);      A = Newa;      }/* Set the new return value to reference and increase refcount */a->is_ref = 1;      a->refcount++;  *return_value_ptr = A; } #endif/* PHP >= 5.1.0 */

The Return_value_ptr parameter is another common parameter that all intrinsic functions pass, which is the zval * * type, which contains a pointer to Return_value. by calling Zval_ptr_dtor () above it, the default return_value of Zval * will be released. Then you are free to choose a new zval * to replace it, where the variable $ A is selected, selectively zval isolated, set its is_ref to 1, set to Return_value_ptr.

If you compile and run the code now, you will get a segment error anyway. In order to make it work, you need to add the following code to the php_sample.h:

#if (Php_major_version > 5) | | (Php_major_version = = 5 && \                            Php_minor_version > 0)  static      zend_begin_arg_info_ex (Php_sample_retref_arginfo, 0, 1, 0)      Zend_end_ Arg_info ()  #endif/* PHP >= 5.1.0 */

Translation: In the php-5.4.9 used by the translator, the ZEND_BEGIN_ARG_INFO_EX macro definition already contains the static modifier, so the book example needs to be modified accordingly, so readers should pay attention to this point during reading.

Next, use this structure when declaring your function in Php_sample_functions:

#if (Php_major_version > 5) | | (Php_major_version = = 5 && \                            Php_minor_version > 0)      php_fe (sample_reference_a, Php_sample_retref_arginfo)  #endif/* PHP >= 5.1.0 */

This structure you will learn in detail later in this chapter to provide important hints about function calls to the Zend engine. Here it tells the Zend engine that Return_value needs to be overwritten and should get the correct address from the return_value_ptr. Without this hint, the Zend engine simply sets null in Return_value_ptr, which may cause the program to crash when it executes to Zval_ptr_dtor ().

Each fragment of this code is wrapped in an # if block, which instructs the compiler to enable this support only if the PHP version is greater than or equal to 5.1. Without these conditional directives, this extension will not compile on php4 (because some elements contained in return_value_ptr do not exist) and cannot provide the correct functionality in php5.0 (there is a bug that causes the returned reference to be copied in a value)

Reference method return value

It is no problem to use the return (syntax) structure to pass values and variables back to the caller, but sometimes you need to return multiple values from a function. You can use arrays (we'll work with arrays and hash tables in the 8th chapter) to do this, or you can use the parameter stack to return values.

Reference value at call

A simple way to refer to a pass-through variable is to use the Fetch address character (&) in front of the parameter variable name, as in the following user-space code:

function Sample_byref_calltime ($a) {      $a. = ' (modified by ref!) ';  }  $foo = ' I am a string ';  Sample_byref_calltime (& $foo);  Echo $foo;

The Fetch address character (&) before the parameter variable name causes the $foo actual zval to be sent to the function instead of its content copy. This allows the function to change this value to return information by passing this parameter. If you call Sample_byref_calltime () without using the Fetch address character (&) in front of $foo, the modification within the function does not affect the original variable.

Repetition of this behavior at the C level does not require special coding. Create the following function after Sample_long () in your sample.c source file:

Php_function (sample_byref_calltime)  {      zval *a;      int addtl_len = sizeof ("(Modified by ref!)")-1;        if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "Z", &a) = = FAILURE) {          return_null ();      }      if (!a->is_ref) {/          * does not pass a value by reference and does nothing to leave the function */          return;      }      /* Make sure the variable is a string *      /convert_to_string (a);      /* Expand the buffer of a so that the data to be appended is saved *      /z_strval_p (a) = Erealloc (Z_strval_p (a),          z_strlen_p (a) + Addtl_len + 1);      memcpy (Z_strval_p (a) + Z_strlen_p (a),      "(Modified by ref!)", Addtl_len + 1);      Z_strlen_p (a) + = Addtl_len;  }

As always, this function needs to be added to the php_sample_functions structure.

Php_fe (Sample_byref_calltime,        NULL)

The runtime reference value is completely deprecated in the version used by the translator (php-5.4.9 Zendengine 2.4.0). Earlier versions can be enabled using the Allow_call_time_pass_reference directive in php.ini. Be aware of the version issue when testing.

Compile-time reference pass-through value

The more common way is the compile-time reference pass-through value. Here the parameters of the function are defined to be used only in reference, passing constants or other intermediate values (such as the result of a function call) will result in an error, because there is no place for the function to store the result value to return. The compile-time reference value code for the user space is as follows:

Function Sample_byref_compiletime (& $a) {      $a. = ' (modified by ref!) ';  }  $foo = ' I am a string ';  Sample_byref_compiletime ($foo);  Echo $foo;

As you can see, this differs from invoking the reference value only in the location where the address character is taken. When you look at this function on the C level, the function code is exactly the same. The only difference is the declaration of the function in the php_sample_functions block:

Php_fe (Sample_byref_compiletime, Php_sample_byref_arginfo)

Php_sample_byref_arginfo is a constant structure, and you need to define it before you use it.

In fact, the code for the Is_ref check can be removed from the compile-time reference, because it is always guaranteed to be referenced. But there's no harm in keeping it here.

In Zend Engine 1 (PHP4), this structure is a simple char * list, the first element specifies the length, and the next is a collection of tokens that describe each function parameter.

static unsigned char php_sample_byref_arginfo[] =                                  {1, byref_force};

Here, 1 indicates that the vector contains only one parameter of information. The following elements describe the parameter-specific tag information in sequence, and the second element describes the first parameter. If the second or third argument is involved, the corresponding one needs to add the third and fourth elements in the vector. Optional values for parameter information are as follows:

In Zend Engine 2 (php 5+), you will use a more extensible structure that contains more information about the class, such as the minimum and maximum parameter requirements, type hinting, whether to force references, and so on.

First, the parameter information structure uses one definition from two macros. The simpler one is Zend_begin_arg_info (), which requires two parameters:

Zend_begin_arg_info (name, pass_rest_by_reference)

Name is very simple, that is, the extension in other places to use the structure when the name, the current example we use the name is: Php_sample_byref_arginfo

The meaning of pass_rest_by_reference and byref_force_rest is used in the Zend Engine 1 parameter information vector when the last element is consistent. If this parameter is set to 1, all parameters that are not explicitly described in the structure are considered parameters of the compile-time reference pass-through values.

There is also an optional start macro that introduces two new options not available for Zend Engine 1, which is ZEND_BEGIN_ARG_INFO_EX ():

ZEND_BEGIN_ARG_INFO_EX (name, Pass_rest_by_reference, Return_reference,                               Required_num_args)

Of course, name and pass_rest_by_reference are the same meaning as before. As mentioned earlier in this chapter, Return_reference is telling Zend that your function needs to overwrite return_value_ptr with your own zval.

The last argument, Required_num_args, is another type of hint that tells Zend to skip the function call completely when a call is considered incomplete.

After you have a proper start macro, you can then be 0 or more zend_arg_*info elements. The types and usage of these macros are shown in the following table.

Finally, all parameter information structures that use the Zend Engine 2 macro settings must end with Zend_end_arg_info (). For your sample function, you need to select a structure like this:

Zend_begin_arg_info (php_sample_byref_arginfo, 0)      zend_arg_pass_info (1)  zend_end_arg_info ()

To make the extensions compatible with Zend Engines 1 and 2, you need to define the ARG_INFO structure for both by using the #ifdef statement:

#ifdef zend_engine_2  static      zend_begin_arg_info (php_sample_byref_arginfo, 0)          zend_arg_pass_info (1)      zend_end_arg_info ()  #else/* ZE 1 */  static unsigned char php_sample_byref_arginfo[] =                                  {1, Byref_force };  #endif

Note that these snippets are lumped together, and it's time to create a real compile-time reference pass. First, we will place the php_sample_byref_arginfo blocks defined for Zend Engines 1 and 2 in the header file php_sample.h.

Next, you can have two options, one copy of Php_function (Sample_byref_calltime), and rename it to Php_function (Sample_byref_compiletime), followed by php_ Sample_functions Add a row Php_fe (Sample_byref_compiletime, Php_sample_byref_arginfo)

This approach is simple to move and, after a period of modification, is less likely to cause confusion. Because this is just the sample code, we can relax a little bit and use the Php_falias () you learned in the previous chapter to avoid code duplication.

Thus, instead of assigning php_function (sample_byref_calltime), add a line directly to the php_sample_functions:

Php_falias (Sample_byref_compiletime, Sample_byref_calltime,      php_sample_byref_arginfo)

Recalling the 5th chapter, this creates a user-space function named Sample_byref_compiletime () that corresponds to the internal implementation of the Code for Sample_byref_calltime (). Php_sample_byref_arginfo is a special place in this version.

Summary

In this chapter you see how to return a value from an intrinsic function, including a return value and a reference method, and return by a parameter stack reference. It is also simple to understand that the parameter types of Zend Engine 2 imply structural zend_arg_info.

In the next chapter you will continue to explore the acceptance of basic zval parameters and the use of zend_parse_parameters () powerful type tricks.

The above is [translation][php extended development and Embedded] Chapter 6th-The content of the return value, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!

  • 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.