一、分析
需求:以字节流的形式直接修改laravel框架中的config文件夹下的配置文件,并不影响注释
语言:php
知识点:文件存储方式、文件io读写、栈的使用
思路:直接读取文件,查找key的指针位和旧值的长度后,通过fwrite写入覆盖
问题:
- 如何排除注释内的内容?
答:用栈存储弹出注释符号来忽略注释的解析。 - 如果存在相同的key,如何区分不同的数组?
答:倒数第二个key时,先存储一个"[",循环读取到 "]"即可结束。 - 如果存在value==key,那就会定位错误,如何解决?
答:从当前位置出发查找 "=>" 来判断当前是key还是value。 - fwrite写入覆盖,长短替换和短长替换时出现格式错误,如何解决?
答:将value后的数据临时保存后清空,写入新值后接上临时数据。 - 兼容部分格式?比如字符之间没有空格、存在换行逗号等。
答:针对空格换行逗号进行处理。
调用方式:
配置文件bank.php中 test['a']['b'] = 1,
调用 ConfigHelper::setOrigin(config_path("bank.php"), "test.a.b", "test")后,
修改1为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) {
throw new \Exception($e->getMessage() .'-'. $e->getLine());
}
}
三、代码优化
待续。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。