Passed
Push — master ( 0a3404...d96050 )
by Andreas
16:55
created

midcom_helper__styleloader::leave_context()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 11
ccs 6
cts 7
cp 0.8571
crap 3.0261
rs 10
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
     */
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)
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
        foreach ($element_mc->list_keys() as $element_guid => $value) {
237
            $value = $element_mc->get_subkey($element_guid, 'value');
238
            midcom::get()->cache->content->register($element_guid);
239
            $cached[$cache_key] = $value;
240
            return $value;
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
        foreach ($style_mc->list_keys() as $style_guid => $value) {
251
            midcom::get()->cache->content->register($style_guid);
252
            $up = $style_mc->get_subkey($style_guid, 'up');
253
            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

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

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