Passed
Push — master ( d96050...da4683 )
by Andreas
21:41
created

midcom_helper__styleloader::_merge_styledirs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.helper
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * This class is responsible for all style management. It is instantiated by the MidCOM framework
11
 * and accessible through the midcom::get()->style object.
12
 *
13
 * The method <code>show($style)</code> returns the style element $style for the current
14
 * component:
15
 *
16
 * It checks whether a style path is defined for the current component.
17
 *
18
 * - If there is a user defined style path, the element named $style in
19
 *   this path is returned,
20
 * - otherwise the element "$style" is taken from the default style of the
21
 *   current component (/path/to/component/style/$path).
22
 *
23
 * (The default fallback is always the default style, e.g. if $style
24
 * is not in the user defined style path)
25
 *
26
 * To enable cross-style referencing and provide the opportunity to access
27
 * any style element, "show" can be called with a full qualified style
28
 * path (like "/mystyle/element1", while the current page's style may be set
29
 * to "/yourstyle").
30
 *
31
 * Note: To make sure sub-styles and elements included in styles are handled
32
 * correctly, use:
33
 *
34
 * <code>
35
 * <?php midcom_show_style ("elementname"); ?>
36
 * </code>
37
 *
38
 * Style Inheritance
39
 *
40
 * The basic path the styleloader follows to find a style element is:
41
 * 1. Topic style -> if the current topic has a style set
42
 * 2. Inherited topic style -> if the topic inherits a style from another topic.
43
 * 3. Site-wide per-component default style -> if defined in MidCOM configuration key styleengine_default_styles
44
 * 4. Theme style -> the style of the MidCOM component.
45
 * 5. The file style. This is usually the elements found in the component's style directory.
46
 *
47
 * Regarding nr. 4:
48
 * It is possible to add extra file styles if so is needed for example by a portal component.
49
 * This is done either using the append/prepend component_style functions or by setting it
50
 * to another directory by calling (append|prepend)_styledir directly.
51
 *
52
 * NB: You cannot change this in another style element or in a _show() function in a component.
53
 *
54
 * @package midcom.helper
55
 */
56
class midcom_helper__styleloader
57
{
58
    /**
59
     * Current style scope
60
     *
61
     * @var array
62
     */
63
    private $_scope = [];
64
65
    /**
66
     * Current topic
67
     *
68
     * @var midcom_db_topic
69
     */
70
    private $_topic;
71
72
    /**
73
     * Default style path
74
     *
75
     * @var string
76
     */
77
    private $_snippetdir;
78
79
    /**
80
     * Context stack
81
     *
82
     * @var midcom_core_context[]
83
     */
84
    private $_context = [];
85
86
    /**
87
     * Style element cache
88
     *
89
     * @var array
90
     */
91
    private $_styles = [];
0 ignored issues
show
introduced by
The private property $_styles is not used, and could be removed.
Loading history...
92
93
    /**
94
     * Default style element cache
95
     *
96
     * @var array
97
     */
98
    private $_snippets = [];
99
100
    /**
101
     * List of styledirs to handle after componentstyle
102
     *
103
     * @var array
104
     */
105
    private $_styledirs_append = [];
106
107
    /**
108
     * List of styledirs to handle before componentstyle
109
     *
110
     * @var array
111
     */
112
    private $_styledirs_prepend = [];
113
114
    /**
115
     * The stack of directories to check for styles.
116
     */
117
    private $_styledirs = [];
118
119
    /**
120
     * Data to pass to the style
121
     *
122
     * @var array
123
     */
124
    public $data;
125
126
    /**
127
     * Returns the path of the style described by $id.
128
     *
129
     * @param int $id    Style id to look up path for
130
     */
131
    public function get_style_path_from_id($id) : string
132
    {
133
        static $path_cache = [];
134
        if (isset($path_cache[$id])) {
135
            return $path_cache[$id];
136
        }
137
        // Construct the path
138
        $path_parts = [];
139
        $original_id = $id;
140
141
        try {
142
            while (($style = new midcom_db_style($id))) {
143
                $path_parts[] = $style->name;
144
                $id = $style->up;
145
146
                if ($style->up == 0) {
147
                    // Toplevel style
148
                    break;
149
                }
150
            }
151
        } catch (midcom_error $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
152
        }
153
154
        $path_parts = array_reverse($path_parts);
155
156
        $path_cache[$original_id] = '/' . implode('/', $path_parts);
157
158
        return $path_cache[$original_id];
159
    }
160
161
    /**
162
     * Returns the id of the style described by $path.
163
     *
164
     * Note: $path already includes the element name, so $path looks like
165
     * "/rootstyle/style/style/element".
166
     *
167
     * @todo complete documentation
168
     * @param string $path      The path to retrieve
169
     * @param int $rootstyle    ???
170
     * @return    int ID of the matching style or false
171
     */
172
    public function get_style_id_from_path($path, $rootstyle = 0)
173
    {
174
        static $cached = [];
175
176
        $cache_key = $rootstyle . '::' . $path;
177
178
        if (array_key_exists($cache_key, $cached)) {
179
            return $cached[$cache_key];
180
        }
181
182
        $path = preg_replace("/^\/(.*)/", "$1", $path); // leading "/"
183
        $cached[$cache_key] = false;
184
        $current_style = 0;
185
186
        $path_array = array_filter(explode('/', $path));
187
        if (!empty($path_array)) {
188
            $current_style = $rootstyle;
189
        }
190
191
        foreach ($path_array as $path_item) {
192
            $mc = midgard_style::new_collector('up', $current_style);
193
            $mc->set_key_property('guid');
194
            $mc->add_value_property('id');
195
            $mc->add_constraint('name', '=', $path_item);
196
            $mc->execute();
197
            $styles = $mc->list_keys();
198
199
            foreach (array_keys($styles) as $style_guid) {
200
                $current_style = $mc->get_subkey($style_guid, 'id');
201
                midcom::get()->cache->content->register($style_guid);
202
            }
203
        }
204
205
        if ($current_style != 0) {
206
            $cached[$cache_key] = $current_style;
207
        }
208
209
        return $cached[$cache_key];
210
    }
211
212
    /**
213
     * Returns a style element that matches $name and is in style $id.
214
     * It also returns an element if it is not in the given style,
215
     * but in one of its parent styles.
216
     *
217
     * @param int $id        The style id to search in.
218
     * @param string $name    The element to locate.
219
     * @return string    Value of the found element, or false on failure.
220
     */
221
    private function _get_element_in_styletree($id, string $name) : ?string
222
    {
223
        static $cached = [];
224
        $cache_key = $id . '::' . $name;
225
226
        if (array_key_exists($cache_key, $cached)) {
227
            return $cached[$cache_key];
228
        }
229
230
        $element_mc = midgard_element::new_collector('style', $id);
231
        $element_mc->set_key_property('guid');
232
        $element_mc->add_value_property('value');
233
        $element_mc->add_constraint('name', '=', $name);
234
        $element_mc->execute();
235
236
        if ($keys = $element_mc->list_keys()) {
237
            $element_guid = key($keys);
238
            $cached[$cache_key] = $element_mc->get_subkey($element_guid, 'value');
239
            midcom::get()->cache->content->register($element_guid);
240
            return $cached[$cache_key];
241
        }
242
243
        // No such element on this level, check parents
244
        $style_mc = midgard_style::new_collector('id', $id);
245
        $style_mc->set_key_property('guid');
246
        $style_mc->add_value_property('up');
247
        $style_mc->add_constraint('up', '>', 0);
248
        $style_mc->execute();
249
250
        if ($keys = $style_mc->list_keys()) {
251
            $style_guid = key($keys);
252
            midcom::get()->cache->content->register($style_guid);
253
            $up = $style_mc->get_subkey($style_guid, 'up');
254
            return $this->_get_element_in_styletree($up, $name);
0 ignored issues
show
Bug introduced by
It seems like $up can also be of type false; however, parameter $id of midcom_helper__styleload..._element_in_styletree() does only seem to accept integer, 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

254
            return $this->_get_element_in_styletree(/** @scrutinizer ignore-type */ $up, $name);
Loading history...
255
        }
256
257
        $cached[$cache_key] = null;
258
        return $cached[$cache_key];
259
    }
260
261
    /**
262
     * Looks for a style element matching $path (either in a user defined style
263
     * or the default style snippetdir) and displays/evaluates it.
264
     *
265
     * @param string $path    The style element to show.
266
     * @return boolean            True on success, false otherwise.
267
     */
268 202
    public function show($path) : bool
269
    {
270 202
        if ($this->_context === []) {
271
            debug_add("Trying to show '{$path}' but there is no context set", MIDCOM_LOG_INFO);
272
            return false;
273
        }
274
275 202
        $style = $this->load($path);
276
277 202
        if ($style === false) {
278 24
            if ($path == 'ROOT') {
279
                // Go to fallback ROOT instead of displaying a blank page
280
                $this->show_midcom($path);
281
                return true;
282
            }
283
284 24
            debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
285 24
            return false;
286
        }
287 201
        $this->render($style, $path);
288
289 201
        return true;
290
    }
291
292
    /**
293
     * Load style element content
294
     *
295
     * @param string $path The element name
296
     * @return false|string
297
     */
298 202
    public function load($path)
299
    {
300 202
        $element = $path;
301
        // we have full qualified path to element
302 202
        if (preg_match("|(.*)/(.*)|", $path, $matches)) {
303
            $stylepath = $matches[1];
304
            $element = $matches[2];
305
        }
306
307 202
        if (   isset($stylepath)
308 202
            && $styleid = $this->get_style_id_from_path($stylepath)) {
309
            array_unshift($this->_scope, $styleid);
310
        }
311
312 202
        if (!empty($this->_scope[0])) {
313
            $style = $this->_get_element_in_styletree($this->_scope[0], $element);
314
        }
315
316 202
        if (!empty($styleid)) {
317
            array_shift($this->_scope);
318
        }
319
320 202
        if (empty($style)) {
321 202
            $style = $this->_get_element_from_snippet($element);
322
        }
323 202
        return $style;
324
    }
325
326
    /**
327
     * Renders the style element with current request data
328
     *
329
     * @param string $style The style element content
330
     * @param string $path the element's name
331
     * @throws midcom_error
332
     */
333 201
    private function render(string $style, string $path)
334
    {
335 201
        if (midcom::get()->config->get('wrap_style_show_with_name')) {
336
            $style = "\n<!-- Start of style '{$path}' -->\n" . $style;
337
            $style .= "\n<!-- End of style '{$path}' -->\n";
338
        }
339
340
        // This is a bit of a hack to allow &(); tags
341 201
        $preparsed = midcom_helper_misc::preparse($style);
342 201
        if (midcom_core_context::get()->has_custom_key('request_data')) {
343 201
            $data =& midcom_core_context::get()->get_custom_key('request_data');
0 ignored issues
show
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
Bug introduced by
'request_data' of type string is incompatible with the type integer expected by parameter $key of midcom_core_context::get_custom_key(). ( Ignorable by Annotation )

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

343
            $data =& midcom_core_context::get()->get_custom_key(/** @scrutinizer ignore-type */ 'request_data');
Loading history...
344
        }
345
346 201
        if (eval('?>' . $preparsed) === false) {
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
347
            // Note that src detection will be semi-reliable, as it depends on all errors being
348
            // found before caching kicks in.
349
            throw new midcom_error("Failed to parse style element '{$path}', see above for PHP errors.");
350
        }
351 201
    }
352
353
    /**
354
     * Looks for a midcom core style element matching $path and displays/evaluates it.
355
     * This offers a bit reduced functionality and will only look in the DB root style,
356
     * the theme directory and midcom's style directory, because it has to work even when
357
     * midcom is not yet fully initialized
358
     *
359
     * @param string $path    The style element to show.
360
     * @return boolean            True on success, false otherwise.
361
     */
362 1
    public function show_midcom($path) : bool
363
    {
364 1
        $_element = $path;
365 1
        $_style = false;
366
367 1
        $this->_snippetdir = MIDCOM_ROOT . '/midcom/style';
368 1
        $context = midcom_core_context::get();
369 1
        if (isset($this->_styledirs[$context->id])) {
370 1
            $styledirs_backup = $this->_styledirs;
371
        }
372
373 1
        $this->_styledirs[$context->id][0] = $this->_snippetdir;
374
375
        try {
376 1
            $root_topic = $context->get_key(MIDCOM_CONTEXT_ROOTTOPIC);
377 1
            if (   $root_topic->style
378 1
                && $db_style = $this->get_style_id_from_path($root_topic->style)) {
379 1
                $_style = $this->_get_element_in_styletree($db_style, $_element);
380
            }
381
        } catch (midcom_error_forbidden $e) {
382
            $e->log();
383
        }
384
385 1
        if ($_style === false) {
386 1
            $_style = $this->_get_element_from_snippet($_element);
387
        }
388
389 1
        if (isset($styledirs_backup)) {
390 1
            $this->_styledirs = $styledirs_backup;
391
        }
392
393 1
        if ($_style !== false) {
394 1
            $this->render($_style, $path);
395 1
            return true;
396
        }
397
        debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
398
        return false;
399
    }
400
401
    /**
402
     * Try to get element from default style snippet
403
     */
404 202
    private function _get_element_from_snippet(string $_element)
405
    {
406 202
        $src = "{$this->_snippetdir}/{$_element}";
407 202
        if (array_key_exists($src, $this->_snippets)) {
408 14
            return $this->_snippets[$src];
409
        }
410 202
        if (   midcom::get()->config->get('theme')
411 202
            && $content = midcom_helper_misc::get_element_content($_element)) {
412 6
            $this->_snippets[$src] = $content;
413 6
            return $content;
414
        }
415
416 202
        $current_context = midcom_core_context::get()->id;
417 202
        foreach ($this->_styledirs[$current_context] as $path) {
418 202
            $filename = $path . "/{$_element}.php";
419 202
            if (file_exists($filename)) {
420 192
                if (!array_key_exists($filename, $this->_snippets)) {
421 147
                    $this->_snippets[$filename] = file_get_contents($filename);
422
                }
423 192
                return $this->_snippets[$filename];
424
            }
425
        }
426 24
        return false;
427
    }
428
429
    /**
430
     * Gets the component style.
431
     *
432
     * @todo Document
433
     *
434
     * @return int Database ID if the style to use in current view or false
435
     */
436 268
    private function _get_component_style()
437
    {
438 268
        $_st = false;
439 268
        if (!$this->_topic) {
440 1
            return $_st;
441
        }
442
        // get user defined style for component
443
        // style inheritance
444
        // should this be cached somehow?
445 268
        if ($style = $this->_topic->style ?: midcom_core_context::get()->get_inherited_style()) {
446
            if (substr($style, 0, 6) === 'theme:') {
447
                $theme_dir = OPENPSA2_THEME_ROOT . midcom::get()->config->get('theme') . '/style';
448
                $parts = explode('/', str_replace('theme:/', '', $style));
449
450
                foreach ($parts as &$part) {
451
                    $theme_dir .= '/' . $part;
452
                    $part = $theme_dir;
453
                }
454
                foreach (array_reverse(array_filter($parts, 'is_dir')) as $dirname) {
455
                    $this->prepend_styledir($dirname);
456
                }
457
            } else {
458
                $_st = $this->get_style_id_from_path($style);
459
            }
460
        } else {
461
            // Get style from sitewide per-component defaults.
462 268
            $styleengine_default_styles = midcom::get()->config->get('styleengine_default_styles');
463 268
            if (isset($styleengine_default_styles[$this->_topic->component])) {
464
                $_st = $this->get_style_id_from_path($styleengine_default_styles[$this->_topic->component]);
465
            }
466
        }
467
468 268
        if ($_st) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $_st of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
469
            $substyle = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_SUBSTYLE);
470
471
            if (is_string($substyle)) {
472
                $chain = explode('/', $substyle);
473
                foreach ($chain as $stylename) {
474
                    if ($_subst_id = $this->get_style_id_from_path($stylename, $_st)) {
475
                        $_st = $_subst_id;
476
                    }
477
                }
478
            }
479
        }
480 268
        return $_st;
481
    }
482
483
    /**
484
     * Gets the component styledir associated with the topic's component.
485
     *
486
     * @return mixed the path to the component's style directory.
487
     */
488 268
    private function _get_component_snippetdir()
489
    {
490
        // get component's snippetdir (for default styles)
491 268
        $loader = midcom::get()->componentloader;
492 268
        if (empty($this->_topic->component)) {
493 1
            return null;
494
        }
495 268
        if (!empty($loader->get_manifest($this->_topic->component)->extends)) {
496
            $this->append_component_styledir($loader->get_manifest($this->_topic->component)->extends);
497
        }
498
499 268
        return $loader->path_to_snippetpath($this->_topic->component) . "/style";
500
    }
501
502
    /**
503
     * Adds an extra style directory to check for style elements at
504
     * the end of the styledir queue.
505
     *
506
     * @param string $dirname path of style directory within midcom.
507
     * @throws midcom_error exception if directory does not exist.
508
     */
509 73
    function append_styledir($dirname)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
510
    {
511 73
        if (!file_exists($dirname)) {
512
            throw new midcom_error("Style directory $dirname does not exist!");
513
        }
514 73
        $this->_styledirs_append[midcom_core_context::get()->id][] = $dirname;
515 73
    }
516
517
    /**
518
     * Function prepend styledir
519
     *
520
     * @param string $dirname path of styledirectory within midcom.
521
     * @return boolean true if directory appended
522
     * @throws midcom_error if directory does not exist.
523
     */
524 81
    function prepend_styledir($dirname)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
525
    {
526 81
        if (!file_exists($dirname)) {
527
            throw new midcom_error("Style directory {$dirname} does not exist.");
528
        }
529 81
        $this->_styledirs_prepend[midcom_core_context::get()->id][] = $dirname;
530 81
        return true;
531
    }
532
533
    /**
534
     * Append the styledir of a component to the queue of styledirs.
535
     *
536
     * @param string $component Component name
537
     * @throws midcom_error exception if directory does not exist.
538
     */
539
    function append_component_styledir($component)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
540
    {
541
        $loader = midcom::get()->componentloader;
542
        $path = $loader->path_to_snippetpath($component) . "/style";
543
        $this->append_styledir($path);
544
    }
545
546
    /**
547
     * Prepend the styledir of a component
548
     *
549
     * @param string $component component name
550
     */
551 81
    public function prepend_component_styledir($component)
552
    {
553 81
        $loader = midcom::get()->componentloader;
554 81
        $path = $loader->path_to_snippetpath($component) . "/style";
555 81
        $this->prepend_styledir($path);
556 81
    }
557
558
    /**
559
     * Appends a substyle after the currently selected component style.
560
     *
561
     * Enables a depth of more than one style during substyle selection.
562
     *
563
     * @param string $newsub The substyle to append.
564
     */
565 31
    public function append_substyle($newsub)
566
    {
567
        // Make sure try to use only the first argument if we get space separated list, fixes #1788
568 31
        if (strpos($newsub, ' ') !== false) {
569
            $newsub = preg_replace('/^(.+?) .+/', '$1', $newsub);
570
        }
571
572 31
        $context = midcom_core_context::get();
573 31
        $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE);
574
575 31
        if (!empty($current_style)) {
576
            $newsub = $current_style . '/' . $newsub;
577
        }
578
579 31
        $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub);
580 31
    }
581
582
    /**
583
     * Prepends a substyle before the currently selected component style.
584
     *
585
     * Enables a depth of more than one style during substyle selection.
586
     *
587
     * @param string $newsub The substyle to prepend.
588
     */
589
    function prepend_substyle($newsub)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
590
    {
591
        $context = midcom_core_context::get();
592
        $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE);
593
594
        if (!empty($current_style)) {
595
            $newsub .= "/" . $current_style;
596
        }
597
        debug_add("Updating Component Context Substyle from $current_style to $newsub");
598
599
        $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub);
600
    }
601
602
    /**
603
     * Merge the prepend and append styles with the componentstyle. This happens when the
604
     * enter_context function is called.
605
     * You cannot change the style call stack after that (unless you call enter_context again of course).
606
     *
607
     * @param string $component_style
608
     */
609 268
    private function _merge_styledirs($component_style)
610
    {
611 268
        $current_context = midcom_core_context::get()->id;
612
        /* first the prepend styles */
613 268
        $this->_styledirs[$current_context] = $this->_styledirs_prepend[$current_context];
614
        /* then the contextstyle */
615 268
        $this->_styledirs[$current_context][count($this->_styledirs[$current_context])] = $component_style;
616
617 268
        $this->_styledirs[$current_context] = array_merge($this->_styledirs[$current_context], $this->_styledirs_append[$current_context]);
618 268
    }
619
620
    /**
621
     * Switches the context (see dynamic load). Private variables $_context, $_topic
622
     * and $_snippetdir are adjusted.
623
     *
624
     * @todo check documentation
625
     * @param midcom_core_context $context The context to enter
626
     */
627 268
    public function enter_context(midcom_core_context $context)
628
    {
629
        // set new context and topic
630 268
        array_unshift($this->_context, $context); // push into context stack
631
632 268
        $this->_topic = $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC);
0 ignored issues
show
Documentation Bug introduced by
It seems like $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC) can also be of type false. However, the property $_topic is declared as type midcom_db_topic. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
633
634
        // Prepare styledir stacks
635 268
        if (!isset($this->_styledirs[$context->id])) {
636 268
            $this->_styledirs[$context->id] = [];
637
        }
638 268
        if (!isset($this->_styledirs_prepend[$context->id])) {
639 200
            $this->_styledirs_prepend[$context->id] = [];
640
        }
641 268
        if (!isset($this->_styledirs_append[$context->id])) {
642 195
            $this->_styledirs_append[$context->id] = [];
643
        }
644
645 268
        if ($_st = $this->_get_component_style()) {
646
            array_unshift($this->_scope, $_st);
647
        }
648
649 268
        $this->_snippetdir = $this->_get_component_snippetdir();
650
651 268
        $this->_merge_styledirs($this->_snippetdir);
652 268
    }
653
654
    /**
655
     * Switches the context (see dynamic load). Private variables $_context, $_topic
656
     * and $_snippetdir are adjusted.
657
     *
658
     * @todo check documentation
659
     */
660 268
    public function leave_context()
661
    {
662 268
        if ($this->_get_component_style()) {
663
            array_shift($this->_scope);
664
        }
665 268
        array_shift($this->_context);
666
667 268
        $previous_context = (empty($this->_context)) ? midcom_core_context::get() : $this->_context[0];
668 268
        $this->_topic = $previous_context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC);
0 ignored issues
show
Documentation Bug introduced by
It seems like $previous_context->get_k...M_CONTEXT_CONTENTTOPIC) can also be of type false. However, the property $_topic is declared as type midcom_db_topic. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
669
670 268
        $this->_snippetdir = $this->_get_component_snippetdir();
671 268
    }
672
}
673