В расширении PHP рекомендуется возвращать значение из std :: string - PullRequest
0 голосов
/ 22 мая 2018

У нас есть простая функция PHP, цель которой - вызвать функцию C ++ free std::string callLibrary(std::string) и вернуть std::string возвращаемое значение.

В настоящее время выглядит так:

PHP_FUNCTION(call_library)
{
    char *arg = NULL;
    size_t arg_len, len;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE)
    {
        return;
    }

    // Call underlying library
    std::string callResult = callLibrary(arg);
    zend_string * result = zend_string_init(callResult.c_str(), callResult.size(), 0);
    RETURN_STR(result);
}

Мы не можем найти справочное руководство, описывающее поведение zend_string_init или RETURN_STR(), самое близкое, что у нас есть: http://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html

В частности, в нем говорится о последнем параметре zend_string_init

Если вы передадите 0, вы попросите движок использовать выделение кучи с привязкой к запросу с помощью Zend Memory Manager.Такое распределение будет уничтожено в конце текущего запроса.Если вы не сделаете это сами при отладочной сборке, движок будет кричать вам об утечке памяти, которую вы только что создали.Если вы передадите 1, вы спросите о том, что мы назвали «постоянным» распределением, то есть механизм будет использовать традиционный вызов C malloc () и не будет отслеживать распределение памяти любым способом.

Кажется, нам нужно значение 0, но освобождает ли RETURN_STR() выделенную память?(текст немного двусмысленный, но кажется, что уничтожение должно быть явным). Есть ли более идиоматический способ вернуть такое значение std::string из функции расширения PHP?

1 Ответ

0 голосов
/ 11 июня 2018

Чтобы ответить на ваш вопрос, я немного расскажу о распределении памяти PHP, а затем о вашем конкретном вопросе.

О выделении памяти PHP

При написании PHPРасширения, есть два вида выделения памяти, которые вы можете выполнить:

  • отслеживаемые выделения памяти
  • постоянные выделения памяти

Отслеживаемое выделение памяти является оптимизациейэто позволяет движку PHP иметь больше контроля над необработанным выделением памяти.Менеджер памяти Zend (ZendMM) действует как оболочка над стандартными библиотеками выделения памяти.Этот диспетчер памяти позволяет PHP избежать утечек памяти путем очистки любой отслеживаемой памяти, которая не была явно освобождена в конце запроса.Кроме того, это позволяет двигателю устанавливать пределы памяти (такие как php.ini memory_limit).По этим причинам отслеживаемая память также упоминается как память на запрос .

Постоянное выделение памяти - это стандартное выделение памяти, управляемое библиотекой C (например, malloc и друзья).Стоит также отметить, что в C ++ new и delete обычно вызывают до malloc и free соответственно.В PHP постоянное выделение памяти сохраняется после обработки запроса и может существовать для обслуживания более одного запроса.По этой причине возможна утечка памяти с этими типами распределения.

В PHP API есть некоторые макросы, которые определены для выполнения отслеженных или постоянных выделений памяти.Например, emalloc и efree являются аналогами malloc и free для управления отслеживаемой (т.е. по запросу) памятью.Макросы pemalloc и pefree предназначены для отслеживаемых или постоянных распределений, имеющих параметр для переключения.Например, pemalloc(32,1) выделяет блок из 32 постоянных байтов, тогда как pemalloc(32,0) эквивалентен emalloc(32), который выделяет блок из 32 отслеживаемых байтов.

В дополнение к необработанным функциям выделения памяти, PHP APIтакже обеспечивает управление распределением памяти, инициируемым функциями более высокого уровня.Например, создание структуры PHP7 zend_string с zend_string_init позволяет вам выбрать, какой тип выделения памяти вы хотите через третий параметр.Это соответствует общепринятому идиому в API: 0 обозначает отслеживаемое выделение и 1 обозначает постоянное выделение.

Относительно zend_string, zend_string_init и RETURN_STR

Я не так знаком с PHP7, как с PHP5, но многие концепции перенесли, и я думаю, что прочитал достаточно исходного кода, чтобы ответить на вопрос.Когда вы присваиваете zend_string для zval, используя RETURN_STR, zval становится ответственным за освобождение zend_string (в PHP5 это было просто char*, но концепция та же самая).Движок Zend ожидает, что большинство, если не все объекты будут размещены с помощью отслеживаемого менеджера памяти.В PHP5 строка, присвоенная zval, должна быть выделена через emalloc, поскольку код всегда вызывает efree в строковом буфере, когда zval уничтожается.В PHP7, кажется, исключение , поскольку структура zend_string может запомнить, какой тип размещения использовался.Несмотря на это, рекомендуется всегда использовать отслеживаемые выделения по умолчанию, если только у вас нет веских причин поступить иначе.Поэтому ваш текущий код выглядит хорошо, поскольку он передает 0 в качестве третьего параметра zend_string_init.

Уничтожение zend_string не должно быть явным в вашем коде, так как zval будет обрабатывать это внемного позже.Кроме того, этот процесс зависит от того, как пользовательское пространство работает с возвращаемым zval.Вам не о чем беспокоиться.

...