Completed
Push — master ( a656f3...e0dfe2 )
by Andreas
14:48
created

midcom_helper_misc::parse_config()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 5
cp 0.8
crap 2.032
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
use Cocur\Slugify\Slugify;
10
11
/**
12
 * Miscellaneous helper functions
13
 *
14
 * @package midcom.helper
15
 */
16
class midcom_helper_misc
17
{
18
    /**
19
     * @param integer $length
20
     * @param string $characters
21
     * @throws InvalidArgumentException
22
     */
23 13
    public static function random_string($length, $characters) : string
24
    {
25 13
        if ($length < 1) {
26
            throw new InvalidArgumentException('invalid length');
27
        }
28 13
        $size = strlen($characters) - 1;
29 13
        if ($size < 1) {
30
            throw new InvalidArgumentException('invalid characters');
31
        }
32 13
        $return = '';
33 13
        for ($i = 0; $i < $length; $i++) {
34 13
            $return .= $characters[random_int(0, $size)];
35
        }
36 13
        return $return;
37
    }
38
39
    /**
40
     * @param string $input
41
     */
42 27
    public static function urlize($input) : string
43
    {
44 27
        $slugify = new Slugify;
45 27
        return $slugify->slugify($input);
46
    }
47
48
    /**
49
     * Turn midcom config files into PHP arrays
50
     *
51
     * @param string $data The data to parse
52
     * @throws midcom_error
53
     */
54 509
    public static function parse_config($data) : array
55
    {
56 509
        $data = eval("return [{$data}\n];");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
57 509
        if ($data === false) {
58
            throw new midcom_error("Failed to parse config data, see above for PHP errors.");
59
        }
60 509
        return $data;
61
    }
62
63
    /**
64
     * This helper function searches for a snippet either in the Filesystem
65
     * or in the database and returns its content or code-field, respectively.
66
     *
67
     * Prefix the snippet Path with 'file:' for retrieval of a file relative to
68
     * MIDCOM_ROOT; omit it to get the code field of a Snippet.
69
     *
70
     * Any error (files not found) will return null. If you want to trigger an error,
71
     * look for midcom_helper_misc::get_snippet_content.
72
     *
73
     * @param string $path  The URL to the snippet.
74
     * @return string       The content of the snippet/file.
75
     */
76 509
    public static function get_snippet_content_graceful($path)
77
    {
78 509
        static $cached_snippets = [];
79
80 509
        if (!array_key_exists($path, $cached_snippets)) {
81 62
            if (substr($path, 0, 5) == 'file:') {
82 46
                $cached_snippets[$path] = self::load_from_file($path);
83 21
            } elseif (substr($path, 0, 5) == 'conf:') {
84 21
                $cached_snippets[$path] = self::load(midcom::get()->config->get('midcom_config_basedir') . '/midcom' . substr($path, 5));
85
            } else {
86 16
                $cached_snippets[$path] = self::load_from_snippet($path);
87
            }
88
        }
89
90 509
        return $cached_snippets[$path];
91
    }
92
93 16
    private static function load_from_snippet(string $path)
94
    {
95 16
        $snippet = new midgard_snippet();
96 16
        if (!$snippet->get_by_path($path)) {
97 16
            return null;
98
        }
99
        if (isset(midcom::get()->cache->content)) {
100
            midcom::get()->cache->content->register($snippet->guid);
101
        }
102
        return $snippet->code;
103
    }
104
105 46
    private static function load_from_file(string $path)
106
    {
107 46
        $filename = MIDCOM_ROOT . substr($path, 5);
108 46
        if (!file_exists($filename)) {
109
            // try in src
110 2
            $filename = preg_replace('/\/lib\/?$/', '/src', MIDCOM_ROOT) . substr($path, 5);
111 2
            if (!file_exists($filename)) {
112
                //If we can't find the file in-tree, we look for out-of-tree components before giving up
113
                $filename = substr($path, 6);
114
                if (preg_match('|.+?/.+?/.+?/|', $filename)) {
115
                    $component_name = preg_replace('|(.+?)/(.+?)/(.+?)/.+|', '$1.$2.$3', $filename);
116
                    if (midcom::get()->componentloader->is_installed($component_name)) {
117
                        $filename = substr($filename, strlen($component_name));
118
                        $filename = midcom::get()->componentloader->path_to_snippetpath($component_name) . $filename;
119
                    }
120
                }
121
            }
122
        }
123 46
        return self::load($filename);
124
    }
125
126 62
    private static function load(string $filename)
127
    {
128 62
        if (!file_exists($filename)) {
129 21
            return null;
130
        }
131 46
        return file_get_contents($filename);
132
    }
133
134
    /**
135
     * This helper function searches for a snippet either in the Filesystem
136
     * or in the database and returns its content or code-field, respectively.
137
     *
138
     * Prefix the snippet Path with 'file:' for retrieval of a file relative to
139
     * MIDCOM_ROOT; omit it to get the code field of a Snippet.
140
     *
141
     * Any error (files not found) will raise a MidCOM Error. If you want a more
142
     * graceful behavior, look for midcom_helper_misc::get_snippet_content_graceful
143
     *
144
     * @param string $path    The URL to the snippet.
145
     */
146 244
    public static function get_snippet_content($path) : string
147
    {
148 244
        $data = self::get_snippet_content_graceful($path);
149 244
        if ($data === null) {
0 ignored issues
show
introduced by
The condition $data === null is always false.
Loading history...
150
            throw new midcom_error("Could not load the contents of the snippet {$path}: Snippet does not exist.");
151
        }
152 244
        return $data;
153
    }
154
155
    /**
156
     * Preparse and include snippet
157
     *
158
     * @param string $path    The path of the snippet that should be included.
159
     * @return boolean Returns false if the snippet could not be loaded or true, if it was evaluated successfully.
160
     */
161
    public static function include_snippet_php($path) : bool
162
    {
163
        $code = self::get_snippet_content_graceful($path);
164
        if (empty($code)) {
165
            debug_add("Could not find snippet {$path}: ", MIDCOM_LOG_ERROR);
166
            return false;
167
        }
168
        debug_add("Evaluating snippet {$path}.");
169
        eval('?>' . self::preparse($code));
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
170
        return true;
171
    }
172
173
    /**
174
     * This is a bit of a hack to allow &(); tags
175
     *
176
     * @param string $code The unprocessed code
177
     */
178 201
    public static function preparse($code) : string
179
    {
180
        // Get style elements
181 201
        $code = preg_replace_callback("/<\\(([a-zA-Z0-9 _-]+)\\)>/", [midcom_helper_misc::class, 'include_element'], $code);
182
        // Echo variables
183 201
        return preg_replace_callback("%&\(([^)]*)\);%i", [midcom_helper_formatter::class, 'convert_to_php'], $code);
184
    }
185
186
    /**
187
     * Include a theme element
188
     */
189 20
    public static function include_element($name) : string
190
    {
191 20
        if (is_array($name)) {
192 20
            $element = $name[1];
193
        } else {
194
            $element = $name;
195
        }
196
197 20
        switch ($element) {
198 20
            case 'title':
199 10
                return midcom::get()->config->get('midcom_site_title');
200 19
            case 'content':
201 19
                return '<?php midcom_core_context::get()->show(); ?>';
202
            default:
203 9
                $value = self::get_element_content($element);
204
205 9
                if (empty($value)) {
206
                    return '';
207
                }
208 9
                return preg_replace_callback("/<\\(([a-zA-Z0-9 _-]+)\\)>/", [midcom_helper_misc::class, 'include_element'], $value);
209
        }
210
    }
211
212
    /**
213
     * Find MIME type image for a document
214
     *
215
     * Used in midcom.helper.imagepopup, midgard.admin.asgard and org.openpsa.documents.
216
     *
217
     * @param string $mimetype  Document MIME type
218
     * @return string    Path to the icon
219
     */
220
    public static function get_mime_icon($mimetype) : string
221
    {
222
        $mime_fspath = MIDCOM_STATIC_ROOT . '/stock-icons/mime';
223
        $mime_urlpath = MIDCOM_STATIC_URL . '/stock-icons/mime';
224
        $mimetype_filename = str_replace('/', '-', $mimetype);
225
        if (!is_readable($mime_fspath)) {
226
            debug_add("Couldn't read directory {$mime_fspath}", MIDCOM_LOG_WARN);
227
        }
228
229
        $check_files = [];
230
        switch ($mimetype_filename) {
231
            case 'application-x-zip-compressed':
232
                $check_files[] = "gnome-application-zip.png";
233
                break;
234
            default:
235
                $check_files[] = "{$mimetype_filename}.png";
236
                $check_files[] = "gnome-{$mimetype_filename}.png";
237
                break;
238
        }
239
240
        // Return first match
241
        foreach ($check_files as $filename) {
242
            if (is_readable("{$mime_fspath}/{$filename}")) {
243
                return "{$mime_urlpath}/{$filename}";
244
            }
245
        }
246
        // Default icon if there is none for the MIME type
247
        return $mime_urlpath . '/gnome-unknown.png';
248
    }
249
250
    /**
251
     * Pretty print file sizes
252
     *
253
     * @param int $size  File size in bytes
254
     */
255 7
    public static function filesize_to_string($size) : string
256
    {
257 7
        if ($size >= 1048576) {
258
            // More than a meg
259
            return sprintf("%01.1f", $size / 1048576) . " MB";
260
        }
261 7
        if ($size >= 1024) {
262
            // More than a kilo
263
            return sprintf("%01.1f", $size / 1024) . " KB";
264
        }
265 7
        return $size . " Bytes";
266
    }
267
268
    /**
269
     * Fix newline etc encoding issues in serialized data
270
     *
271
     * @param string $data The data to fix.
272
     * @return string $data with serializations fixed.
273
     */
274
    public static function fix_serialization($data)
275
    {
276
        //Skip on empty data
277
        if (empty($data)) {
278
            return $data;
279
        }
280
281
        $preg='/s:([0-9]+):"(.*?)";/ms';
282
        preg_match_all($preg, $data, $matches);
283
        $cache = [];
284
285
        foreach ($matches[0] as $k => $origFullStr) {
286
            $origLen = $matches[1][$k];
287
            $origStr = $matches[2][$k];
288
            $newLen = strlen($origStr);
289
            if ($newLen != $origLen) {
290
                $newFullStr = "s:$newLen:\"$origStr\";";
291
                //For performance we cache information on which strings have already been replaced
292
                if (!array_key_exists($origFullStr, $cache)) {
293
                    $data = str_replace($origFullStr, $newFullStr, $data);
294
                    $cache[$origFullStr] = true;
295
                }
296
            }
297
        }
298
299
        return $data;
300
    }
301
302
    /**
303
     * Returns the first instance of a given component on the site.
304
     *
305
     * @param string $component The component name
306
     * @return array NAP array of the first component instance found
307
     */
308 6
    public static function find_node_by_component($component)
309
    {
310 6
        static $cache = [];
311
312 6
        if (!array_key_exists($component, $cache)) {
313 2
            $cache[$component] = null;
314
315 2
            $nap = new midcom_helper_nav;
316 2
            $node_id = $nap->get_root_node();
317 2
            $root_node = $nap->get_node($node_id);
318
319 2
            if ($root_node[MIDCOM_NAV_COMPONENT] == $component) {
320
                $cache[$component] = $root_node;
321
            } else {
322 2
                $qb = midcom_db_topic::new_query_builder();
323 2
                $qb->add_constraint('component', '=', $component);
324 2
                $qb->add_constraint('name', '<>', '');
325 2
                $qb->add_constraint('up', 'INTREE', $node_id);
326 2
                $qb->set_limit(1);
327 2
                $topics = $qb->execute();
328
329 2
                if (count($topics) === 1) {
330 1
                    $cache[$component] = $nap->get_node($topics[0]->id);
331
                }
332
            }
333
        }
334
335 6
        return $cache[$component];
336
    }
337
338
    /**
339
     * Get the content of the element by the passed element name.
340
     * Tries to resolve path according to theme-name & page
341
     *
342
     * @param string $element_name
343
     */
344 202
    public static function get_element_content($element_name)
345
    {
346 202
        $theme = midcom::get()->config->get('theme');
347 202
        $path_array = explode('/', $theme);
348
349
        //get the page if there is one
350 202
        $page = midcom_connection::get_url('page_style');
351 202
        $substyle = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_SUBSTYLE);
352
        //check if we have elements for the sub-styles
353 202
        while (!empty($path_array)) {
354 202
            $theme_path = implode('/', $path_array);
355 202
            $candidates = [];
356 202
            if ($substyle) {
357 31
                $candidates[] = '/' . $substyle . '/' . $element_name;
358
            }
359 202
            if ($page) {
360
                $candidates[] = $page . '/' . $element_name;
361
            }
362 202
            $candidates[] = '/' . $element_name;
363
364 202
            foreach ($candidates as $candidate) {
365 202
                $filename = OPENPSA2_THEME_ROOT . $theme_path . '/style' . $candidate . '.php';
366 202
                if (file_exists($filename)) {
367 12
                    return file_get_contents($filename);
368
                }
369
            }
370
371
            //remove last theme part
372 202
            array_pop($path_array);
373
        }
374
375 202
        return false;
376
    }
377
}
378