在 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_init
或 RETURN_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++ 中,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 还提供对由更高级别函数启动的内存分配的控制。例如,使用 zend_string_init
创建 PHP7 zend_string
结构可以让您通过第三个参数选择您想要的内存分配类型。这遵循整个 API 中的一个常见习语,0
表示跟踪分配,1
表示持久分配。
关于zend_string
、zend_string_init
和RETURN_STR
我对 PHP7 的熟悉不如对 PHP5 的熟悉,但是很多概念都沿用了下来,我想我已经阅读了足够多的源代码来回答题。当您使用 RETURN_STR
将 zend_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
进行操作。这不是你需要担心的事情。
我们有一个简单的 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_init
或 RETURN_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++ 中,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 还提供对由更高级别函数启动的内存分配的控制。例如,使用 zend_string_init
创建 PHP7 zend_string
结构可以让您通过第三个参数选择您想要的内存分配类型。这遵循整个 API 中的一个常见习语,0
表示跟踪分配,1
表示持久分配。
关于zend_string
、zend_string_init
和RETURN_STR
我对 PHP7 的熟悉不如对 PHP5 的熟悉,但是很多概念都沿用了下来,我想我已经阅读了足够多的源代码来回答题。当您使用 RETURN_STR
将 zend_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
进行操作。这不是你需要担心的事情。