php如何进行内存调试

php如何进行内存调试

内存调试

本章是有关PHP源代码的内存调试的简要介绍。 这不是一门完整的课程:内存调试并不难, 但是你需要一些它的使用经验,大量的练习可能是你在设计任何C编写的代码时都必须要做的事情。我们将在这里介绍一个非常著名的内存调试器: valgrind; 以及如何将其与PHP一起使用来调试内存问题。

Valgrind简介

Valgrind是许多Unix环境下使用的知名工具,可以在任何C/C++编写的软件中调试许多常见的内存问题。 Valgrind 是有关内存调试的多功能前端工具。最常用的底层工具称为 “memcheck”。它的工作方式是用自己的堆分配替换每个libc的堆分配,并跟踪你对它们所做的事情。你可能还会对 “massif” 感兴趣: 它是一个内存跟踪器,对于了解程序的常规堆内存使用情况非常有用。

为了进行内存分配替换,你需要通过 valgrind 运行要分析的程序(此处为PHP),也就是启动 valgrind 二进制文件。

当 valgrind 替换并跟踪所有 libc 的堆分配时,它往往会大大降低调试程序的速度。对于PHP,你会注意到它。尽管 PHP 的速度下降并不那么剧烈,但是仍然可以清楚地感觉到;如果你注意到它,不用担心,这是正常的。

Valgrind 不是你可能会使用的唯一工具,但是是最常用的工具。还有其他工具,例如 Dr.Memory、LeakSanitizer、Electric Fence、AddressSanitizer。

在开始之前

以下是在存储器调试方面具有良好经验并减轻发现缺陷并减少调试时间的机会所需的步骤:

-您应始终使用PHP的调试版本。尝试调试生产版本中的内存是无关紧要的。
-您应该始终在 USE_ZEND_ALLOC = 0 环境下启动调试器。您可能已经在Zend Memory Manager章节中了解到,此环境var会在当前进程启动时禁用ZendMM。强烈建议在启动内存调试器时这样做。完全绕过ZendMM有助于了解valgrind生成的跟踪。
-强烈建议在环境 ZEND_DONT_UNLOAD_MODULES = 1 下启动内存调试器。这样可以防止PHP在过程结束时卸载扩展程序的.so文件。这是为了获得更好的valgrind报告跟踪;如果在valgrind将要显示其错误时PHP将卸载扩展名,则稍后将不完整,因为从中获取信息的文件不再是进程内存映像的一部分。
-您可能需要一些抑制措施。当您告诉PHP在过程结束时不要卸载其扩展名时,可能会在valgrind输出中给您误报。将检查PHP扩展是否泄漏,如果您在平台上误报,则可以使用抑制功能将其关闭像这样。可以根据这样的示例随意编写自己的文件。
-与Zend Memory Manager相比,Valgrind显然是更好的工具,可以查找泄漏和其他与内存相关的问题。您应该始终在代码上运行valgrind,这实际上是每个C程序员都必须执行的步骤。无论是因为崩溃而想要找到并调试它,还是作为看起来好像没有任何坏处的高质量工具来运行它,valgrind都是这种工具,它可以指出隐藏的瑕疵,准备好将其吹拂一次或以后。即使您认为代码似乎一切都很好,也可以使用它:您可能会感到惊讶。

内存泄漏检测示例

入门

Valgrind是一个完整的堆内存调试器。它还可以调试过程内存映射和功能堆栈。请在其文档中获取更多信息。

让我们去检测动态内存泄漏,并尝试一个简单的,最常见的泄漏:

PHP_RINIT_FUNCTION(pib)
{
    void *foo = emalloc(128);
}

上面的代码每次请求都会泄漏128字节,因为它没有与此类缓冲区有关的efree()相关调用。由于它是对emalloc()的调用,因此会通过Zend Memory Manager,因此稍后会警告我们就像我们在ZendMM章节中看到的那样。我们还要看看valgrind是否可以注意到泄漏:

> ZEND_DONT_UNLOAD_MODULES=1 USE_ZEND_ALLOC=0 valgrind --leak-check=full --suppressions=/path/to/suppression
--show-reachable=yes --track-origins=yes ~/myphp/bin/php -dextension=pib.so /tmp/foo.php

我们使用valgrind启动PHP-CLI进程。我们在这里假设一个名为“ pib”的扩展名。这是输出:

==28104== 128 bytes in 1 blocks are definitely lost in loss record 1 of 1
==28104==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28104==    by 0xA3701E: __zend_malloc (zend_alloc.c:2820)
==28104==    by 0xA362E7: _emalloc (zend_alloc.c:2413)
==28104==    by 0xE896F99: zm_activate_pib (pib.c:1880)
==28104==    by 0xA79F1B: zend_activate_modules (zend_API.c:2537)
==28104==    by 0x9D31D3: php_request_startup (main.c:1673)
==28104==    by 0xB5909A: do_cli (php_cli.c:964)
==28104==    by 0xB5A423: main (php_cli.c:1381)

==28104== LEAK SUMMARY:
==28104==    definitely lost: 128 bytes in 1 blocks
==28104==    indirectly lost: 0 bytes in 0 blocks
==28104==    possibly lost: 0 bytes in 0 blocks
==28104==    still reachable: 0 bytes in 0 blocks
==28104==    suppressed: 7,883 bytes in 40 blocks

在我们看来,“绝对失落”是我们必须关注的。

Valgrind抓住了我们的漏洞。

很容易,现在我们可以使用持久分配(也就是绕过ZendMM并使用传统libc的动态内存分配)来产生泄漏。走:

PHP_RINIT_FUNCTION(pib)
{
    void *foo = malloc(128);
}

这是报告:

==28758==    128 bytes in 1 blocks are definitely lost in loss record 1 of 1
==28758==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28758==    by 0xE896F82: zm_activate_pib (pib.c:1880)
==28758==    by 0xA79F1B: zend_activate_modules (zend_API.c:2537)
==28758==    by 0x9D31D3: php_request_startup (main.c:1673)
==28758==    by 0xB5909A: do_cli (php_cli.c:964)
==28758==    by 0xB5A423: main (php_cli.c:1381)

也抓到了。

更复杂的用例

这是一个更复杂的设置。您可以在下面的代码中发现泄漏吗?

static zend_array ar;

PHP_MINIT_FUNCTION(pib)
{
    zend_string *str;
    zval string;

    str = zend_string_init("yo", strlen("yo"), 1);
    ZVAL_STR(&string, str);

    zend_hash_init(&ar, 8, NULL, ZVAL_PTR_DTOR, 1);
    zend_hash_next_index_insert(&ar, &string);
}

这里有两个泄漏。首先,我们分配一个zend_string,但我们没有释放它。其次,我们分配一个新的zend_hash,但是我们也不释放它。让我们用valgrind启动它,然后查看结果:

==31316== 296 (264 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 1 of 2
==32006==    by 0xA3701E: __zend_malloc (zend_alloc.c:2820)
==32006==    by 0xA814B2: zend_hash_real_init_ex (zend_hash.c:133)
==32006==    by 0xA816D2: zend_hash_check_init (zend_hash.c:161)
==32006==    by 0xA83552: _zend_hash_index_add_or_update_i (zend_hash.c:714)
==32006==    by 0xA83D58: _zend_hash_next_index_insert (zend_hash.c:841)
==32006==    by 0xE896AF4: zm_startup_pib (pib.c:1781)
==32006==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==32006==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==32006==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==32006==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)

==31316== 32 bytes in 1 blocks are indirectly lost in loss record 2 of 2
==31316==    by 0xA3701E: __zend_malloc (zend_alloc.c:2820)
==31316==    by 0xE880B0D: zend_string_alloc (zend_string.h:122)
==31316==    by 0xE880B76: zend_string_init (zend_string.h:158)
==31316==    by 0xE896F9D: zm_activate_pib (pib.c:1781)
==31316==    by 0xA79F1B: zend_activate_modules (zend_API.c:2537)
==31316==    by 0x9D31D3: php_request_startup (main.c:1673)
==31316==    by 0xB5909A: do_cli (php_cli.c:964)
==31316==    by 0xB5A423: main (php_cli.c:1381)

==31316== LEAK SUMMARY:
==31316== definitely lost: 328 bytes in 2 blocks

如预期的那样,两个泄漏都被报告。如您所见,valgrind是准确的,它将您的眼睛放在需要的地方。

现在修复它们:

PHP_MSHUTDOWN_FUNCTION(pib)
{
    zend_hash_destroy(&ar);
}

我们在PHP程序结束时在MSHUTDOWN中销毁了持久数组。创建它时,我们将其作为析构函数传递给ZVAL_PTR_DTOR,它将在插入的所有项目上运行该回调。这是zval的析构函数,它将破坏zval分析它们的内容。对于IS_STRING类型,析构函数将释放zend_string并在必要时释放它。做完了

缓冲区上溢/下溢检测

内存泄漏很糟糕。这将导致您的程序一次或以后触发OOM,并且将大大降低主机的速度,因为随着时间的流逝,后者将获得越来越少的可用内存。这是内存泄漏的征兆。

但是更糟的是:缓冲区越界访问。访问超出分配限制的指针是许多邪恶操作(例如在计算机上获得root shell)的根源,因此您绝对应该防止它们。较轻的越界访问也经常会由于内存损坏而导致程序崩溃。但是,这全部取决于硬件目标计算机,使用的编译器和选项,操作系统内存布局,使用的libc等……许多因素。

因此,越界访问非常令人讨厌,它们是炸弹,可能会爆炸,也可能不会爆炸,或者在一分钟内,或者如果您非常幸运,它们将永远不会爆炸。

  • Valgrind *是一个内存调试器,因此能够检测到来自任何内存区域(堆和堆栈)的任何越界访问。这与查找泄漏使用的是相同的memcheck工具。

让我们看一个简单的例子:

PHP_MINIT_FUNCTION(pib)
{
    char *foo = malloc(16);
    foo[16] = 'a';
    foo[-1] = 'a';
}

这段代码分配了一个缓冲区,并故意在边界后一个字节和边界后一个字节写入数据。现在,如果您运行这样的代码,您就有大约两次机会中有一次立即崩溃,然后随机崩溃。您可能还已经在PHP中创建了一个安全漏洞,但是它可能无法被远程利用(这种行为很少见)。

让我们问一下valgrind,使用与之前完全相同的命令行来启动它,除了输出内容外,其他都没有改变:

==12802== Invalid write of size 1
==12802==    at 0xE896A98: zm_startup_pib (pib.c:1772)
==12802==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==12802==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==12802==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==12802==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==12802==    by 0x9D4541: php_module_startup (main.c:2260)
==12802==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==12802==    by 0xB5A367: main (php_cli.c:1348)
==12802==  Address 0xeb488f0 is 0 bytes after a block of size 16 alloc'd
==12802==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12802==    by 0xE896A85: zm_startup_pib (pib.c:1771)
==12802==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==12802==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==12802==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==12802==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==12802==    by 0x9D4541: php_module_startup (main.c:2260)
==12802==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==12802==    by 0xB5A367: main (php_cli.c:1348)
==12802==
==12802== Invalid write of size 1
==12802==    at 0xE896AA6: zm_startup_pib (pib.c:1773)
==12802==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==12802==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==12802==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==12802==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==12802==    by 0x9D4541: php_module_startup (main.c:2260)
==12802==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==12802==    by 0xB5A367: main (php_cli.c:1348)
==12802==  Address 0xeb488df is 1 bytes before a block of size 16 alloc'd
==12802==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12802==    by 0xE896A85: zm_startup_pib (pib.c:1771)
==12802==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==12802==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==12802==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==12802==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==12802==    by 0x9D4541: php_module_startup (main.c:2260)
==12802==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==12802==    by 0xB5A367: main (php_cli.c:1348)

这两个无效的写入都已被检测到,现在您的目标是跟踪并修复它们。

在这里,我们使用了一个示例,其中我们超出范围地写入内存,这是最糟糕的情况,因为您的写入操作成功后(可能会立即导致SIGSEGV)将覆盖该指针旁边的一些关键区域。当我们使用libc的malloc()进行分配时,我们将覆盖libc用于管理和跟踪其分配的关键头尾块。取决于许多因素(平台,使用的libc,如何编译等等),这将导致崩溃。

Valgrind也可能报告无效读取。这意味着您将在分配的指针的范围之外执行内存读取操作。更好的情况是块被覆盖,但您仍然不应该访问内存区域,在这种情况下又可能会导致立即崩溃,或者稍后崩溃,或者永远不会访问?不要那样做

这是有关字符串连接的第二个示例:

char *foo = strdup("foo");
char *bar = strdup("bar");

char *foobar = malloc(strlen("foo") + strlen("bar"));

memcpy(foobar, foo, strlen(foo));
memcpy(foobar + strlen("foo"), bar, strlen(bar));

fprintf(stderr, "%s", foobar);

free(foo);
free(bar);
free(foobar);

你能发现问题吗?

让我们问一下valgrind:

==13935== Invalid read of size 1
==13935==    at 0x4C30F74: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13935==    by 0x768203E: fputs (iofputs.c:33)
==13935==    by 0xE896B91: zm_startup_pib (pib.c:1779)
==13935==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==13935==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==13935==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==13935==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==13935==    by 0x9D4541: php_module_startup (main.c:2260)
==13935==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==13935==    by 0xB5A367: main (php_cli.c:1348)
==13935==  Address 0xeb48986 is 0 bytes after a block of size 6 alloc'd
==13935==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13935==    by 0xE896B14: zm_startup_pib (pib.c:1774)
==13935==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==13935==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==13935==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==13935==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==13935==    by 0x9D4541: php_module_startup (main.c:2260)
==13935==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==13935==    by 0xB5A367: main (php_cli.c:1348)

第1779行指向fprintf()调用。该调用确实要求fputs(),其本身称为strlen()(均来自libc),在这里strlen()读取1个字节无效。

我们只是忘记了\\ 0来终止我们的字符串。我们传递fprintf()无效的字符串。它首先尝试计算调用strlen()的字符串的长度。然后strlen()将扫描缓冲区,直到找到\\ 0,并且它将扫描缓冲区的边界,因为我们忘记了对其进行零终止。我们在这里很幸运,strlen()仅从末尾传递一个字节。那可能更多,并且可能崩溃了,因为我们真的不知道下一个\\ 0在内存中的位置,这是随机的。

解:

size_t len   = strlen("foo") + strlen("bar") + 1;   /* note the +1 for \
size_t len   = strlen("foo") + strlen("bar") + 1;   /* note the +1 for \\0 */
char *foobar = malloc(len);
/* ... ... same code ... ... */
foobar[len - 1] = '\\0'; /* terminate the string properly */
*/ char *foobar = malloc(len); /* ... ... same code ... ... */ foobar[len - 1] = '\
size_t len   = strlen("foo") + strlen("bar") + 1;   /* note the +1 for \\0 */
char *foobar = malloc(len);
/* ... ... same code ... ... */
foobar[len - 1] = '\\0'; /* terminate the string properly */
'; /* terminate the string properly */

最后,这里是最后一个示例,展示了一个有余使用的场景。这也是C编程中的一个非常常见的错误,与错误的内存访问一样严重:它创建了安全缺陷,可能导致非常讨厌的行为。显然,valgrind可以检测到无用后使用。这是一个:

char *foo = strdup("foo");
free(foo);

memcpy(foo, "foo", sizeof("foo"));

同样,这里是一个与PHP无关的PHP场景。我们释放一个指针,然后再使用它。这是一个大错误。让我们问一下valgrind:

==14594== Invalid write of size 1
==14594==    at 0x4C3245C: memcpy@GLIBC_2.2.5 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14594==    by 0xE896AA1: zm_startup_pib (pib.c:1774)
==14594==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==14594==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==14594==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==14594==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==14594==    by 0x9D4541: php_module_startup (main.c:2260)
==14594==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==14594==    by 0xB5A367: main (php_cli.c:1348)
==14594==  Address 0xeb488e0 is 0 bytes inside a block of size 4 free'd
==14594==    at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14594==    by 0xE896A86: zm_startup_pib (pib.c:1772)
==14594==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==14594==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==14594==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==14594==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==14594==    by 0x9D4541: php_module_startup (main.c:2260)
==14594==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==14594==    by 0xB5A367: main (php_cli.c:1348)
==14594==  Block was alloc'd at
==14594==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14594==    by 0x769E8D9: strdup (strdup.c:42)
==14594==    by 0xE896A70: zm_startup_pib (pib.c:1771)
==14594==    by 0xA774F7: zend_startup_module_ex (zend_API.c:1843)
==14594==    by 0xA77559: zend_startup_module_zval (zend_API.c:1858)
==14594==    by 0xA85AF5: zend_hash_apply (zend_hash.c:1508)
==14594==    by 0xA77B25: zend_startup_modules (zend_API.c:1969)
==14594==    by 0x9D4541: php_module_startup (main.c:2260)
==14594==    by 0xB5802F: php_cli_startup (php_cli.c:427)
==14594==    by 0xB5A367: main (php_cli.c:1348)

这里的一切再次变得清晰。

结论

在投入生产之前,请使用内存调试器。正如您在本章中学到的那样,您在计算中忘记的小字节可能导致可利用的安全漏洞。它还经常(非常频繁地)导致简单的崩溃。这意味着您的扩展很酷,可以减少整个服务器(服务器)及其每个客户端的数量。

C是一种非常严格的编程语言。您将获得数十亿字节的内存来进行编程,并且必须安排这些内存来执行一些计算。但是请不要搞砸这种强大的功能:在最好的情况下(罕见),什么都不会发生,在更坏的情况下(非常常见),您会在这里和那里随机崩溃,在最坏的情况下,您会创建一个漏洞在恰好可以被远程利用的程序中...

您的工具娴熟,聪明,请确实照顾机器内存。

关于php如何进行内存调试的文章就分享到这,如果对你有帮助欢迎继续关注我们哦

本文来自投稿,不代表重蔚自留地立场,如若转载,请注明出处https://www.cwhello.com/40503.html

如有侵犯您的合法权益请发邮件951076433@qq.com联系删除

(0)
php学习php学习订阅用户
上一篇 2022年6月13日 23:28
下一篇 2022年6月13日

相关推荐

  • 在PHP中实现微信商城订单管理

    随着电子商务的发展,越来越多的商家选择在微信平台上开设自己的商城。然而,如何高效地管理订单成为商家面临的一个难题。PHP作为目前最流行的开发语言,在实现微信商城订单管理方面也有着很好的表现。接下来,本文…

    2023年5月18日
    03
  • 如何使用PHP实现微信小程序中的进度条和组件。

    随着微信小程序的不断普及,越来越多的开发者开始关注微信小程序的开发。在微信小程序中,进度条和组件通常用于提高用户体验和界面美观度。本文将介绍如何使用PHP实现微信小程序中的进度条和组件。一、进度条使用HT…

    2023年6月3日
    00
  • 一个算法示例:PHP实现开心消消乐

    本文主要介绍了关于PHP如何实现我们大家都知道的开心消消乐的算法。一、需求描述: 1、在一个8*8的矩阵方格中随机出现5种颜色的色块。 2、当有三个或以上色块在横向或纵向上相连,则消除这些色块。 3、色块消除后,…

    2022年6月23日
    0118
  • 分享宝塔php-fpm。

    宝塔面板是一款服务器管理软件,可以方便地安装、配置和管理PHP-FPM。通过宝塔面板,您可以快速搭建一个稳定高效的PHP环境。 宝塔面板是一款非常实用的服务器管理工具,可以帮助我们轻松地安装和管理PHP扩展,下面…

    2024年6月27日
    00
  • PHP数据库操作:memcache用法分析(附代码)

    memcache简介 Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。简单的说就是将数据调用到…

    2018年3月19日
    0180
  • PHP数组的相关介绍

    数组的覆盖 说明:如果后面的元素中的下标和前面元素的下标“重复”了,后面元素的值会覆盖前面的元素值。 数组的自增 说明:有一个固定的数组,这个时候需要在网数组中添加一个新的元素。 数组的分类 索引数组:下标…

    2018年4月7日 PHP自学教程
    0259
  • 如何处理PHP中的XML和JSON数据。

    在Web开发中,我们经常需要处理不同格式的数据,包括XML和JSON格式的数据。在PHP中,处理这些数据是一个常见的任务,因为PHP是一种非常流行的服务器端脚本语言,而XML和JSON都是常用的数据交换格式。在本文中,我们…

    2023年5月30日
    01
  • 浅谈PHP中需要禁用的危险函数

    本篇文章给大家聊聊PHP安全,介绍一些危险的内置函数,以及禁用函数的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。PHP配置文件中的disable_functions选项能够在PHP中禁用函数,PHP内置…

    2023年3月29日
    01

联系我们

QQ:951076433

在线咨询:点击这里给我发消息邮件:951076433@qq.com工作时间:周一至周五,9:30-18:30,节假日休息