Passed
Push — main ( 46cb87...b800f4 )
by Marc
18:17 queued 13:49
created

exception_handler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php declare(strict_types=1);
2
3
use html_go\exceptions\InternalException;
4
use html_go\i18n\I18n;
5
use html_go\indexing\IndexManager;
6
use html_go\markdown\MarkdownParser;
7
use html_go\markdown\ParsedownParser;
8
use html_go\model\Config;
9
use html_go\model\ModelFactory;
10
use html_go\templating\TemplateEngine;
11
use html_go\templating\TwigTemplateEngine;
12
13
/**
14
 * Returns an multi-dimensional array. The first level is the menu name, the
15
 * second level is an array of stdClass objects each representing a menu node.
16
 * @return array<mixed>
17
 */
18
function get_menus(): array {
19
    return get_index_manager()->getMenusIndex();
20
}
21
22
/**
23
 * Returns the page number from the query string (if there is one).
24
 * @return int Default value is one (1)
25
 */
26
function get_pagination_pagenumber(): int {
27
    $pageNum = 1;
28
    if (($page = get_query_parameter('page')) !== null && \ctype_digit($page)) {
29
        $pageNum = (int)$page;
30
    }
31
    return $pageNum;
32
}
33
34
/**
35
 * Returns a pagination page of tags.
36
 * @param int $pageNum The page number
37
 * @param int $perPage Items per page. Default is 0 (zero) which means return all
38
 * @return array<\stdClass>
39
 */
40
function get_tags(int $pageNum = 1, int $perPage = 0): array {
41
    $tags = get_index_manager()->getTagIndex();
42
    if ($perPage > 0) {
43
        $tags = \array_slice($tags, ($pageNum - 1) * $perPage, $perPage);
44
    }
45
    return get_model_list_from_index($tags);
46
}
47
48
/**
49
 * Returns a pagination page of categories.
50
 * @param int $pageNum The page number
51
 * @param int $perPage Items per page. Default is 0 (zero) which means return all
52
 * @return array<\stdClass> The resulting list of posts
53
 */
54
function get_categories(int $pageNum = 1, int $perPage = 0): array {
55
    $cats = get_index_manager()->getCategoriesIndex();
56
    if ($perPage > 0) {
57
        $cats = \array_slice($cats, ($pageNum - 1) * $perPage, $perPage);
58
    }
59
    return get_model_list_from_index($cats);
60
}
61
62
/**
63
 * Takes an array of index objects (<code>stdClass</code>) and converts them to an array of
64
 * <code>stdClass</code> content objects.
65
 * @param array<string, \stdClass> $indexList
66
 * @return array<\stdClass>
67
 */
68
function get_model_list_from_index(array $indexList): array {
69
    $list = [];
70
    $factory = get_model_factory();
71
    foreach ($indexList as $obj) {
72
        $list[] = $factory->createContentObject($obj);
73
    }
74
    return $list;
75
}
76
77
/**
78
 * Takes an array of URIs (<code>string</code>) and translates them to an array of
79
 * <code>stdClass</code> content objects.
80
 * @param array<string> $uris
81
 * @return array<\stdClass>
82
 */
83
function get_model_list_from_uris(array $uris): array {
84
    $manager = get_index_manager();
85
    $factory = get_model_factory();
86
    $list = [];
87
    foreach ($uris as $slug) {
88
        $indexElement = $manager->getElementFromSlugIndex($slug);
89
        $list[] = $factory->createContentObject($indexElement);
90
    }
91
    return $list;
92
}
93
94
/**
95
 * Build and return the template context.
96
 * @param \stdClass $content
97
 * @return array<mixed>
98
 */
99
function get_template_context(\stdClass $content): array {
100
    $template = DEFAULT_TEMPLATE;
101
    if (isset($content->template)) {
102
        $template = $content->template;
103
    }
104
    return [
105
        'widgets' => get_widgets(),
106
        'i18n' => get_i18n(),
107
        'content' => $content,
108
        TEMPLATE_TPLVAR_KEY => $template
109
    ];
110
}
111
112
/**
113
 * Return the <code>i18n</code> instance.
114
 * @return I18n
115
 */
116
function get_i18n(): I18n {
117
    static $object = null;
118
    if (empty($object)) {
119
        $object = new I18n(LANG_ROOT.DS.get_config()->getString(Config::KEY_LANG).'.messages.php');
120
    }
121
    return $object;
122
}
123
124
/**
125
 * Render the given template placing the given variables into the template context.
126
 * Note: template defined in the front matter of the content file takes precendence.
127
 * @param array<mixed> $vars
128
 * @return string
129
 */
130
function render(array $vars = []): string {
131
    $tpl = SINGLE_TEMPLATE;
132
    // Front matter from content file takes precendence
133
    if (isset($vars[TEMPLATE_TPLVAR_KEY])) {
134
        $tpl = $vars[TEMPLATE_TPLVAR_KEY];
135
    }
136
    return get_template_engine()->render($tpl, $vars);
137
}
138
139
/**
140
 * Helper function doing a redirect to the 404 page.
141
 */
142
function not_found(): void {
143
    header('Location: '.get_config()->getString(Config::KEY_SITE_URL).FWD_SLASH.'not-found');
144
}
145
146
/**
147
 * Get the configured template engine.
148
 * @return TemplateEngine
149
 */
150
function get_template_engine(): TemplateEngine {
151
    static $engine = null;
152
    if (empty($engine)) {
153
        $themeName = get_config()->getString(Config::KEY_THEME_NAME);
154
        $adminThemeName = get_config()->getString(Config::KEY_ADMIN_THEME_NAME);
155
        $engineName = get_config()->getString(Config::KEY_TPL_ENGINE);
156
157
        switch ($engineName) {
158
            case 'twig':
159
                $engine = build_twig_template_engine($themeName, $adminThemeName);
160
                break;
161
            case 'smarty': // @codeCoverageIgnore
162
            case 'php': // @codeCoverageIgnore
163
                throw new InternalException("Implement template engine [$engineName]"); // @codeCoverageIgnore
164
            default:
165
                throw new \InvalidArgumentException("Unsupported template engine [$engineName]"); // @codeCoverageIgnore
166
        }
167
    }
168
    return $engine;
169
}
170
171
/**
172
 * Build the twig template engine.
173
 * @param string $themeName The name of the configure theme
174
 * @param string $adminThemeName
175
 * @return TemplateEngine
176
 */
177
function build_twig_template_engine(string $themeName, string $adminThemeName): TemplateEngine {
178
    $caching = false;
179
    if (get_config()->getBool(Config::KEY_TPL_CACHING)) {
180
        $caching = TEMPLATE_CACHE_ROOT;
181
    }
182
    $strict = get_config()->getBool(Config::KEY_TPL_STRICT_VARS_TWIG);
183
    $options = [
184
        'cache' => $caching,
185
        'strict_variables' => $strict
186
    ];
187
    $templateDirs = [THEMES_ROOT.DS.'twig'.DS.$themeName, ADMIN_THEMES_ROOT.DS.$adminThemeName];
188
    return new TwigTemplateEngine($templateDirs, $options);
189
}
190
191
/**
192
 * Returns the instance of the <code>IndexManager</code>.
193
 * @return IndexManager
194
 */
195
function get_index_manager(): IndexManager {
196
    static $manager = null;
197
    if ($manager === null) {
198
        $manager = new IndexManager(APP_ROOT);
199
    }
200
    return $manager;
201
}
202
203
/**
204
 * Return the instance of the <code>Config</code>.
205
 * @return Config
206
 */
207
function get_config(): Config {
208
    static $config = null;
209
    if (empty($config)) {
210
        $config = new Config(CONFIG_ROOT);
211
    }
212
    return $config;
213
}
214
215
/**
216
 * Returns the instance of the <code>ModelFactory</code>.
217
 * @return ModelFactory
218
 */
219
function get_model_factory(): ModelFactory {
220
    static $factory = null;
221
    if (empty($factory)) {
222
        $factory = new ModelFactory(get_config(), get_markdown_parser(), get_index_manager());
223
    }
224
    return $factory;
225
}
226
227
/**
228
 * Returns the instance of the <code>MarkdownParser</code>.
229
 * @return MarkdownParser
230
 */
231
function get_markdown_parser(): MarkdownParser {
232
    static $parser = null;
233
    if (empty($parser)) {
234
        $parser = new ParsedownParser();
235
    }
236
    return $parser;
237
}
238
239
/**
240
 * Get a <code>Content</code> object (if any) associated with the given slug.
241
 * @param string $slug
242
 * @param array<\stdClass> $list List of <code>Content</code> for this <code>Content</code> object.
243
 * @param string $template
244
 * @return \stdClass|NULL if no content was found associated with the given slug <code>null</code> is returned.
245
 */
246
function get_content_object(string $slug, array $list = [], string $template = DEFAULT_TEMPLATE): ?\stdClass {
247
    $manager = get_index_manager();
248
    if ($manager->elementExists($slug) === false) {
249
        return null;
250
    }
251
    $content = get_model_factory()->createContentObject($manager->getElementFromSlugIndex($slug));
252
    if (empty($list) === false) {
253
        $content->list = $list;
254
    }
255
    $content->menus = get_menus();
256
    if ($content !== null && empty($content->template)) {
257
        $content->template = $template;
258
    }
259
    return $content;
260
}
261
262
/**
263
 * Returns a pagination page of posts.
264
 * @param int $pageNum The page number
265
 * @param int $perPage Items per page
266
 * @return array<\stdClass> The resulting list of posts
267
 */
268
function get_posts(int $pageNum = 1, int $perPage = 5): array {
269
    $posts = get_index_manager()->getPostsIndex();
270
    $posts = \array_slice($posts, ($pageNum - 1) * $perPage, $perPage);
271
    $list = [];
272
    $factory = get_model_factory();
273
    foreach ($posts as $post) {
274
        $list[] = $factory->createContentObject($post);
275
    }
276
    return $list;
277
}
278
279
/**
280
 * Checks if the given slug exists in the index manager (alias for <code>IndexManager::elementExists()</code>).
281
 * @param string $slug
282
 * @return bool
283
 */
284
function slug_exists(string $slug): bool {
285
    return get_index_manager()->elementExists($slug);
286
}
287
288
/**
289
 * Returns a list of widgets
290
 * @return array<mixed>
291
 */
292
function get_widgets(): array {
293
    return [
294
        'recent_posts' => get_posts(),
295
        'category_list' => get_categories(),
296
        'tag_list' => get_tags()
297
    ];
298
}
299
300
/**
301
 * Returns an array of post objects for the given section URI.
302
 * @param string $section
303
 * @param string $uri
304
 * @param int $pageNum
305
 * @param int $perPage
306
 * @throws InternalException
307
 * @throws \InvalidArgumentException
308
 * @return array<\stdClass>
309
 */
310
function get_posts_for_section(string $section, string $uri, int $pageNum = 1, int $perPage = 5): array {
311
    $manager = get_index_manager();
312
    if ($manager->elementExists($uri) === false) {
313
        throw new InternalException("Element does not exist [$uri]");
314
    }
315
316
    switch ($section) {
317
        case CATEGORY_SECTION:
318
            $posts = $manager->getPostsForCategoryIndex()[$uri];
319
            break;
320
        case TAG_SECTION:
321
            $posts = $manager->getPostsForTagIndex()[$uri];
322
            break;
323
        default:
324
            throw new \InvalidArgumentException("Unsupported section [$section]");
325
    }
326
327
    $posts = \array_slice($posts, ($pageNum - 1) * $perPage, $perPage);
328
    return get_model_list_from_uris($posts);
329
}
330
331
function exception_handler(mixed $exception): void {
332
    echo $exception->getMessage();
333
}