Completed
Push — dev-master ( 7c3485...49fab8 )
by Vijay
20:59
created

App::loadConfigData()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 6
nop 1
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace FFCMS;
4
5
use FFMVC\Helpers;
6
use FFCMS\Models;
7
8
/**
9
 * fat-free framework application
10
 *
11
 * @author Vijay Mahrra <[email protected]>
12
 * @copyright (c) Copyright 2013 Vijay Mahrra
13
 * @license GPLv3 (http://www.gnu.org/licenses/gpl-3.0.html)
14
 */
15
class App
16
{
17
    /**
18
     * Application-wide dependencies are injected
19
     *
20
     * @param \Log $logger
21
     * @param \DB\SQL $db
22
     */
23
    public function __construct(\Log $logger, \DB\SQL $db)
24
    {
25
        // single instances to registry
26
        \Registry::set('logger', $logger);
27
        \Registry::set('db', $db);
28
    }
29
30
31
    /**
32
     * Helper to automatically load base config values for keys from the database
33
     * and cache them
34
     *
35
     * @param \Base $f3
36
     * @return array $cfg
37
     */
38
    private static function loadConfigData(\Base $f3): array
39
    {
40
        $cfgKeys = $f3->get('cfg.keys');
41
        $keysToLoad = [];
42
        foreach ($cfgKeys as $k => $v) {
43
            if (is_array($v)) {
44
                $keysToLoad = array_merge($keysToLoad, $v);
45
            }
46
        }
47
48
        $cache = \Cache::instance();
49
        $key = 'cfg-' . md5(join('-', array_keys($cfgKeys)));
50
        if (!$cache->exists($key, $cfg)) {
51
            // now set the value of cfg to the keys we want to load
52
            $m = Models\ConfigData::instance();
53
            $cfg = $m->getValues($keysToLoad);
54
            $cache->set($key, $cfg, $f3->get('cfg.ttl.cfg'));
55
        }
56
57
        // cfg.keys.* are no longer required as we know which ones to load in now
58
        $f3->clear('cfg.keys');
59
60
        // replace cfg with values loaded in
61
        $f3->set('cfg', $cfg);
62
63
        return $cfg;
64
    }
65
66
67
    /**
68
     * The main application to run after environment is loaded
69
     */
70
    public function Main()
71
    {
72
        $f3 = \Base::instance();
73
74
        // specify keys of rows in config_data table to load in from .ini files
75
        $f3->set('cfg.keys.load', $f3->split($f3->get('cfg.keys.load')));
76
77
        // is the url under /api ?
78
        $api = '/api' == substr($f3->get('PATH'), 0, 4);
79
        $f3->set('api', $api);
80
        $language = $f3->get('LANG');
81
82
        // do not use sessions for api calls
83
        if (PHP_SAPI == 'cli' ||  $api) {
84
            if (session_status() !== PHP_SESSION_NONE) {
85
                session_write_close();
86
            }
87
        } elseif (session_status() == PHP_SESSION_NONE) {
88
            session_start();
89
            // this is an array so not in registry
90
            $f3->set('notifications', $f3->get('SESSION.notifications'));
91
            $f3->set('uuid', $f3->get('SESSION.uuid')); // logged-in user id
92
93
            // initialise gettext
94
            // override language from request
95
            $language = $f3->get('REQUEST.language');
96
            if (!empty($language)) {
97
                $f3->set('SESSION.language', $language);
98
            }
99
100
            // get language from session if set
101
            if (empty($language)) {
102
                $language = $f3->get('SESSION.language');
103
            }
104
        }
105
106
        // enable gettext if set
107
        if (!empty($f3->get('app.gettext'))) {
108
            // will now fall back to client browser language
109
            $language = empty($language) ? substr($f3->get('LANGUAGE'), 0, 2) : $language;
110
            // use LANG because f3 appends to LANGUAGE when setting
111
            $f3->set('LANG', $language);
112
            putenv('LANG=' . $language);
113
            setlocale(LC_ALL, $language);
114
            $domain = 'messages';
115
            bindtextdomain($domain, $f3->get('HOMEDIR') . '/app/i18n');
116
            bind_textdomain_codeset($domain, 'UTF-8');
117
            textdomain($domain);
118
        }
119
120
            // load cli routes and finish
121
        if (PHP_SAPI == 'cli') {
122
            $f3->route('GET /docs/@page', function ($f3, array $params) {
123
                $filename = '../docs/'.strtoupper($params['page']).'.md';
124
                if (!file_exists($filename)) {
125
                    echo "Documentation Error!\n\nNo such document exists!\n";
126
                    return;
127
                } else {
128
                    echo $f3->read($filename);
129
                }
130
            });
131
132
            // load cli config keys
133
            $f3->set('cfg.keys.cli', $f3->split($f3->get('cfg.keys.cli')));
134
            static::loadConfigData($f3);
0 ignored issues
show
Bug introduced by
Since loadConfigData() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of loadConfigData() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
135
136
            // @see http://fatfreeframework.com/routing-engine
137
            //load routes from ini file
138
            $f3->config('config/routes-cli.ini');
139
            $f3->run();
140
            return;
141
        }
142
143
        // web start
144
145
        // user feedback messages helper, inisialise so methods can be called statically
146
        $notifications = Helpers\Notifications::instance();
147
        $notifications->init();
148
149
        // Use https://github.com/filp/whoops if debug level is 4
150
        $debug = $f3->get('DEBUG');
151
152
        if (!$api && $debug == 4) {
153
            $whoops = new \Whoops\Run;
154
            $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
1 ignored issue
show
Documentation introduced by
new \Whoops\Handler\PrettyPageHandler() is of type object<Whoops\Handler\PrettyPageHandler>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
155
            $whoops->register();
156
        }
157
158
        // custom error handler if debugging
159
        $f3->set('ONERROR',
160
            function () use ($f3) {
161
            $logger = \Registry::get('logger');
162
            if (is_object($logger)) {
163
                $logger->write(print_r($f3->get('ERROR')), $f3->get('log.date'));
164
            }
165
166
            // recursively clear existing output buffers:
167
            while (ob_get_level()) {
168
                ob_end_clean();
169
            }
170
171
            $debug = $f3->get('DEBUG');
172
            $api = !empty($f3->get('api'));
173
            $language = $f3->get('LANG');
174
            $e = $f3->get('ERROR');
175
176
            if (!$api && $e['code'] == '404') {
177
                $error_template = 'templates/' . $language . '/website/error/404.phtml';
178
                if (!file_exists($error_template)) {
179
                    $error_template = 'templates/en/website/error/404.phtml';
180
                }
181
                include_once $error_template;
182
            } else {
183
                if (!$api) {
184
                    $error_template = 'templates/' . $language . '/website/error/error.phtml';
185
                    if (!file_exists($error_template)) {
186
                        $error_template = 'templates/en/website/error/error.phtml';
187
                    }
188
189
                    $debug_template = 'templates/' . $language . '/website/error/error.phtml';
190
                    if (!file_exists($debug_template)) {
191
                        $debug_template = 'templates/en/website/error/debug.phtml';
192
                    }
193
194
                    include_once ('production' == $f3->get('app.env') && $debug < 1) ? $error_template
195
                                : $debug_template;
196
                } else {
197
                    $response = Helpers\Response::instance();
198
199
                    $data = [
200
                        'method' => $f3->get('VERB')
201
                    ];
202
203
                    $data['error'] = [
204
                        'code' => substr($f3->snakecase(str_replace(' ', '',
205
                                    $e['status'])), 0),
206
                        'description' => $e['code'] . ' ' . $e['text']
207
                    ];
208
                    if ($debug > 2) {
209
                        // show the $e['trace'] but it's in HTML!
210
                        $data['error']['trace'] = $e['trace'];
211
                    }
212
213
                    $params = ['http_status' => $e['code']];
214
                    $return = $f3->get('REQUEST.return');
215
216
                    switch ($return) {
217
                        default:
218
                        case 'json':
219
                            $response->json($data, $params);
220
                    }
221
                }
222
            }
223
            // http://php.net/manual/en/function.ob-end-flush.php
224
            while (@ob_end_flush());
225
        });
226
227
        // clean ALL incoming user input by default
228
        $request = [];
229
        $utf = \UTF::instance();
230
        foreach (['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'COOKIE'] as $var) {
231
            $f3->copy($var, $var . '_UNCLEAN');
232
            $input = $f3->get($var);
233
            if (is_array($input) && count($input)) {
234
                $cleaned = [];
235
                foreach ($input as $k => $v) {
236
                    $cleaned[strtolower($utf->trim($f3->clean($k)))] = $f3->recursive($v, function ($v) use ($f3, $utf) {
237
                        return $utf->trim($f3->clean($v));
238
                    });
239
                }
240
                ksort($cleaned);
241
                $request = array_merge_recursive($request, $cleaned);
242
                $f3->set($var, $cleaned);
243
            }
244
        }
245
246
        unset($cleaned);
247
248
        // we don't want to include the session name in the request data
249
        $session_name = strtolower(session_name());
250
        if (array_key_exists($session_name, $request)) {
251
            unset($request[$session_name]);
252
        }
253
254
        ksort($request);
255
        $f3->copy('REQUEST', 'REQUEST_UNCLEAN');
256
        $f3->set('REQUEST', $request);
257
        unset($request);
258
259
        // get the access token and basic auth and set it in REQUEST.access_token
260
        $token = $f3->get('REQUEST.access_token');
261
        foreach ($f3->get('SERVER') as $k => $header) {
262
            if (stristr($k, 'authorization') !== false) {
263
                if (preg_match('/Bearer\s+(?P<access_token>.+)$/i', $header, $matches)) {
264
                    $token = $matches['access_token'];
265
                } elseif (preg_match('/Basic\s+(?P<data>.+)$/i', $header, $matches)) {
266
                    $data = preg_split('/:/', base64_decode($matches['data']));
267
268
                    $f3->mset([
269
                        'SERVER.PHP_AUTH_USER' => $data[0],
270
                        'SERVER.PHP_AUTH_PW' => $data[1],
271
                        'REQUEST.PHP_AUTH_USER' => $data[0],
272
                        'REQUEST.PHP_AUTH_PW' => $data[1]
273
                    ]);
274
                }
275
            }
276
        }
277
        if (!empty($token)) {
278
            $f3->set('REQUEST.access_token', $token);
279
        }
280
281
        // load /api/* routes and finish
282
        if (!empty($api)) {
283
284
            // load api config keys
285
            $f3->set('cfg.keys.api', $f3->split($f3->get('cfg.keys.api')));
286
            static::loadConfigData($f3);
0 ignored issues
show
Bug introduced by
Since loadConfigData() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of loadConfigData() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
287
288
            $f3->config('config/routes-api.ini');
289
            $f3->run();
290
            return;
291
        }
292
293
        // check csrf value if present, set input csrf to boolean true/false if matched session csrf
294
        if (!empty($f3->get('security.csrf'))) {
295
            $csrf = $f3->get('REQUEST.csrf');
296
297
            if (!$api && !empty($csrf)) {
298
                $f3->set('csrf', $csrf == $f3->get('SESSION.csrf'));
299
                $f3->clear('SESSION.csrf');
300
            }
301
        }
302
303
        $f3->route('GET /docs/@page', function ($f3, array $params) {
304
305
            $filename = '../docs/'.strtoupper($params['page']).'.md';
306
307
            if (!file_exists($filename)) {
308
                $html = '<h1>Documentation Error</h1><p>No such document exists!</p>';
309
                $f3->status(404);
310
            } else {
311
                $html = \Markdown::instance()->convert($f3->read($filename));
312
            }
313
314
            $f3->set('html', $html);
315
            echo \View::instance()->render('/markdown-template.phtml');
316
317
        }, $f3->get('ttl.md'));
318
319
        // @see http://fatfreeframework.com/optimization
320
        $f3->route('GET /minify/@type',
321
            function ($f3) {
322
                    $path = realpath(dirname(__FILE__) . '/../www/');
323
                    $files = str_replace('../', '', $f3->get('GET.files')); // close potential hacking attempts
324
                    echo \Web::instance()->minify($files, null, true, $path);
325
            },
326
            $f3->get('minify.ttl')
327
        );
328
329
        // load language-based routes, default english
330
        $f3->config('config/routes-en.ini');
331
        $file = 'config/routes-' . $language  . '.ini';
332
        if (file_exists($file)) {
333
            $f3->config($file);
334
        }
335
336
        // auto-load config keys for www and cms (if needed)
337
        $f3->set('cfg.keys.www', $f3->split($f3->get('cfg.keys.www')));
338
        $cms = '/cms' == substr($f3->get('PATH'), 3, 5);
339
        if ($cms) {
340
            $f3->set('cfg.keys.cms', $f3->split($f3->get('cfg.keys.cms')));
341
        }
342
        static::loadConfigData($f3);
0 ignored issues
show
Bug introduced by
Since loadConfigData() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of loadConfigData() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
343
344
        // from here we add-in routes generated from the database (cms routes)
345
        $f3->run();
346
    }
347
}
348