Passed
Push — develop ( 16b665...d246a9 )
by Felipe
05:54
created

ContainerUtils::maybeRenderIframes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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

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