antonio's blog

antonio's blog


Блог о всяком разном, связанном с разработкой ПО. Пишу редко, когда есть время и желание.

antonio
Author

Share


Tags


Разработка расширений для PHP. Функции с аргументами передаваемыми по ссылке

antonioantonio

Начиная с версии 5.3.0 в PHP изменился механизм передачи аргументов в функцию по ссылке. Передача аргументов в функцию по ссылке объявлена устаревшей и более не поддерживается http://www.php.net/manual/en/migration53.deprecated.php.

Аргументы передаваемый по ссылке следует указывать в объявлении функции. Т.е. так писать уже нельзя:

<?php

function my_last_error($errcode, $err) {  
  // Что-то делаем

  $errcode = 1005;
  $err = 'Last error';
}

$errno = 0;
$error = NULL;

my_last_error(&$error, &$errno);

?>

Теперь нужно писать так:

<?php

function my_last_error(&$errcode, &$err) {  
    // Что-то делаем

    $errcode = 1005;
    $err = 'Last error';
}

$errno = 0;
$error = NULL;

my_last_error($error, $errno);

?>

Для того чтобы создать функцию, в расширении PHP, принимающую аргументы по ссылке, первым делом нужно описать ее аргументы:

ZEND_BEGIN_ARG_INFO_EX( arginfo_my_last_error, 0, 0, 2 )  
ZEND_ARG_INFO(1, errno)  
ZEND_ARG_INFO(1, error)  
ZEND_END_ARG_INFO()  

Макросы ZEND_BEGIN_ARG_INFO_EX(), ZEND_ARG_INFO() и ZEND_END_ARG_INFO() создают массив структур zend_arg_info с информацией об аргументах функции. Каждый аргумент функции описывается отдельной структурой.

Объявление макросов

Данные макросы имеют следующие объявление в PHP 5.3.5:

#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \
        static const zend_arg_info name[] = { \
            { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },

#define ZEND_ARG_INFO(pass_by_ref, name)  { #name, sizeof(#name)-1, NULL, 0, 0, 0, pass_by_ref, 0, 0 },

#define ZEND_END_ARG_INFO()  ;

Аргументы макроса ZEND_BEGIN_ARG_INFO_EX():

ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)

Аргументы макроса ZEND_ARG_INFO():

ZEND_ARG_INFO(pass_by_ref, name)

Все аргументы необходимо перечислять в том же порядке в каком они будут передаваться в функцию и соответственно обязательные аргументы, кол-во которых задается в макросе ZEND_BEGIN_ARG_INFO_EX(), перечисляются первыми.

На этапе препроцессинга макросы будут развернуты следующим образом:

static const zend_arg_info arginfo_my_last_error[] = {  
   { NULL, 0, NULL, 0, 0, 0, 0, 0, 2 },
   { errno, 5, NULL, 0, 0, 0, 1, 0, 0 },
   { error, 5, NULL, 0, 0, 0, 1, 0, 0 },
};

Объявление структуры zend_arg_info:

typedef struct _zend_arg_info {  
    const char *name;
    zend_uint name_len;
    const char *class_name;
    zend_uint class_name_len;
    zend_bool array_type_hint;
    zend_bool allow_null;
    zend_bool pass_by_reference;
    zend_bool return_reference;
    int required_num_args;
} zend_arg_info;

После того как аргументы функции описаны, создаем собственно саму функцию:

PHP_FUNCTION(my_last_error) {  
   zval *ref_errcode = NULL;
   zval *ref_error = NULL;

   if ( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "zz", &ref_error, &ref_errcode ) == FAILURE ) {
            return;
    }

    zval_dtor(ref_errcode);
    zval_dtor(ref_error);

    ZVAL_LONG(ref_errcode, 1005 );
    ZVAL_STRING(ref_error, "Last error", 1);
}

Регистрация функции:

const zend_function_entry my_functions[] = {  
   ....
   PHP_FE(my_last_error, arginfo_my_last_erro )
   ....
};

Пример использования:

<?php  
   $errno = 0;
   $error = NULL;

   my_last_error($error, $errno);

   echo $error.' (errno: '.$errno.')';  // Last error (errno: 1005)
?>
antonio
Author

antonio

Comments