Cancelled
Pull Request — develop (#267)
by Felipe
06:23
created

ContainerUtils::setConf()   B

Complexity

Conditions 8
Paths 1

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 22
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 40
rs 8.4444
1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-RC9
5
 */
6
7
namespace PHPPgAdmin;
8
9
use Psr\Container\ContainerInterface;
10
use Slim\App;
11
12
\defined('BASE_PATH') || \define('BASE_PATH', \dirname(__DIR__, 2));
13
\defined('THEME_PATH') || \define('THEME_PATH', BASE_PATH . '/assets/themes');
14
15
\defined('DEBUGMODE') || \define('DEBUGMODE', false);
16
\defined('IN_TEST') || \define('IN_TEST', false);
17
18
/**
19
 * @file
20
 * A class that adds convenience methods to the container
21
 */
22
23
/**
24
 * A class that adds convenience methods to the container.
25
 */
26
class ContainerUtils
27
{
28
    use \PHPPgAdmin\Traits\HelperTrait;
29
    /**
30
     * @var string
31
     */
32
    const BASE_PATH = BASE_PATH;
33
    /**
34
     * @var string
35
     */
36
    const SUBFOLDER = PHPPGA_SUBFOLDER;
37
    /**
38
     * @var string
39
     */
40
    const DEBUGMODE = DEBUGMODE;
41
42
    /**
43
     * @var string
44
     */
45
    const THEME_PATH = THEME_PATH;
46
47
    /**
48
     * @var ContainerInterface
49
     */
50
    protected $container;
51
52
    /**
53
     * @var App
54
     */
55
    protected $_app;
56
57
    /**
58
     * @var array
59
     */
60
    protected $conf;
61
62
    /**
63
     * @var self
64
     */
65
    protected static $_instance;
66
67
    /**
68
     * Constructor of the ContainerUtils class.
69
     */
70
    public function __construct()
71
    {
72
        $composerinfo = \json_decode(\file_get_contents(BASE_PATH . '/composer.json'));
73
        $appVersion = $composerinfo->extra->version;
74
75
        $phpMinVer = (\str_replace(['<', '>', '='], '', $composerinfo->require->php));
76
        //$this->prtrace($appVersion);
77
        //$this->dump($composerinfo);
78
        $settings = [
79
            'determineRouteBeforeAppMiddleware' => true,
80
            'base_path' => self::BASE_PATH,
81
            'debug' => self::DEBUGMODE,
82
83
            // Configuration file version.  If this is greater than that in config.inc.php, then
84
            // the app will refuse to run.  This and $conf['version'] should be incremented whenever
85
            // backwards incompatible changes are made to config.inc.php-dist.
86
            'base_version' => 60,
87
            // Application version
88
            'appVersion' => 'v' . $appVersion,
89
            // Application name
90
            'appName' => 'phpPgAdmin6',
91
92
            // PostgreSQL and PHP minimum version
93
            'postgresqlMinVer' => '9.3',
94
            'phpMinVer' => $phpMinVer,
95
            'displayErrorDetails' => self::DEBUGMODE,
96
            'addContentLengthHeader' => false,
97
        ];
98
99
        if (!self::DEBUGMODE && !IN_TEST) {
100
            $settings['routerCacheFile'] = self::BASE_PATH . '/temp/route.cache.php';
101
        }
102
        $config = [
103
            'msg' => '',
104
            'appThemes' => [
105
                'default' => 'Default',
106
                'cappuccino' => 'Cappuccino',
107
                'gotar' => 'Blue/Green',
108
                'bootstrap' => 'Bootstrap3',
109
            ],
110
            'settings' => $settings,
111
        ];
112
113
        $this->_app = new App($config);
114
115
        // Fetch DI Container
116
        $container = $this->_app->getContainer();
117
        $container['utils'] = $this;
118
        $container['version'] = 'v' . $appVersion;
119
        $container['errors'] = [];
120
        $container['requestobj'] = $container['request'];
121
        $container['responseobj'] = $container['response'];
122
        $this->container = $container;
123
    }
124
125
    /**
126
     * Gets the container instance.
127
     *
128
     * @throws \Exception (description)
129
     *
130
     * @return ContainerInterface the container instance
131
     */
132
    public static function getContainerInstance()
133
    {
134
        $_instance = self::getInstance();
135
136
        if (!$container = $_instance->container) {
137
            throw new \Exception('Could not get a container');
138
        }
139
140
        return $container;
141
    }
142
143
    /**
144
     * Gets the instance.
145
     *
146
     * @return self the instance
147
     */
148
    public static function getInstance()
149
    {
150
        if (!self::$_instance) {
151
            self::$_instance = new self();
152
        }
153
154
        return self::$_instance;
155
    }
156
157
    /**
158
     * Creates a container.
159
     *
160
     * @param array $conf The conf
161
     *
162
     * @return [ContainerInterface,App] ( description_of_the_return_value )
0 ignored issues
show
Documentation Bug introduced by
The doc comment [ContainerInterface,App] at position 0 could not be parsed: Unknown type name '[' at position 0 in [ContainerInterface,App].
Loading history...
163
     */
164
    public static function createContainer($conf)
165
    {
166
        $_instance = self::getInstance();
167
168
        $_instance
169
            ->setConf($conf)
170
            ->setExtra()
171
            ->setMisc()
172
            ->setViews();
173
174
        //ddd($container->subfolder);
175
        return [$_instance->container, self::$_instance->_app];
176
    }
177
178
    public function maybeRenderIframes($response, $subject, $query_string)
179
    {
180
        $c = self::getContainerInstance();
181
182
        $in_test = $c->view->offsetGet('in_test');
0 ignored issues
show
Bug introduced by
Accessing view on the interface Psr\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
183
184
        if ('1' === $in_test) {
185
            $className = '\PHPPgAdmin\Controller\\' . \ucfirst($subject) . 'Controller';
186
            $controller = new $className($c);
187
188
            return $controller->render();
189
        }
190
191
        $viewVars = [
192
            'url' => '/src/views/' . $subject . ($query_string ? '?' . $query_string : ''),
193
            'headertemplate' => 'header.twig',
194
        ];
195
196
        return $c->view->render($response, 'iframe_view.twig', $viewVars);
197
    }
198
199
    /**
200
     * Gets the theme from
201
     * 1. The $_REQUEST global (when it's chosen from start screen)
202
     * 2. Server specific config theme 3.- $_SESSION global (subsequent requests after 1.) 4.- $_COOKIE global (mostly
203
     *    fallback for $_SESSION after 1.- and 3.-) 5.- theme as set in config 6.- 'default' theme.
204
     *
205
     * @param array      $conf         The conf
206
     * @param null|mixed $_server_info
207
     *
208
     * @return string the theme
209
     */
210
    public function getTheme(array $conf, $_server_info = null)
211
    {
212
        $_theme = null;
213
        // List of themes
214
        $themefolders = $this->getThemeFolders();
215
        // Check if theme is in $_REQUEST, $_SESSION or $_COOKIE
216
        // 1.- First priority: $_REQUEST, this happens when you use the selector
217
        if (\array_key_exists('theme', $_REQUEST) &&
218
            \array_key_exists($_REQUEST['theme'], $themefolders)) {
219
            $_theme = $_REQUEST['theme'];
220
        } elseif ( // otherwise, see if there's a theme associated with this particular server
0 ignored issues
show
Coding Style introduced by
First condition of a multi-line IF statement must directly follow the opening parenthesis
Loading history...
221
            null !== $_server_info &&
222
            \array_key_exists('theme', $_server_info) &&
223
            \is_string($_server_info['theme']) &&
224
            \array_key_exists($_COOKIE['ppaTheme'], $themefolders)) {
225
            $_theme = $_server_info['theme'];
226
        } elseif (\array_key_exists('ppaTheme', $_SESSION) &&
227
            \array_key_exists($_SESSION['ppaTheme'], $themefolders)) {
228
            // otherwise check $_SESSION
229
            $_theme = $_SESSION['ppaTheme'];
230
        } elseif (\array_key_exists('ppaTheme', $_SESSION) &&
231
            \array_key_exists($_COOKIE['ppaTheme'], $themefolders)) {
232
            // oterwise check $_COOKIE
233
            $_theme = $_COOKIE['ppaTheme'];
234
        } elseif ( // see if there's a valid theme set in config file
0 ignored issues
show
Coding Style introduced by
First condition of a multi-line IF statement must directly follow the opening parenthesis
Loading history...
235
            \array_key_exists('theme', $conf) &&
236
            \is_string($conf['theme']) &&
237
            \array_key_exists($conf['theme'], $themefolders)) {
238
            $_theme = $conf['theme'];
239
        } else {
240
            // okay then, use default theme
241
            $_theme = 'default';
242
        }
243
244
        return $_theme;
245
    }
246
247
    /**
248
     * Determines the redirection url according to query string.
249
     *
250
     * @return string the redirect url
251
     */
252
    public function getRedirectUrl()
253
    {
254
        $query_string = $this->container->requestobj->getUri()->getQuery();
0 ignored issues
show
Bug introduced by
Accessing requestobj on the interface Psr\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
255
256
        // if server_id isn't set, then you will be redirected to intro
257
        if (null === $this->container->requestobj->getQueryParam('server')) {
258
            $destinationurl = self::SUBFOLDER . '/src/views/intro';
259
        } else {
260
            // otherwise, you'll be redirected to the login page for that server;
261
            $destinationurl = self::SUBFOLDER . '/src/views/login' . ($query_string ? '?' . $query_string : '');
262
        }
263
264
        return $destinationurl;
265
    }
266
267
    /**
268
     * Adds a flash message to the session that will be displayed on the next request.
269
     *
270
     * @param mixed  $content msg content (can be object, array, etc)
271
     * @param string $key     The key to associate with the message. Defaults to the stack
272
     *                        trace of the closure or method that called addFlassh
273
     */
274
    public function addFlash($content, $key = ''): void
275
    {
276
        if ('' === $key) {
277
            $key = self::getBackTrace();
278
        }
279
        // $this->dump(__METHOD__ . ': addMessage ' . $key . '  ' . json_encode($content));
280
        if ($this->container->flash) {
0 ignored issues
show
Bug introduced by
Accessing flash on the interface Psr\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
281
            $this->container->flash->addMessage($key, $content);
282
        }
283
    }
284
285
    /**
286
     * Gets the destination with the last active tab selected for that controller
287
     * Usually used after going through a redirect route.
288
     *
289
     * @param string $subject The subject, usually a view name like 'server' or 'table'
290
     *
291
     * @return string The destination url with last tab set in the query string
292
     */
293
    public function getDestinationWithLastTab($subject)
294
    {
295
        $_server_info = $this->container->misc->getServerInfo();
0 ignored issues
show
Bug introduced by
Accessing misc on the interface Psr\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
296
        $this->addFlash($subject, 'getDestinationWithLastTab');
297
        //$this->prtrace('$_server_info', $_server_info);
298
        // If username isn't set in server_info, you should login
299
        if (!isset($_server_info['username'])) {
300
            $destinationurl = $this->getRedirectUrl();
301
        } else {
302
            $url = $this->container->misc->getLastTabURL($subject);
303
            $this->addFlash($url, 'getLastTabURL for ' . $subject);
304
            // Load query vars into superglobal arrays
305
            if (isset($url['urlvars'])) {
306
                $urlvars = [];
307
308
                foreach ($url['urlvars'] as $key => $urlvar) {
309
                    //$this->prtrace($key, $urlvar);
310
                    $urlvars[$key] = \PHPPgAdmin\Decorators\Decorator::get_sanitized_value($urlvar, $_REQUEST);
311
                }
312
                $_REQUEST = \array_merge($_REQUEST, $urlvars);
313
                $_GET = \array_merge($_GET, $urlvars);
314
            }
315
316
            $actionurl = \PHPPgAdmin\Decorators\Decorator::actionurl($url['url'], $_GET);
317
            $destinationurl = $actionurl->value($_GET);
318
        }
319
        $destinationurl = \str_replace('views/?', "views/{$subject}?", $destinationurl);
320
        // $this->prtrace('destinationurl for ' . $subject, $destinationurl);
321
        return $destinationurl;
322
    }
323
324
    /**
325
     * Adds an error to the errors array property of the container.
326
     *
327
     * @param string $errormsg The error msg
328
     *
329
     * @return ContainerInterface The app container
330
     */
331
    public function addError(string $errormsg): ContainerInterface
332
    {
333
        $errors = $this->container->get('errors');
334
        $errors[] = $errormsg;
335
        $this->container->offsetSet('errors', $errors);
336
337
        return $this->container;
338
    }
339
340
    private function setConf($conf)
341
    {
342
        $container = self::getContainerInstance();
343
        $conf['plugins'] = [];
344
345
        $container['conf'] = static function ($c) use ($conf) {
346
            $display_sizes = $conf['display_sizes'];
347
348
            if (\is_array($display_sizes)) {
349
                $conf['display_sizes'] = [
350
                    'schemas' => (bool) isset($display_sizes['schemas']) && true === $display_sizes['schemas'],
351
                    'tables' => (bool) isset($display_sizes['tables']) && true === $display_sizes['tables'],
352
                ];
353
            } else {
354
                $conf['display_sizes'] = [
355
                    'schemas' => (bool) $display_sizes,
356
                    'tables' => (bool) $display_sizes,
357
                ];
358
            }
359
360
            if (!isset($conf['theme'])) {
361
                $conf['theme'] = 'default';
362
            }
363
364
            foreach ($conf['servers'] as &$server) {
365
                if (!isset($server['port'])) {
366
                    $server['port'] = 5432;
367
                }
368
369
                if (!isset($server['sslmode'])) {
370
                    $server['sslmode'] = 'unspecified';
371
                }
372
            }
373
374
            return $conf;
375
        };
376
377
        $container->subfolder = self::SUBFOLDER;
0 ignored issues
show
Bug introduced by
Accessing subfolder on the interface Psr\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
378
379
        return $this;
380
    }
381
382
    /**
383
     * Sets the views.
384
     *
385
     * @return self ( description_of_the_return_value )
386
     */
387
    private function setViews()
388
    {
389
        $container = self::getContainerInstance();
390
391
        /**
392
         * return ViewManager.
393
         */
394
        $container['view'] = static function ($c) {
395
            $misc = $c->misc;
396
            $view = new ViewManager(BASE_PATH . '/assets/templates', [
397
                'cache' => BASE_PATH . '/temp/twigcache',
398
                'auto_reload' => $c->get('settings')['debug'],
399
                'debug' => $c->get('settings')['debug'],
400
            ], $c);
401
402
            $misc->setView($view);
403
404
            return $view;
405
        };
406
407
        return $this;
408
    }
409
410
    /**
411
     * Sets the instance of Misc class.
412
     *
413
     * @return self ( description_of_the_return_value )
414
     */
415
    private function setMisc()
416
    {
417
        $container = self::getContainerInstance();
418
        $container['misc'] = static function ($c) {
419
            $misc = new \PHPPgAdmin\Misc($c);
420
421
            $conf = $c->get('conf');
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
422
423
            // 4. Check for theme by server/db/user
424
            $_server_info = $misc->getServerInfo();
425
426
            /* starting with PostgreSQL 9.0, we can set the application name */
427
            if (isset($_server_info['pgVersion']) && 9 <= $_server_info['pgVersion']) {
428
                \putenv('PGAPPNAME=' . $c->get('settings')['appName'] . '_' . $c->get('settings')['appVersion']);
429
            }
430
431
            return $misc;
432
        };
433
434
        return $this;
435
    }
436
437
    private function setExtra()
438
    {
439
        $container = self::getContainerInstance();
440
        $container['flash'] = static function () {
441
            return new \Slim\Flash\Messages();
442
        };
443
444
        $container['lang'] = static function ($c) {
445
            $translations = new \PHPPgAdmin\Translations($c);
446
447
            return $translations->lang;
448
        };
449
450
        return $this;
451
    }
452
453
    /**
454
     * Traverse THEME_PATH, consider as theme folders those which
455
     * contain a `global.css` stylesheet.
456
     *
457
     * @return array the theme folders
458
     */
459
    private function getThemeFolders()
460
    {
461
        // no THEME_PATH (how?) then return empty array
462
        if (!$gestor = \opendir(self::THEME_PATH)) {
463
            \closedir($gestor);
0 ignored issues
show
Bug introduced by
It seems like $gestor can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

463
            \closedir(/** @scrutinizer ignore-type */ $gestor);
Loading history...
464
465
            return [];
466
        }
467
        $themefolders = [];
468
469
        /* This is the right way to iterate on a folder */
470
        while (false !== ($foldername = \readdir($gestor))) {
471
            if ('.' === $foldername || '..' === $foldername) {
472
                continue;
473
            }
474
475
            $folderpath = \sprintf('%s%s%s', self::THEME_PATH, \DIRECTORY_SEPARATOR, $foldername);
476
            $stylesheet = \sprintf('%s%s%s', $folderpath, \DIRECTORY_SEPARATOR, 'global.css');
477
            // if $folderpath if indeed a folder and contains a global.css file, then it's a theme
478
            if (\is_dir($folderpath) &&
479
                \is_file($stylesheet)) {
480
                $themefolders[$foldername] = $folderpath;
481
            }
482
        }
483
484
        \closedir($gestor);
485
486
        return $themefolders;
487
    }
488
}
489