EasySwoole配置类源码剖析

Config类在vendor\easyswoole\src\Config.php里.本篇将对它的实现进行刨析.

我们在需要用到Config的地方 $config = Config::getInstance() 获取实例.

通过 $config->setConf()$config->getConf() 读写配置项,那这些内容存在在哪里呢? 我们可以看下它的构造方法.

class Config
{
    private $conf; // 实现AbstractConfig的类 (框架提供的 TableConfig)

    use Singleton; // 引入单例

    public function __construct(?AbstractConfig $config = null)
    {
        if($config == null){
            $config = new TableConfig(); // 默认传入 TableConfig 类(基于Swoole Table)
        }
        $this->conf = $config;
    }
}

默认为 TableConfig 类.这个类继承了AbstractConfig,如果你想编写自己的存储方式,可以编写一个类继承这个抽象类,实现里面所有方法.

abstract class AbstractConfig
{
    abstract function getConf($key = null); // 获取key
    abstract function setConf($key,$val):bool ; // 设置key
    abstract function load(array $array):bool ; // 批量读入key
    abstract function merge(array $array):bool ; // 合并value
    abstract function clear():bool ; // 清除所有配置项
}

框架中还提供了一个类 SplArrayConfig, 它有一个弊端就是动态生成的配置项只在当前进程有效(进程隔离问题),我们重点分析TableConfig的实现

主要分析 setConfgetConf 这两个方法.


function setConf($key, $val): bool
{
    if (strpos($key, ".") > 0) {
        $temp = explode(".", $key);
        $key = array_shift($temp);
        $data = $this->getConf($key);
        if (is_array($data)) {
            $data = new SplArray($data);
        } else {
            $data = new SplArray();
        }
        $data->set(implode('.', $temp), $val);
        return $this->table->set($key, [
            'data' => serialize($data->getArrayCopy())
        ]);
    } else {
        // 如果没有设置多级配置项直接序列化后写入SwooleTable
        return $this->table->set($key, [
            'data' => serialize($val)
        ]);
    }
}

我们可以通过setConfig('a.b.c', '1') 设置多级配置项.当遇到多级配置项时,会实例化一个SplArray

它是一个数组类库提供了很多操作数组的方法

举个例子:

$arr = new \EasySwoole\Spl\SplArray([
    'a' => [
        'b' => [
            'c' => [
                'd' => '123'
            ]
        ]
    ]
]);

var_dump($arr->get('a.b'));

结果:

array(1) {
  ["c"]=>
  array(1) {
    ["d"]=>
    string(3) "123"
  }
}

下面回归我们的setConfig方法,如果发现多级配置项则获取该多级配置项下其他数据(通过strpos函数判断key中是否有.)

$temp = explode(".", $key); // key名转为一个数组
$key = array_shift($temp); // 弹出第一个元素, 主域名作为SwooleTable里的key
$data = $this->getConf($key); // 获取主域名下的数据
if (is_array($data)) { // 如果有数据则读入SplArray
    $data = new SplArray($data);
} else {
   $data = new SplArray();
}
$data->set(implode('.', $temp), $val); // 将新增的元素插入SplArray

return $this->table->set($key, [
    'data' => serialize($data->getArrayCopy()) // 写入SwooleTable
]);

setConfig方法我们分析完毕了,接着再看看getConfig

function getConf($key = null)
{
    if ($key == null) { // 如果为没有传入数据则打印所有存入的配置项
        $data = [];
        foreach ($this->table as $key => $item) {
            $data[$key] = unserialize($item['data']);
        }
        return $data;
    }
    if (strpos($key, ".") > 0) { // 判断是否为多级配置项
        $temp = explode(".", $key); // 将key转为数组
        $data = $this->table->get(array_shift($temp)); // 在setConfig中,我们只将主域名作为Key存入SwooleTable
        if ($data) {
            $data = unserialize($data['data']);
            /*
             * 数组才有意义进行二次搜索
             */
            if (is_array($data)) {
                $data = new SplArray($data); // 将取出的数组交给SplArray解析
                return $data->get(implode('.', $temp));
            }
        }
    } else {
        $data = $this->table->get($key); // 非多级配置项直接取出反序列化即可
        if ($data) {
            return unserialize($data['data']);
        }
    }
    return null;
}

至此,我们的SetConfiggetConfig方法已经分析结束.

总结:

如果是多级配置项,只将主域名作为Key存入SwooleTable,读取的时候将下面的子配置项传入框架内的SplArray类.

$config = \EasySwoole\EasySwoole\Config::getInstance();
$config->setConf('a.b.c.d.e', 123);
var_dump($config->getTest()->get('a'));

$config->getTest() 是我获取SwooleTable对象的.为了查看下它存入的数据结构.

array(1) {
  ["data"]=>
  string(62) "a:1:{s:1:"b";a:1:{s:1:"c";a:1:{s:1:"d";a:1:{s:1:"e";i:123;}}}}"
}

可以通过unserialize来转成数组.