Anatomia de l'index.php: totes les funcions explicades
Sense categoria 01/06/2026

Anatomia de l'index.php: totes les funcions explicades

Mapa complet del gestor de blog monolític: autenticació, MySQL, posts auto-generats, CRUD amb categories i etiquetes, galeries people, flash d'èxit i navegació Apache.

Aquest index.php és alhora el panell d'administració i la web pública del blog. Combina backend PHP + MySQL amb frontend Tailwind, Alpine.js, Fancybox i Clipboard.js, tot en un sol fitxer d'uns 2.900 línies.

La constant EXPLORER_SCRIPT (nom del fitxer actual) s'utilitza als formularis i redireccions perquè el codi sigui portable si es desplaça o es renombra.

1. Autenticació i sessió

  • admin_auth_config() — Llegeix credencials d'administració des de variables d'entorn (BLOG_ADMIN_USER, BLOG_ADMIN_PASS_HASH, BLOG_ADMIN_PASS). Si no n'hi ha cap, usa admin / admin123 (només desenvolupament local).
  • authenticate_project_user() — Autentica contra la taula users (Laravel). Permet login per email o name amb password_verify().
  • user_is_identified() — Comprova indicadors de sessió (authenticated, user_id, logged_in…) i capçaleres HTTP per decidir si l'usuari pot editar.
  • inc/portal-auth.php — Autenticació compartida amb altres mòduls del portal; gestiona portal_safe_return_url() per redirigir després del login.

Accions POST: login (primer prova taula users, després fallback admin) i logout (destrueix la sessió).

2. Connexió a la base de dades

posts_adk_connect() obre una connexió mysqli a posts_adk (taules b_*) amb charset utf8mb4. Retorna null si falla.

function posts_adk_connect(): ?mysqli
{
    $mysqli = @new mysqli('localhost', 'joan', '****', 'posts_adk');
    if ($mysqli->connect_error) {
        return null;
    }
    $mysqli->set_charset('utf8mb4');
    return $mysqli;
}

3. Missatges flash (avisos d'èxit)

  • blog_flash_redirect($message, $type) — Desa un missatge a $_SESSION['blog_flash'] i redirigeix a l'index. S'usa després de crear, editar o eliminar posts.
  • blog_consume_flash() — Llegeix i esborra el missatge de la sessió en carregar la pàgina.

El frontend mostra un toast verd (èxit) o vermell (error) durant 5 segons. Les dades del flash viuen en un <script type="application/json"> separat per no trencar l'atribut x-data d'Alpine.js.

4. Auto-generació de contingut (s'executa en carregar)

Aquestes funcions ensure_*_post() mantenen posts de documentació sincronitzats amb el codi. En actualitzar un post existent, només es refresquen excerpt, content i imgno es sobreescriu la categoria ni les etiquetes que l'usuari hagi triat manualment.

  • sync_people_folders_to_posts() — Escaneja img/people/ i crea un post per cada carpeta nova (només títol).
  • ensure_db_connection_guide_post() — Guia de connexió MySQL amb PHP.
  • ensure_bootstrap5_guide_post() — Post Bootstrap 5 amb plantilla CDN.
  • ensure_lluis_llach_post() — Post dedicat a Lluís Llach.
  • ensure_lm_studio_qwen_post() — Post sobre LM Studio i Qwen.
  • ensure_apache_windows_stack_post() — Documentació Apache + PHP + MySQL + phpMyAdmin a Windows.
  • ensure_nitida_studio_portal_post() — Documentació del portal Nítida Studio.
  • ensure_proxmox_deepseek_web_project_post() — Projecte Proxmox + Ubuntu + DeepSeek.
  • ensure_obsidian_second_brain_post() — Obsidian i segon cervell en Markdown local.
  • ensure_notebooklm_post() — Post sobre NotebookLM (Google).
  • ensure_wordpress_novacompt_169_post() — Demo NovaCompt al CT 105 (192.168.1.169).
  • ensure_powershell_ia_laravel_post() — PowerShell 7, CMD i IA per Laravel local.
  • ensure_n8n_proxmox_lxc_post() — n8n en LXC Proxmox (tteck) i API Token.
  • ensure_index_functions_post()Aquest mateix post.

5. Etiquetes i categories

  • ensure_post_tags_by_names() — Assigna etiquetes a un post per nom; crea etiquetes noves a b_etiquetas si cal.
  • syncPostTags() — Sincronitza etiquetes des del formulari (checkboxes + camp «nova etiqueta») a b_post_etiquetas.
  • resolvePostCategoryId() — Resol la categoria del post: si s'omple «nova categoria» la crea o la busca; si no, usa la del desplegable.
  • post_scalar() — Llegeix un valor escalar del POST de forma segura (si arriba com a array, agafa l'últim).

Al formulari d'edició es pot triar categoria existent, crear-ne una de nova, i afegir etiquetes existents o noves.

6. Utilitats d'escapat i imatges

  • h() — Drecera de htmlspecialchars(ENT_QUOTES, UTF-8) per evitar XSS.
  • posts_img_url() — Converteix el nom desat a la BBDD en URL pública sota img/. Accepta URLs absolutes i rutes que ja comencen per img/.
  • posts_img_url_versioned() — Afegeix ?v= (timestamp del fitxer o updated_at) per invalidar la caché del navegador.
// A la BBDD només es guarda el nom: "index.jpg".
// posts_img_url() hi afegeix el prefix "img/" per construir la URL pública.
function posts_img_url(?string $path): string
{
    $path = trim((string) $path);
    if ($path === '') return '';
    if (preg_match('#^https?://#i', $path)) return $path;
    if (stripos($path, 'img/') === 0) return $path;
    return 'img/' . ltrim($path, '/');
}

Regla clau: a b_posts.img només es desa el nom del fitxer (p. ex. index.jpg), mai la ruta completa.

7. Galeries de img/people

  • post_people_public_url() — URL pública segura sota img/people/{carpeta}/…, sense permetre ...
  • post_people_gallery_for_post() — Cerca la carpeta de img/people que coincideix amb el títol del post (diverses normalitzacions) i recull imatges, vídeos, àudios i PDFs de forma recursiva. Exclou _thumbnails.

La galeria es carrega via AJAX amb ?action=get_post_details quan s'obre un post.

8. CRUD de posts

Controladors segons $_POST['action'] o $_GET['action']:

AccióMètodeFunció
create_postPOSTInsereix a b_posts + etiquetes
update_postPOSTActualitza post + re-sincronitza etiquetes
delete_postPOSTEsborra post i relacions d'etiquetes
get_post_detailsGET (JSON)Retorna galeria people i llibres associats

Funcions de suport:

  • plainTextToHtml() — Converteix text pla en HTML amb paràgrafs; respecta HTML existent si detecta etiquetes.
  • saveUploadedPostImage() — Puja imatges (jpg, png, gif, webp, bmp, avif) a img/ i retorna només el nom del fitxer.

Després de cada operació CRUD exitosa es crida blog_flash_redirect() amb un missatge d'èxit.

9. Càrrega de dades per a la vista

Abans de pintar l'HTML es consulten:

  • b_categories — Llista per al desplegable de categories (renderitzat en PHP, no Alpine).
  • b_etiquetas — Totes les etiquetes per al formulari.
  • b_posts + JOIN amb categories i etiquetes — GROUP_CONCAT per agrupar etiquetes per post.

Les dades es passen a Alpine.js com a JSON escapat dins x-data. El filtre per categoria, etiqueta i cerca és reactiu al client.

10. Navegació de projectes del servidor

  • nav_entry_skip() — Amaga vendor, node_modules, .git.
  • is_laravel_project() — Detecta Laravel (artisan, public/index.php, laravel/framework).
  • nav_laravel_test_url() — Genera http://{carpeta}.test/.
  • nav_vhost_entries() — Llegeix httpd-vhosts.conf per construir el menú de VirtualHosts i Alias locals.

11. Frontend (Alpine.js)

El cos de la pàgina usa un únic bloc x-data amb:

  • filteredPosts — Filtre per categoria, etiqueta i text.
  • selectedPost — Detall d'un article.
  • editingPost — Modal de crear/editar amb sincronització de categoria (syncCategorySelect, syncCategoryBeforeSubmit).
  • flashNotice — Toast d'èxit/error carregat des del JSON de sessió.
  • initCodeCopy() — Botons «Copiar» als blocs de codi amb Clipboard.js.

12. Taules MySQL principals

TaulaÚs
b_postsArticles del blog
b_categoriesCategories (amb camp svg opcional)
b_etiquetasEtiquetes
b_post_etiquetasRelació N:N post ↔ etiqueta
b_books + b_post_booksLlibres associats a posts (detall AJAX)
usersUsuaris Laravel per al login

Resum

index.php és un mini-CMS autocontingut: autentica usuaris, genera documentació automàtica, ofereix CRUD complet amb categories, etiquetes i imatges, mostra avisos flash, explora projectes Apache i enriqueix posts «people» amb galeries multimèdia. Tot en un fitxer, sense framework PHP, però amb patrons clars de separació per funcions.

Enllaços d'interès

Relacionats

No hi ha posts relacionats en aquesta categoria.