【问题标题】:deleting JSON array element in PHP, and re-encoding as JSON删除 PHP 中的 JSON 数组元素,并重新编码为 JSON
【发布时间】:2019-04-25 19:36:04
【问题描述】:
function deleteNews($selected) {
    $file = file_get_contents('news.json', true);

    $data=json_decode($file,true);
    unset($file);

foreach($selected as $index){
 unset($data[$index]);
 }


  $result=json_encode($data);

  file_put_contents('news.json', $result);
    echo $result;
    unset($result);
    $url="./deleteNews.php";
   //redirect($url);
    }

上述函数应该从 JSON 中删除一个条目,例如:

[
     {
           "dummy":"dummy",
           "dummy1":"dumy"
      },
      {
           "dummy":"dummy1",
           "dummy1":"dummy"
      }
]

但函数最后会在 JSON 中插入数字索引,这是不可取的。

结果是这样的

[
    "0":
       {
          "dummy":"dummy",
          "dummy1":"dumy"
       },
     "1":
       {
           "dummy":"dummy1",
           "dummy1":"dummy"
       }
 ]

如何从中获取我的原始 JSON 格式?我不想要那个 0 1 2 索引部分。

注意:是foreach 做到了这一点。如果没有 foreach,JSON 将按预期创建。

【问题讨论】:

  • 您从问题中省略了某些内容。您引用的结果是由 non-numeric 键的存在引起的。取消设置数字索引是您唯一要做的事情吗?我怀疑不是。如果我尝试这样做,我会得到正确的 JSON。尝试发布你得到的完全错误的 JSON。我敢打赌,在某个地方的最低层有一个文本键。
  • 是的,确切的json包含非数字键,键是:“newsHead”和“newsDetail”代替dummy和dummy1,但我不明白你在暗示什么......
  • 对不起,我不清楚。您发布的输出中的真正问题是实际索引不是 0 和 1,而更可能是 1 和 2 或 0 和 2。有关肮脏的详细信息,请参阅我的未回答 :-)

标签: php json


【解决方案1】:

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)可能是我能够实际向该特定客户销售的唯一干预措施。

【讨论】:

  • 谢谢你的详尽解释,反正我是替换当前目录下同一个news.json,所以没关系...而且正如你所怀疑的,我正在从中间删除一个元素JSON数组的......
  • 如果是这种情况,您应该从file_get_contents... 中删除那个“,真”以防万一。我在我已经很长的答案中添加了一个脚注:-D
【解决方案2】:

用 array_values 试试:

$result = json_encode(array_values($data));

【讨论】:

  • 可能有效——但如果有效,它实际上是隐藏了一个错误。我建议不要这样做,直到正确理解正在发生的事情。
  • lserni 是有道理的。令人不安的指数显然会扰乱底层结构。也许你可以拼接数组或类似的东西,而不是取消设置。
【解决方案3】:

函数 deleteNews($selected) { $file = file_get_contents('news.json', true);

$data=json_decode($file,true);
unset($file);

foreach($selected as $index){
  unset($data[$index]);
}

foreach($data as $value){
  $new_data[] = $value;
}

$result=json_encode($new_data);

file_put_contents('news.json', $result);
echo $result;
unset($result);
$url="./deleteNews.php";

//重定向($url); }

【讨论】:

  • 这会起作用——除了$new_data没有被定义——但它和调用array_values是一样的...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-20
  • 1970-01-01
  • 2013-09-26
  • 2016-09-19
  • 2020-10-21
  • 1970-01-01
相关资源
最近更新 更多