Issues (808)

src/midcom/templating/loader.php (2 issues)

Labels
1
<?php
2
/**
3
 * @package midcom.templating
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
namespace midcom\templating;
10
11
use midcom;
12
use midcom_db_topic;
13
use midcom_core_context;
14
use midcom_connection;
15
16
/**
17
 * templating loader class
18
 *
19
 * Style Inheritance
20
 *
21
 * The basic path the styleloader follows to find a style element is:
22
 * 1. Topic style -> if the current topic has a style set
23
 * 2. Inherited topic style -> if the topic inherits a style from another topic.
24
 * 3. Site-wide per-component default style -> if defined in MidCOM configuration key styleengine_default_styles
25
 * 4. Theme style -> the style of the MidCOM component.
26
 * 5. The file style. This is usually the elements found in the component's style directory.
27
 *
28
 * Regarding no. 5:
29
 * It is possible to add extra file styles if so is needed for example by a portal component.
30
 * This is done either using the append/prepend component_style functions of midcom::get()->style or by setting it
31
 * to another directory by calling (append|prepend)_styledir directly.
32
 *
33
 * @package midcom.templating
34
 */
35
class loader
36
{
37
    /**
38
     * Default style element cache
39
     *
40
     * @var string[]
41
     */
42
    protected array $cache = [];
43
44
    /**
45
     * The stack of directories to check for styles.
46
     *
47
     * @var string[]
48
     */
49
    private array $directories = [];
50
51
    private string $theme_root;
52
53 1
    public function __construct(string $theme_root)
54
    {
55 1
        $this->theme_root = $theme_root;
56
    }
57
58 276
    public function set_directories(?midcom_db_topic $topic, array $prepend, array $append)
59
    {
60 276
        $this->directories = $prepend;
61 276
        if ($snippetdir = $this->get_component_snippetdir($topic)) {
62 276
            $this->directories[] = $snippetdir;
63
        }
64 276
        $this->directories = array_merge($this->directories, $append);
65
    }
66
67
    /**
68
     * Gets the component styledir associated with the topic's component.
69
     *
70
     * @return ?string the path to the component's style directory.
71
     */
72 276
    private function get_component_snippetdir(?midcom_db_topic $topic) : ?string
73
    {
74 276
        if (empty($topic->component)) {
75 2
            return null;
76
        }
77 276
        return midcom::get()->componentloader->path_to_snippetpath($topic->component) . "/style";
78
    }
79
80
    /**
81
     * Try to get element from default style snippet
82
     */
83 207
    public function get_element(string $name, bool $scope_from_path) : ?string
84
    {
85 207
        if (midcom::get()->config->get('theme')) {
86 207
            $src = "theme:{$name}";
87 207
            if (array_key_exists($src, $this->cache)) {
88 15
                return $this->cache[$src];
89
            }
90 207
            if ($content = $this->get_element_from_theme($name)) {
91 2
                return $this->add_to_cache($src, $content);
92
            }
93
        }
94
95 207
        foreach ($this->directories as $path) {
96 207
            $filename = $path . "/{$name}.php";
97 207
            if (file_exists($filename)) {
98 197
                if (array_key_exists($filename, $this->cache)) {
99 78
                    return $this->cache[$filename];
100
                }
101 150
                return $this->add_to_cache($filename, file_get_contents($filename));
102
            }
103
        }
104 24
        return null;
105
    }
106
107
    /**
108
     * Get the content of the element by the passed element name.
109
     * Tries to resolve path according to theme-name & page
110
     */
111 207
    private function get_element_from_theme(string $element_name) : ?string
112
    {
113 207
        $theme = midcom::get()->config->get('theme');
114 207
        $path_array = explode('/', $theme);
0 ignored issues
show
It seems like $theme can also be of type null; however, parameter $string of explode() does only seem to accept string, 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

114
        $path_array = explode('/', /** @scrutinizer ignore-type */ $theme);
Loading history...
115
116
        //get the page if there is one
117 207
        $page = midcom_connection::get_url('page_style');
118 207
        $substyle = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_SUBSTYLE);
119
        //check if we have elements for the sub-styles
120 207
        while (!empty($path_array)) {
121 207
            $theme_path = implode('/', $path_array);
122 207
            $candidates = [];
123 207
            if ($substyle) {
124 31
                $candidates[] = '/' . $substyle . '/' . $element_name;
125
            }
126 207
            if ($page) {
127
                $candidates[] = $page . '/' . $element_name;
128
            }
129 207
            $candidates[] = '/' . $element_name;
130
131 207
            foreach ($candidates as $candidate) {
132 207
                $filename = $this->theme_root . $theme_path . '/style' . $candidate . '.php';
133 207
                if (file_exists($filename)) {
134 2
                    return file_get_contents($filename);
135
                }
136
            }
137
138
            //remove last theme part
139 207
            array_pop($path_array);
140
        }
141
142 207
        return null;
143
    }
144
145 151
    protected function add_to_cache(string $cache_key, string $content) : string
146
    {
147 151
        $this->cache[$cache_key] = $this->resolve_includes($content);
148 151
        return $this->cache[$cache_key];
149
    }
150
151 151
    private function resolve_includes(string $content) : string
152
    {
153 151
        return preg_replace_callback("/<\\(([a-zA-Z0-9 _-]+)\\)>/", function (array $matches) {
154 3
            $element = $matches[1];
155
156
            switch ($element) {
157 3
                case 'title':
158 2
                    return '<?= midcom::get()->config->get("midcom_site_title") ?>';
159 2
                case 'content':
160 2
                    return '<?php midcom_core_context::get()->show(); ?>';
161
                default:
162 1
                    if ($value = $this->get_element_from_theme($element)) {
163 1
                        return $this->resolve_includes($value);
164
                    }
165
                    return '';
166
            }
167 151
        }, $content);
168
    }
169
170
    /**
171
     * Initializes style sources
172
     */
173 276
    public function initialize(midcom_core_context $context, ?string $style)
174
    {
175 276
        if ($style && str_starts_with($style, 'theme:')) {
176
            $theme_dir = $this->theme_root . midcom::get()->config->get('theme') . '/style';
177
            $parts = explode('/', str_replace('theme:/', '', $style));
178
179
            foreach ($parts as &$part) {
180
                $theme_dir .= '/' . $part;
181
                $part = $theme_dir;
182
            }
183
            foreach (array_reverse(array_filter($parts, is_dir(...))) as $dirname) {
0 ignored issues
show
The type midcom\templating\is_dir was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
184
                midcom::get()->style->prepend_styledir($dirname);
185
            }
186
        }
187
    }
188
}
189