TL;DR PHP 数组,所有数字键,从零开始,排序和没有孔 是唯一一种会被渲染成 JSON 数组 [ "square", "brackets" ] 的类型。所有其他类型将成为 JSON 字典{ "curly": "brackets" }。
您观察到的行为的原因,以及 array_values 似乎可以解决问题的原因(在这种情况下,实际上确实)是PHP 和 JSON 数组和字典的区别。
这是一个带有连续数字键的 PHP 数组:
$a = array( "Apple", "Banana", "Canteloupe" );
这是真的
$a = array( 0 => "Apple", 1 => "Banana", 2 => "Canteloupe" );
在 JSON 中,这变成了一个数组:
[ "Apple", "Banana", "Canteloupe" ]
这是一个带有文本键(哈希)的 PHP 数组:
$a = array( "a" => "Apple", "b" => "Banana", "c" => "Canteloupe" );
在 JSON 中,这是一个字典(注意花括号):
{ "a": "Apple", "b": "Banana", "c": "Canteloupe" }
某些操作会将 PHP-array-rendered-as-array (ARA) 更改为 -rendered-as-dictionary (ARD)。
这些操作是:
将 TEXT 键添加到 NUMERIC 数组。
数字键不在不间断的排序序列 0...N.
所以这些看似是数组,但实际上是字典:
$a = array ( 2 => "Canteloupe", 1 => "Banana", 0 => "Apple" );
$a = array ( 0 => "Apple", 2 => "Canteloupe" );
$a = array ( 1 => "Banana", 0 => "Apple" );
这也是为什么删除一个不是最后一个的键会破坏你的数组并使其成为字典的原因:
0 1 2 3 => remove 3 => 0 1 2 => still an array!
0 1 2 3 => remove 2 => 0 1 3 => NOT an array!
0 1 2 3 => remove 2 => 0 1 3 => not an array => remove 3 => 0 1 => again an array!
如果您要对数组进行任何重新编号操作或array_values,您将再次获得一个纯数字数组:
/**
* delete news items given their index.
*
* @param array $selected the list of indexes (e.g. [ 0, 1, 7 ])
* @return nothing
*/
function deleteNews(array $selected = [ ]) {
try {
$news = array_values( // Reorder...
array_diff_key( // ...all keys...
json_decode(file_get_contents('news.json', true), true), // ...in here...
array_flip($selected) // ...that are not in $selected.
)
);
} catch (\Exception $e) {
// TODO handle errors (e.g. file not found and bad JSON)
}
try {
file_put_contents('news.json', json_encode($news));
} catch (\Exception $e) {
// TODO handle errors
}
// $url="./deleteNews.php";
// redirect($url);
}
因此,如果您的代码删除了数组的 last 索引,那么事情就会 出现 正常工作。一旦取消设置在数组中创建了一个洞,或者添加了一个文本键......
排序也是如此:[ 1 => "B", 0 => "A" ] 是一个 JSON 字典。对它进行排序,保留与[ 0 => "A", 1 => "B" ] 的键关联,它就变成了一个 JSON 数组。
关于您的代码需要注意的一点:您在阅读“news.json”时将 include-path 设置为 true。但随后将其保存到当前目录中。如果您的路径中除当前目录之外的其他任何地方都有“news.json”,那么您最终将拥有 两个 news.json 文件;然后使用两者中的哪一个取决于包含路径本身。
本地文件和安全
保存本地文件,就像这里发生的 news.json 一样,是可以的,但通常需要一些预防措施。
一般来说,最好将此类“变量”文件放在它们自己的目录中(例如“./cache”或“./temp”),并带有合适的.htaccess,以防止直接读取/执行,除非需要,并且可以更清楚地执行权限。
例如,可以使用“./data”目录,并将 PHP 文件 everywhere 设置为 Web 服务器的只读文件;最后指示 Apache Web 服务器,这样这个目录虽然可以被 Web 服务器写入,但不能轻易地用于利用系统:
<directory "/var/www/mysite/htdocs/data">
# You CANNOT ask for /data and have a directory listing. Just in case.
options -Indexes
# You CANNOT save "news.php" and have it executed :-)
php_flag engine off
</directory>
这样,即使有人成功在您的系统上上传了一个文件,并且该文件包含可执行的恶意 PHP 代码,该代码也不会被允许执行(至少不能直接执行)。
更多关于安全性:真实体验
允许直接访问受第三方控制的文件,即使是间接访问,总是具有潜在危险 - 如果不是对您,对其他人也是如此。例如,存储一个 JSON 对象,其中包含在第三个站点上收集的信息。我刚刚在客户的网站上完成了成功的攻击测试。 比照,
- 我在自己的网站上设置了一个包含精心制作的数据的页面。
- 我用我的帐户登录了 WWW.CLIENT(好吧,所以我留下了痕迹...)
- 我指示 WWW.CLIENT 访问 EVIL.COM 并获取数据
- 从我自己的帐户中,我可以看到后来在 WWW.CLIENT 上呈现的内容取决于转义码、格式错误的 UTF8 字符和其他技巧
- 最后 (1) 我能够注入命令以加载类似 CDN 的 Javascript 文件(位于 EVIL.COM 上)并让它在 WWW.CLIENT 的安全上下文中执行 (2)
- 经过几次诡计,我能够将 WWW.CLIENT 的客户(当然,我仍然有一个测试帐户)引导到 WWW.CLIENT 上的一个精心设计的链接,该链接会默默地向 EVIL.COM 提供客户的身份验证令牌(3)、(4) 允许(例如)通过删除他的商品、添加我自己的商品和更改送货地址来修改现有订单 (5)。
这利用了总共五个安全漏洞(例如 4:身份验证令牌未链接到所有者的 IP 地址,或未在每次交易中重新生成,5:更改发票或交付未触发请求重新输入信用卡信息)。虽然您可能认为此类安全漏洞是,嗯,安全漏洞,并且客户端没有意识到它们,但它们实际上是已知功能,设计上,以“可用性”和“用户”的名义方便”。缺陷 2 源于在不希望很快改变的辅助库中对 eval() 的恶意使用。最后,更改本地数据存储能力(缺陷 1)可能是我能够实际向该特定客户销售的唯一干预措施。