3

以字节流的形式修改laravel框架config配置文件

 3 years ago
source link: https://segmentfault.com/a/1190000040430741
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

以字节流的形式修改laravel框架config配置文件

需求:以字节流的形式直接修改laravel框架中的config文件夹下的配置文件,并不影响注释

语言:php

知识点:文件存储方式、文件io读写、栈的使用

思路:直接读取文件,查找key的指针位和旧值的长度后,通过fwrite写入覆盖

问题:

  1. 如何排除注释内的内容?
    答:用栈存储弹出注释符号来忽略注释的解析。
  2. 如果存在相同的key,如何区分不同的数组?
    答:倒数第二个key时,先存储一个"[",循环读取到 "]"即可结束。
  3. 如果存在value==key,那就会定位错误,如何解决?
    答:从当前位置出发查找 "=>" 来判断当前是key还是value。
  4. fwrite写入覆盖,长短替换和短长替换时出现格式错误,如何解决?
    答:将value后的数据临时保存后清空,写入新值后接上临时数据。
  5. 兼容部分格式?比如字符之间没有空格、存在换行逗号等。
    答:针对空格换行逗号进行处理。

调用方式:

配置文件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());
        }
    }

三、代码优化
待续。。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK