最近在项目中处理一个关于商品数据重复需要删除多余的商品记录,但是删除一条商品必然要把关联的其他表商品的id和其他商品信息更换为正确的,删除一个商品记录,同时要去修改100多张表的关联商品数据,在项目中引用了tp orm 1.2版本,由于项目是php5.6版本,没法使用最新orm,在代码中每处理1个商品则开启1个事务,相当于循环的事务,下面的代码做个简单的演示(非真实):
$list = Db::table('member')->select();
//循环事务
foreach ($list as $item)
{
try
{
//开启事务
Db::startTrans();
//部分数据不需要处理
if ($item['sex'] == 2)
{
continue;
}
//更新数据
$isUpdate = Db::table('member')->where(['id' => $item['id']])->update([
'name' => 'chen'
]);
if (!$isUpdate)
{
throw new Exception('更新数据失败');
}
//更新其他表(假装这里写了代码)
// 提交事务
Db::commit();
}
catch (Exception $e)
{
Db::rollback();
echo '异常事务回滚:' . $e->getMessage() . PHP_EOL;
}
}
echo '执行完成'.PHP_EOL;上面的代码中每循环一条数据开启1个事务,执行完成提交事务,异常会回滚。但是在预发布环境执行出现前面的数据可以执行,后面的很多数据未执行。于是我在代码中加了文件日志,保存了生成的sql,甚至在提交事务后面加了日志,确保执行到提交事务后面了。最终结果是程序输出了"执行完成",且执行过程中未报错,无任何异常抛出,包括未捕捉的异常。由于我们每次提交代码都需要找运维,本来是想揪出原因,但是怕麻烦运维的小伙伴,于是简单粗暴的使用了tp的事务闭包函数。tp会自动处理错误,然后你可以捕捉tp这个方法抛出的异常来记录日志即可,不需要自己处理事务。
Db::transaction(function (){
//逻辑写在这里
});个人比较较真,一直找不到原因非常不爽,于是抽空下班后完整模拟了下终于发现问题。原来tp在开启事务会在orm中记录事务的次数+1,事务提交和事务回滚都会将事务的次数进行-1操作。例如:
/**
* 启动事务
* @access public
* @return void
* @throws \PDOException
* @throws \Exception
*/
public function startTrans()
{
$this->initConnect(true);
if (!$this->linkID) {
return false;
}
++$this->transTimes;
try {
if (1 == $this->transTimes) {
$this->linkID->beginTransaction();
} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
$this->linkID->exec(
$this->parseSavepoint('trans' . $this->transTimes)
);
}
} catch (\Exception $e) {
if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans();
}
throw $e;
}
}
/**
* 事务回滚
* @access public
* @return void
* @throws PDOException
*/
public function rollback()
{
$this->initConnect(true);
if (1 == $this->transTimes) {
$this->linkID->rollBack();
} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
$this->linkID->exec(
$this->parseSavepointRollBack('trans' . $this->transTimes)
);
}
$this->transTimes = max(0, $this->transTimes - 1);
}
/**
* 用于非自动提交状态下面的查询提交
* @access public
* @return void
* @throws PDOException
*/
public function commit()
{
$this->initConnect(true);
if (1 == $this->transTimes) {
$this->linkID->commit();
}
--$this->transTimes;
}通过上面commit方法我们可以看到当事务次数只有1的时候才会执行事务提交,上面我们的业务代码直接continue导致下次执行的时候事务的次数一直在递增,因为tp orm是静态的,因为只要continue就会导致普通commit事务执行失败,这块tp文档未说明,且在方法中未作异常处理,开发者特别容易进坑。
理解抽象类最快的方法就是使用场景,最近在看布尔PHP视频中看到这样的讲解非常容易懂,分享出来。 假设如下场景: 团队准备开发某网站,表建好了,页面设计好了. ...
php官方的超全局变量$_SERVER['PHP_SELF']也能直接获取,只不过如果url参数太多也会获取。下面提供一个方法获取/* * 获取当前PHP文件名称 */ if (!function_exists('phpself...
如果想在windows中执行php,并且让php脚本在后台运行,可以用下面的cmd命令start /b php D:\wwwroot\default\demo1\run.php例如上面的命令意思后台运行run.php,如果想用php编写异步代码: ...
(1).前端文件:<form action="upload.php" method="post" enctype="multipart/form-data"> &...
(1).学习目标: 了解常见信号类型(百度PHP支持的信号类型),(2).相关函数学习: (2.1).pcntl_signal函数用于设置一个信号管理器接收进程信号,参数1:信号类型,参数2:回调函数,用于在接收到参数1类型的信...
<?php /** * @throws Exception */ function curl() { throw new \Exception('err...