You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP中file_put_contents后执行unlink导致写入内容全为NULL问题求解

PHP发票状态处理脚本写入文件异常问题分析

问题背景

我尝试从JSON文件中检测发票状态,若状态为已确认付款,则更新状态后将JSON写入新路径,再删除原有路径的JSON文件,代码如下:

<?php
  // get posted variables or die;
  if (isset($_POST['num'])) {
    $invoice = strip_tags($_POST['num']);
    $filename = $invoice.'.json';
  } else {
    die;
  }
  if (isset($_POST['status'])) {
    $status = strip_tags($_POST['status']);
  } else {
    die;
  }

  // get existing invoice 
  $content = file_get_contents('data/'.$invoice.'.json');
  $data = json_decode($content, true);

  // read json into variables
  $email =  $data['email'];
  $id = $data['id'];
  $addr = $data['tac_address'];
  $os = $data['os'];
  $exp = $data['experience'];
  $hosting = $data['type'];
  if (isset($data['telegram']) && $data['telegram'] != '') { $telegram = $data['telegram']; } else { $telegram = ''; }
  if (isset($data['linkedin']) && $data['linkedin'] != '') { $linkedin = $data['linkedin']; } else { $linkedin = ''; }
  if (isset($data['pay_status']) && $data['pay_status'] != '' && $data['pay_status'] == $status) { $status = $data['pay_status']; }
  $payment_addr = $data['bitcoin'];
  $payment_value = $data['value'];
  $payment =  substr($payment_value, 0, -4);

  // turn variables into json array
  $arr = array(
    'id' => $invoice,
    'email' => $email,
    'tac_address' => $addr,
    'os' => $os,
    'experience' => $exp,
    'type' => $hosting,
    'telegram' => $telegram,
    'linkedin' => $linkedin,
    'bitcoin' => $payment_addr,
    'value' => $payment_value,
    'pay_status' => $status
  );
  $json = json_encode($arr);

  // check status if paid save output to new location and delete old file
  if ($status == 'Confirmed Payment') {
    file_put_contents('paid_data/'.$filename, $json);
    unlink('data/'.$filename);
  }

我遇到的问题是:执行file_put_contents('paid_data/'.$filename, $json)后生成的文件中存在大量NULL变量,若移除unlink代码则写入结果正常,重新加回unlink后变量又全部为NULL。请问如何验证file_put_contents执行完成后再执行unlink操作?另外该问题的成因是什么?PHP本应为线性执行逻辑,file_put_contents应该执行完成后才会运行下一行代码,为什么会出现unlink先于文件写入完成执行的情况?


问题成因

file_put_contents默认是同步阻塞执行的,写完才会返回结果,不会出现unlink先执行的情况,问题主要出自以下两个场景:

  • 并发请求冲突:两个相同发票ID的请求同时触发,第一个请求刚读完原目录的JSON文件还没执行写入逻辑,第二个请求先跑完了完整流程删掉了原文件,第一个请求后续解析字段时原文件已经失效,json_decode返回null,最终写入的变量全为空值。
  • 无错误校验逻辑:paid_data目录权限不足导致写入失败,你看到的无unlink时的正常文件实际是之前遗留的旧文件,加了unlink后旧文件被删,写入失败的新文件自然为空。

修复方案

1. 加文件操作校验,确认写入成功后再删除原文件

改写最后一段逻辑,所有操作都做合法性校验:

if ($status == 'Confirmed Payment') {
    // 先校验json编码是否成功
    if ($json === false) {
        // 可自行加日志记录错误
        die('json编码失败');
    }
    $targetPath = 'paid_data/'.$filename;
    $writeBytes = file_put_contents($targetPath, $json);
    // 多重校验确保写入成功:返回字节数匹配、文件存在、内容一致
    if ($writeBytes === false 
        || $writeBytes !== strlen($json) 
        || !file_exists($targetPath) 
        || file_get_contents($targetPath) !== $json
    ) {
        die('文件写入失败');
    }
    // 确认写入完成再删除原文件
    unlink('data/'.$filename);
}

2. 加文件锁避免并发冲突

读取原文件时加独占锁,防止多个请求同时操作同一个发票文件:

// 替换原来的file_get_contents读取逻辑
$sourcePath = 'data/'.$invoice.'.json';
$fp = fopen($sourcePath, 'r');
if (!$fp || !flock($fp, LOCK_EX)) {
    die('文件锁获取失败');
}
$content = fread($fp, filesize($sourcePath));
flock($fp, LOCK_UN);
fclose($fp);

// 加json解码校验
$data = json_decode($content, true);
if ($data === null) {
    die('无效的JSON文件');
}

3. 所有字段读取加默认值

避免原文件字段缺失导致变量未定义:

$email = $data['email'] ?? '';
$id = $data['id'] ?? '';
$addr = $data['tac_address'] ?? '';
// 其余所有字段都按此逻辑改写即可

内容的提问来源于stack exchange,提问作者Bruce

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 20:09:03