declare(strict_types=1);/** * Dostępne kody języków */const SUPPORTED_LANGS = ['pl', 'en'];/** * Domyślny język */const DEFAULT_LANG = 'pl';/** * Mapowanie tras per język */$routes = [ 'pl' => [ 'sklep' => [ 'url' => 'https://shoppawelpiechnik.com', 'title' => 'Sklep', 'menu' => true, 'mini_menu' => false, 'target' => '_blank', ], 'start' => [ 'file' => 'pl/start.php', 'title' => 'Paweł Piechnik', 'menu' => false, 'mini_menu' => false, ], 'komiksy' => [ 'file' => 'pl/komiksy.php', 'title' => 'Komiksy', 'menu' => true, 'mini_menu' => true, 'children' => [ 'chleb-wolnosciowy-tom-1' => ['file' => 'pl/chleb-wolnosciowy-tom-1.php', 'title' => 'Chleb wolnościowy – tom 1'], 'chleb-wolnosciowy-tom-2' => ['file' => 'pl/chleb-wolnosciowy-tom-2.php', 'title' => 'Chleb wolnościowy – tom 2'], 'sybir-tom-1' => ['file' => 'pl/sybir-tom-1.php', 'title' => 'Sybir, tom 1'], 'sybir-tom-2' => ['file' => 'pl/sybir-tom-2.php', 'title' => 'Sybir, tom 2'], ], ], 'murale' => [ 'file' => 'pl/murale.php', 'title' => 'Murale', 'menu' => true, 'mini_menu' => true, 'children' => [ '1000-lat' => ['file' => 'pl/1000-lat.php', 'title' => '1000 lat'], ], ], 'grafiki' => [ 'file' => 'pl/grafiki.php', 'title' => 'Grafiki', 'menu' => true, 'mini_menu' => true, 'children' => [ 'dziadek' => ['file' => 'pl/dziadek.php', 'title' => 'Dziadek'], 'jelen' => ['file' => 'pl/jelen.php', 'title' => 'Jeleń'], 'meble' => ['file' => 'pl/meble.php', 'title' => 'Meble'], 'mewy' => ['file' => 'pl/mewy.php', 'title' => 'Mewy'], 'samuraj-i' => ['file' => 'pl/samuraj-i.php', 'title' => 'Samuraj I'], 'samuraj-ii' => ['file' => 'pl/samuraj-ii.php', 'title' => 'Samuraj II'], ], ], 'assety' => [ 'file' => 'pl/assety.php', 'title' => 'Assety', 'menu' => true, 'mini_menu' => true, ], 'storyboardy' => [ 'file' => 'pl/storyboardy.php', 'title' => 'Storyboardy', 'menu' => true, 'mini_menu' => true, 'children' => [ 'the-liberator' => ['file' => 'pl/the-liberator.php', 'title' => 'The Liberator'], 'tom-clancys-the-division-2' => ['file' => 'pl/tom-clancys-the-division-2.php', 'title' => 'Tom Clancy\'s The Division 2'], 'jeszcze-dzien-zycia' => ['file' => 'pl/jeszcze-dzien-zycia.php', 'title' => 'Jeszcze dzień życia'], 'total-war-three-kingdoms' => ['file' => 'pl/total-war-three-kingdoms.php', 'title' => 'Total War - Three Kingdoms'], 'for-honor-2' => ['file' => 'pl/for-honor-2.php', 'title' => 'For Honor Marching Fire'], 'sniper-3' => ['file' => 'pl/sniper-3.php', 'title' => 'Snajper 3'], 'warhammer-total-war-ii' => ['file' => 'pl/warhammer-total-war-ii.php', 'title' => 'Warhammer Total War II'], 'dead-island-survivors' => ['file' => 'pl/dead-island-survivors.php', 'title' => 'Dead Island Survivors'], 'wiedzmin-3' => ['file' => 'pl/wiedzmin-3.php', 'title' => 'Wiedźmin 3 Dziki Gon'], 'trojan-horse' => ['file' => 'pl/trojan-horse.php', 'title' => 'Trojan horse was a unicorn'], 'hero-and-the-message' => ['file' => 'pl/hero-and-the-message.php', 'title' => 'Hero and the message'], 'risen-iii-titan-lords' => ['file' => 'pl/risen-iii-titan-lords.php', 'title' => 'Risen III: Titan Lords'], ], ], 'o-mnie' => [ 'file' => 'pl/o-mnie.php', 'title' => 'O mnie', 'menu' => true, 'mini_menu' => false, ], ], 'en' => [ 'shop' => [ 'url' => 'https://shoppawelpiechnik.com', 'title' => 'Shop', 'menu' => true, 'mini_menu' => false, 'target' => '_blank', ], 'start' => [ 'file' => 'en/start.php', 'title' => 'Paweł Piechnik', 'menu' => false, 'mini_menu' => false, ], 'comics' => [ 'file' => 'en/comics.php', 'title' => 'Comics', 'menu' => true, 'mini_menu' => true, 'children' => [ 'bread-of-freedom-1' => ['file' => 'en/bread-of-freedom-vol-1.php', 'title' => 'Bread of Freedom – vol. 1'], 'bread-of-freedom-2' => ['file' => 'en/bread-of-freedom-vol-2.php', 'title' => 'Bread of Freedom – vol. 2'], 'siberia-vol-1' => ['file' => 'en/siberia-vol-1.php', 'title' => 'Siberia, vol. 1'], 'siberia-vol-2' => ['file' => 'en/siberia-vol-2.php', 'title' => 'Siberia, vol. 2'], ], ], 'murals' => [ 'file' => 'en/murals.php', 'title' => 'Murals', 'menu' => true, 'mini_menu' => true, 'children' => [ '1000-years' => ['file' => 'en/1000-years.php', 'title' => '1000 years'], ], ], 'illustrations' => [ 'file' => 'en/illustrations.php', 'title' => 'Illustrations', 'menu' => true, 'mini_menu' => true, 'children' => [ 'grandpa' => ['file' => 'en/grandpa.php', 'title' => 'Grandpa'], 'deer' => ['file' => 'en/deer.php', 'title' => 'Deer'], 'furniture' => ['file' => 'en/furniture.php', 'title' => 'Furniture'], 'seagulls' => ['file' => 'en/seagulls.php', 'title' => 'Seagulls'], 'samurai-i' => ['file' => 'en/samurai-i.php', 'title' => 'Samurai I'], 'samurai-ii' => ['file' => 'en/samurai-ii.php', 'title' => 'Samurai II'], ], ], 'assets' => [ 'file' => 'en/assets.php', 'title' => 'Assets', 'menu' => true, 'mini_menu' => true, ], 'storyboards' => [ 'file' => 'en/storyboards.php', 'title' => 'Storyboards', 'menu' => true, 'mini_menu' => true, 'children' => [ 'another-day-of-life' => ['file' => 'en/another-day-of-life.php', 'title' => 'Another Day of Life'], 'risen-iii-titan-lords' => ['file' => 'en/risen-iii-titan-lords.php', 'title' => 'Risen III: Titan Lords'], 'warhammer-total-war-ii' => ['file' => 'en/warhammer-total-war-ii.php', 'title' => 'Warhammer Total War II'], 'hero-and-the-message' => ['file' => 'en/hero-and-the-message.php', 'title' => 'Hero and the message'], 'dead-island-survivors' => ['file' => 'en/dead-island-survivors.php', 'title' => 'Dead Island Survivors'], 'total-war-three-kingdoms' => ['file' => 'en/total-war-three-kingdoms.php', 'title' => 'Total War - Three Kingdoms'], 'tom-clancys-the-division-2' => ['file' => 'en/tom-clancys-the-division-2.php', 'title' => 'Tom Clancy\'s The Division 2'], 'for-honor-2' => ['file' => 'en/for-honor-2.php', 'title' => 'For Honor 2'], ], ], 'about-me' => [ 'file' => 'en/about-me.php', 'title' => 'About me', 'menu' => true, 'mini_menu' => false, ], ],];/** * stały sufix do tytułów */const SITE_TITLE_SUFFIX = ' – Paweł Piechnik';/** * Base path */$basePath = rtrim(str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '')), '/');if ($basePath === '.') { $basePath = ''; }/** * Aktualny język – będzie dostępny globalnie (np. w podstronach) * Domyślnie PL, nadpisany po sparsowaniu URL * * @var string $currentLang */$currentLang = DEFAULT_LANG;/** * Generowanie URL: * url('komiksy') -> /pl/komiksy (lub /en/comics, jeśli $currentLang = 'en') * url('', 'en') -> /en */function url(string $path = '', ?string $lang = null): string { global $basePath, $currentLang; $lang = $lang ?? $currentLang ?? DEFAULT_LANG; $prefix = $basePath === '' ? '' : $basePath; $path = ltrim($path, '/'); if ($path === '') { // tylko język, np. /pl $full = $lang; } else { // język + slug, np. /pl/komiksy $full = $lang . '/' . $path; } return $prefix . '/' . $full;}/** * Slug + język z REQUEST_URI – obcinamy query string i basePath */$requestUri = $_SERVER['REQUEST_URI'] ?? '/';$uriPath = parse_url($requestUri, PHP_URL_PATH) ?? '/';if ($basePath !== '' && strpos($uriPath, $basePath) === 0) { $uriPath = substr($uriPath, strlen($basePath));}// np. "" (root), "pl/komiksy", "en/comics"$trimmed = trim($uriPath, '/');$segments = $trimmed === '' ? [] : explode('/', $trimmed);// Czy URL ma prefiks języka?$hasLangPrefixInUrl = isset($segments[0]) && in_array($segments[0], SUPPORTED_LANGS, true);if ($hasLangPrefixInUrl) { $currentLang = $segments[0]; $slug = $segments[1] ?? '';} else { // Brak prefiksu – traktujemy to jako domyślny język (PL), // ale zaraz przekierujemy na kanoniczny adres /pl/... $currentLang = DEFAULT_LANG; $slug = $segments[0] ?? '';}/** * Domyślna startowa */if ($slug === '') { $slug = 'start';}/** * Trailing slash + kanonizacja: * - / -> /pl/ * - /start -> /pl/ * - /komiksy -> /pl/komiksy/ * - /pl -> /pl/ * - /pl/start/ -> /pl/ * - /en/comics -> /en/comics/ */$hasTrailingSlash = substr($uriPath, -1) === '/';$qs = parse_url($requestUri, PHP_URL_QUERY) ?: '';$redirectTo = null;// 1) Brak prefiksu języka – przekieruj na domyślny PL z prefiksemif (!$hasLangPrefixInUrl) { if ($slug === 'start') { $redirectTo = url('', $currentLang) . '/'; // /pl/ } else { $redirectTo = url($slug, $currentLang) . '/'; // /pl/komiksy/ }}// 2) Jest prefiks, ale brak slasha na końcu (oprócz ewentualnych parametrów)elseif (!$hasTrailingSlash) { if ($slug === 'start') { $redirectTo = url('', $currentLang) . '/'; // /pl/ lub /en/ } else { $redirectTo = url($slug, $currentLang) . '/'; // /pl/komiksy/ }}// 3) Start z językiem, ale z "start" w ścieżce – kanonicznie /pl/ albo /en/elseif ($slug === 'start' && $uriPath !== '/' . $currentLang . '/') { $redirectTo = url('', $currentLang) . '/';}if ($redirectTo !== null) { if ($qs !== '') { $redirectTo .= '?' . $qs; } header('Location: ' . $redirectTo, true, 301); exit;}/** * Wyznacz bieżącą trasę i tytuł */$pagesDir = __DIR__ . '/pages/';// Funkcja pomocnicza do szukania trasy w zagnieżdżonej tablicyfunction findRoute(array $routes, string $slug): ?array { if (isset($routes[$slug])) { return $routes[$slug]; } foreach ($routes as $key => $data) { if (isset($data['children']) && is_array($data['children'])) { $found = findRoute($data['children'], $slug); if ($found) { return $found; } } } return null;}$currentRoute = findRoute($routes[$currentLang], $slug);$pageFile = $currentRoute['file'] ?? null;/** * 404 – brak dopasowania lub brak pliku */http_response_code(200);if ($pageFile === null || !is_file($pagesDir . $pageFile)) { http_response_code(404); $pageFile = is_file($pagesDir . '404.php') ? '404.php' : null; $pageTitleRaw = ($currentLang === 'en') ? '404 – Not found' : '404 – Nie znaleziono';} else { $pageTitleRaw = $currentRoute['title'] ?? ucfirst(str_replace(['-', '_'], ' ', $slug));}/** * Finalny tytuł */$pageTitle = htmlspecialchars($pageTitleRaw . SITE_TITLE_SUFFIX, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');if ($slug === 'start') { $pageTitle = $pageTitleRaw; }/** * Wspólne nagłówki/stopki * (w podstronach masz dostępne $currentLang oraz np. url('komiksy')) */require __DIR__ .'/' .$currentLang .'.page-header.php';if ($slug !== 'start') { require __DIR__ .'/' .$currentLang .'.layout-header.php';}if ($pageFile) { require $pagesDir . $pageFile;} else { echo "

404

Nie znaleziono strony.

";}if ($slug !== 'start') { require __DIR__ .'/' .$currentLang .'.layout-footer.php';}require __DIR__ .'/' .$currentLang .'.page-footer.php';