Routing
Every unknown URL is rewritten to index.php by the root .htaccess. parseRequestUri() (in core/data-functions.php) then turns the URI into a routing decision.
parseRequestUri() reads $GLOBALS['data']['page'] directly and its position in the bootstrap order is fixed — index.php also writes its results back into $_GET. Treat $_GET as untrusted in this request cycle regardless of where a value appears to originate.
parseRequestUri(): array
Reads the url_slug_* values from the active front-end locale, splits the URI into segments, and applies:
| Segments | Pattern | Result |
|---|---|---|
| (empty) | — | Homepage |
{plural_slug}/ | e.g. articles/ | Content-type list page |
{cat_slug}/ | 2+ segments, prefix = category slug | Category page |
{tag_slug}/{name}/ | — | Tag page |
{type_slug}/{slug}/ | Article or page, no category | Single content item |
{type_slug}/{cat…}/{slug}/ | Project with hierarchical category | Single project |
Returns a normalised [type, slug, category, tag] array.
Localized URL slugs
"url_slug_article": "article",
"url_slug_articles": "articles",
"url_slug_project": "project",
"url_slug_projects": "projects",
"url_slug_page": "page",
"url_slug_pages": "pages",
"url_slug_category": "category",
"url_slug_tag": "tag"
These same keys drive cleanUrl() (URL generation), parseRequestUri() (parsing), and admin content links — one source of truth. Settings → Reading can rename content types for display, but the URL segments themselves still come from these locale keys.
Bootstrap load order
index.php
├── core/llms.php or llms-full.php ← short-circuits before functions.php if ?_llms is set
└── core/functions.php
├── core-functions.php
├── data-functions.php
├── render/theme-api.php → tf-markdown, tf-shortcodes, tf-cards, tf-navigation, tf-page
├── data-layer.php
├── plugin-api.php ← pl_load_active_plugins()
└── lang-cache.php
↓
loadConfig()
sl_build_data_array(['article','page','project'], false) → $GLOBALS['data']
↓
parseRequestUri() → routing
↓
if single item: sl_load_item_by_slug() → $data[$type] = [$fullItem]
↓
processContent() / renderCategoryPage() / renderTagPage()
↓
loadThemeTemplate('header', …) → echo $pageContent → loadThemeTemplate('footer', …)
Plugin hooks fire at two points in this cycle: early_request right after functions.php finishes loading (before routing or output — for blocking/redirecting the whole request), and after_routing once the route is resolved, with a flag for whether it's a genuine 404.
