PHP で実装するコンフィグツール

ソースコードには含めたくない設定内容を外部ファイルから読み込みたい、というケースはあらゆるアプリケーションで発生します。
今までも、各種 PHP スクリプトで活用できる Config ツールを書いてきており、その内容は単一の config.json ファイルを読み込み、すべての設定をスタティックで保持し、値もスタティック関数で読み込む、という単純なものでした。

それらは今までのアプリケーションによく適応できましたが、アプリケーション規模が大きくなるにつれ、単一の設定ファイルだけのサポートでは見通しが悪くなってきました。

そこで、以下の様なコンフィグツールを設計してみます。

  • 複数の設定ファイルは名前で区別して連想配列に保持
    • 指定されたディレクトリに存在する指定された拡張子のコンフィグをロードする。
    • 設定ファイルを区別する名前はファイル名の拡張子を除いたファイル名とする。
    • サブディレクトリ下の設定ファイル読み込みはサポートしない。
  • 名前単位でコンフィグインスタンスを取得することが可能
  • インスタンスではプロパティのみで値を取得
  • DI などは必要とせず、クラス自体をサービスロケーターとして利用する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
namespace Wws\Config;
 
use Symfony\Component\Yaml\Yaml;
use Yosymfony\Toml\Toml;
 
class Config
{
    private static $instances = [];
 
    public static function loadDir(string $dir): void
    {
        $files = scandir($dir);
        foreach ($files as $file) {
            if (in_array(['.', '..'], $file)) continue;
            $ns   = pathinfo($file, PATHINFO_FILENAME);
            $path = "{$dir}/{$file}";
            Config::load($ns, $path);
        }
    }
 
    public static function load(string $ns, string $path): void
    {
        $instance = new static();
        $instance->loadFile($path);
        Config::setInstance($ns, $instance);
    }
 
    public static function setInstance(string $ns, Config $instance): void
    {
        self::$instances[$ns] = $instance;
    }
 
    public static function getInstance(string $ns, Config $instance): Config
    {
        return self::$instances[$ns];
    }
 
    const INI  = 'ini';
    const YML  = 'yml';
    const YAML = 'yaml';
    const JSON = 'json';
    const TOML = 'toml';
     
    private $_data = null;
     
    public function loadFile(string $path): void
    {
        if (!file_exists($path)) {
            throw new \InvalidArgumentException($path . ' not found. Please specify existing file.');
        }
        if (!$file = file_get_contents($path)) {
            throw new \InvalidArgumentException($path . ' could not read. Please confirm access rights.');
        };
        if (self::YAML === substr($path, -1 * strlen(self::YAML)) || $type == self::YAML) {
            $this->_data = Yaml::parse($file);
        } elseif (self::YML === substr($path, -1 * strlen(self::YML)) || $type == self::YML) {
            $this->_data = Yaml::parse($file);
        } elseif (self::JSON === substr($path,-1 * strlen(self::JSON)) || $type == self::JSON) {
            $this->_data = json_decode($file, true);
        } elseif (self::INI === substr($path,-1 * strlen(self::INI)) || $type == self::INI) {
            $this->_data = Toml::Parse($file);
        } elseif (self::TOML === substr($path,-1 * strlen(self::TOML)) || $type == self::TOML) {
            $this->_data = Toml::Parse($file);
        } else {
            throw new \InvalidArgumentException('Please specify valid config file type. .yaml .json .toml');
        }
    }
 
    public function setValue(string $path, $obj, string $delimiter='.')
    {
        $nodes = explode($delimiter, $path);
        $value = &$this->_data;
        for ($i=0; $i < count($nodes); $i++) {
            $node = $nodes[$i];
            if ($i == count($nodes) - 1) {
                if (preg_match('/(?P<prop>\w+)\[(?P<idx>\d*)\]/', $node, $matches)) {
                    $prop = $matches['prop'];
                    if (!array_key_exists($prop, $value))
                        $value[$prop] = [];
                    if ('' === $matches['idx']) {
                        $value[$prop][] = $obj;
                    } else {
                        $idx = (int)$matches['idx'];
                        $value[$prop][$idx] = $obj;
                    }
                } else {
                    $value[$node] = $obj;
                }
            } else {
                if (preg_match('/(?P<prop>\w+)\[(?P<idx>\d+)\]/', $node, $matches)) {
                    $prop = $matches['prop'];
                    if (!array_key_exists($prop, $value))
                        $value[$prop] = [];
                    if ('' === $matches['idx']) {
                        $len = count($value[$prop]);
                        $value[$prop][] = [];
                        $value = &$value[$prop][$len];
                    } else {
                        $idx = (int)$matches['idx'];
                        $value = &$value[$prop][$idx];
                    }
                } else {
                    $value = &$value[$node];
                }
            }
        }
    }
 
    public function hasValue(?string $path=null, string $delimiter='.')
    {
        if (is_null($path)) return true;
        try {
            $this->getValue($path, $delimiter);
            return true;
        } catch (\OutOfRangeException $e) {
            return false;
        }
    }
 
    public function getValue(?string $path=null, string $delimiter='.')
    {
        if (is_null($path)) return $this->_data;
 
        $nodes = explode($delimiter, $path);
        $value = $this->_data;
 
        foreach ($nodes as $node) {
            if ($node === '') continue;
            if (preg_match('/(?P<prop>\w+)\[(?P<idx>\d*)\]/', $node, $matches)) {
                $prop = $matches['prop'];
                $idx  = (int)$matches['idx'];
                if (!array_key_exists($prop, $value) || !array_key_exists($idx, $value[$prop])) {
                    throw new \OutOfRangeException('The config has no value for '.$path);
                }
                $value = $value[$prop][$idx];
            } else {
                if (!array_key_exists($node, $value)) {
                    throw new \OutOfRangeException('The config has no value for '.$path);
                }
                $value = $value[$node];
            }
        }
        return $value;
    }
}

  • devops/php/config_util.txt
  • 最終更新: 2024/12/18 11:32
  • by 127.0.0.1