Writing Functions

Functions and Methods in PHP take much the same form, a method is simply a function with a specific scope; the scope of their class entry. The Hacker will read about class entries in other sections of the Hacker's guide. The purpose of this section is to introduce the Hacker to the anatomy of a function or method; the Hacker will learn how to define functions, how to accept variables and how to return variables to the programmer of PHP.

The anatomy of a function could not be simpler:

PHP_FUNCTION(hackers_function) {
  /* your accepted arguments here */
  long number;
  
  /* accepting arguments */
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &number) != SUCCESS) {
      return;
  }
  
  /* do some work on the input */
  number *= 2;
  
  /* set return value */
  RETURN_LONG(number);
}

The PHP_FUNCTION(hackers_function) preprocessor instruction will result in the following declaration:

void zif_hackers_function(INTERNAL_FUNCTION_PARAMETERS)

INTERNAL_FUNCTION_PARAMETERS is defined as a macro and explained in the following table:

INTERNAL_FUNCTION_PARAMETERS
Name and type Description Access macros
int ht Number of actual parameters passed by user ZEND_NUM_ARGS()
zval *return_value Pointer to a PHP variable that can be filled with the return value passed to the user. The default type is IS_NULL. RETVAL_*, RETURN_*
zval **return_value_ptr When returning references to PHP set this to a pointer to your variable. It is not suggested to return references.  
zval *this_ptr If this is a method call, points to the PHP variable holding the $this object. getThis()
int return_value_used Flag indicating whether the returned value will be used by the caller.  

For clarity, the fully expanded declaration for PHP_FUNCTION(hackers_function) looks like:

void zif_hackers_function(int ht, zval* return_value, zval** return_value_ptr, zval* this_ptr, int return_value_used)

The presence of this_ptr may be confusing, classes are covered in detail in later sections, suffice to say that PHP_METHOD(MyClass, hackersFunction) would result in the following declaration:

void zim_MyClass_hackersFunction(INTERNAL_FUNCTION_PARAMETERS)

hackers_function doesn't do anything useful, it accepts a number using the zend_parse_parameters API, doubles it, and returns it to the engine. It is obvious that a normal function would have to do something more complex than double the input, for the purposes of education we are keeping it simple. On entry to the function return_value is allocated and initialized to null, making null the default return value of any function in PHP.

If zend_parse_parameters does not recieve what is specified by the Hacker as the correct arguments, and the arguments recieved cannot be converted to conform with type_spec an error will be raised, and by convention, the Hacker should return immediately.

Notă:

Arrays, Objects, and Resources cannot be converted.

Parsing Parameters Prototypes
int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...)
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...)
int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...)

Notă:

zend_parse_parameter is available from version 5.5, it behaves like zend_parse_parameters_ex except that instead of reading the arguments from the stack, it receives a single zval to convert, which may be changed in place.

Notă:

flags is intended to be a mask, currently only ZEND_PARSE_PARAMS_QUIET will have any impact (supress warnings)

The variable arguments recieved by these API functions are expected to be the address of C variables, and should be considered the output of the zend_parse_parameters API functions.

Type Specifiers
Spec Type Locals
a array zval*
A array or object zval*
b boolean zend_bool
C class zend_class_entry*
d double double
f function zend_fcall_info*, zend_fcall_info_cache*
h array HashTable*
H array or object HashTable*
l long long
L long (limits out-of-range LONG_MAX/LONG_MIN) long
o object zval*
O object (of specified zend_class_entry) zval*, zend_class_entry*
p string (a valid path) char*, int
r resource zval*
s string char*, int
z mixed zval*
Z mixed zval**

Notă:

Where the type specifier is O, the local zend_class_entry* is to be considered input (part of the type spec) to zend_parse_parameter

Advanced Type Specifiers
Spec Description
* a variable number of argument of the preceeding type, 0 or more
+ a variable number of argument of the preceeding type, 1 or more
| indicates that the remaining parameters are optional
/ SEPARATE_ZVAL_IF_NOT_REF on the parameter it follows
! the preceeding parameter can be of the specified type or null For 'b', 'l' and 'd', an extra argument of type zend_bool* must be passed after the corresponding bool*, long* or double* addresses which will be set true if null is recieved.

Notă:

Consult README.PARAMETER_PARSING_API included in source distributions for more information on parsing parameters

Once the Hacker's function has executed whatever it was implemented to execute, it is time to set the return_value for the engine. The RETURN_ and RETVAL_ macros are just wrappers around Z_*_P macros that work with return_value.

Notă:

RETURN_ macros cause execution to leave the function immediately (ie: return;), while RETVAL_ macros allow execution to continue after return_value has been set.

The Hacker should now have a reasonable understanding of the anatomy of a function, and to some degree, the anatomy of a method.

add a note add a note

User Contributed Notes 2 notes

up
5
ka5 at ua dot fm
5 years ago
The 'l' spec expects a parameter of the type zend_long, not long anymore.
The 's' spec expects parameters of the type char * and size_t, no int anymore.
(c) https://github.com/php/php-src/blob/PHP-7.0.0/UPGRADING.INTERNALS#L75
up
1
Anton
4 years ago
To handle string parameters you should specify string length:

char *s1;
size_t s1_len;

char *s2;
size_t s2_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
                          &l, &s1, &s1_len, &s2 &s2_len)  != SUCCESS) {
    return;
}
To Top