Php ';安全';json_解码(,,)以防止耗尽内存

Php ';安全';json_解码(,,)以防止耗尽内存,php,json,Php,Json,在我的应用程序中,我经常调用一个返回json字符串的外部api $url = 'api.example.com/xyz'; $blah = json_decode( file_get_contents( $url ) ); 但在某些情况下,我会 PHP致命错误:允许的内存大小为xxx字节,在 我无法控制外部API,当然我可以增加php的内存,但这有一些缺点 1-无论我设置了什么尺寸,都可能太小。 2-如果我将内存大小设置为“无限”,那么我可能会冒杀死服务器的风险 理想情况下,我希望在调用jso

在我的应用程序中,我经常调用一个返回json字符串的外部api

$url = 'api.example.com/xyz';
$blah = json_decode( file_get_contents( $url ) );
但在某些情况下,我会

PHP致命错误:允许的内存大小为xxx字节,在

我无法控制外部API,当然我可以增加php的内存,但这有一些缺点

1-无论我设置了什么尺寸,都可能太小。 2-如果我将内存大小设置为“无限”,那么我可能会冒杀死服务器的风险

理想情况下,我希望在调用json_decode(…)之前“检查”字符串是否导致内存耗尽


这有可能吗?

如果JSON响应耗尽了服务器的内存,那么您肯定会得到一些海量的JSON响应。下面是一些指标,其中一个1MB文件包含一个多维关联数组(包含准备输入三个不同数据类型的MySQL表的数据)

当我
include
并将文件作为数组加载到内存中时,我的内存使用量将达到9 MB。如果我使用
file\u get\u contents()
获取原始数据,则需要1 MB的内存。然后,PHP数组与数据的
strlen()

当我运行
json\u encode()
时,峰值内存使用不会增加。(PHP以块的形式分配内存,因此通常会有一点开销,在本例中,这足以包含JSON的字符串数据;但它可能会使您再增加一个块。)结果JSON数据作为字符串需要670 KB

当我将包含
file\u get\u contents
的JSON数据加载到字符串中时,预计需要0.75MB的内存。当我在上面运行
json\u decode()
时,它需要7MB的内存。然后,我将根据RAM要求,将解码为本机PHP数组或对象的JSON数据bytesize的最小比率考虑为1:10

要在解码JSON数据之前对其运行测试,可以执行以下操作:

if (strlen($my_json) * 10 > ($my_mb_memory * 1024 * 1024)) {
    die ('Decoding this would exhaust the server memory. Sorry!');
}
…其中,
$my_json
是原始json响应,
$my_mb_memory
是分配给您的RAM,它被转换为字节以与传入数据进行比较。(当然,您也可以使用
intval(ini\u get('memory\u limit'))
将内存限制设置为整数。)

正如下面指出的,RAM的使用也将取决于您的数据结构。相比之下,还有几个快速测试用例,因为我自己也很好奇:

  • 如果我创建一个整数为1-60000的一维数组,则保存的PHP数组大小为1MB,但峰值RAM使用量在10.5到12.5MB之间(奇怪的振荡),或者是1:12的比率
  • 如果我创建一个1 MB文件的数据作为12000个随机字符串作为基本关联数组,加载时内存使用量只有5 MB;比例为1:5
  • 如果我创建一个1 MB的文件作为一个类似的关联数组,其中一半的条目是带有数字索引的字符串数组,则内存使用率为7 MB,比率为1:7
因此,您的实际RAM里程可能会有很大差异。还应注意,如果您将大量数据循环传递,并执行一些这样或那样的操作,那么您的内存使用率可能会比
json_decode()
单独导致的内存使用率高得多(或成倍增长,取决于您的代码经济性)

要调试内存使用情况,您可以在代码的主要间隔使用
memory\u get\u usage()
和/或
memory\u get\u peak\u usage()
来记录或输出代码不同部分中使用的内存。

我上面的第一个答案纯粹是关于避免内存限制如果您不想丢弃一些数据,但数据有时会超出您的内存限制,那么如何处理这些数据

假设您不需要一次性和绝对实时地解析响应。然后,您可以简单地将响应拆分为大小合适的块,例如使用
explode()
preg_split()
,并将它们保存到临时目录中,稍后在批处理操作中进行处理

我假设大型API响应一次返回多个数据集;如果没有,您还可以将单个多维条目拼接成更易于管理的块,稍后再重新连接,尽管这需要更精确的外科手术来制作JSON字符串拆分器函数


如果在以后的处理中需要关联多个数据集(例如数据库条目),您还需要一个包含批处理操作元数据的聚合器文件(或者将其全部粘贴到数据库中)。当然,您必须确保分块的数据格式良好。这并不理想,但没有内存也不理想。批处理是处理此问题的一种方法。

如果JSON文件太大,您可以使用基于事件的JSON解析器处理任意大小的JSON文件,如。一次只将对象/数组的一小部分加载到内存中。

如果您只需要在大小不可预测的json中迭代项目,请尝试。它在解析任何大小的json时都不会耗尽内存,只需使用
foreach
就可以做到这一点,而无需使用火箭科学。无需事先检查大小“安全性”,也无需增加php内存限制。它的工作原理如下:

if (strlen($my_json) * 10 > ($my_mb_memory * 1024 * 1024)) {
    die ('Decoding this would exhaust the server memory. Sorry!');
}

我明白了这个问题,但我认为这不应该发生在有正常到中等大小响应的情况下。在你的案例中,反应有多大?(以字节为单位)我同意不应该发生这种情况,但它确实会不时发生,响应有时可能超过32Mb(我的服务器限制)。正如我所说,限制可以/应该增加,但我不想增加到目前为止效果很好的内容,只是为了一些无论如何都可能超过限制的内容。请注意,上面有两个单独的操作可能会耗尽内存:
file\u get\u contents
,以及单独的
json\u decode
。艾尔