Completed
Push — master ( 8849ee...a1b70f )
by Andreas
24:35
created

_get_element_from_snippet()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 15
nc 6
nop 1
dl 0
loc 23
ccs 16
cts 16
cp 1
crap 7
rs 8.8333
c 1
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 = [];
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
     * @return    string Style path
131
     */
132
    public function get_style_path_from_id($id)
133
    {
134
        static $path_cache = [];
135
        if (isset($path_cache[$id])) {
136
            return $path_cache[$id];
137
        }
138
        // Construct the path
139
        $path_parts = [];
140
        $original_id = $id;
141
142
        try {
143
            while (($style = new midcom_db_style($id))) {
144
                $path_parts[] = $style->name;
145
                $id = $style->up;
146
147
                if ($style->up == 0) {
148
                    // Toplevel style
149
                    break;
150
                }
151
            }
152
        } 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...
153
        }
154
155
        $path_parts = array_reverse($path_parts);
156
157
        $path_cache[$original_id] = '/' . implode('/', $path_parts);
158
159
        return $path_cache[$original_id];
160
    }
161
162
    /**
163
     * Returns the id of the style described by $path.
164
     *
165
     * Note: $path already includes the element name, so $path looks like
166
     * "/rootstyle/style/style/element".
167
     *
168
     * @todo complete documentation
169
     * @param string $path      The path to retrieve
170
     * @param int $rootstyle    ???
171
     * @return    int ID of the matching style or false
172
     */
173
    public function get_style_id_from_path($path, $rootstyle = 0)
174
    {
175
        static $cached = [];
176
177
        $cache_key = $rootstyle . '::' . $path;
178
179
        if (array_key_exists($cache_key, $cached)) {
180
            return $cached[$cache_key];
181
        }
182
183
        $path = preg_replace("/^\/(.*)/", "$1", $path); // leading "/"
184
        $cached[$cache_key] = false;
185
        $current_style = 0;
186
187
        $path_array = array_filter(explode('/', $path));
188
        if (!empty($path_array)) {
189
            $current_style = $rootstyle;
190
        }
191
192
        foreach ($path_array as $path_item) {
193
            $mc = midgard_style::new_collector('up', $current_style);
194
            $mc->set_key_property('guid');
195
            $mc->add_value_property('id');
196
            $mc->add_constraint('name', '=', $path_item);
197
            $mc->execute();
198
            $styles = $mc->list_keys();
199
200
            foreach (array_keys($styles) as $style_guid) {
201
                $current_style = $mc->get_subkey($style_guid, 'id');
202
                midcom::get()->cache->content->register($style_guid);
203
            }
204
        }
205
206
        if ($current_style != 0) {
207
            $cached[$cache_key] = $current_style;
208
        }
209
210
        return $cached[$cache_key];
211
    }
212
213
    /**
214
     * Returns a style element that matches $name and is in style $id.
215
     * It also returns an element if it is not in the given style,
216
     * but in one of its parent styles.
217
     *
218
     * @param int $id        The style id to search in.
219
     * @param string $name    The element to locate.
220
     * @return string    Value of the found element, or false on failure.
221
     */
222
    private function _get_element_in_styletree($id, $name)
223
    {
224
        static $cached = [];
225
        $cache_key = $id . '::' . $name;
226
227
        if (array_key_exists($cache_key, $cached)) {
228
            return $cached[$cache_key];
229
        }
230
231
        $element_mc = midgard_element::new_collector('style', $id);
232
        $element_mc->set_key_property('guid');
233
        $element_mc->add_value_property('value');
234
        $element_mc->add_constraint('name', '=', $name);
235
        $element_mc->execute();
236
237
        foreach ($element_mc->list_keys() as $element_guid => $value) {
238
            $value = $element_mc->get_subkey($element_guid, 'value');
239
            midcom::get()->cache->content->register($element_guid);
240
            $cached[$cache_key] = $value;
241
            return $value;
242
        }
243
244
        // No such element on this level, check parents
245
        $style_mc = midgard_style::new_collector('id', $id);
246
        $style_mc->set_key_property('guid');
247
        $style_mc->add_value_property('up');
248
        $style_mc->execute();
249
250
        foreach ($style_mc->list_keys() as $style_guid => $value) {
251
            midcom::get()->cache->content->register($style_guid);
252
253
            if ($up = $style_mc->get_subkey($style_guid, 'up')) {
254
                $value = $this->_get_element_in_styletree($up, $name);
255
                $cached[$cache_key] = $value;
256
                return $value;
257
            }
258
        }
259
260
        $cached[$cache_key] = false;
261
        return $cached[$cache_key];
262
    }
263
264
    /**
265
     * Looks for a style element matching $path (either in a user defined style
266
     * or the default style snippetdir) and displays/evaluates it.
267
     *
268
     * @param string $path    The style element to show.
269
     * @return boolean            True on success, false otherwise.
270
     */
271 202
    public function show($path)
272
    {
273 202
        if ($this->_context === []) {
274
            debug_add("Trying to show '{$path}' but there is no context set", MIDCOM_LOG_INFO);
275
            return false;
276
        }
277
278 202
        $style = $this->load($path);
279
280 202
        if ($style === false) {
281 22
            debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
282 22
            return false;
283
        }
284 202
        $this->render($style, $path);
285
286 202
        return true;
287
    }
288
289
    /**
290
     * Load style element content
291
     *
292
     * @param string $path The element name
293
     * @return false|string
294
     */
295 202
    public function load($path)
296
    {
297 202
        $element = $path;
298
        // we have full qualified path to element
299 202
        if (preg_match("|(.*)/(.*)|", $path, $matches)) {
300
            $stylepath = $matches[1];
301
            $element = $matches[2];
302
        }
303
304 202
        if (   isset($stylepath)
305 202
            && $styleid = $this->get_style_id_from_path($stylepath)) {
306
            array_unshift($this->_scope, $styleid);
307
        }
308
309 202
        $style = $this->_find_element_in_scope($element);
310
311 202
        if (!empty($styleid)) {
312
            array_shift($this->_scope);
313
        }
314
315 202
        if (!$style) {
316 202
            $style = $this->_get_element_from_snippet($element);
317
        }
318 202
        return $style;
319
    }
320
321
    /**
322
     * Renders the style element with current request data
323
     *
324
     * @param string $style The style element content
325
     * @param string $path the element's name
326
     * @throws midcom_error
327
     */
328 202
    private function render($style, $path)
329
    {
330 202
        if (midcom::get()->config->get('wrap_style_show_with_name')) {
331
            $style = "\n<!-- Start of style '{$path}' -->\n" . $style;
332
            $style .= "\n<!-- End of style '{$path}' -->\n";
333
        }
334
335
        // This is a bit of a hack to allow &(); tags
336 202
        $preparsed = midcom_helper_misc::preparse($style);
337 202
        if (midcom_core_context::get()->has_custom_key('request_data')) {
338 202
            $data =& midcom_core_context::get()->get_custom_key('request_data');
0 ignored issues
show
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

338
            $data =& midcom_core_context::get()->get_custom_key(/** @scrutinizer ignore-type */ 'request_data');
Loading history...
Unused Code introduced by
The assignment to $data is dead and can be removed.
Loading history...
339
        }
340
341 202
        if (eval('?>' . $preparsed) === false) {
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
342
            // Note that src detection will be semi-reliable, as it depends on all errors being
343
            // found before caching kicks in.
344
            throw new midcom_error("Failed to parse style element '{$path}', see above for PHP errors.");
345
        }
346 202
    }
347
348
    /**
349
     * Looks for a midcom core style element matching $path and displays/evaluates it.
350
     * This offers a bit reduced functionality and will only look in the DB root style,
351
     * the theme directory and midcom's style directory, because it has to work even when
352
     * midcom is not yet fully initialized
353
     *
354
     * @param string $path    The style element to show.
355
     * @return boolean            True on success, false otherwise.
356
     */
357 1
    public function show_midcom($path)
358
    {
359 1
        $_element = $path;
360 1
        $_style = false;
361
362 1
        $this->_snippetdir = MIDCOM_ROOT . '/midcom/style';
363 1
        $context = midcom_core_context::get();
364 1
        if (isset($this->_styledirs[$context->id])) {
365 1
            $styledirs_backup = $this->_styledirs;
366
        }
367
368 1
        $this->_styledirs[$context->id][0] = $this->_snippetdir;
369
370
        try {
371 1
            $root_topic = $context->get_key(MIDCOM_CONTEXT_ROOTTOPIC);
372 1
            if (   $root_topic->style
373 1
                && $db_style = $this->get_style_id_from_path($root_topic->style)) {
374 1
                $_style = $this->_get_element_in_styletree($db_style, $_element);
375
            }
376
        } catch (midcom_error_forbidden $e) {
377
            $e->log();
378
        }
379
380 1
        if ($_style === false) {
381 1
            $_style = $this->_get_element_from_snippet($_element);
382
        }
383
384 1
        if (isset($styledirs_backup)) {
385 1
            $this->_styledirs = $styledirs_backup;
386
        }
387
388 1
        if ($_style !== false) {
389 1
            $this->render($_style, $path);
390 1
            return true;
391
        }
392
        debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
393
        return false;
394
    }
395
396
    /**
397
     * Try to find element in current / given scope
398
     */
399 202
    private function _find_element_in_scope($_element)
400
    {
401 202
        if (!empty($this->_scope)) {
402
            $src = "{$this->_scope[0]}/{$_element}";
403
404
            if (array_key_exists($src, $this->_styles)) {
405
                return $this->_styles[$src];
406
            }
407
            if (   $this->_scope[0] != ''
408
                && $result = $this->_get_element_in_styletree($this->_scope[0], $_element)) {
409
                $this->_styles[$src] = $result;
410
                return $this->_styles[$src];
411
            }
412
        }
413 202
        return false;
414
    }
415
416
    /**
417
     * Try to get element from default style snippet
418
     */
419 202
    private function _get_element_from_snippet($_element)
420
    {
421 202
        $src = "{$this->_snippetdir}/{$_element}";
422 202
        if (array_key_exists($src, $this->_snippets)) {
423 14
            return $this->_snippets[$src];
424
        }
425 202
        if (   midcom::get()->config->get('theme')
426 202
            && $content = midcom_helper_misc::get_element_content($_element)) {
427 6
            $this->_snippets[$src] = $content;
428 6
            return $content;
429
        }
430
431 202
        $current_context = midcom_core_context::get()->id;
432 202
        foreach ($this->_styledirs[$current_context] as $path) {
433 202
            $filename = $path . "/{$_element}.php";
434 202
            if (file_exists($filename)) {
435 193
                if (!array_key_exists($filename, $this->_snippets)) {
436 147
                    $this->_snippets[$filename] = file_get_contents($filename);
437
                }
438 202
                return $this->_snippets[$filename];
439
            }
440
        }
441 22
        return false;
442
    }
443
444
    /**
445
     * Gets the component style.
446
     *
447
     * @todo Document
448
     *
449
     * @param midcom_db_topic $topic    Current topic
450
     * @return int Database ID if the style to use in current view or false
451
     */
452 275
    private function _get_component_style(midcom_db_topic $topic)
453
    {
454 275
        $_st = false;
455
        // get user defined style for component
456
        // style inheritance
457
        // should this be cached somehow?
458 275
        if ($topic->style) {
459
            $_st = $this->get_style_id_from_path($topic->style);
460 275
        } elseif ($inherited = midcom_core_context::get()->get_inherited_style()) {
461
            // get user defined style inherited from topic tree
462
            $_st = $this->get_style_id_from_path($inherited);
463
        } else {
464
            // Get style from sitewide per-component defaults.
465 275
            $styleengine_default_styles = midcom::get()->config->get('styleengine_default_styles');
466 275
            if (isset($styleengine_default_styles[$topic->component])) {
467
                $_st = $this->get_style_id_from_path($styleengine_default_styles[$topic->component]);
468
            }
469
        }
470
471 275
        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...
472
            $substyle = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_SUBSTYLE);
473
474
            if (is_string($substyle)) {
475
                $chain = explode('/', $substyle);
476
                foreach ($chain as $stylename) {
477
                    if ($_subst_id = $this->get_style_id_from_path($stylename, $_st)) {
478
                        $_st = $_subst_id;
479
                    }
480
                }
481
            }
482
        } else {
483 275
            $style = $topic->style ?: midcom_core_context::get()->get_inherited_style();
484 275
            if (   is_string($style)
485 275
                && strpos($style, 'theme:') === 0) {
486
                $theme_dir = OPENPSA2_THEME_ROOT . midcom::get()->config->get('theme') . '/style';
487
                $parts = explode('/', str_replace('theme:/', '', $style));
488
489
                foreach ($parts as &$part) {
490
                    $theme_dir .= '/' . $part;
491
                    $part = $theme_dir;
492
                }
493
                foreach (array_reverse(array_filter($parts, 'is_dir')) as $dirname) {
494
                    $this->prepend_styledir($dirname);
495
                }
496
            }
497
        }
498 275
        return $_st;
499
    }
500
501
    /**
502
     * Gets the component styledir associated with the topic's component.
503
     *
504
     * @return mixed the path to the component's style directory.
505
     */
506 275
    private function _get_component_snippetdir()
507
    {
508
        // get component's snippetdir (for default styles)
509 275
        $loader = midcom::get()->componentloader;
510 275
        if (empty($this->_topic->component)) {
511 1
            return null;
512
        }
513 275
        if (!empty($loader->manifests[$this->_topic->component]->extends)) {
514
            $this->append_component_styledir($loader->manifests[$this->_topic->component]->extends);
515
        }
516
517 275
        return $loader->path_to_snippetpath($this->_topic->component) . "/style";
518
    }
519
520
    /**
521
     * Adds an extra style directory to check for style elements at
522
     * the end of the styledir queue.
523
     *
524
     * @param string $dirname path of style directory within midcom.
525
     * @throws midcom_error exception if directory does not exist.
526
     */
527 71
    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...
528
    {
529 71
        if (!file_exists($dirname)) {
530
            throw new midcom_error("Style directory $dirname does not exist!");
531
        }
532 71
        $this->_styledirs_append[midcom_core_context::get()->id][] = $dirname;
533 71
    }
534
535
    /**
536
     * Function prepend styledir
537
     *
538
     * @param string $dirname path of styledirectory within midcom.
539
     * @return boolean true if directory appended
540
     * @throws midcom_error if directory does not exist.
541
     */
542 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...
543
    {
544 81
        if (!file_exists($dirname)) {
545
            throw new midcom_error("Style directory {$dirname} does not exist.");
546
        }
547 81
        $this->_styledirs_prepend[midcom_core_context::get()->id][] = $dirname;
548 81
        return true;
549
    }
550
551
    /**
552
     * Append the styledir of a component to the queue of styledirs.
553
     *
554
     * @param string $component Component name
555
     * @throws midcom_error exception if directory does not exist.
556
     */
557
    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...
558
    {
559
        $loader = midcom::get()->componentloader;
560
        $path = $loader->path_to_snippetpath($component) . "/style";
561
        $this->append_styledir($path);
562
    }
563
564
    /**
565
     * Prepend the styledir of a component
566
     *
567
     * @param string $component component name
568
     */
569 81
    public function prepend_component_styledir($component)
570
    {
571 81
        $loader = midcom::get()->componentloader;
572 81
        $path = $loader->path_to_snippetpath($component) . "/style";
573 81
        $this->prepend_styledir($path);
574 81
    }
575
576
    /**
577
     * Appends a substyle after the currently selected component style.
578
     *
579
     * Enables a depth of more than one style during substyle selection.
580
     *
581
     * @param string $newsub The substyle to append.
582
     */
583 31
    public function append_substyle($newsub)
584
    {
585
        // Make sure try to use only the first argument if we get space separated list, fixes #1788
586 31
        if (strpos($newsub, ' ') !== false) {
587
            $newsub = preg_replace('/^(.+?) .+/', '$1', $newsub);
588
        }
589
590 31
        $context = midcom_core_context::get();
591 31
        $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE);
592
593 31
        if (!empty($current_style)) {
594
            $newsub = $current_style . '/' . $newsub;
595
        }
596
597 31
        $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub);
598 31
    }
599
600
    /**
601
     * Prepends a substyle before the currently selected component style.
602
     *
603
     * Enables a depth of more than one style during substyle selection.
604
     *
605
     * @param string $newsub The substyle to prepend.
606
     */
607
    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...
608
    {
609
        $context = midcom_core_context::get();
610
        $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE);
611
612
        if (!empty($current_style)) {
613
            $newsub .= "/" . $current_style;
614
        }
615
        debug_add("Updating Component Context Substyle from $current_style to $newsub");
616
617
        $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub);
618
    }
619
620
    /**
621
     * Merge the prepend and append styles with the componentstyle. This happens when the
622
     * enter_context function is called.
623
     * You cannot change the style call stack after that (unless you call enter_context again of course).
624
     *
625
     * @param string $component_style
626
     */
627 275
    private function _merge_styledirs($component_style)
628
    {
629 275
        $current_context = midcom_core_context::get()->id;
630
        /* first the prepend styles */
631 275
        $this->_styledirs[$current_context] = $this->_styledirs_prepend[$current_context];
632
        /* then the contextstyle */
633 275
        $this->_styledirs[$current_context][count($this->_styledirs[$current_context])] = $component_style;
634
635 275
        $this->_styledirs[$current_context] = array_merge($this->_styledirs[$current_context], $this->_styledirs_append[$current_context]);
636 275
    }
637
638
    /**
639
     * Switches the context (see dynamic load). Private variables $_context, $_topic
640
     * and $_snippetdir are adjusted.
641
     *
642
     * @todo check documentation
643
     * @param midcom_core_context $context The context to enter
644
     */
645 275
    public function enter_context(midcom_core_context $context)
646
    {
647
        // set new context and topic
648 275
        array_unshift($this->_context, $context); // push into context stack
649
650 275
        $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...
651
652
        // Prepare styledir stacks
653 275
        if (!isset($this->_styledirs[$context->id])) {
654 275
            $this->_styledirs[$context->id] = [];
655
        }
656 275
        if (!isset($this->_styledirs_prepend[$context->id])) {
657 205
            $this->_styledirs_prepend[$context->id] = [];
658
        }
659 275
        if (!isset($this->_styledirs_append[$context->id])) {
660 204
            $this->_styledirs_append[$context->id] = [];
661
        }
662
663 275
        if (   $this->_topic
664 275
            && $_st = $this->_get_component_style($this->_topic)) {
665
            array_unshift($this->_scope, $_st);
666
        }
667
668 275
        $this->_snippetdir = $this->_get_component_snippetdir();
669
670 275
        $this->_merge_styledirs($this->_snippetdir);
671 275
    }
672
673
    /**
674
     * Switches the context (see dynamic load). Private variables $_context, $_topic
675
     * and $_snippetdir are adjusted.
676
     *
677
     * @todo check documentation
678
     */
679 275
    public function leave_context()
680
    {
681 275
        if (   $this->_topic
682 275
            && $this->_get_component_style($this->_topic)) {
683
            array_shift($this->_scope);
684
        }
685 275
        array_shift($this->_context);
686
687 275
        $previous_context = (empty($this->_context)) ? midcom_core_context::get() : $this->_context[0];
688 275
        $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...
689
690 275
        $this->_snippetdir = $this->_get_component_snippetdir();
691 275
    }
692
}
693