一、剖析
需要:以字节流的模式间接批改 laravel 框架中的 config 文件夹下的配置文件,并不影响正文
语言:php
知识点:文件存储形式、文件 io 读写、栈的应用
思路:间接读取文件,查找 key 的指针位和旧值的长度后,通过 fwrite 写入笼罩
问题:
- 如何排除正文内的内容?
答:用栈存储弹出正文符号来疏忽正文的解析。 - 如果存在雷同的 key,如何辨别不同的数组?
答:倒数第二个 key 时,先存储一个 ”[“, 循环读取到 “]” 即可完结。 - 如果存在 value==key,那就会定位谬误,如何解决?
答:从以后地位登程查找 “=>” 来判断以后是 key 还是 value。 - fwrite 写入笼罩,长短替换和短长替换时呈现格局谬误,如何解决?
答:将 value 后的数据长期保留后清空,写入新值后接上长期数据。 - 兼容局部格局?比方字符之间没有空格、存在换行逗号等。
答:针对空格换行逗号进行解决。
调用形式: ConfigHelper::setOrigin(config_path(“bank.php”), “test.a.b”, “test”);
二、例子 + 代码
1、配置文件内容:
<?php
/*
* @Description:
*/
/*
|--------------------------------------------------------------------------
| 配置文件
|--------------------------------------------------------------------------
*/
return [
// 最大绑定数量
'max' => 5,
// 是否为单银行账号绑定 false => 多个,true => 单个
'single' => false,
// 平台是否为单银行账号绑定 false => 多个,true => 单个
'single_admin' => true,
"test" => [
'a' => ["b" => 1]
],
"test1" => ['b' => "b"]
];
2. 代码:
public static function setOrigin(string $filename, $key, $value)
{
try {if (empty($filename) || empty($key) || empty($value)) {throw new \Exception("the params is not empty");
}
$f = fopen($filename, "r+");
$keys = explode(".", $key);
$total = count($keys);
// 替换开始的指针地位
$start = 0;
// 替换值的长度
$length = 0;
// 栈来管制正文域
$stack = [];
// 栈来管制数组域
$stack1 = [];
// 完结标记
$end = "";
$map = [
'//' => "\n",
'/*' => "*/",
];
foreach ($keys as $k => $val) {
// 兼容 ""和''
$goal1 = "\"" . $val . "\"";
$goal2 = "'". $val ."'";
$len = strlen($goal1);
while (!feof($f)) {$first = fread($f, 1);
// 记录以后地位
$pointer = ftell($f);
// 读取两个字节判断正文
$second = $first . fread($f, 1);
// 文件完结或出了数组域则退出
if (feof($f) || (count($stack1) > 0 && $first == "]")) {break;}
// 入栈
if (array_key_exists($second, $map)) {$stack[] = $second;
continue;
}
// 出栈
if (count($stack) != 0 && ($map[$stack[count($stack) - 1]] == $first || $map[$stack[count($stack) - 1]] == $second)) {array_pop($stack);
continue;
}
// 获取指标字符串
$tmp = $second . fread($f, $len - 2);
// 判断是否匹配指标字符串 key
if (count($stack) == 0 && ($tmp == $goal1 || $tmp == $goal2)) {
// 排除匹配到的字符串是 value
$status = 0;
// 找到这行完结看看是否有 =>
while (($equl = fread($f, 1)) != "=" && ($equl != "\n" && $equl != ",")) { }
if ($equl . fread($f, 1) == "=>") {$status = 1;}
// 真正匹配到 key
if ($status == 1) {
// 匹配 key 后,倒数第二个索引有个数组域
if ($total > 1 && $k == $total - 2) {$stack1[] = "[";}
// 最初一个 key,要思考没有空格的状况 >
if ($k == $total - 1) {while (fread($f, 1) == " ") { }
$start = ftell($f) - 1;
//value 的完结标记:换行或空格或,while (($tag = fread($f, 1)) != "\n" && $tag != "" && $tag !=",") { }
$end = $tag == "," ? "" : $tag;
$length = ftell($f) - $start;
break;
}
break;
}
}
// 回到以后地位
fseek($f, $pointer);
}
}
// 未找到
if ($start == 0 && $length == 0) throw new \Exception("the config is not found");
// 将 start + lenght 之后的数据长期保留
$tmp = "";
fseek($f, $start + $length);
while (!feof($f)) {$tmp .= fread($f, 2);
}
// 清空前面的数据
ftruncate($f, $start);
// 挪动指针到值地位并写入新值
fseek($f, $start);
$replace = "\"" . $value . "\"," . $end;
fwrite($f, $replace, strlen($replace));
// 将前面的数据 tmp 从新写入
fwrite($f, $tmp, strlen($tmp));
fclose($f);
} catch (\Exception $e) {echo $e->getMessage();
// throw new \Exception($e->getMessage() .'-'. $e->getLine());
}
}
三、代码优化
待续。。