1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Core;
6:
7: /**
8: * This trait makes it possible for you to read config files and various configurations
9: * use:
10: * 1. use Trait in your APP Class
11: * use \Atk4\Core\ConfigTrait;
12: * 2. create config-default.php and/or config.php file and add config values like
13: * return ['key' => 'value'];
14: * 3. call $this->readConfig();
15: * before using config.
16: */
17: trait ConfigTrait
18: {
19: /** @var array<string, mixed> This property stores config values. Use getConfig() method to access its values. */
20: protected array $config = [];
21:
22: /**
23: * Read config file or files and store it in $config property.
24: *
25: * Supported formats:
26: * php - PHP file with return ['foo' => 'bar'] structure
27: * json - JSON file with { 'foo': 'bar' } structure
28: * yaml - YAML file with yaml structure
29: *
30: * @param string|array<int, string> $files One or more filenames
31: * @param string $format Optional format for config files
32: *
33: * @return $this
34: */
35: public function readConfig($files = ['config.php'], string $format = 'php')
36: {
37: if (!is_array($files)) {
38: $files = [$files];
39: }
40:
41: $configs = [];
42: foreach ($files as $file) {
43: if (!is_readable($file)) {
44: throw (new Exception('Cannot read config file'))
45: ->addMoreInfo('file', $file)
46: ->addMoreInfo('format', $format);
47: }
48:
49: $tempConfig = [];
50:
51: switch (strtolower($format)) {
52: case 'php':
53: $tempConfig = require $file;
54:
55: break;
56: case 'json':
57: $tempConfig = json_decode(file_get_contents($file), true);
58:
59: break;
60: case 'yaml':
61: $tempConfig = \Symfony\Component\Yaml\Yaml::parseFile($file);
62:
63: break;
64: default:
65: throw (new Exception('Unknown Format. Allowed formats: php, json, yml'))
66: ->addMoreInfo('file', $file)
67: ->addMoreInfo('format', $format);
68: }
69:
70: if (!is_array($tempConfig)) {
71: throw (new Exception('File was read but has a bad format'))
72: ->addMoreInfo('file', $file)
73: ->addMoreInfo('format', $format);
74: }
75:
76: $configs[] = $tempConfig;
77: }
78:
79: $this->config = array_replace_recursive($this->config, ...$configs);
80:
81: return $this;
82: }
83:
84: /**
85: * Manually set configuration option.
86: *
87: * @param string|array<string, mixed> $paths Path to configuration element to set or array of [path => value]
88: * @param ($paths is array ? never : mixed) $value Value to set
89: *
90: * @return $this
91: */
92: public function setConfig($paths = [], $value = null)
93: {
94: if (!is_array($paths)) {
95: $paths = [$paths => $value];
96: }
97: unset($value);
98:
99: foreach ($paths as $path => $value) {
100: $pos = &$this->_lookupConfigElement($path, true);
101:
102: if (is_array($pos) && count($pos) > 0 && is_array($value)) {
103: // special treatment for arrays - merge them
104: $pos = array_merge($pos, $value);
105: } else {
106: // otherwise just assign value
107: $pos = $value;
108: }
109: }
110:
111: return $this;
112: }
113:
114: /**
115: * Get configuration element.
116: *
117: * @param string $path path to configuration element
118: * @param mixed $defaultValue Default value returned if element don't exist
119: *
120: * @return mixed
121: */
122: public function getConfig(string $path, $defaultValue = null)
123: {
124: $pos = &$this->_lookupConfigElement($path, false);
125:
126: // path element don't exist - return default value
127: if ($pos === false) {
128: return $defaultValue;
129: }
130:
131: return $pos;
132: }
133:
134: /**
135: * Internal method to lookup config element by given path.
136: *
137: * @param string $path Path to navigate to
138: * @param bool $createElements Should we create elements it they don't exist
139: *
140: * @return mixed|false Pointer to element in $this->config or false is element don't exist and $createElements === false
141: */
142: private function &_lookupConfigElement(string $path, bool $createElements = false)
143: {
144: $path = explode('/', $path);
145: $pos = &$this->config;
146: foreach ($path as $el) {
147: if (!is_array($pos) || !array_key_exists($el, $pos)) {
148: if (!is_array($pos) || !$createElements) {
149: $res = false;
150:
151: return $res;
152: }
153:
154: $pos[$el] = [];
155: }
156:
157: $pos = &$pos[$el];
158: }
159:
160: return $pos;
161: }
162: }
163: