Completed
Push — develop ( b08052...da90b6 )
by Neil
04:13
created

Settings::prepend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 1
nc 1
nop 2
1
<?php
2
/*
3
 * Copyright (C) 2016 Tony Murray <[email protected]>
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
/**
18
 * Settings.php
19
 *
20
 * @package    LibreNMS
21
 * @author     Tony Murray <[email protected]>
22
 * @copyright  2016 Tony Murray
23
 * @license    @license http://opensource.org/licenses/GPL-3.0 GNU Public License v3 or later
24
 */
25
namespace App;
26
27
28
use App\Models\DbConfig;
29
use Cache;
30
use Config;
31
use Illuminate\Contracts\Config\Repository as ConfigContract;
32
33
// adds the possibility to replace the default Config facade
34
35
class Settings implements ConfigContract
36
{
37
    /**
38
     * The tag for the cache.
39
     *
40
     * @var string
41
     */
42
    public static $cache_tag = "Settings";
43
    /**
44
     * The amount of time in minutes to store values in cache
45
     *
46
     * @var int
47
     */
48
    private $cache_time;
49
50
    /**
51
     * Settings constructor.
52
     */
53
    public function __construct()
54
    {
55
        $this->cache_time = env('CACHE_LIFETIME', 60);
56
    }
57
58
    /**
59
     * Set a key value pair into the Settings store.
60
     * 
61
     * @param string $key A . separated path to this setting
62
     * @param array|null $value A value or an array. If value is an array it will be converted to a . separate path(s) concatinated onto the given key
63
     */
64
    public function set($key, $value = null)
65
    {
66
        if (is_array($value)) {
67
            // repeat until value contains no arrays
68
            foreach ($value as $k => $v) {
69
                if (!empty($key)) {
70
                    if (is_string($k) && !str_contains($k, '.') && DbConfig::exactKey($key)->exists() && DbConfig::key($key)->count() == 1) {
0 ignored issues
show
Bug introduced by
The method exactKey() does not exist on App\Models\DbConfig. Did you maybe mean scopeExactKey()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
71
                        // check that we aren't trying to set an array onto an existing value only setting
72
                        throw new \Exception("Attempting to set array value to existing non-array value at the key '" . $key . "'");
73
                    }
74
                    else {
75
                        // we are not at the leaf yet, add this chunk to the key and recurse
76
                        $this->set($key . '.' . $k, $v);
77
                    }
78
                }
79
                else {
80
                    // a leaf, recurse one last time
81
                    $this->set($k, $v);
82
                }
83
            }
84
        }
85
        else {
86
            // make sure we can save this
87
            if ($this->isReadOnly($key)) {
88
                throw new \Exception("The setting '" . $key . "' is read only");
89
            }
90
91
            // flush the cache and save the value in db and cache
92
            $this->flush($key);
93
            DbConfig::updateOrCreate(['config_name' => $key], ['config_value' => $value]);
1 ignored issue
show
Bug introduced by
The method updateOrCreate() does not exist on App\Models\DbConfig. Did you maybe mean create()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
94
            Cache::tags(self::$cache_tag)->put($key, $value, $this->cache_time);
95
        }
96
97
    }
98
99
    /**
100
     * Get a value from the Settings store.
101
     *
102
     * @param string $key A full or partial . separated key.
103
     * @param null $default If the key isn't found, return this value. By default undefined keys return null.
104
     * @return mixed If the $key is a full path, a bare value will be returned.  If it is a partial path, a nested array will be retuned.
105
     */
106
    public function get($key, $default = null)
107
    {
108
        // return value from cache or fetch it and return it
109
        return Cache::tags(self::$cache_tag)->remember($key, $this->cache_time, function () use ($key, $default) {
110
            // fetch the value from config.php first
111
            if (Config::has('config.' . $key)) {
112
                $config_data = Config::get('config.' . $key, $default);
113
                if (!is_array($config_data)) {
114
                    // return the value from config.php if it is a value
115
                    return $config_data;
116
                }
117
            }
118
119
            // fetch the value from the database
120
            $db_data = DbConfig::key($key)->get(['config_name', 'config_value']);
121
122
            if (count($db_data) == 1 && $db_data->first()->config_name == $key) {
123
                // return a value if we are getting one item
124
                return $db_data->first()->config_value;
125
            }
126
            elseif (count($db_data) >= 1) {
127
                // convert the collection to an array
128
                $result = self::collectionToArray($db_data, $key);
129
130
                // if we have config_data, merge them
131
                if (isset($config_data)) {
132
                    return array_replace_recursive($result, $config_data);
133
                }
134
                else {
135
                    return $result;
136
                }
137
            }
138
            // we couldn't find the key, return the default
139
            return $default;
140
        });
141
    }
142
143
    /**
144
     * Convert an Eloquent Collection into a nested array
145
     *
146
     * @param $data \Illuminate\Database\Eloquent\Collection The Collection.
147
     * @param string $prefix Path to prepend. Do not include trailing .
148
     * @return array The resulting nested array.
149
     */
150
    private static function collectionToArray($data, $prefix = "")
151
    {
152
        $tree = array();
153
        foreach ($data as $item) {
154
            $key = $item->config_name;
155
            if (starts_with($key, $prefix)) {
156
                $key = substr($key, strlen($prefix));
157
            }
158
            $parts = explode('.', trim($key, '.'));
159
160
            $temp = &$tree;
161
            foreach ($parts as $part) {
162
                $temp = &$temp[$part];
163
            }
164
            $temp = $item->config_value;
165
            unset($temp);
166
        }
167
        return $tree;
168
    }
169
170
    /**
171
     * Check if the key is defined in the Settings store.
172
     * 
173
     * @param string $key Only full paths will return true.
174
     * @return bool 
175
     */
176
    public function has($key)
177
    {
178
        return (Cache::tags(self::$cache_tag)->has($key) || Config::has('config.' . $key) || DbConfig::key($key)->exists());
179
    }
180
181
    /**
182
     * Check if the key is read only.  This is when the setting is defined in config.php.
183
     *
184
     * @param string $key The path to check
185
     * @return bool
186
     */
187
    public function isReadOnly($key)
188
    {
189
        return Config::has('config.' . $key);
190
    }
191
192
    /**
193
     * Forget a key.  Gets to forgotten keys will return null instead of the default.
194
     *
195
     * @param $key string Only works for full paths.
196
     */
197
    public function forget($key)
198
    {
199
        // set to null to prevent falling back to Config
200
        DbConfig::key($key)->update(['config_value' => null]);
201
        Cache::tags(self::$cache_tag)->forget($key);
202
    }
203
204
    /**
205
     * Get all settings defined in the Settings store.
206
     * 
207
     * @return array A nested array of all settings.
208
     */
209
    public function all()
210
    {
211
        // no caching :(
212
        $config_settings = Config::all()['config'];
213
        $db_settings = self::collectionToArray(DbConfig::all());
214
        return array_replace_recursive($config_settings, $db_settings);
215
    }
216
217
    // ---- Local Utility functions ----
218
219
    /**
220
     * Clear the settings cache.
221
     * If path is set, only clear the path and it's parents.
222
     *
223
     * @param null $key The path to clear.
224
     */
225
    public function flush($key = null)
226
    {
227
        if (is_null($key)) {
228
            // Clear all cache
229
            Cache::tags(self::$cache_tag)->flush();
230
        }
231
        else {
232
            // Clear specific path
233
            $path = [];
234
            foreach (explode('.', $key) as $element) {
235
                $path[] = $element;
236
                Cache::tags(self::$cache_tag)->forget(join('.', $path));
237
            }
238
        }
239
    }
240
241
    /**
242
     * Prepend a value onto an array configuration value.
243
     *
244
     * @param  string $key
245
     * @param  mixed $value
246
     * @return void
247
     */
248
    public function prepend($key, $value)
249
    {
250
        // TODO: Implement prepend() method.
251
    }
252
253
    /**
254
     * Push a value onto an array configuration value.
255
     *
256
     * @param  string $key
257
     * @param  mixed $value
258
     * @return void
259
     */
260
    public function push($key, $value)
261
    {
262
        // TODO: Implement push() method.
263
    }
264
}
265