Header   C
last analyzed

Complexity

Total Complexity 56

Size/Duplication

Total Lines 515
Duplicated Lines 0 %

Test Coverage

Coverage 82.19%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 56
eloc 232
dl 0
loc 515
ccs 203
cts 247
cp 0.8219
rs 5.5199
c 1
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A setTitle() 0 3 1
A getMenu() 0 3 1
A getJsParamsCode() 0 5 1
A getPageTitle() 0 23 6
A getVariablesForJavaScript() 0 8 3
A disableMenuAndConsole() 0 4 1
A setBodyId() 0 3 1
A getMessage() 0 23 6
A setIsTransformationWrapper() 0 3 1
A addDefaultScripts() 0 18 1
A getScripts() 0 3 1
A sendHttpHeaders() 0 15 3
A getHttpHeaders() 0 64 4
A disableWarnings() 0 3 1
A getVersionParameter() 0 3 1
F getDisplay() 0 98 14
A getJsParams() 0 33 3
B getCspHeaders() 0 57 6
A __construct() 0 13 1

How to fix   Complexity   

Complex Class

Complex classes like Header often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Header, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Used to render the header of PMA's pages
4
 */
5
6
declare(strict_types=1);
7
8
namespace PhpMyAdmin;
9
10
use PhpMyAdmin\ConfigStorage\Relation;
11
use PhpMyAdmin\Container\ContainerBuilder;
12
use PhpMyAdmin\Html\Generator;
13
use PhpMyAdmin\Navigation\Navigation;
14
use PhpMyAdmin\Theme\ThemeManager;
15
16
use function array_merge;
17
use function defined;
18
use function gmdate;
19
use function header;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, PhpMyAdmin\header. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
20
use function htmlspecialchars;
21
use function ini_get;
22
use function json_encode;
23
use function sprintf;
24
use function strtolower;
25
use function urlencode;
26
27
use const JSON_HEX_TAG;
28
29
/**
30
 * Class used to output the HTTP and HTML headers
31
 */
32
class Header
33
{
34
    /**
35
     * Scripts instance
36
     */
37
    private Scripts $scripts;
38
    /**
39
     * Menu instance
40
     */
41
    private Menu $menu;
42
    /**
43
     * The page title
44
     */
45
    private string $title = '';
46
    /**
47
     * The value for the id attribute for the body tag
48
     */
49
    private string $bodyId = '';
50
    /**
51
     * Whether to show the top menu
52
     */
53
    private bool $menuEnabled;
54
    /**
55
     * Whether to show the warnings
56
     */
57
    private bool $warningsEnabled = true;
58
59
    private UserPreferences $userPreferences;
60
61
    private bool $isTransformationWrapper = false;
62
63 36
    public function __construct(
64
        private readonly Template $template,
65
        private readonly Console $console,
66
        private readonly Config $config,
67
    ) {
68 36
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

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

68
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
69 36
        $this->menuEnabled = $dbi->isConnected();
70 36
        $relation = new Relation($dbi);
71 36
        $this->menu = new Menu($dbi, $this->template, $this->config, $relation, Current::$database, Current::$table);
72 36
        $this->scripts = new Scripts($this->template);
73 36
        $this->addDefaultScripts();
74
75 36
        $this->userPreferences = new UserPreferences($dbi, $relation, $this->template);
76
    }
77
78
    /**
79
     * Loads common scripts
80
     */
81 36
    private function addDefaultScripts(): void
82
    {
83 36
        $this->scripts->addFile('runtime.js');
84 36
        $this->scripts->addFile('vendor/jquery/jquery.min.js');
85 36
        $this->scripts->addFile('vendor/jquery/jquery-migrate.min.js');
86 36
        $this->scripts->addFile('vendor/sprintf.js');
87 36
        $this->scripts->addFile('vendor/jquery/jquery-ui.min.js');
88 36
        $this->scripts->addFile('name-conflict-fixes.js');
89 36
        $this->scripts->addFile('vendor/bootstrap/bootstrap.bundle.min.js');
90 36
        $this->scripts->addFile('vendor/js.cookie.min.js');
91 36
        $this->scripts->addFile('vendor/jquery/jquery.validate.min.js');
92 36
        $this->scripts->addFile('vendor/jquery/jquery-ui-timepicker-addon.js');
93 36
        $this->scripts->addFile('index.php', ['route' => '/messages', 'l' => $GLOBALS['lang']]);
94 36
        $this->scripts->addFile('shared.js');
95 36
        $this->scripts->addFile('menu_resizer.js');
96 36
        $this->scripts->addFile('main.js');
97
98 36
        $this->scripts->addCode($this->getJsParamsCode());
99
    }
100
101
    /**
102
     * Returns, as an array, a list of parameters
103
     * used on the client side
104
     *
105
     * @return mixed[]
106
     */
107 36
    public function getJsParams(): array
108
    {
109 36
        $pftext = $_SESSION['tmpval']['pftext'] ?? '';
110
111 36
        $params = [
112
            // Do not add any separator, JS code will decide
113 36
            'common_query' => Url::getCommonRaw([], ''),
114 36
            'opendb_url' => Util::getScriptNameForOption($this->config->settings['DefaultTabDatabase'], 'database'),
115 36
            'lang' => $GLOBALS['lang'],
116 36
            'server' => Current::$server,
117 36
            'table' => Current::$table,
118 36
            'db' => Current::$database,
119 36
            'token' => $_SESSION[' PMA_token '],
120 36
            'text_dir' => LanguageManager::$textDir,
121 36
            'LimitChars' => $this->config->settings['LimitChars'],
122 36
            'pftext' => $pftext,
123 36
            'confirm' => $this->config->settings['Confirm'],
124 36
            'LoginCookieValidity' => $this->config->settings['LoginCookieValidity'],
125 36
            'session_gc_maxlifetime' => (int) ini_get('session.gc_maxlifetime'),
126 36
            'logged_in' => DatabaseInterface::getInstance()->isConnected(),
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

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

126
            'logged_in' => /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance()->isConnected(),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
127 36
            'is_https' => $this->config->isHttps(),
128 36
            'rootPath' => $this->config->getRootPath(),
129 36
            'arg_separator' => Url::getArgSeparator(),
130 36
            'version' => Version::VERSION,
131 36
        ];
132 36
        if ($this->config->hasSelectedServer()) {
133
            $params['auth_type'] = $this->config->selectedServer['auth_type'];
134
            if (isset($this->config->selectedServer['user'])) {
135
                $params['user'] = $this->config->selectedServer['user'];
136
            }
137
        }
138
139 36
        return $params;
140
    }
141
142
    /**
143
     * Returns, as a string, a list of parameters
144
     * used on the client side
145
     */
146 36
    public function getJsParamsCode(): string
147
    {
148 36
        $params = $this->getJsParams();
149
150 36
        return 'window.Navigation.update(window.CommonParams.setAll(' . json_encode($params, JSON_HEX_TAG) . '));';
151
    }
152
153
    /**
154
     * Returns the Scripts object
155
     *
156
     * @return Scripts object
157
     */
158 8
    public function getScripts(): Scripts
159
    {
160 8
        return $this->scripts;
161
    }
162
163
    /**
164
     * Returns the Menu object
165
     *
166
     * @return Menu object
167
     */
168
    public function getMenu(): Menu
169
    {
170
        return $this->menu;
171
    }
172
173
    /**
174
     * Setter for the ID attribute in the BODY tag
175
     *
176
     * @param string $id Value for the ID attribute
177
     */
178 4
    public function setBodyId(string $id): void
179
    {
180 4
        $this->bodyId = htmlspecialchars($id);
181
    }
182
183
    /**
184
     * Setter for the title of the page
185
     *
186
     * @param string $title New title
187
     */
188
    public function setTitle(string $title): void
189
    {
190
        $this->title = htmlspecialchars($title);
191
    }
192
193
    /**
194
     * Disables the display of the top menu
195
     */
196
    public function disableMenuAndConsole(): void
197
    {
198
        $this->menuEnabled = false;
199
        $this->console->disable();
200
    }
201
202
    /**
203
     * Disables the display of the top menu
204
     */
205 4
    public function disableWarnings(): void
206
    {
207 4
        $this->warningsEnabled = false;
208
    }
209
210
    /** @return mixed[] */
211 4
    public function getDisplay(): array
212
    {
213 4
        $this->sendHttpHeaders();
214
215 4
        $baseDir = defined('PMA_PATH_TO_BASEDIR') ? PMA_PATH_TO_BASEDIR : '';
216
217
        /** @var ThemeManager $themeManager */
218 4
        $themeManager = ContainerBuilder::getContainer()->get(ThemeManager::class);
219 4
        $theme = $themeManager->theme;
220
221 4
        $version = self::getVersionParameter();
222
223
        // The user preferences have been merged at this point
224
        // so we can conditionally add CodeMirror, other scripts and settings
225 4
        if ($this->config->settings['CodemirrorEnable']) {
226
            $this->scripts->addFile('vendor/codemirror/lib/codemirror.js');
227
            $this->scripts->addFile('vendor/codemirror/mode/sql/sql.js');
228
            $this->scripts->addFile('vendor/codemirror/addon/runmode/runmode.js');
229
            $this->scripts->addFile('vendor/codemirror/addon/hint/show-hint.js');
230
            $this->scripts->addFile('vendor/codemirror/addon/hint/sql-hint.js');
231
            if ($this->config->settings['LintEnable']) {
232
                $this->scripts->addFile('vendor/codemirror/addon/lint/lint.js');
233
                $this->scripts->addFile('codemirror/addon/lint/sql-lint.js');
234
            }
235
        }
236
237 4
        if ($this->config->settings['SendErrorReports'] !== 'never') {
238
            $this->scripts->addFile('vendor/tracekit.js');
239
            $this->scripts->addFile('error_report.js');
240
        }
241
242 4
        if ($this->config->settings['enable_drag_drop_import'] === true) {
243
            $this->scripts->addFile('drag_drop_import.js');
244
        }
245
246 4
        if (! $this->config->get('DisableShortcutKeys')) {
247
            $this->scripts->addFile('shortcuts_handler.js');
248
        }
249
250 4
        $this->scripts->addCode($this->getVariablesForJavaScript());
251
252 4
        $this->scripts->addCode(
253 4
            'ConsoleEnterExecutes=' . ($this->config->settings['ConsoleEnterExecutes'] ? 'true' : 'false'),
254 4
        );
255 4
        $this->scripts->addFiles($this->console->getScripts());
256
257 4
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

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

257
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
258 4
        if ($this->menuEnabled && Current::$server > 0) {
259
            $navigation = (new Navigation($this->template, new Relation($dbi), $dbi, $this->config))->getDisplay();
260
        }
261
262 4
        $customHeader = Config::renderHeader();
263
264
        // offer to load user preferences from localStorage
265
        if (
266 4
            $this->config->get('user_preferences') === 'session'
267 4
            && ! isset($_SESSION['userprefs_autoload'])
268
        ) {
269
            $loadUserPreferences = $this->userPreferences->autoloadGetHeader();
270
        }
271
272 4
        if ($this->menuEnabled && Current::$server > 0) {
273
            $menu = $this->menu->getDisplay();
274
        }
275
276 4
        $console = $this->console->getDisplay();
277 4
        $messages = $this->getMessage();
278 4
        $isLoggedIn = $dbi->isConnected();
279
280 4
        $this->scripts->addFile('datetimepicker.js');
281 4
        $this->scripts->addFile('validator-messages.js');
282
283 4
        return [
284 4
            'lang' => $GLOBALS['lang'],
285 4
            'allow_third_party_framing' => $this->config->settings['AllowThirdPartyFraming'],
286 4
            'base_dir' => $baseDir,
287 4
            'theme_path' => $theme->getPath(),
288 4
            'version' => $version,
289 4
            'text_dir' => LanguageManager::$textDir,
290 4
            'server' => Current::$server,
291 4
            'title' => $this->getPageTitle(),
292 4
            'scripts' => $this->scripts->getDisplay(),
293 4
            'body_id' => $this->bodyId,
294 4
            'navigation' => $navigation ?? '',
295 4
            'custom_header' => $customHeader,
296 4
            'load_user_preferences' => $loadUserPreferences ?? '',
297 4
            'show_hint' => $this->config->settings['ShowHint'],
298 4
            'is_warnings_enabled' => $this->warningsEnabled,
299 4
            'is_menu_enabled' => $this->menuEnabled,
300 4
            'is_logged_in' => $isLoggedIn,
301 4
            'menu' => $menu ?? '',
302 4
            'console' => $console,
303 4
            'messages' => $messages,
304 4
            'theme_color_mode' => $theme->getColorMode(),
305 4
            'theme_color_modes' => $theme->getColorModes(),
306 4
            'theme_id' => $theme->getId(),
307 4
            'current_user' => $dbi->getCurrentUserAndHost(),
308 4
            'is_mariadb' => $dbi->isMariaDB(),
309 4
        ];
310
    }
311
312
    /**
313
     * Returns the message to be displayed at the top of
314
     * the page, including the executed SQL query, if any.
315
     */
316 8
    public function getMessage(): string
317
    {
318 8
        $retval = '';
319 8
        $message = '';
320 8
        if (! empty($GLOBALS['message'])) {
321 4
            $message = $GLOBALS['message'];
322 4
            unset($GLOBALS['message']);
323 4
        } elseif (! empty($_REQUEST['message'])) {
324
            $message = $_REQUEST['message'];
325
        }
326
327 8
        if ($message !== '') {
328 4
            if (isset($GLOBALS['buffer_message'])) {
329
                $bufferMessage = $GLOBALS['buffer_message'];
330
            }
331
332 4
            $retval .= Generator::getMessage($message);
333 4
            if (isset($bufferMessage)) {
334
                $GLOBALS['buffer_message'] = $bufferMessage;
335
            }
336
        }
337
338 8
        return $retval;
339
    }
340
341
    /**
342
     * Sends out the HTTP headers
343
     */
344 4
    public function sendHttpHeaders(): void
345
    {
346 4
        if (defined('TESTSUITE')) {
347 4
            return;
348
        }
349
350
        /**
351
         * Sends http headers
352
         */
353
        $GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT';
354
355
        $headers = $this->getHttpHeaders();
356
357
        foreach ($headers as $name => $value) {
358
            header(sprintf('%s: %s', $name, $value));
359
        }
360
    }
361
362
    /** @return array<string, string> */
363 12
    public function getHttpHeaders(): array
364
    {
365 12
        $headers = [];
366
367
        /* Prevent against ClickJacking by disabling framing */
368 12
        if (strtolower((string) $this->config->settings['AllowThirdPartyFraming']) === 'sameorigin') {
369 4
            $headers['X-Frame-Options'] = 'SAMEORIGIN';
370 8
        } elseif ($this->config->settings['AllowThirdPartyFraming'] !== true) {
371 4
            $headers['X-Frame-Options'] = 'DENY';
372
        }
373
374 12
        $headers['Referrer-Policy'] = 'same-origin';
375
376 12
        $headers = array_merge($headers, $this->getCspHeaders());
377
378
        /**
379
         * Re-enable possible disabled XSS filters.
380
         *
381
         * @see https://developer.mozilla.org/docs/Web/HTTP/Headers/X-XSS-Protection
382
         */
383 12
        $headers['X-XSS-Protection'] = '1; mode=block';
384
385
        /**
386
         * "nosniff", prevents Internet Explorer and Google Chrome from MIME-sniffing
387
         * a response away from the declared content-type.
388
         *
389
         * @see https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Content-Type-Options
390
         */
391 12
        $headers['X-Content-Type-Options'] = 'nosniff';
392
393
        /**
394
         * Adobe cross-domain-policies.
395
         *
396
         * @see https://www.sentrium.co.uk/labs/application-security-101-http-headers
397
         */
398 12
        $headers['X-Permitted-Cross-Domain-Policies'] = 'none';
399
400
        /**
401
         * Robots meta tag.
402
         *
403
         * @see https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag
404
         */
405 12
        $headers['X-Robots-Tag'] = 'noindex, nofollow';
406
407
        /**
408
         * The HTTP Permissions-Policy header provides a mechanism to allow and deny
409
         * the use of browser features in a document
410
         * or within any <iframe> elements in the document.
411
         *
412
         * @see https://developer.mozilla.org/docs/Web/HTTP/Headers/Permissions-Policy
413
         */
414 12
        $headers['Permissions-Policy'] = 'fullscreen=(self), oversized-images=(self), interest-cohort=()';
415
416 12
        $headers = array_merge($headers, Core::getNoCacheHeaders());
417
418
        /**
419
         * A different Content-Type is set in {@see \PhpMyAdmin\Controllers\Transformation\WrapperController}.
420
         */
421 12
        if (! $this->isTransformationWrapper) {
422
            // Define the charset to be used
423 12
            $headers['Content-Type'] = 'text/html; charset=utf-8';
424
        }
425
426 12
        return $headers;
427
    }
428
429
    /**
430
     * If the page is missing the title, this function
431
     * will set it to something reasonable
432
     */
433 4
    public function getPageTitle(): string
434
    {
435 4
        if ($this->title === '') {
436 4
            if (Current::$server > 0) {
437
                if (Current::$table !== '') {
438
                    $tempTitle = $this->config->settings['TitleTable'];
439
                } elseif (Current::$database !== '') {
440
                    $tempTitle = $this->config->settings['TitleDatabase'];
441
                } elseif ($this->config->selectedServer['host'] !== '') {
442
                    $tempTitle = $this->config->settings['TitleServer'];
443
                } else {
444
                    $tempTitle = $this->config->settings['TitleDefault'];
445
                }
446
447
                $this->title = htmlspecialchars(
448
                    Util::expandUserString($tempTitle),
449
                );
450
            } else {
451 4
                $this->title = 'phpMyAdmin';
452
            }
453
        }
454
455 4
        return $this->title;
456
    }
457
458
    /**
459
     * Get all the CSP allow policy headers
460
     *
461
     * @return array<string, string>
462
     */
463 12
    private function getCspHeaders(): array
464
    {
465 12
        $mapTileUrl = ' tile.openstreetmap.org';
466 12
        $captchaUrl = '';
467 12
        $cspAllow = $this->config->settings['CSPAllow'];
468
469
        if (
470 12
            ! empty($this->config->settings['CaptchaLoginPrivateKey'])
471 12
            && ! empty($this->config->settings['CaptchaLoginPublicKey'])
472 12
            && ! empty($this->config->settings['CaptchaApi'])
473 12
            && ! empty($this->config->settings['CaptchaRequestParam'])
474 12
            && ! empty($this->config->settings['CaptchaResponseParam'])
475
        ) {
476 8
            $captchaUrl = ' ' . $this->config->settings['CaptchaCsp'] . ' ';
477
        }
478
479 12
        $headers = [];
480
481 12
        $headers['Content-Security-Policy'] = sprintf(
482 12
            'default-src \'self\' %s%s;script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' %s%s;'
483 12
                . 'style-src \'self\' \'unsafe-inline\' %s%s;img-src \'self\' data: %s%s%s;object-src \'none\';',
484 12
            $captchaUrl,
485 12
            $cspAllow,
486 12
            $captchaUrl,
487 12
            $cspAllow,
488 12
            $captchaUrl,
489 12
            $cspAllow,
490 12
            $cspAllow,
491 12
            $mapTileUrl,
492 12
            $captchaUrl,
493 12
        );
494
495 12
        $headers['X-Content-Security-Policy'] = sprintf(
496 12
            'default-src \'self\' %s%s;options inline-script eval-script;'
497 12
                . 'referrer no-referrer;img-src \'self\' data: %s%s%s;object-src \'none\';',
498 12
            $captchaUrl,
499 12
            $cspAllow,
500 12
            $cspAllow,
501 12
            $mapTileUrl,
502 12
            $captchaUrl,
503 12
        );
504
505 12
        $headers['X-WebKit-CSP'] = sprintf(
506 12
            'default-src \'self\' %s%s;script-src \'self\' %s%s \'unsafe-inline\' \'unsafe-eval\';'
507 12
                . 'referrer no-referrer;style-src \'self\' \'unsafe-inline\' %s;'
508 12
                . 'img-src \'self\' data: %s%s%s;object-src \'none\';',
509 12
            $captchaUrl,
510 12
            $cspAllow,
511 12
            $captchaUrl,
512 12
            $cspAllow,
513 12
            $captchaUrl,
514 12
            $cspAllow,
515 12
            $mapTileUrl,
516 12
            $captchaUrl,
517 12
        );
518
519 12
        return $headers;
520
    }
521
522
    /**
523
     * Returns the phpMyAdmin version to be appended to the url to avoid caching
524
     * between versions
525
     *
526
     * @return string urlencoded pma version as a parameter
527
     */
528 4
    public static function getVersionParameter(): string
529
    {
530 4
        return 'v=' . urlencode(Version::VERSION);
531
    }
532
533 4
    private function getVariablesForJavaScript(): string
534
    {
535 4
        $maxInputVars = ini_get('max_input_vars');
536 4
        $maxInputVarsValue = $maxInputVars === false || $maxInputVars === '' ? 'false' : (int) $maxInputVars;
537
538 4
        return $this->template->render('javascript/variables', [
539 4
            'first_day_of_calendar' => $this->config->settings['FirstDayOfCalendar'] ?? 0,
540 4
            'max_input_vars' => $maxInputVarsValue,
541 4
        ]);
542
    }
543
544
    public function setIsTransformationWrapper(bool $isTransformationWrapper): void
545
    {
546
        $this->isTransformationWrapper = $isTransformationWrapper;
547
    }
548
}
549