【发布时间】:2020-01-18 12:42:11
【问题描述】:
我最近需要一个已编译信号名称的列表,这样我就可以打印出像“被 SIGINT 中断 (2)”这样的好消息。
get_defined_constants() 对此无法使用,因为它在完全不相关的定义(具有相同的整数值)中混杂了 SIGINT、SIGTRAP 等。
信号名称根据操作系统映射到不同的值,有时它们并没有全部编译到 PHP 中,因此最直接的干净解决方案是一个新函数,它只返回一个已编译的信号名称数组。
嗯...一个将静态数组返回给 PHP 用户空间的函数...这听起来像是一个非常好的第一个源代码黑客项目,对吧?
没有:)
下面的代码(再往下一点)是一个超最小化的测试用例,展示了我撞到的非常奇怪的砖墙。
我有一个GINIT 函数将扩展全局test_array 初始化为一个数组,然后我用add_assoc_long() 填充一些条目(就像我对pcntl 所做的更改一样)(在这种情况下使用@ 987654329@ 为数组键(如!!!、"""、### 等)生成虚拟字符串。
然后我有一个演示函数test_test1() 将ZVAL_COPYs 预构建的test_array 到return_value。
请打鼓;看看当我尝试 print_r() 结果时会发生什么:
Array
(
[PWD] => 0
[i336] => 1
[LOGNAME] => 2
[tty] => 3
[HOME] => 4
[LANG] => 5
[user] => 6
[xterm] => 7
[TERM] => 8
[i336] => 9
[USER] => 10
[:0] => 11
[DISPLAY] => 12
[SHLVL] => 13
[9:22836] => 14
[PATH] => 15
[111] => 16
[222] => 17
[333] => 18
[444] => 19
[555] => 20
[666] => 21
[777] => 22
[888] => 23
[999] => 24
[HG] => 25
[MAIL] => 26
[OLDPWD] => 27
[] => 28
[] => 29
[] => 30
[STDIN] => 31
[STDOUT] => 32
[STDERR] => 33
[print_r] => 34
[DDD] => 35
[EEE] => 36
[FFF] => 37
[GGG] => 38
[HHH] => 39
[III] => 40
[JJJ] => 41
[KKK] => 42
[LLL] => 43
[MMM] => 44
[NNN] => 45
[OOO] => 46
[PPP] => 47
[QQQ] => 48
[RRR] => 49
<<snipped>>
真正奇怪的是条目 0 到 15 已损坏;条目 16 到 24 可以;条目 25 到 34 已损坏;条目 35 上的很好。
0-15 / 16-24 有一种奇怪的感觉; 25-34 / 35-∞ 不是。
无论如何,如果我替换 test_test1 为以下内容(对GINIT 函数的代码稍作修改):
zval test;
array_init(&test);
for (int i = 0; i < 80; i++) {
char buf[4];
sprintf(buf, "%1$c%1$c%1$c", i+33);
add_assoc_long(&test, buf, i);
}
ZVAL_COPY_OR_DUP(return_value, &test);
zval_ptr_dtor(&test);
我得到了更多的期望
(
[!!!] => 0
["""] => 1
[###] => 2
[$$$] => 3
[%%%] => 4
[&&&] => 5
['''] => 6
[(((] => 7
[)))] => 8
[***] => 9
[+++] => 10
[,,,] => 11
[---] => 12
[...] => 13
[///] => 14
[000] => 15
[111] => 16
[222] => 17
[333] => 18
[444] => 19
[555] => 20
[666] => 21
[777] => 22
[888] => 23
[999] => 24
[:::] => 25
[;;;] => 26
[<<<] => 27
[===] => 28
<<snipped>>
除了一些关于我做错了什么的提示(我知道我有一些倒退的东西...... :)),我会非常 很想了解为什么 PHP 将部分看似随机的环境变量转储到我的数组中!
我停止自己的探索/解决过程并发布此问题的主要原因是我意识到我不知道我不知道什么,再加上我不知道该去哪里尝试解决这个问题。
提供 PHP 文档的资源越来越多,但不幸的是,弄清楚如何完成简单的任务似乎需要将来自不同来源的大量细节拼凑在一起(老实说,我被困在一些看起来很简单的东西上)表面)。
我还对我所阅读的内容的实际更新程度是有疑问。
一个例子:ZEND_MODULE_GLOBALS_ACCESSOR() 宏,用于线程安全地访问每个模块的全局值,被使用了 37 次(看起来是 ext/ 内容的不到一半)。然而,所有我读过的信息,包括在 phpinternals.net 和 phpinternalsbook.net 等网站上,都指定了包含特定 5 行 #define 的硬性要求,以便设置访问模块全局变量。我偶然发现了前面提到的宏,它在 PHP 本身中实现了#define,因此没有人必须再通过阅读源代码来自己做。
我可以完全接受事情并不完全同步 - 也许那个宏是新的。
但是我在哪里可以找到最新的参考信息来回答我的问题?
真正的问题。
我在下面添加了config.m4,因此可以编译它以进行测试:
php_test.h:
#ifndef PHP_TEST_H
# define PHP_TEST_H
extern zend_module_entry test_module_entry;
# define phpext_test_ptr &test_module_entry
# define PHP_TEST_VERSION "0.1.0"
ZEND_BEGIN_MODULE_GLOBALS(test)
zval test_array;
ZEND_END_MODULE_GLOBALS(test)
# if defined(ZTS) && defined(COMPILE_DL_TEST)
ZEND_TSRMLS_CACHE_EXTERN()
# endif
ZEND_DECLARE_MODULE_GLOBALS(test)
#endif /* PHP_TEST_H */
test.c:
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "php_test.h"
PHP_FUNCTION(test_test1)
{
ZVAL_COPY(return_value, &ZEND_MODULE_GLOBALS_ACCESSOR(test, test_array));
}
PHP_RINIT_FUNCTION(test)
{
#if defined(ZTS) && defined(COMPILE_DL_TEST)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
PHP_MINIT_FUNCTION(test)
{
return SUCCESS;
}
PHP_GSHUTDOWN_FUNCTION(test)
{ }
PHP_GINIT_FUNCTION(test)
{
// Thanks to #php.pecl on efnet for pointing me in the direction of `GINIT`.
// I'd seriously hit my SIGSEGV limit, and really appreciated the valid pointers (punintended).
array_init(&ZEND_MODULE_GLOBALS_ACCESSOR(test, test_array));
for (int i = 0; i < 80; i++) {
char buf[4];
sprintf(buf, "%1$c%1$c%1$c", i+33);
add_assoc_long(&ZEND_MODULE_GLOBALS_ACCESSOR(test, test_array), buf, i);
}
return SUCCESS;
}
PHP_MINFO_FUNCTION(test)
{
php_info_print_table_start();
php_info_print_table_header(2, "test support", "enabled");
php_info_print_table_end();
}
ZEND_BEGIN_ARG_INFO(arginfo_test_test1, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_test_test2, 0)
ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()
static const zend_function_entry test_functions[] = {
PHP_FE(test_test1, arginfo_test_test1)
PHP_FE_END
};
zend_module_entry test_module_entry = {
STANDARD_MODULE_HEADER,
"test", /* Extension name */
test_functions, /* zend_function_entry */
PHP_MINIT(test), /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(test), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(test), /* PHP_MINFO - Module info */
PHP_TEST_VERSION, /* Version */
PHP_MODULE_GLOBALS(test),
PHP_GINIT(test),
PHP_GSHUTDOWN(test),
NULL, /* PRSHUTDOWN() */
STANDARD_MODULE_PROPERTIES_EX
};
#ifdef COMPILE_DL_TEST
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(test)
#endif
config.m4:
PHP_ARG_ENABLE([test2],
[whether to enable test2 support],
[AS_HELP_STRING([--enable-test2],
[Enable test2 support])],
[no])
if test "$PHP_TEST2" != "no"; then
AC_DEFINE(HAVE_TEST2, 1, [ Have test2 support ])
PHP_NEW_EXTENSION(test2, test2.c, $ext_shared)
fi
【问题讨论】:
-
循环遍历
get_defined_constants()并获取以SIG开头的那些怎么样? -
一个很好的建议,也是我尝试开始的。由于所有重叠值,返回的数组以
[-1 => SIG_DFL, 0 => SIG_IGN, 1 => SIG_UNBLOCK, 2 => SIG_SETMASK, 3 => SIGQUIT, ...]开头。我真的,真的不想做preg_match('/^SIG[^_]/')- 更改pcntl理论上可能会破坏未来的行为。 -
@Barmar:[感谢添加
php-internals标签!] -
虽然这样做需要解释条件化名称的
#ifdef行。 -
您可能想通过chat.stackoverflow.com/rooms/11/php 寻求有关 PHP 扩展开发的帮助。
标签: php c php-internals