1

1. Analysis

requirements: directly modify the configuration file in the config folder in the laravel framework in the form of byte stream, and does not affect the comments

Language: php

knowledge points: file storage method, file io reading and writing, stack usage

Idea: reads the file directly, finds the pointer bit of the key and the length of the old value, and writes and overwrites it through fwrite

question:

  1. How to exclude content in comments?
    Answer: Use stack storage to pop up comment symbols to ignore comment parsing.
  2. If the same key exists, how to distinguish between different arrays?
    Answer: When the penultimate key is stored, first store a "[", and read the cycle to "]" to end.
  3. If there is value==key, there will be a positioning error. How to solve it?
    Answer: Start from the current position and search for "=>" to determine whether it is currently key or value.
  4. How to solve the format error when fwrite write overwrite, long-short replacement and short-length replacement?
    Answer: The data after value is temporarily saved and then cleared, and the temporary data is connected after writing the new value.
  5. Compatible with some formats? For example, there are no spaces between characters, and there are newline commas, etc.
    Answer: deal with the space-wrap comma.

call method:

配置文件bank.php中 test['a']['b'] = 1, 
调用 ConfigHelper::setOrigin(config_path("bank.php"), "test.a.b", "test")后,
修改1为test

Two, example + code

1. Configuration file content:

 <?php
        /*
        * @Description:
        */
        /*
          |-----------------------------------------------------------------
          | 配置文件
          |-----------------------------------------------------------------
         */
    return [
        //    最大绑定数量
        'max' => 5,
        //    是否为单银行账号绑定 false => 多个, true => 单个
        'single' => false,
        //    平台是否为单银行账号绑定 false => 多个, true => 单个
        'single_admin' => true,

        "test" => [
            'a' => [
                 "b" => 1
             ]
        ],

        "test1" => [
            'b' => "b"
        ]

    ];

2. Code:

 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());
        }
    }

Three, code optimization
to be continued. .


sengerlion
55 声望401 粉丝

了解自己到细胞粒度。