Gå til indhold
Jan Engelbrecht Pederseb Logo

Avanceret PHP-Webudvikling

Et komplet kursus fra introduktion til grundlæggende programmering, objektorienteret PHP, databaser og professionel MVC-arkitektur.

Avanceret PHP-Webudvikling

Et komplet kursus fra introduktion til grundlæggende programmering, objektorienteret PHP, databaser og professionel MVC-arkitektur.

Del 1: Introduktion og opsætning (Niveau: Begynder)

Del 2: Grundlæggende PHP-programmering

Del 3: Webinteraktion og formularhåndtering

Del 4: Datahåndtering og databaser (Niveau: Øvet)

Del 5: Objektorienteret PHP (OOP)

Del 6: Professionel udvikling og sikkerhed (Niveau: Avanceret)

Del 7: Arkitektur, API'er og afslutning

Del 1: Introduktion og opsætning

Dette modul klæder dig på til at forstå, hvordan PHP fungerer under motorhjelmen, og hjælper dig med at gøre din egen computer klar til at kode.

Lektion 1: Introduktion til PHP

Hvad er PHP, og hvordan fungerer internettet?
PHP (Hypertext Preprocessor) er et open-source-programmeringssprog, der kører på en webserver (såkaldt server-side). Når en bruger besøger en hjemmeside, sker der følgende:

[Brugerens Browser] ----(1. Anmodning om side)----> [Webserver]
                                                        |
                                                 (2. PHP-koden køres)
                                                        |
[Brugerens Browser] <---(3. Rent HTML/CSS retur)--- [Webserver]
            

Browseren (Chrome, Safari, Edge) forstår ikke PHP-kode. Den forstår kun HTML, CSS og JavaScript. Webserverens opgave er at oversætte PHP-koden til almindelig HTML, før siden sendes tilbage til brugeren.

Statiske vs. dynamiske websider
For at forstå værdien af PHP, skal vi se på forskellen mellem statisk og dynamisk indhold:

Egenskab Statiske sider (Kun HTML/CSS) Dynamiske sider (Med PHP)
IndholdDet samme for alle brugere.Ændrer sig baseret på brugeren eller data.
OpdateringKræver manuel ændring i kildekoden.Sker automatisk via en database.
EksemplerVisittkortsider, simple portfolier.Facebook, webshops, nyhedssider, logins.

PHP's rolle i moderne webudvikling
Selvom PHP er et ældre sprog, driver det i dag over 75% af alle hjemmesider med et kendt serversidesprog. Det er det fundamentale sprog bag store Content Management Systemer (CMS) som WordPress, Joomla og Drupal, samt kraftfulde professionelle frameworks som Laravel.

Lektion 2: Opsætning af udviklingsmiljø

PHP kan ikke køres ved bare at dobbeltklikke på en fil på din computer. Det kræver en lokal webserver.

1. Installation af lokal server (XAMPP)
For at simulere en rigtig webserver på din computer, bruger vi en softwarepakke. XAMPP er gratis og virker på både Windows, Mac og Linux.

  • Trin 1: Download XAMPP fra Apache Friends' officielle hjemmeside.
  • Trin 2: Installer programmet med standardindstillingerne.
  • Trin 3: Åbn XAMPP Control Panel og klik på Start ud for Apache (og MySQL, hvis du skal bruge database senere).

2. Valg af IDE (Kodeeditor)
Du har brug for et godt program at skrive din kode i. Det mest populære og gratis valg er Visual Studio Code (VS Code).

  • Download og installer VS Code.
  • Gå til "Extensions" (udvidelser) i VS Code og søg efter PHP Intelephense for at få kodehjælp og farvefremhævning.

Dit første PHP-script og syntaks

Lektion 3: Dit første PHP-script

Nu skal vi skrive og køre din allerførste PHP-fil.

Trin 1: Find den rigtige mappe
XAMPP kigger kun efter filer i en helt bestemt mappe på din computer. Du skal placere alle dine projekter her:

  • Windows: C:\xampp\htdocs\
  • Mac: /Applications/XAMPP/htdocs/

Opret en ny mappe inde i htdocs, som du kalder kursus.

Trin 2: Skriv koden
Åbn din nye kursus-mappe i VS Code. Opret en fil med navnet index.php. Skriv følgende kode:

<?php
// Dette er et PHP-tag. Al PHP-kode skal stå herinde.

echo "Hello World! Velkommen til mit PHP kursus.";

// PHP-tagget lukkes automatisk i bunden af rene PHP-filer, så ?> er valgfrit.
            

Trin 3: Kør koden i browseren
Åbn din browser (f.eks. Chrome) og skriv følgende i adressefeltet: http://localhost/kursus/
Du vil nu se teksten: Hello World! Velkommen til mit PHP kursus. på skærmen.

Lektion 4: Syntaksregler og fejlfinding

For at undgå fejl i din kode, er der tre gyldne grundregler i PHP, du skal lære med det samme:

1. Semikolon (;)
Hver eneste linje med en kommando i PHP skal afsluttes med et semikolon. Glemmer du det, går siden ned med en fejl.

echo "Dette er rigtigt"; // Korrekt
echo "Dette vil give en fejl" // FEJL! Mangler semikolon
            

2. Kommentarer
Kommentarer er tekst i koden, som webserveren fuldstændig ignorerer. De bruges til at lave noter til dig selv eller andre udviklere.

// Dette er en enkeltlinjet kommentar

/*
   Dette er en kommentar,
   der strækker sig over
   flere linjer.
*/
            

3. Fejlfinding (Debugging)
Hvis du skriver noget forkert, vil PHP vise en fejlmeddelelse i din browser (f.eks. en Parse error eller Syntax error).

  • Læs linjenummeret: Fejlmeddelelsen vil næsten altid fortælle dig præcis, hvilken fil og hvilket linjenummer, fejlen er opstået på.
  • Tjek linjen før: Hvis PHP siger, der er en fejl på linje 10, er det meget ofte fordi, du har glemt et semikolon i bunden af linje 9.

Del 2: Grundlæggende PHP-programmering

Dette modul lærer dig at gemme data, styre programmets logik og genbruge din kode effektivt.

Lektion 5: Variabler, datatyper og operatorer

Variabler og konstanter
En variabel er en container til at gemme information. I PHP starter alle variabler med et dollartegn ($).

  • Variabler er case-sensitive ($navn og $Navn er to forskellige variabler).
  • De skal starte med et bogstav eller en understreg (_), aldrig et tal.
<?php
$fornavn = "Lars"; // En variabel, der kan ændres senere
$alder = 28;

// Konstanter ændres ALDRIG, når de først er defineret
define("PI", 3.14159);
const APPARAT_NAVN = "Web-Server-01";
            

De 4 primære datatyper
PHP finder selv ud af datatypen baseret på værdien, du tildeler (dynamisk typet sprog).

Datatype Forklaring Eksempel
StringTekststreng omsluttet af anførselstegn.$by = "Aarhus";
IntegerHeltal uden decimaler.$antal = 15;
FloatDecimaltal (bruger punktum, ikke komma).$pris = 49.95;
BooleanSandt eller falsk.$erLoggetInd = true;

Operatorer
Bruges til at beregne og sammenligne værdier.

// 1. Aritmetiske (Regning)
$sum = 10 + 5;        // 15
$rest = 10 % 3;       // 1 (Modulus / rest ved division)

// 2. Sammenkædning af tekst (Punktum-operator)
$fuldtNavn = $fornavn . " Jensen"; 

// 3. Sammenligning
$x = 5;
$y = "5";
$test1 = ($x == $y);  // true  (Tjekker kun værdi)
$test2 = ($x === $y); // false (Tjekker både værdi OG datatype)
            

Lektion 6: Kontrolstrukturer og logik

Betingede sætninger (if, else, switch, match)
Betingelser styrer, hvilken kode der skal køres hvornår.

<?php
$alder = 20;

// simple if/else
if ($alder >= 18) {
    echo "Du er myndig.";
} else {
    echo "Du er ikke myndig endnu.";
}

// match (Introduceret i PHP 8 - mere præcis end switch)
$statusKode = 200;
$besked = match ($statusKode) {
    200 => "OK / Succes",
    404 => "Siden blev ikke fundet",
    500 => "Serverfejl",
    default => "Ukendt statuskode",
};
            

Loops (Løkker)
Loops gentager en blok kode, så længe en betingelse er opfyldt.

// For-loop: Bruges når du kender antallet af gentagelser på forhånd
for ($i = 1; $i <= 3; $i++) {
    echo "Nummer: " . $i . "<br>";
}

// While-loop: Kører så længe noget er sandt
$tæller = 0;
while ($tæller < 3) {
    echo "Tæller er: " . $tæller . "<br>";
    $tæller++;
}
            

Arrays og funktioner

Lektion 7: Arrays (Tabeller)

Et array kan indeholde flere værdier i én enkelt variabel.

1. Indekserede arrays (Automatiske talindeks)

<?php
$frugter = ["Æble", "Banan", "Appelsin"];
echo $frugter[0]; // Udskriver: Æble (indeks starter altid ved 0)
            

2. Associative arrays (Nøgle/værdi-par)
Her definerer du selv navngivne "nøgler" (keys) i stedet for tal.

$bruger = [
    "navn" => "Mette",
    "email" => "mette@example.dk",
    "rolle" => "Admin"
];

echo $bruger["email"]; // Udskriver: mette@example.dk
            

Gennemløb af arrays med foreach
Den mest effektive måde at trække data ud af et array på:

foreach ($bruger as $nøgle => $værdi) {
    echo $nøgle . " er: " . $værdi . "<br>";
}
            

Lektion 8: Funktioner

Funktioner er små kodestumper, der udfører en specifik opgave. De forhindrer, at du skal skrive den samme kode mange gange.

Oprettelse af en funktion

<?php
// Funktion uden typer
$momsSats = 0.25;

function beregnMoms($beløb) {
    global $momsSats; // Henter en variabel uden for funktionen ind
    return $beløb * $momsSats;
}

echo beregnMoms(100); // Udskriver: 25
            

Type-hinting og strenge typer (Best Practice)
For at undgå fejl i større systemer, bør du låse datatyperne fast i dine funktioner ved hjælp af strict_types. Det skal stå på den absolut øverste linje i din fil:

<?php
declare(strict_types=1); // Tvinger PHP til at respektere datatyper stramt

function velkomst(string $navn, int $alder): string {
    return "Hej " . $navn . ", du er " . $alder . " år gammel.";
}

// Dette virker perfekt:
echo velkomst("Peter", 34); 

// Dette vil udløse en fatal fejl med strict_types slået til:
// echo velkomst("Peter", "34"); // Fordi "34" er en streng, ikke et heltal
            

Del 3: Webinteraktion og formularhåndtering

Dette modul bygger bro mellem statisk HTML og dynamisk PHP-funktionalitet. Du lærer at modtage, validere og beskytte brugerdata samt håndtere logins.

Lektion 9: PHP og HTML-integration

Når du bygger websider, vil du ofte blande HTML-tags med PHP-logik.

Alternativ syntaks til templates
Når du skal udskrive arrays eller køre betingelser midt i HTML-kode, bliver de traditionelle tuborgklammer ({}) hurtigt uoverskuelige. Derfor bruger man i stedet en renere "skabelonsyntaks", hvor { erstattes af : og } erstattes af f.eks. endif; eller endforeach;.

<?php
$frugter = ["Æble", "Banan", "Appelsin"];
$visListe = true;
?>

<!DOCTYPE html>
<html lang="da">
<head>
    <meta charset="UTF-8">
    <title>Min Frugtbutik</title>
</head>
<body>
    <h1>Velkommen til butikken</h1>

    <!-- Alternativ IF-syntaks -->
    <?php if ($visListe): ?>
        <ul>
            <!-- Alternativ FOREACH-syntaks -->
            <?php foreach ($frugter as $frugt): ?>
                <li><?= htmlspecialchars($frugt) ?></li> 
                <!-- Obs: <?= "tekst" ?> er en genvej til <?php echo "tekst" ?> -->
            <?php endforeach; ?>
        </ul>
    <?php else: ?>
        <p>Der er ingen frugter på lager lige nu.</p>
    <?php endif; ?>
</body>
</html>
            

Lektion 10: Superglobals og formularer

Når en bruger sender data fra en HTML-formular, opsamler PHP disse data i indbyggede globale arrays, kaldet Superglobals. De to vigtigste til formularer er $_GET og $_POST.

Forskellen på GET og POST

Egenskab $_GET $_POST
DatatransportSynligt i browserens URL (side.php?navn=Lars).Skjult i selve HTTP-anmodningen (Request Body).
DatagrænseBegrænset (ca. 2000 tegn i URL).Ingen reel grænse (kan sende store filer).
SikkerhedLav (må aldrig bruges til adgangskoder).Højere (bedre til følsomme data).
BrugsscenarieSøgninger, filtre, paginering.Logins, formularer, oprettelse af brugere.

Sikker modtagelse af formulardata (XSS Beskyttelse)
Du må aldrig stole på data fra en bruger. Hvis en bruger indtaster ondsindet JavaScript i en formular, og du udskriver det direkte på siden, kan din side blive hacket. Dette kaldes Cross-Site Scripting (XSS).
Vi bruger htmlspecialchars() til at desinficere data og filter_var() til at validere data (f.eks. tjekke om en e-mail er ægte).
Her er et komplet, sikkert eksemple på en formular og dens modtagelse (kontakt.php):

<?php
// Initialiser variabler til fejl og succesbeskeder
$fejl = "";
$succes = "";

// Tjek om formularen overhovedet er blevet sendt
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    
    // 1. Modtag og rens data for unødvendige mellemrum
    $navn = trim($_POST["navn"] ?? '');
    $email = trim($_POST["email"] ?? '');

    // 2. Validering
    if (empty($navn)) {
        $fejl = "Du skal udfylde dit navn.";
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $fejl = "Indtast venligst en gyldig e-mailadresse.";
    } else {
        // Data er valide! Nu kan vi behandle dem
        $succes = "Mange tak, " . htmlspecialchars($navn) . ". Vi har modtaget din besked!";
    }
}
?>

<!-- HTML Formular -->
<?php if ($fejl): ?>
    <p style="color: red;"><?= $fejl ?></p>
<?php endif; ?>

<?php if ($succes): ?>
    <p style="color: green;"><?= $succes ?></p>
<?php endif; ?>

<form action="kontakt.php" method="POST">
    <label for="navn">Navn:</label><br>
    <input type="text" id="navn" name="navn" value="<?= isset($navn) ? htmlspecialchars($navn) : '' ?>"><br><br>

    <label for="email">E-mail:</label><br>
    <input type="email" id="email" name="email" value="<?= isset($email) ? htmlspecialchars($email) : '' ?>"><br><br>

    <button type="submit">Send besked</button>
</form>
            

Lektion 11: Sessions og Cookies

HTTP-protokollen er "statsløs". Det betyder, at serveren glemmer, hvem du er, i det sekund den har sendt HTML-siden til din browser. For at en webshop kan huske dine varer i kurven, eller et kontrolpanel kan huske, at du er logget ind, bruger vi Sessions og Cookies.

  • Cookies: Gemmes direkte på brugerens computer (i browseren). Kan manipuleres af brugeren. Godt til "Husk mig"-funktioner eller sprogpræferencer.
  • Sessions: Gemmes sikkert på webserveren. Browseren får kun tildelt et midlertidigt ID (et Sessions-id via en cookie), som matcher serverens data. Slettes automatisk, når browseren lukkes.

Opbygning af et simpelt loginsystem

Her bygger vi et system fordelt over to filer: Loginsiden og den beskyttede administrationsside.

1. login.php (Selve login-formularen)

<?php
session_start(); // SKAL kaldes øverst på alle sider, der bruger sessions

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $brugernavn = $_POST["username"] ?? '';
    $adgangskode = $_POST["password"] ?? '';

    // Statisk tjek (Senere henter vi dette fra en database)
    if ($brugernavn === "admin" && $adgangskode === "hemmelig123") {
        // Login succes: Gem brugerens status i en session
        $_SESSION["logget_ind"] = true;
        $_SESSION["bruger"] = "admin";
        
        // Videresend brugeren til den lukkede side
        header("Location: admin.php");
        exit;
    } else {
        $fejlmeddelelse = "Forkert brugernavn eller adgangskode!";
    }
}
?>

<!DOCTYPE html>
<html>
<body>
    <h2>Login-system</h2>
    <?php if (isset($fejlmeddelelse)): ?>
        <p style="color:red;"><?= $fejlmeddelelse ?></p>
    <?php endif; ?>

    <form method="POST">
        <input type="text" name="username" placeholder="Brugernavn" required><br><br>
        <input type="password" name="password" placeholder="Adgangskode" required><br><br>
        <button type="submit">Log ind</button>
    </form>
</body>
</html>
            

2. admin.php (Den beskyttede side)

<?php
session_start();

// Tjek om brugeren rent faktisk har lov til at se siden
if (!isset($_SESSION["logget_ind"]) || $_SESSION["logget_ind"] !== true) {
    // Hvis ikke, smides de koldt og kontant tilbage til login-siden
    header("Location: login.php");
    exit;
}
?>

<!DOCTYPE html>
<html>
<body>
    <h1>Velkommen til kontrolpanelet, <?= htmlspecialchars($_SESSION["bruger"]) ?>!</h1>
    <p>Dette er tophemmelig information, som kun indloggede brugere kan se.</p>
    
    <!-- Link til at logge ud -->
    <a href="logout.php">Log ud</a>
</body>
</html>
            

3. logout.php (Nulstilling af sessionen)

<?php
session_start();
session_unset();   // Tømmer alle sessions-variabler
session_destroy(); // Sletter selve sessionen på serveren

// Send brugeren tilbage til loginsiden
header("Location: login.php");
exit;
            

Del 4: Datahåndtering og databaser

Dette modul dækker opbevaring, strukturering og manipulation af data, så dine webapplikationer kan huske information permanent.

Lektion 12: Filhåndtering og filupload

Nogle gange har du brug for at gemme data direkte i filer på serveren eller give brugere lov til at uploade billeder, CV'er eller dokumenter.

Læsning og skrivning til tekstfiler
PHP har indbyggede funktioner til hurtigt at læse og skrive data til disk uden en database.

<?php
$filnavn = "log.txt";

// Skriv data til en fil (Sletter eksisterende indhold)
file_put_contents($filnavn, "Første linje i loggen.\n");

// Tilføj data til bunden af en eksisterende fil (FILE_APPEND)
file_put_contents($filnavn, "Ny aktivitet registreret.\n", FILE_APPEND);

// Læs hele filen ind i en variabel og udskriv den
if (file_exists($filnavn)) {
    $indhold = file_get_contents($filnavn);
    echo nl2br(htmlspecialchars($indhold)); // nl2br omdanner \n til <br> i HTML
}
            

Sikker upload af filer via formularer
Når brugere uploader filer, gemmer PHP informationerne i superglobalen $_FILES. Husk altid at tilføje enctype="multipart/form-data" til din HTML-formular, ellers virker filupload ikke.
Her er et komplet og sikkert eksempel på et script til billed-upload (upload.php):

<?php
$besked = "";

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_FILES["billede"])) {
    $fil = $_FILES["billede"];
    
    // 1. Definer tilladte indstillinger
    $tilladteTyper = ["image/jpeg", "image/png", "image/webp"];
    $maksStørrelse = 2 * 1024 * 1024; // 2 MB i bytes
    $uploadMappe = "uploads/";

    // Opret mappen hvis den ikke findes
    if (!is_dir($uploadMappe)) {
        mkdir($uploadMappe, 0755, true);
    }

    // 2. Validering af filen
    if ($fil["error"] !== UPLOAD_ERR_OK) {
        $besked = "Der skete en fejl under upload.";
    } elseif (!in_array($fil["type"], $tilladteTyper)) {
        $besked = "Ugyldig filtype! Kun JPG, PNG og WebP er tilladt.";
    } elseif ($fil["size"] > $maksStørrelse) {
        $besked = "Filen er for stor! Maksimum er 2 MB.";
    } else {
        // 3. Generer et unikt filnavn for at undgå at overskrive gamle filer
        $filExtension = pathinfo($fil["name"], PATHINFO_EXTENSION);
        $nytNavn = uniqid("img_", true) . "." . $filExtension;
        $målSti = $uploadMappe . $nytNavn;

        // 4. Flyt filen fra den midlertidige mappe til destinationsmappen
        if (move_uploaded_file($fil["tmp_name"], $målSti)) {
            $besked = "Succes! Dit billede er uploadet som: " . $nytNavn;
        } else {
            $besked = "Kunne ikke gemme filen på serveren.";
        }
    }
}
?>

<!DOCTYPE html>
<html>
<body>
    <h2>Upload et profilbillede</h2>
    <p><?= $besked ?></p>
    <form action="upload.php" method="POST" enctype="multipart/form-data">
        <input type="file" name="billede" required><br><br>
        <button type="submit">Upload billede</button>
    </form>
</body>
</html>
            

Lektion 13: MySQL Databaser & PDO (PHP Data Objects)

I professionelle systemer gemmes data i en database. Vi bruger PDO, fordi det er det sikreste og mest fleksible databaselag i PHP. Det understøtter mange forskellige databaser (MySQL, PostgreSQL, SQLite) uden at du skal omskrive din kode.

1. Forbindelse til MySQL med PDO
Først opretter vi en forbindelse. Det gøres i en særskilt fil, som vi kan inkludere på andre sider.

<?php
// db.php - Databaseforbindelse
$host = "localhost";
$db   = "kursus_db";
$user = "root";       // Standard i XAMPP
$pass = "";           // Standard i XAMPP er tomt
$charset = "utf8mb4";

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // Kaster fejl som Exceptions
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // Returnerer associative arrays
    PDO::ATTR_EMULATE_PREPARES   => false,                  // Bruger rigtige prepared statements
];

try {
     $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
     throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
            

2. CRUD-operationer med Prepared Statements
Husk den absolut vigtigste sikkerhedsregel: Sæt aldrig variabler direkte ind i dine SQL-strenge (f.eks. WHERE id = $id). Det åbner op for SQL Injection-hacking. Brug altid Prepared Statements (pladsholdere).
Her ser vi, hvordan vi udfører de fire grundlæggende CRUD-operationer (Create, Read, Update, Delete).

<?php
require_once "db.php"; // Hent databaseforbindelsen ($pdo)

// --- 1. CREATE (Indsæt data) ---
$navn = "Jens Hansen";
$email = "jens@example.dk";

$sqlInsert = "INSERT INTO brugere (navn, email) VALUES (:navn, :email)";
$stmt = $pdo->prepare($sqlInsert);
$stmt->execute(['navn' => $navn, 'email' => $email]);

echo "Bruger oprettet med ID: " . $pdo->lastInsertId() . "<br>";


// --- 2. READ (Hent data) ---
$sqlSelect = "SELECT id, navn, email FROM brugere WHERE email = :email";
$stmt = $pdo->prepare($sqlSelect);
$stmt->execute(['email' => 'jens@example.dk']);
$bruger = $stmt->fetch(); // fetch() henter én enkelt række

if ($bruger) {
    echo "Fundet bruger: " . htmlspecialchars($bruger['navn']) . "<br>";
}

// Hent ALLE brugere:
$stmtAll = $pdo->query("SELECT * FROM brugere");
$alleBrugere = $stmtAll->fetchAll(); // fetchAll() henter alle rækker i et array


// --- 3. UPDATE (Opdater data) ---
$nytNavn = "Jens Peter Hansen";
$brugerId = 1;

$sqlUpdate = "UPDATE brugere SET navn = :navn WHERE id = :id";
$stmt = $pdo->prepare($sqlUpdate);
$stmt->execute(['navn' => $nytNavn, 'id' => $brugerId]);


// --- 4. DELETE (Slet data) ---
$sletId = 2;

$sqlDelete = "DELETE FROM brugere WHERE id = :id";
$stmt = $pdo->prepare($sqlDelete);
$stmt->execute(['id' => $sletId]);
            

Database-struktur: Det fulde overblik
For at køre ovenstående eksempler i din lokale database (via f.eks. http://localhost/phpmyadmin/), skal du have en tabel. Her er SQL-koden til at oprette tabellen brugere:

Kolonnenavn Datatype Attributter Beskrivelse
idINTPRIMARY KEY, AUTO_INCREMENTUnikt ID for hver bruger
navnVARCHAR(100)NOT NULLBrugerens fulde navn
emailVARCHAR(150)NOT NULL, UNIQUEE-mailadresse (må ikke genbruges)
oprettet_atTIMESTAMPDEFAULT CURRENT_TIMESTAMPTidspunkt for oprettelse

Del 5: Objektorienteret PHP (OOP)

Dette modul lærer dig at tænke i genanvendelige byggeklodser. OOP gør det markant nemmere at vedligeholde og udbygge store applikationer.

Lektion 14: Grundlæggende OOP

I objektorienteret programmering tænker vi på kode som objekter fra den virkelige verden (f.eks. en bruger, et produkt eller en bil).

  • Klasse (Class): En skabelon eller en arkitekttegning.
  • Objekt (Object): Den konkrete ting, der er bygget ud fra skabelonen (en instans).

Klasser, egenskaber, metoder og konstruktører
En klasse indeholder egenskaber (variabler) og metoder (funktioner). Vi bruger en konstruktør (__construct) til automatisk at tildele værdier, så snart et nyt objekt oprettes.

<?php
declare(strict_types=1);

class Produkt {
    // Egenskaber (Properties) med synlighed (Visibility)
    public string $navn;
    public float $pris;

    // Konstruktøren kører automatisk, når vi skriver 'new Produkt()'
    public function __construct(string $navn, float $pris) {
        $this->navn = $navn; // $this refererer til det aktuelle objekt
        $this->pris = $pris;
    }

    // En metode (Funktion inde i en klasse)
    public function hentPrisMedMoms(): float {
        return $this->pris * 1.25;
    }
}

// Oprettelse af to unikke objekter ud fra samme klasse
$produkt1 = new Produkt("Bærbar computer", 5000.00);
$produkt2 = new Produkt("Kaffemaskine", 400.00);

echo $produkt1->navn . " koster med moms: " . $produkt1->hentPrisMedMoms() . " kr.<br>";
echo $produkt2->navn . " koster med moms: " . $produkt2->hentPrisMedMoms() . " kr.";
            

Indkapsling og synlighed (Encapsulation)
For at beskytte data mod at blive ændret forkert udefra, bruger vi synlighedsniveauer. Vi gør egenskaber private og bruger Getters og Setters (metoder) til at læse og ændre dem sikkert.

Synlighed Forklaring
publicKan tilgås og ændres alle steder fra (både i og uden for klassen).
privateKan kun tilgås og ændres inde i selve klassen.
protectedKan tilgås inde i klassen og af klasser, der arver fra den.
<?php
class Bruger {
    private string $email; // Ingen kan ændre eller læse denne direkte udefra

    public function __construct(string $email) {
        $this->setEmail($email); // Bruger vores setter til validering
    }

    // Getter: Giver kontrolleret adgang til at læse værdien
    public function getEmail(): string {
        return $this->email;
    }

    // Setter: Giver kontrolleret adgang til at ændre værdien med validering
    public function setEmail(string $email): void {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Ugyldig e-mailadresse!");
        }
        $this->email = $email;
    }
}

$bruger = new Bruger("test@example.dk");
// $bruger->email = "FEJL"; // Giver en fatal fejl, fordi den er private
$bruger->setEmail("nyemail@example.dk"); // Korrekt måde at ændre data på
echo $bruger->getEmail();
            

Lektion 15: Avanceret OOP

Når du har styr på det grundlæggende, kan du udnytte de tre vigtigste avancerede koncepter i OOP: Arv, Interfaces og Namespaces.

1. Arv (Inheritance)
Arv gør det muligt for en klasse at genbruge og bygge videre på kode fra en anden klasse.

<?php
// Basis-klasse (Forælder)
class Medarbejder {
    protected string $navn;

    public function __construct(string $navn) {
        $this->navn = $navn;
    }

    public function getNavn(): string {
        return $this->navn;
    }
}

// Sub-klasse (Barn) der ARVER fra Medarbejder med 'extends'
class Manager extends Medarbejder {
    private array $ansatte = [];

    public function tilføjAnsat(string $navn): void {
        $this->ansatte[] = $navn;
    }
}

$manager = new Manager("Ida Hansen");
echo $manager->getNavn(); // Virker fordi Manager arvede metoden fra Medarbejder
            

2. Interfaces (Kontrakter)
Et interface indeholder ingen konkret kode. Det er en ren aftale eller en kontrakt, der dikterer, hvilke metoder en klasse skal have. Det er essentielt til at bygge fleksible systemer, hvor komponenter nemt kan udskiftes.

<?php
// Kontrakten: Alle betalingsmetoder skal have en 'betal' metode
interface BetalingInterface {
    public function betal(float $beløb): void;
}

class MobilePay implements BetalingInterface {
    public function betal(float $beløb): void {
        echo "Betaler $beløb kr. via MobilePay appen.";
    }
}

class Stripe implements BetalingInterface {
    public function betal(float $beløb): void {
        echo "Trækker $beløb kr. fra kreditkort via Stripe API.";
    }
}

// En funktion der er ligeglad med HVORDAN der betales, bare kontrakten overholdes
class WebshopKurv {
    public function afslutOrdre(BetalingInterface $betalingsSystem, float $total) {
        $betalingsSystem->betal($total);
    }
}

$kurv = new WebshopKurv();
$kurv->afslutOrdre(new MobilePay(), 250.00); // Nemt at skifte ud med new Stripe() i morgen
            

3. Namespaces og Autoloading
Når dit projekt vokser, vil du have mange klasser fordelt på mange filer. For at undgå navnesammenstød (f.eks. hvis to eksterne plugins begge har en klasse, der hedder Bruger), bruger vi Namespaces (virtuelle mapper for din kode).

<?php
// I filen: src/Gateway/Stripe.php
namespace App\Gateway;

class Client {
    // Kode her...
}

// I din hovedfil: index.php
// I stedet for manuelt at inkludere 100 filer med 'require', bruger man en autoloader (f.eks. via Composer)
use App\Gateway\Client as StripeClient;

$stripe = new StripeClient();
            

Del 6: Professionel udvikling og sikkerhed

Dette modul klæder dig på til at sikre dine applikationer mod nedbrud og hackerangreb, samt håndtere eksterne programpakker.

Lektion 16: Fejlhåndtering og undtagelser (Exceptions)

Når et script rammer en uventet situation (f.eks. at en database er nede, eller en fil mangler), må applikationen ikke bare crashe eller vise en grim teknisk fejlside til brugeren. Vi bruger try, catch og throw til at fange og håndtere fejl elegant.

Try-Catch i praksis
Når du pakker din kode ind i en try-blok, holder PHP øje med fejl. Hvis der opstår en fejl, afbrydes koden i try-blokken øjeblikkeligt, og kontrollen gives videre til catch-blokken.

<?php
declare(strict_types=1);

function delTal(float $a, float $b): float {
    if ($b === 0.0) {
        // Vi kaster en undtagelse (Exception), hvis man forsøger at dele med nul
        throw new Exception("Division med nul er ikke tilladt.");
    }
    return $a / $b;
}

try {
    // Dette vil fejle
    echo delTal(10, 0); 
    echo "Denne linje bliver aldrig kørt.";
} catch (Exception $e) {
    // Vi fanger fejlen og håndterer den kontrolleret
    echo "Hov, der skete en fejl: " . $e->getMessage() . "<br>";
    // Log fejlen i et system (Best practice)
    error_log("Fejl opstået: " . $e->getMessage());
} finally {
    // Denne blok kører ALTID, uanset om koden fejlede eller ej
    echo "Fejlhåndtering afsluttet.";
}
            

Lektion 17: PHP sikkerhed (Security Best Practices)

Websikkerhed er ikke valgfrit. Hvis du håndterer brugerdata, har du ansvaret for at beskytte dem.

1. Kryptering af passwords (Hashing)
Du må aldrig, aldrig gemme en brugers adgangskode som ren tekst i din database. Hvis databasen bliver hacket, er alle brugernes konti kompromitteret. PHP har indbyggede, topmoderne algoritmer til at "hashe" adgangskoder uomvendeligt.

<?php
// --- Ved oprettelse af bruger / registrering ---
$brugerKode = "minHemmeligeKode123";

// password_hash genererer automatisk et unikt salt og krypterer koden sikkert
$hashedKode = password_hash($brugerKode, PASSWORD_BCRYPT);

echo "Dette gemmes i databasen: " . $hashedKode . "<br><br>";


// --- Ved login ---
$indtastetKode = "minHemmeligeKode123"; // Det brugeren taster i login-formularen

// password_verify tjekker om den indtastede kode matcher den hashede kode fra databasen
if (password_verify($indtastetKode, $hashedKode)) {
    echo "Login godkendt! Adgangskoden er korrekt.";
} else {
    echo "Forkert adgangskode.";
}
            

2. Forebyggelse af CSRF (Cross-Site Request Forgery)
CSRF er et angreb, hvor en ondsindet hjemmeside narrer en intetanende bruger til at udføre handlinger på en anden side, hvor de allerede er logget ind (f.eks. at overføre penge eller ændre deres e-mail).
Vi beskytter os ved at generere en unik CSRF-token (en hemmelig nøgle), der gemmes i brugerens session og tjekkes hver gang en formular sendes.

<?php
session_start();

// Opret en unik token, hvis den ikke findes i forvejen
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Når formularen sendes (POST)
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        // Token matcher ikke - afvis anmodningen!
        die("CSRF-validering fejlede. Anmodning afvist.");
    }
    
    echo "Formularen blev behandlet sikkert!";
}
?>

<!-- HTML Formular med skjult CSRF-token -->
<form method="POST">
    <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
    <label>Skriv besked:</label>
    <input type="text" name="besked">
    <button type="submit">Send</button>
</form>
            

Lektion 18: Afhængighedshåndtering med Composer

I moderne PHP genopfinder man ikke den dybe tallerken hver gang. Hvis du skal sende e-mails, generere PDF-filer eller integrere betalingssystemer (som Stripe), bruger du eksterne kodebiblioteker (pakker). Composer er det officielle værktøj til at installere og styre disse pakker.

Sådan bruges Composer
Du installerer Composer på din computer, åbner din terminal/kommandoprompt i din projektmappe og kører en række simple kommandoer.

Trin Handling Terminalkommando Beskrivelse
1Initialiser Composercomposer initOpretter en composer.json fil, der holder styr på dine pakker.
2Installer en pakkecomposer require phpmailer/phpmailerHenter automatisk det populære PHPMailer-bibliotek til dit projekt.
3Opdater pakkercomposer updateOpdaterer alle dine installerede pakker til nyeste sikre versioner.

Inkludering af Composer i din kode
Når du bruger Composer, skal du ikke skrive require for hver eneste fil i dine pakker. Composer genererer automatisk én samlet fil, som sørger for at indlæse alt automatisk (Autoloading).

<?php
// index.php
// Denne ene linje i toppen af dit projekt indlæser ALLE dine installerede pakker automatisk
require_once __DIR__ . '/vendor/autoload.php';

// Nu kan du direkte bruge klasser fra de installerede pakker
use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer();
// ... Opsæt og send din mail sikkert herfra
            

Del 7: Arkitektur, API'er og afslutning

Dette modul tager dig fra at skrive isolerede PHP-scripts til at strukturere store, skalerbare og professionelle systemer, der kan kommunikere med apps og moderne frontend-frameworks.

Lektion 19: MVC-Arkitektur (Model-View-Controller)

Når applikationer vokser, duer det ikke at have HTML, database-kald og forretningslogik blandet sammen i de samme filer. MVC (Model-View-Controller) er et designmønster, der deler din kode op i tre adskilte ansvarsområder:

[Brugerens Browser] 
       │
   (Anmodning)
       ▼
┌─────────────────────────┐
│     CONTROLLER          │ <── Styrer logikken og koordinerer alt.
└────┬────────────────┬───┘
     │                │
     ▼                ▼
┌──────────────┐    ┌──────────────┐
│    MODEL     │    │    VIEW      │
│ (Database &  │    │  (HTML/CSS/  │
│  Data-logik) │    │  Skabelon)   │
└──────────────┘    └──────────────┘
            
  • Model (Modellen): Håndterer udelukkende data og forretningslogik. Det er her, dine PDO-kald og databaseforespørgsler bor.
  • View (Visningen): HTML- og CSS-koden. Det præsenterer blot data for brugeren og indeholder ingen tung logik.
  • Controller (Styringen): Broen mellem Model og View. Den modtager input fra brugeren (f.eks. et URL-kald), henter de rette data fra Modellen og sender dem videre til Visningen.

Routing og pæne URL'er
I professionelle systemer tilgår man ikke filer direkte (f.eks. produkter.php?id=5). I stedet sendes alle anmodninger igennem én central fil – en såkaldt Front Controller (index.php) – ved hjælp af en .htaccess-fil (på Apache-servere):

# .htaccess file - Sender alt trafik til index.php hvis filen ikke findes fysisk
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
            

Lektion 20: Byg et RESTful API

Moderne webapps (som dem der er bygget i React, Vue eller mobile apps) læser ikke rå HTML fra en server. De kommunikerer med et API (Application Programming Interface), der leverer rå data i JSON-format.
Et ægte RESTful API bruger HTTP-metoder til at definere handlinger:

  • GET /api/brugere -> Hent alle brugere
  • POST /api/brugere -> Opret en ny bruger
  • DELETE /api/brugere/5 -> Slet brugeren med ID 5

Eksempel på et simpelt JSON-API i PHP (api.php)
Her er et komplet eksempel på, hvordan PHP modtager en anmodning, henter data og spytter JSON ud med korrekte HTTP-statuskoder:

<?php
// api.php
// 1. Fortæl browseren/klienten, at vi returnerer JSON og tillader adgang udefra (CORS)
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");

require_once "db.php"; // Antager vi har vores PDO databaseforbindelse her

$metode = $_SERVER["REQUEST_METHOD"];

if ($metode === "GET") {
    try {
        // Hent data fra databasen
        $stmt = $pdo->query("SELECT id, navn, email FROM brugere");
        $brugere = $stmt->fetchAll();

        // Sæt HTTP Statuskode til 200 OK
        http_response_code(200);
        
        // Lav arrayet om til en JSON-streng og udskriv den
        echo json_encode([
            "status" => "success",
            "data" => $brugere
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(["status" => "error", "message" => "Serverfejl: " . $e->getMessage()]);
    }
} elseif ($metode === "POST") {
    // Modtag rå JSON-data sendt fra en frontend-app
    $inputData = json_decode(file_get_contents("php://input"), true);

    $navn = trim($inputData["navn"] ?? '');
    $email = trim($inputData["email"] ?? '');

    if (empty($navn) || empty($email)) {
        http_response_code(400); // Bad Request
        echo json_encode(["status" => "error", "message" => "Udfyld venligst både navn og email."]);
        exit;
    }

    // Indsæt i database
    $stmt = $pdo->prepare("INSERT INTO brugere (navn, email) VALUES (:navn, :email)");
    $stmt->execute(['navn' => $navn, 'email' => $email]);

    http_response_code(201); // Created
    echo json_encode(["status" => "success", "message" => "Bruger oprettet med succes!"]);
} else {
    // Hvis nogen prøver PUT eller DELETE, som vi ikke understøtter her
    http_response_code(405); // Method Not Allowed
    echo json_encode(["status" => "error", "message" => "Metoden er ikke tilladt."]);
}
            

Lektion 21: Introduktion til PHP Frameworks (Næste skridt)

Når du har fuldført dette kursus, har du opbygget et solidt fundament i rå PHP (ofte kaldet Vanilla PHP). Men i professionelle virksomheder bygger man sjældent store systemer helt fra bunden. Man bruger i stedet et Framework, som indeholder færdigbygget, gennemtestet arkitektur til routing, sikkerhed, database-håndtering og test.
De to absolut største giganter på markedet i dag er:

Framework Fokus og fordele Popularitet
LaravelKendt for sin utrolig elegante syntaks ("udvikler-lykke"), fantastiske dokumentation, og et gigantisk økosystem (Forge, Vapor, Nova). Det mest populære valg til nye virksomheder og SaaS-produkter.🌟 Meget høj (Markedsleder)
SymfonyEkstremt robust, modulært og stabilt. Bruges ofte i meget store enterprise-løsninger og korporationer. Mange andre systemer (inklusive Laravel og Drupal) bruger dele af Symfonys kildekode under motorhjelmen.💼 Høj (Enterprise-standard)

Din karrierevej som PHP-udvikler

Du har nu bevæget dig igennem alt fra variabler til objekter, databaser og API'er. For at gå fra kursus til virkelighed anbefales følgende rækkefølge:

  1. Byg et lille portfolioprojekt i Vanilla PHP (f.eks. en simpel blog eller en opgaveliste med database og login).
  2. Lær Composer ud og ind, så du kan integrere pakker.
  3. Kast dig over Laravel – start med deres officielle, gratis videogit-guide på Laracasts.com ("Laravel 11 from Scratch" eller nyeste version). Det vil føles som at få superkræfter, fordi du nu forstår alt det PHP-fundament, som Laravel er bygget ovenpå!

Tag dit næste store skridt

Brug denne viden til at bygge robuste systemer og skabe sikre og skalerbare webapplikationer.