在 PHP 扩展中,推荐的方法是 return 来自 std::string 的值

In a PHP extension, recommended way to return the value from and std::string

我们有一个简单的 PHP 函数,其目的是调用一个 C++ 自由函数 std::string callLibrary(std::string) 并 return 它的 std::string return 值。

目前看起来是这样的:

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_initRETURN_STR() 行为的参考手册,最接近的是: http://www.phpinternalsbook.com/php7/internal_types/strings/zend_strings.html

特别是zend_string_init

的最后一个参数

If you pass 0, you ask the engine to use a request-bound heap allocation using the Zend Memory Manager. Such allocation will be destroyed at the end of the current request. If you don’t do it yourself, on a debug build, the engine will shout at you about a memory leak you just created. If you pass 1, you ask for what we called a “persistent” allocation, that is the engine will use a traditional C malloc() call and will not track the memory allocation in any way.

似乎我们想要 0 值,但是 RETURN_STR() 会释放分配的内存吗? (文字有点模棱两可,不过看来破坏应该是明确的) 是否有更惯用的方法来 return 来自 PHP 扩展函数的 std::string 值?

为了回答你的问题,我会稍微谈谈 PHP 内存分配,然后再谈谈你的具体问题。

关于PHP内存分配

编写 PHP 扩展时,可以执行两种内存分配:

  • 跟踪内存分配
  • 持久内存分配

跟踪内存分配是一种优化,允许 PHP 引擎对原始内存分配有更多控制。 Zend 内存管理器 (ZendMM) 充当标准内存分配库之上的包装器。此内存管理器允许 PHP 通过清理在请求结束时未明确释放的所有跟踪内存来避免内存泄漏。此外,这允许引擎制定内存限制(例如 php.ini 设置 memory_limit)。由于这些原因,跟踪内存也称为 每个请求 内存。

持久内存分配是由 C 库管理的标准内存分配(例如 malloc 和朋友)。还值得注意的是,在 C++ 中,newdelete 通常分别向下调用 mallocfree。在 PHP 中,持久内存分配在请求处理后仍然存在,并且它可能存在以服务多个请求。因此,此类分配可能会导致内存泄漏。

在 PHP API 中,有一些宏定义用于执行跟踪或持久内存分配。例如,emallocefree 类似于 mallocfree 用于跟踪(即每个请求)内存管理。宏 pemallocpefree 用于跟踪分配或持久分配,具有要切换的参数。例如,pemalloc(32,1) 分配一个 32 个持久字节块,而 pemalloc(32,0) 相当于 emalloc(32) 分配一个 32 个跟踪字节块。

除了原始内存分配函数之外,PHP API 还提供对由更高级别函数启动的内存分配的控制。例如,使用 zend_string_init 创建 PHP7 zend_string 结构可以让您通过第三个参数选择您想要的内存分配类型。这遵循整个 API 中的一个常见习语,0 表示跟踪分配,1 表示持久分配。

关于zend_stringzend_string_initRETURN_STR

我对 PHP7 的熟悉不如对 PHP5 的熟悉,但是很多概念都沿用了下来,我想我已经阅读了足够多的源代码来回答题。当您使用 RETURN_STRzend_string 分配给 zval 时,zval 负责释放 zend_string(在 PHP5 中,这只是a char* 但概念是一样的)。 Zend 引擎希望大多数(如果不是全部)对象使用跟踪内存管理器进行分配。在 PHP5 中,分配给 zval 的字符串必须是通过 emalloc 分配的,因为当 zval 是时,代码总是在字符串缓冲区上调用 efree被毁。在 PHP7 中似乎有 an exception 因为 zend_string 结构可以记住使用了哪种分配类型。无论如何,除非您有充分的理由不这样做,否则始终将跟踪分配用作默认值是一种很好的做法。因此,您当前的代码看起来不错,因为它将 0 作为第三个参数传递给 zend_string_init.

zend_string 的销毁不应在您的代码中明确表示,因为 zval 将在稍后处理。另外,该过程取决于用户空间如何对返回的 zval 进行操作。这不是你需要担心的事情。