Completed
Push — master ( 58937a...81ac23 )
by El
07:13
created

Configuration::__construct()   F

Complexity

Conditions 29
Paths 259

Size

Total Lines 125
Code Lines 80

Duplication

Lines 12
Ratio 9.6 %

Code Coverage

Tests 72
CRAP Score 29

Importance

Changes 0
Metric Value
dl 12
loc 125
ccs 72
cts 72
cp 1
rs 3.4833
c 0
b 0
f 0
cc 29
eloc 80
nc 259
nop 0
crap 29

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PrivateBin
4
 *
5
 * a zero-knowledge paste bin
6
 *
7
 * @link      https://github.com/PrivateBin/PrivateBin
8
 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9
 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   1.1.1
11
 */
12
13
namespace PrivateBin;
14
15
use Exception;
16
use PDO;
17
18
/**
19
 * Configuration
20
 *
21
 * parses configuration file, ensures default values present
22
 */
23
class Configuration
24
{
25
    /**
26
     * First line in INI file, to hide contents
27
     *
28
     * @const string
29
     */
30
    const PROTECTION_LINE = ';<?php http_response_code(403); /*';
31
32
    /**
33
     * parsed configuration
34
     *
35
     * @var array
36
     */
37
    private $_configuration;
38
39
    /**
40
     * default configuration
41
     *
42
     * @var array
43
     */
44
    private static $_defaults = array(
45
        'main' => array(
46
            'name'                     => 'PrivateBin',
47
            'discussion'               => true,
48
            'opendiscussion'           => false,
49
            'password'                 => true,
50
            'fileupload'               => false,
51
            'burnafterreadingselected' => false,
52
            'instantburnafterreading'  => false,
53
            'defaultformatter'         => 'plaintext',
54
            'syntaxhighlightingtheme'  => null,
55
            'sizelimit'                => 2097152,
56
            'template'                 => 'bootstrap',
57
            'notice'                   => '',
58
            'languageselection'        => false,
59
            'languagedefault'          => '',
60
            'urlshortener'             => '',
61
            'icon'                     => 'identicon',
62
            'cspheader'                => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups',
63
            'zerobincompatibility'     => false,
64
        ),
65
        'expire' => array(
66
            'default' => '1week',
67
            'clone'   => true,
68
        ),
69
        'expire_options' => array(
70
            '5min'   => 300,
71
            '10min'  => 600,
72
            '1hour'  => 3600,
73
            '1day'   => 86400,
74
            '1week'  => 604800,
75
            '1month' => 2592000,
76
            '1year'  => 31536000,
77
            'never'  => 0,
78
        ),
79
        'formatter_options' => array(
80
            'plaintext'          => 'Plain Text',
81
            'syntaxhighlighting' => 'Source Code',
82
            'markdown'           => 'Markdown',
83
        ),
84
        'traffic' => array(
85
            'limit'  => 10,
86
            'header' => null,
87
            'dir'    => 'data',
88
        ),
89
        'purge' => array(
90
            'limit'     => 300,
91
            'batchsize' => 10,
92
            'dir'       => 'data',
93
        ),
94
        'model' => array(
95
            'class' => 'Filesystem',
96
        ),
97
        'model_options' => array(
98
            'dir' => 'data',
99
        ),
100
    );
101
102
    /**
103
     * parse configuration file and ensure default configuration values are present
104
     *
105
     * @throws Exception
106
     */
107 127
    public function __construct()
108
    {
109 127
        $config     = array();
110 127
        $configFile = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.php';
111 127
        $configIni  = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini';
112
113
        // rename INI files to avoid configuration leakage
114 127
        if (is_readable($configIni)) {
115 2
            $context = stream_context_create();
116
            // don't overwrite already converted file
117 2 View Code Duplication
            if (!is_file($configFile)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
118 2
                $iniHandle = fopen($configIni, 'r', false, $context);
119 2
                file_put_contents($configFile, self::PROTECTION_LINE . PHP_EOL);
120 2
                file_put_contents($configFile, $iniHandle, FILE_APPEND);
121 2
                fclose($iniHandle);
122
            }
123 2
            unlink($configIni);
124
125
            // cleanup sample, too
126 2
            $configSample    = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.sample.php';
127 2
            $configIniSample = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample';
128 2
            if (is_readable($configIniSample)) {
129 2 View Code Duplication
                if (!is_readable($configSample)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130 1
                    $iniSampleHandle = fopen($configIniSample, 'r', false, $context);
131 1
                    file_put_contents($configSample, self::PROTECTION_LINE . PHP_EOL);
132 1
                    file_put_contents($configSample, $iniSampleHandle, FILE_APPEND);
133 1
                    fclose($iniSampleHandle);
134
                }
135 2
                unlink($configIniSample);
136
            }
137
        }
138
139 127
        if (is_readable($configFile)) {
140 125
            $config = parse_ini_file($configFile, true);
141 125
            foreach (array('main', 'model', 'model_options') as $section) {
142 125
                if (!array_key_exists($section, $config)) {
143 125
                    throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2);
144
                }
145
            }
146
        }
147
148 124
        $opts = '_options';
149 124
        foreach (self::getDefaults() as $section => $values) {
150
            // fill missing sections with default values
151 124
            if (!array_key_exists($section, $config) || count($config[$section]) == 0) {
152 6
                $this->_configuration[$section] = $values;
153 6
                if (array_key_exists('dir', $this->_configuration[$section])) {
154 6
                    $this->_configuration[$section]['dir'] = PATH . $this->_configuration[$section]['dir'];
155
                }
156 6
                continue;
157
            }
158
            // provide different defaults for database model
159
            elseif (
160 118
                $section == 'model_options' && in_array(
161 118
                    $this->_configuration['model']['class'],
162 118
                    array('Database', 'privatebin_db', 'zerobin_db')
163
                )
164
            ) {
165
                $values = array(
166 60
                    'dsn' => 'sqlite:' . PATH . 'data' . DIRECTORY_SEPARATOR . 'db.sq3',
167
                    'tbl' => null,
168
                    'usr' => null,
169
                    'pwd' => null,
170
                    'opt' => array(PDO::ATTR_PERSISTENT => true),
171
                );
172
            }
173
174
            // "*_options" sections don't require all defaults to be set
175
            if (
176 118
                $section !== 'model_options' &&
177 118
                ($from = strlen($section) - strlen($opts)) >= 0 &&
178 118
                strpos($section, $opts, $from) !== false
179
            ) {
180 118
                if (is_int(current($values))) {
181 118
                    $config[$section] = array_map('intval', $config[$section]);
182
                }
183 118
                $this->_configuration[$section] = $config[$section];
184
            }
185
            // check for missing keys and set defaults if necessary
186
            else {
187 118
                foreach ($values as $key => $val) {
188 118
                    if ($key == 'dir') {
189 118
                        $val = PATH . $val;
190
                    }
191 118
                    $result = $val;
192 118
                    if (array_key_exists($key, $config[$section])) {
193 118
                        if ($val === null) {
194 3
                            $result = $config[$section][$key];
195 118
                        } elseif (is_bool($val)) {
196 118
                            $val = strtolower($config[$section][$key]);
197 118
                            if (in_array($val, array('true', 'yes', 'on'))) {
198 1
                                $result = true;
199 118
                            } elseif (in_array($val, array('false', 'no', 'off'))) {
200 1
                                $result = false;
201
                            } else {
202 118
                                $result = (bool) $config[$section][$key];
203
                            }
204 118
                        } elseif (is_int($val)) {
205 118
                            $result = (int) $config[$section][$key];
206 118
                        } elseif (is_string($val) && !empty($config[$section][$key])) {
207 118
                            $result = (string) $config[$section][$key];
208
                        }
209
                    }
210 118
                    $this->_configuration[$section][$key] = $result;
211
                }
212
            }
213
        }
214
215
        // support for old config file format, before the fork was renamed and PSR-4 introduced
216 124
        $this->_configuration['model']['class'] = str_replace(
217 124
            'zerobin_', 'privatebin_',
218 124
            $this->_configuration['model']['class']
219
        );
220
221 124
        $this->_configuration['model']['class'] = str_replace(
222 124
            array('privatebin_data', 'privatebin_db'),
223 124
            array('Filesystem', 'Database'),
224 124
            $this->_configuration['model']['class']
225
        );
226
227
        // ensure a valid expire default key is set
228 124
        if (!array_key_exists($this->_configuration['expire']['default'], $this->_configuration['expire_options'])) {
229 1
            $this->_configuration['expire']['default'] = key($this->_configuration['expire_options']);
230
        }
231 124
    }
232
233
    /**
234
     * get configuration as array
235
     *
236
     * return array
237
     */
238 6
    public function get()
239
    {
240 6
        return $this->_configuration;
241
    }
242
243
    /**
244
     * get default configuration as array
245
     *
246
     * return array
247
     */
248 125
    public static function getDefaults()
249
    {
250 125
        return self::$_defaults;
251
    }
252
253
    /**
254
     * get a key from the configuration, typically the main section or all keys
255
     *
256
     * @param string $key
257
     * @param string $section defaults to main
258
     * @throws Exception
259
     * return mixed
260
     */
261 116
    public function getKey($key, $section = 'main')
262
    {
263 116
        $options = $this->getSection($section);
264 116
        if (!array_key_exists($key, $options)) {
265 1
            throw new Exception(I18n::_('Invalid data.') . " $section / $key", 4);
266
        }
267 115
        return $this->_configuration[$section][$key];
268
    }
269
270
    /**
271
     * get a section from the configuration, must exist
272
     *
273
     * @param string $section
274
     * @throws Exception
275
     * return mixed
276
     */
277 116
    public function getSection($section)
278
    {
279 116
        if (!array_key_exists($section, $this->_configuration)) {
280 1
            throw new Exception(I18n::_('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3);
281
        }
282 116
        return $this->_configuration[$section];
283
    }
284
}
285