Issues (806)

lib/midcom/helper/style.php (4 issues)

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
use midcom\templating\loader;
10
11
/**
12
 * This class is responsible for all style management. It is
13
 * accessible through the midcom::get()->style object.
14
 *
15
 * The method <code>show($style)</code> returns the style element $style for the current
16
 * component:
17
 *
18
 * It checks whether a style path is defined for the current component.
19
 *
20
 * - If there is a user defined style path, the element named $style in
21
 *   this path is returned,
22
 * - otherwise the element "$style" is taken from the default style of the
23
 *   current component (/path/to/component/style/$path).
24
 *
25
 * (The default fallback is always the default style, e.g. if $style
26
 * is not in the user defined style path)
27
 *
28
 * To enable cross-style referencing and provide the opportunity to access
29
 * any style element, "show" can be called with a full qualified style
30
 * path (like "/mystyle/element1", while the current page's style may be set
31
 * to "/yourstyle").
32
 *
33
 * Note: To make sure sub-styles and elements included in styles are handled
34
 * correctly, use:
35
 *
36
 * <code>
37
 * <?php midcom_show_style ("elementname"); ?>
38
 * </code>
39
 *
40
 * @package midcom.helper
41
 */
42
class midcom_helper_style
43
{
44
    /**
45
     * Current topic
46
     */
47
    private ?midcom_db_topic $_topic = null;
48
49
    /**
50
     * Context stack
51
     *
52
     * @var midcom_core_context[]
53
     */
54
    private array $_context = [];
55
56
    /**
57
     * List of styledirs to handle after componentstyle
58
     */
59
    private array $_styledirs_append = [];
60
61
    /**
62
     * List of styledirs to handle before componentstyle
63
     */
64
    private array $_styledirs_prepend = [];
65
66
    private loader $loader;
67
68
    /**
69
     * Data to pass to the style
70
     */
71
    public array $data;
72
73 1
    public function __construct(loader $loader)
74
    {
75 1
        $this->loader = $loader;
76
    }
77
78
    /**
79
     * Looks for a style element matching $path (either in a user defined style
80
     * or the default style snippetdir) and displays/evaluates it.
81
     */
82 207
    public function show(string $path) : bool
83
    {
84 207
        if ($this->_context === []) {
85
            debug_add("Trying to show '{$path}' but there is no context set", MIDCOM_LOG_INFO);
86
            return false;
87
        }
88
89 207
        $style = $this->loader->get_element($path, true);
90
91 207
        if ($style === null) {
92 24
            if ($path == 'ROOT') {
93
                // Go to fallback ROOT instead of displaying a blank page
94
                return $this->show_midcom($path);
95
            }
96
97 24
            debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
98 24
            return false;
99
        }
100 206
        $this->render($style, $path);
101
102 206
        return true;
103
    }
104
105
    /**
106
     * Renders the style element with current request data
107
     *
108
     * @param string $style The style element content
109
     * @param string $path the element's name
110
     * @throws midcom_error
111
     */
112 206
    private function render(string $style, string $path)
113
    {
114 206
        if (midcom::get()->config->get('wrap_style_show_with_name')) {
115
            $style = "\n<!-- Start of style '{$path}' -->\n" . $style;
116
            $style .= "\n<!-- End of style '{$path}' -->\n";
117
        }
118
119
        // Resolve variables
120 206
        $preparsed = midcom_helper_formatter::compile($style);
121
122 206
        if (midcom_core_context::get()->has_custom_key('request_data')) {
123 206
            $data =& midcom_core_context::get()->get_custom_key('request_data');
0 ignored issues
show
The assignment to $data is dead and can be removed.
Loading history...
124
        }
125
126
        try {
127 206
            eval('?>' . $preparsed);
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
128
        } catch (ParseError $e) {
0 ignored issues
show
catch (\ParseError $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
129
            throw new midcom_error("Failed to parse style element '{$path}': " . $e->getMessage() . ' in line ' . $e->getLine());
130
        }
131
    }
132
133
    /**
134
     * Looks for a midcom core style element matching $path and displays/evaluates it.
135
     * This offers a bit reduced functionality and will only look in the DB root style,
136
     * the theme directory and midcom's style directory, because it has to work even when
137
     * midcom is not yet fully initialized
138
     */
139 1
    public function show_midcom(string $path) : bool
140
    {
141 1
        $loader = clone $this->loader;
142 1
        $loader->set_directories(null, [MIDCOM_ROOT . '/midcom/style'], []);
143
144 1
        $_style = $loader->get_element($path, false);
145
146 1
        if ($_style !== null) {
147 1
            $this->render($_style, $path);
148 1
            return true;
149
        }
150
        debug_add("The element '{$path}' could not be found.", MIDCOM_LOG_INFO);
151
        return false;
152
    }
153
154
    /**
155
     * Adds an extra style directory to check for style elements at
156
     * the end of the styledir queue.
157
     *
158
     * @throws midcom_error exception if directory does not exist.
159
     */
160 75
    public function append_styledir(string $dirname)
161
    {
162 75
        if (!file_exists($dirname)) {
163
            throw new midcom_error("Style directory $dirname does not exist!");
164
        }
165 75
        $this->_styledirs_append[midcom_core_context::get()->id][] = $dirname;
166
    }
167
168
    /**
169
     * Function prepend styledir
170
     */
171 83
    public function prepend_styledir(string $dirname)
172
    {
173 83
        if (!file_exists($dirname)) {
174
            throw new midcom_error("Style directory {$dirname} does not exist.");
175
        }
176 83
        $this->_styledirs_prepend[midcom_core_context::get()->id][] = $dirname;
177
    }
178
179
    /**
180
     * Append the styledir of a component to the queue of styledirs.
181
     */
182
    public function append_component_styledir(string $component)
183
    {
184
        $path = midcom::get()->componentloader->path_to_snippetpath($component) . "/style";
185
        $this->append_styledir($path);
186
    }
187
188
    /**
189
     * Prepend the styledir of a component
190
     */
191 83
    public function prepend_component_styledir(string $component)
192
    {
193 83
        $path = midcom::get()->componentloader->path_to_snippetpath($component) . "/style";
194 83
        $this->prepend_styledir($path);
195
    }
196
197
    /**
198
     * Appends a substyle after the currently selected component style.
199
     *
200
     * Enables a depth of more than one style during substyle selection.
201
     */
202 31
    public function append_substyle(string $newsub)
203
    {
204
        // Make sure try to use only the first argument if we get space separated list, fixes #1788
205 31
        if (str_contains($newsub, ' ')) {
206
            $newsub = preg_replace('/^(.+?) .+/', '$1', $newsub);
207
        }
208
209 31
        $context = midcom_core_context::get();
210 31
        $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE);
211
212 31
        if (!empty($current_style)) {
213
            $newsub = $current_style . '/' . $newsub;
214
        }
215
216 31
        $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub);
217
    }
218
219
    /**
220
     * Prepends a substyle before the currently selected component style.
221
     *
222
     * Enables a depth of more than one style during substyle selection.
223
     *
224
     * @param string $newsub The substyle to prepend.
225
     */
226
    function prepend_substyle($newsub)
0 ignored issues
show
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...
227
    {
228
        $context = midcom_core_context::get();
229
        $current_style = $context->get_key(MIDCOM_CONTEXT_SUBSTYLE);
230
231
        if (!empty($current_style)) {
232
            $newsub .= "/" . $current_style;
233
        }
234
        debug_add("Updating Component Context Substyle from $current_style to $newsub");
235
236
        $context->set_key(MIDCOM_CONTEXT_SUBSTYLE, $newsub);
237
    }
238
239
    /**
240
     * Switches the context (see dynamic load).
241
     *
242
     * Private variables are adjusted, and the prepend and append styles are merged with the componentstyle.
243
     * You cannot change the style stack after that (unless you call enter_context again of course).
244
     */
245 275
    public function enter_context(midcom_core_context $context)
246
    {
247
        // set new context and topic
248 275
        array_unshift($this->_context, $context); // push into context stack
249
250 275
        if ($this->_topic = $context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC)) {
251 275
            $style = $this->_topic->style ?: $context->get_inherited_style();
252 275
            if (!$style) {
253 275
                $styleengine_default_styles = midcom::get()->config->get('styleengine_default_styles');
254 275
                if (isset($styleengine_default_styles[$this->_topic->component])) {
255
                    $style = $styleengine_default_styles[$this->_topic->component];
256
                }
257
            }
258 275
            $this->loader->initialize($context, $style);
259
        }
260
261 275
        $this->loader->set_directories(
262 275
            $this->_topic,
263 275
            $this->_styledirs_prepend[$context->id] ?? [],
264 275
            $this->_styledirs_append[$context->id] ?? []
265 275
        );
266
    }
267
268
    /**
269
     * Switches the context (see dynamic load). Private variables $_context, $_topic
270
     * and $loader are adjusted.
271
     */
272 275
    public function leave_context()
273
    {
274 275
        array_shift($this->_context);
275 275
        $previous_context = $this->_context[0] ?? midcom_core_context::get();
276
277 275
        $this->_topic = $previous_context->get_key(MIDCOM_CONTEXT_CONTENTTOPIC);
278 275
        $this->loader->set_directories(
279 275
            $this->_topic,
280 275
            $this->_styledirs_prepend[$previous_context->id] ?? [],
281 275
            $this->_styledirs_append[$previous_context->id] ?? []
282 275
        );
283
    }
284
}
285