从 file_get_contents() 确定数据类型
Posted
技术标签:
【中文标题】从 file_get_contents() 确定数据类型【英文标题】:Determine data type from file_get_contents() 【发布时间】:2011-09-01 04:40:40 【问题描述】:我正在用 php 编写一个命令行应用程序,它接受本地输入文件的路径作为参数。输入文件将包含以下内容之一:
JSON 编码的关联数组serialized()
版本的关联数组
serialized()
关联数组的 base 64 编码版本
Base 64 编码的 JSON 编码的关联数组
一个普通的旧 PHP 关联数组
垃圾
简而言之,一旦我真正弄清楚了格式,就有几个我无法控制的不同程序将以我能理解的统一方式写入这个文件。一旦我弄清楚如何摄取数据,我就可以使用它了。
我正在考虑的是:
如果文件的第一个字节是
,试试json_decode()
,看看是否失败。
如果文件的第一个字节是<
或$
,试试include()
,看看会不会失败。
如果文件的前三个字节匹配 a:[0-9],请尝试unserialize()
。
如果不是前三个,试试base64_decode()
,看看会不会失败。如果不:
再次检查解码数据的第一个字节。
如果所有这些都失败了,那就是垃圾。
对于一个相当简单的任务来说,这似乎相当昂贵。我可以用更好的方式来做吗?如果有,怎么做?
【问题讨论】:
这是一个完美的例子,约定在其中发挥作用。如果您知道 all JSON 文件以 .json 结尾,则无需解析。但是,如果您无法控制环境,那么运行不受信任的代码(使用include
)是相当不安全的。
我会让用户简单地指出它是什么类型的文件,然后对其进行完整性检查。这种自动化方法似乎很有可能受到奇怪的边缘情况的影响。
include
会以什么方式失败?
@rdineiu 不幸的是,扩展是不可能的。我正在阅读一个名为“转储”的文件,同时将一些不同的系统拼接成一个连贯的前端。 @lonesomeday include()
如果在使用后无法联系到成员,则会失败,谢天谢地,数组的成员都是相同的......只是格式不同。
将文件写入磁盘时,是否可以在文件开头插入一个额外的字节?如果是这样,您可以使该字节确定文件的类型,然后将其剥离。
【参考方案1】:
这里没有太多需要优化的地方。魔术字节方法已经是可行的方法。但是当然可以避免实际的反序列化功能。对每个都使用验证正则表达式是可行的(尽管 meme 通常比让 PHP 实际解压缩嵌套数组要快)。
base64
很容易探测到。
json
可以使用正则表达式进行检查。 Fastest way to check if a string is JSON in PHP? 是用于在 JS 中保护它的 RFC 版本。但是写一个完整的json(?R)
匹配规则是可行的。
serialize
如果没有适当的解包功能,会有点困难。但是通过一些启发式方法,您已经可以断言它是一个序列化 blob。
php
数组脚本可以使用token_get_all
更快地探测。或者,如果格式和数据受到足够的限制,请再次使用正则表达式。
这里更重要的问题是,您需要可靠性 - 还是简单性和速度?
【讨论】:
正则表达式会比只检查前几个字节便宜吗?速度是最重要的,但我还需要低误报率。 您至少应该手动进行 $string[0] 比较。但 PCRE 的验证速度通常更快。 啊,我明白你的意思了,我没有看到森林,只有很多树。谢谢,是的,一个简短的匹配然后比较 $string[0] 来验证垃圾会比我考虑的更有意义。感谢您的帮助!【参考方案2】:为了提高速度,您可以使用file(1)
实用程序并在/usr/share/file/magic
中添加“幻数”。它应该比纯 PHP 替代方案更快。
【讨论】:
这可能行得通,但这必须是可移植的。它是那些WTF之一?当你得到要求时,但无论如何都必须这样做。投了赞成票,因为如果您只处理 GNU 系统,问题就解决了。【参考方案3】:您可以尝试json_decode()
和unserialize()
,如果它们失败将返回NULL
,然后base64_decode()
并再次运行。它并不快,但它比手动解析它们更不容易出错......
【讨论】:
我试图想出一种方法来做出“最好的猜测”,希望避免每次都在可能性列表中下降。可以想象,这必须在每小时 cron 中加载和比较数千个文件,速度真的很重要。【参考方案4】:这里的问题是,如果您不知道它可能是什么,您将需要开发一种检测算法。应使用扩展名设置约定(检查扩展名,如果失败,请告诉将文件放在那里的人放置正确的扩展名),否则您需要自己检查。大多数检测文件实际上是什么类型的算法确实使用异端技术来确定它的内容(exe、jpg 等),因为它们通常具有某种标识它们的签名。因此,如果您不知道要确定的内容是什么,最好寻找特定于这些内容的功能。这有时确实意味着读取超过几个字节。
【讨论】:
以上是关于从 file_get_contents() 确定数据类型的主要内容,如果未能解决你的问题,请参考以下文章
readfile & file_get_contents异同
file_get_contents() 从 url 而不是 json 返回原始数据 [重复]
从本地 LAMP 访问 SoapClient 和 file_get_contents