Completed
Push — master ( 042f37...231ca8 )
by Andreas
27:43
created

midcom_helper__styleloader::append_substyle()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 4
nop 1
dl 0
loc 15
ccs 6
cts 8
cp 0.75
crap 3.1406
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 = [];
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
            if ($path == 'ROOT') {
282
                /* If we don't have a ROOT element, go to content directly. style-init or style-finish
283
                 * can load the page style
284
                 */
285
                midcom_core_context::get()->show();
286
                return true;
287
            }
288
289 22
            debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
290 22
            return false;
291
        }
292 202
        $this->render($style, $path);
293
294 202
        return true;
295
    }
296
297
    /**
298
     * Load style element content
299
     *
300
     * @param string $path The element name
301
     * @return false|string
302
     */
303 202
    public function load($path)
304
    {
305 202
        $element = $path;
306
        // we have full qualified path to element
307 202
        if (preg_match("|(.*)/(.*)|", $path, $matches)) {
308
            $stylepath = $matches[1];
309
            $element = $matches[2];
310
        }
311
312 202
        if (   isset($stylepath)
313 202
            && $styleid = $this->get_style_id_from_path($stylepath)) {
314
            array_unshift($this->_scope, $styleid);
315
        }
316
317 202
        $style = $this->_find_element_in_scope($element);
318
319 202
        if (!empty($styleid)) {
320
            array_shift($this->_scope);
321
        }
322
323 202
        if (!$style) {
324 202
            $style = $this->_get_element_from_snippet($element);
325
        }
326 202
        return $style;
327
    }
328
329
    /**
330
     * Renders the style element with current request data
331
     *
332
     * @param string $style The style element content
333
     * @param string $path the element's name
334
     * @throws midcom_error
335
     */
336 202
    private function render($style, $path)
337
    {
338 202
        if (midcom::get()->config->get('wrap_style_show_with_name')) {
339
            $style = "\n<!-- Start of style '{$path}' -->\n" . $style;
340
            $style .= "\n<!-- End of style '{$path}' -->\n";
341
        }
342
343
        // This is a bit of a hack to allow &(); tags
344 202
        $preparsed = midcom_helper_misc::preparse($style);
345 202
        if (midcom_core_context::get()->has_custom_key('request_data')) {
346 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

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