Files
guides/templates/Referenz/BeginnerGuide.md
2026-05-25 19:33:48 +02:00

1438 lines
56 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
```
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>PHP Anfänger-Guide</title>
<style>
@page {
size: A4;
margin: 22mm 20mm 20mm 20mm;
@bottom-center {
content: counter(page) " / " counter(pages);
font-family: -apple-system, "Segoe UI", sans-serif;
font-size: 8pt;
color: #888;
}
@bottom-right {
content: "PHP Anfänger-Guide";
font-family: -apple-system, "Segoe UI", sans-serif;
font-size: 8pt;
color: #888;
}
}
@page :first {
margin: 0;
@bottom-center { content: none; }
@bottom-right { content: none; }
}
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--php: #777BB4;
--php-dark: #4F5B93;
--php-darker: #2C3E66;
--ink: #1a1a1a;
--muted: #5a6470;
--line: #d8dde3;
--bg-soft: #f5f5fb;
--code-bg: #1e2a3a;
--code-fg: #e6e6e6;
--plus: #2c8a3e;
--minus: #c0392b;
}
html, body {
font-family: Charter, "Source Serif Pro", Georgia, serif;
color: var(--ink);
font-size: 10.5pt;
line-height: 1.55;
}
/* ===== COVER ===== */
.cover {
width: 210mm;
height: 297mm;
padding: 35mm 25mm;
background: linear-gradient(135deg, var(--php-dark) 0%, var(--php-darker) 100%);
color: white;
display: flex;
flex-direction: column;
justify-content: space-between;
page-break-after: always;
}
.cover-top { display: flex; align-items: center; gap: 8mm; }
.cover-logo {
width: 28mm; height: 28mm;
background: linear-gradient(135deg, var(--php) 0%, white 100%);
border-radius: 7mm;
display: flex; align-items: center; justify-content: center;
font-family: -apple-system, sans-serif;
font-size: 22pt; font-weight: 800;
color: var(--php-dark);
}
.cover-meta {
font-family: -apple-system, sans-serif;
font-size: 9pt;
text-transform: uppercase;
letter-spacing: 2pt;
opacity: 0.8;
}
.cover-main h1 {
font-family: -apple-system, sans-serif;
font-size: 56pt;
font-weight: 800;
letter-spacing: -2pt;
line-height: 0.95;
margin-bottom: 8mm;
}
.cover-main h1 .accent { color: var(--php); }
.cover-main .subtitle {
font-size: 16pt;
font-weight: 400;
line-height: 1.3;
opacity: 0.9;
font-family: -apple-system, sans-serif;
max-width: 130mm;
}
.cover-bottom {
display: grid;
grid-template-columns: 1fr auto;
gap: 10mm;
align-items: end;
font-family: -apple-system, sans-serif;
}
.cover-promise {
font-size: 10pt;
line-height: 1.5;
opacity: 0.85;
max-width: 110mm;
padding-top: 4mm;
border-top: 1pt solid rgba(255,255,255,0.3);
}
.cover-promise b { color: var(--php); text-transform: uppercase; letter-spacing: 1pt; font-size: 8pt; display: block; margin-bottom: 2mm; }
.cover-tag {
background: var(--php);
color: white;
padding: 3mm 6mm;
border-radius: 2mm;
font-weight: 800;
font-size: 11pt;
}
/* ===== TOC ===== */
.toc { page-break-after: always; }
.toc h2 {
font-family: -apple-system, sans-serif;
font-size: 24pt;
font-weight: 800;
color: var(--php-dark);
margin-bottom: 6mm;
border-bottom: 2pt solid var(--php);
padding-bottom: 3mm;
}
.toc-section {
font-family: -apple-system, sans-serif;
font-size: 9pt;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1pt;
color: var(--muted);
margin: 6mm 0 2mm 0;
}
.toc-item {
display: grid;
grid-template-columns: 8mm 1fr auto;
gap: 4mm;
padding: 2.5mm 0;
border-bottom: 0.3pt solid var(--line);
align-items: baseline;
}
.toc-num {
font-family: -apple-system, sans-serif;
font-size: 14pt;
font-weight: 800;
color: var(--php);
line-height: 1;
}
.toc-text { font-family: -apple-system, sans-serif; }
.toc-text h3 {
font-size: 10.5pt;
font-weight: 700;
color: var(--ink);
margin-bottom: 0.5mm;
}
.toc-text p {
font-size: 8.5pt;
color: var(--muted);
margin: 0;
}
.toc-time {
font-family: -apple-system, sans-serif;
font-size: 7.5pt;
color: var(--muted);
background: var(--bg-soft);
padding: 0.8mm 2.5mm;
border-radius: 1.5mm;
white-space: nowrap;
}
/* ===== CHAPTER ===== */
.chapter { page-break-before: always; }
.chapter-head {
display: grid;
grid-template-columns: auto 1fr;
gap: 6mm;
align-items: center;
border-bottom: 2pt solid var(--ink);
padding-bottom: 4mm;
margin-bottom: 6mm;
}
.chapter-num {
font-family: -apple-system, sans-serif;
font-size: 42pt;
font-weight: 800;
color: var(--php);
line-height: 0.9;
width: 22mm;
text-align: center;
}
.chapter-title h1 {
font-family: -apple-system, sans-serif;
font-size: 22pt;
font-weight: 800;
color: var(--php-dark);
letter-spacing: -0.5pt;
line-height: 1.1;
}
.chapter-title .subtitle {
font-family: -apple-system, sans-serif;
font-size: 11pt;
color: var(--muted);
margin-top: 1.5mm;
}
/* ===== GAP ===== */
.gap {
background: var(--bg-soft);
border-left: 3pt solid var(--php);
padding: 4mm 5mm;
margin: 4mm 0 6mm 0;
font-style: italic;
font-size: 10.5pt;
}
.gap b {
font-style: normal;
color: var(--php-dark);
font-family: -apple-system, sans-serif;
text-transform: uppercase;
font-size: 8pt;
letter-spacing: 1pt;
display: block;
margin-bottom: 1.5mm;
}
/* ===== SECTIONS ===== */
.chapter h2 {
font-family: -apple-system, sans-serif;
font-size: 14pt;
font-weight: 700;
color: var(--php-dark);
margin: 7mm 0 3mm 0;
page-break-after: avoid;
}
.chapter h3 {
font-family: -apple-system, sans-serif;
font-size: 11pt;
font-weight: 700;
color: var(--ink);
margin: 5mm 0 2mm 0;
page-break-after: avoid;
}
.chapter p {
margin-bottom: 3mm;
text-align: justify;
hyphens: auto;
}
.chapter p b { color: var(--php-dark); }
.chapter ul, .chapter ol { margin: 2mm 0 4mm 6mm; }
.chapter li { margin-bottom: 1.5mm; }
/* ===== CODE ===== */
.chapter pre {
background: var(--code-bg);
color: var(--code-fg);
font-family: "SF Mono", Consolas, monospace;
font-size: 8.5pt;
line-height: 1.5;
padding: 3mm 4mm;
border-radius: 2mm;
margin: 3mm 0 4mm 0;
white-space: pre;
overflow: hidden;
page-break-inside: avoid;
}
.c { color: #6b8aae; font-style: italic; }
.k { color: #ff79c6; }
.s { color: #f1c40f; }
.f { color: #50fa7b; }
.t { color: #8be9fd; }
.v { color: #ffb86c; }
code.inline {
font-family: "SF Mono", Consolas, monospace;
font-size: 9pt;
background: var(--bg-soft);
padding: 0.3mm 1.5mm;
border-radius: 1mm;
color: var(--php-dark);
}
/* ===== CALLOUTS ===== */
.callout {
border-radius: 2mm;
padding: 3mm 4mm;
margin: 4mm 0;
font-size: 10pt;
page-break-inside: avoid;
display: grid;
grid-template-columns: 6mm 1fr;
gap: 3mm;
align-items: start;
}
.callout-icon {
font-family: -apple-system, sans-serif;
font-weight: 800;
font-size: 14pt;
line-height: 1;
text-align: center;
}
.callout-body > b:first-child {
font-family: -apple-system, sans-serif;
text-transform: uppercase;
font-size: 8pt;
letter-spacing: 1pt;
display: block;
margin-bottom: 1.5mm;
}
.callout.tip { background: #e8f4ea; border-left: 3pt solid var(--plus); }
.callout.tip .callout-icon, .callout.tip .callout-body > b:first-child { color: var(--plus); }
.callout.warn { background: #fdecea; border-left: 3pt solid var(--minus); }
.callout.warn .callout-icon, .callout.warn .callout-body > b:first-child { color: var(--minus); }
.callout.note { background: var(--bg-soft); border-left: 3pt solid var(--php); }
.callout.note .callout-icon, .callout.note .callout-body > b:first-child { color: var(--php-dark); }
/* ===== RECALL ===== */
.recall {
background: linear-gradient(135deg, var(--php-dark) 0%, var(--php-darker) 100%);
color: white;
padding: 5mm 6mm;
border-radius: 2mm;
margin: 6mm 0 0 0;
page-break-inside: avoid;
}
.recall b {
font-family: -apple-system, sans-serif;
display: block;
text-transform: uppercase;
letter-spacing: 1.5pt;
font-size: 8.5pt;
color: var(--php);
margin-bottom: 2.5mm;
}
.recall ol {
margin: 0;
padding-left: 5mm;
font-size: 10pt;
}
.recall li {
margin-bottom: 1.5mm;
color: rgba(255,255,255,0.95);
}
.recall li::marker { color: var(--php); font-weight: 700; }
/* ===== TABLES ===== */
.chapter table {
width: 100%;
border-collapse: collapse;
margin: 3mm 0 4mm 0;
font-size: 9.5pt;
font-family: -apple-system, sans-serif;
}
.chapter th {
background: var(--php-dark);
color: white;
padding: 2mm 3mm;
text-align: left;
font-weight: 700;
font-size: 9pt;
text-transform: uppercase;
letter-spacing: 0.5pt;
}
.chapter td {
padding: 2mm 3mm;
border-bottom: 0.5pt solid var(--line);
vertical-align: top;
}
.chapter td code {
font-family: "SF Mono", Consolas, monospace;
font-size: 8.5pt;
color: var(--php-dark);
}
/* ===== ENDING ===== */
.ending { page-break-before: always; }
.ending h1 {
font-family: -apple-system, sans-serif;
font-size: 28pt;
font-weight: 800;
color: var(--php-dark);
margin-bottom: 6mm;
border-bottom: 2pt solid var(--php);
padding-bottom: 3mm;
}
.ending h2 {
font-family: -apple-system, sans-serif;
font-size: 14pt;
font-weight: 700;
color: var(--php-dark);
margin: 7mm 0 3mm 0;
}
.spaced-plan {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 3mm;
margin: 4mm 0;
}
.spaced-day {
background: var(--bg-soft);
border-top: 3pt solid var(--php);
padding: 4mm 3mm;
border-radius: 2mm;
}
.spaced-day b {
font-family: -apple-system, sans-serif;
display: block;
color: var(--php-dark);
font-size: 9pt;
text-transform: uppercase;
letter-spacing: 0.5pt;
margin-bottom: 2mm;
}
.spaced-day p {
font-size: 9pt;
margin: 0;
text-align: left;
}
</style>
</head>
<body>
<!-- ===== COVER ===== -->
<section class="cover">
<div class="cover-top">
<div class="cover-logo">php</div>
<div class="cover-meta">Anfänger-Guide · 2,5h · Stand 2026</div>
</div>
<div class="cover-main">
<h1>PHP<br><span class="accent">in 10 Kapiteln.</span></h1>
<p class="subtitle">Von der Installation bis zur ersten Composer-Anwendung modern und idiomatisch.</p>
</div>
<div class="cover-bottom">
<div class="cover-promise">
<b>Was du danach kannst</b>
PHP-Code mit Type-Hints schreiben · Arrays und Klassen modern nutzen · Funktionen sauber strukturieren · Fehler richtig behandeln · Composer-Pakete installieren und in eigenen Projekten verwenden.
</div>
<div class="cover-tag">PHP 8.4</div>
</div>
</section>
<!-- ===== TOC ===== -->
<section class="toc">
<h2>Inhalt</h2>
<div class="toc-section">Teil 1 · Grundlagen</div>
<div class="toc-item">
<div class="toc-num">1</div>
<div class="toc-text">
<h3>PHP installieren und starten</h3>
<p>Setup mit Docker, DDEV oder lokal, erstes Script</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">2</div>
<div class="toc-text">
<h3>Variablen und Typen</h3>
<p>$-Variablen, strict_types, Typumwandlung</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">3</div>
<div class="toc-text">
<h3>Strings und Formatierung</h3>
<p>Interpolation, Heredoc, String-Funktionen</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-section">Teil 2 · Strukturen</div>
<div class="toc-item">
<div class="toc-num">4</div>
<div class="toc-text">
<h3>Arrays: Liste und Map</h3>
<p>Indiziert, assoziativ, gemischt</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">5</div>
<div class="toc-text">
<h3>Control Flow</h3>
<p>if, match, foreach, while</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">6</div>
<div class="toc-text">
<h3>Funktionen mit Type-Hints</h3>
<p>Parameter, Rückgabewerte, Named Arguments</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-section">Teil 3 · Objekte und echte Anwendungen</div>
<div class="toc-item">
<div class="toc-num">7</div>
<div class="toc-text">
<h3>Klassen und Objekte</h3>
<p>Promoted Constructor, readonly, Methoden</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">8</div>
<div class="toc-text">
<h3>Namespaces und Autoloading</h3>
<p>PSR-4, use-Statements, Datei-Organisation</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">9</div>
<div class="toc-text">
<h3>Fehlerbehandlung</h3>
<p>Exceptions, try/catch, eigene Fehler</p>
</div>
<div class="toc-time">15 Min</div>
</div>
<div class="toc-item">
<div class="toc-num">10</div>
<div class="toc-text">
<h3>Composer und erste Anwendung</h3>
<p>Pakete installieren, kleines CLI-Tool bauen</p>
</div>
<div class="toc-time">15 Min</div>
</div>
</section>
<!-- ===== KAPITEL 1 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">01</div>
<div class="chapter-title">
<h1>PHP installieren und starten</h1>
<div class="subtitle">Setup mit Docker, DDEV oder lokal</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
Du willst PHP-Code schreiben und ausführen. Aber wie kommt PHP auf deinen Rechner? Klassische XAMPP-Installation, modernes Docker, oder einfach <code>php -S</code> für schnelle Tests? Die richtige Wahl spart dir später Stunden.
</div>
<h2>Drei Wege, PHP zu starten</h2>
<p>Für lokale Entwicklung gibt es drei sinnvolle Optionen, jede mit ihrem Anwendungsfall:</p>
<table>
<tr><th>Methode</th><th>Wann</th></tr>
<tr><td><b>Lokal</b> (brew, apt)</td><td>Kleine Scripts, schneller Test</td></tr>
<tr><td><b>DDEV</b></td><td>Mehrere Projekte mit DB</td></tr>
<tr><td><b>Docker</b> direkt</td><td>Custom-Setup, CI/CD</td></tr>
</table>
<p>Für den Einstieg reicht eine lokale Installation. Auf macOS mit Homebrew: <code class="inline">brew install php</code>. Auf Ubuntu: <code class="inline">apt install php8.4-cli</code>. Auf Windows: am einfachsten WSL2 mit Ubuntu darin.</p>
<h2>Erstes Script</h2>
<p>Lege eine Datei <code class="inline">hello.php</code> an mit folgendem Inhalt:</p>
<pre><span class="t">&lt;?php</span>
<span class="k">declare</span>(strict_types=<span class="s">1</span>);
<span class="k">echo</span> <span class="s">"Hallo Welt!\n"</span>;
<span class="k">echo</span> <span class="s">"PHP-Version: "</span> . PHP_VERSION . <span class="s">"\n"</span>;</pre>
<p>Im Terminal: <code class="inline">php hello.php</code>. Du siehst die Ausgabe. Glückwunsch das ist alles, was PHP zum Start braucht.</p>
<p>Die Zeile <code class="inline">declare(strict_types=1)</code> gehört in jede moderne PHP-Datei. Sie aktiviert strenge Typprüfung und verhindert, dass PHP heimlich Strings in Zahlen umwandelt. Verlässlicher Code.</p>
<h2>Eingebauter Web-Server</h2>
<p>Für Web-Entwicklung braucht PHP normalerweise einen Webserver (Apache, nginx). Für lokales Testen gibt es einen <b>eingebauten Server</b>:</p>
<pre><span class="c"># Im Verzeichnis mit deinen .php-Dateien</span>
php -S localhost:8000</pre>
<p>Öffne <code class="inline">http://localhost:8000/hello.php</code> im Browser. Der Server ist nicht für Production gedacht, aber für lokale Entwicklung perfekt.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body">
<b>DDEV für echte Projekte</b>
Sobald du eine Datenbank brauchst, mehrere Projekte gleichzeitig, oder ein konkretes PHP-Framework wie Shopware oder Laravel, lohnt sich <b>DDEV</b> wraps Docker mit sinnvollen Defaults. <code class="inline">ddev config</code> in deinem Projektordner reicht meist.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Wie führst du eine PHP-Datei im Terminal aus?</li>
<li>Wozu ist <code style="color:#777BB4">declare(strict_types=1)</code> gut?</li>
<li>Wann nutzt du <code style="color:#777BB4">php -S</code>, wann DDEV?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 2 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">02</div>
<div class="chapter-title">
<h1>Variablen und Typen</h1>
<div class="subtitle">$-Variablen, strict_types, Typumwandlung</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
PHP-Variablen beginnen mit einem <code>$</code>. Warum eigentlich? Und wie kann eine Variable mal eine Zahl, mal ein String sein, ohne dass es explodiert? Die Antwort zeigt, wie PHPs Typsystem funktioniert.
</div>
<h2>Variablen mit $</h2>
<p>Jede Variable in PHP beginnt mit dem Dollar-Zeichen. Das macht sie im Code sofort erkennbar du musst nie raten, ob ein Name eine Variable oder eine Funktion ist:</p>
<pre><span class="v">$name</span> = <span class="s">'Marek'</span>;
<span class="v">$age</span> = <span class="s">34</span>;
<span class="v">$height</span> = <span class="s">1.82</span>;
<span class="v">$isActive</span> = <span class="k">true</span>;
<span class="v">$email</span> = <span class="k">null</span>;</pre>
<p>PHP ist <b>dynamisch typisiert</b>: derselbe Variable darf erst eine Zahl, dann ein String zugewiesen werden. Das ist flexibel, kann aber zu Bugs führen deshalb sind Type-Hints in Funktionen so wichtig (Kapitel 6).</p>
<h2>Die wichtigsten Typen</h2>
<table>
<tr><th>Typ</th><th>Beispiel</th><th>Wofür</th></tr>
<tr><td><code>int</code></td><td><code>42</code></td><td>Ganze Zahlen</td></tr>
<tr><td><code>float</code></td><td><code>3.14</code></td><td>Kommazahlen</td></tr>
<tr><td><code>string</code></td><td><code>'Hi'</code></td><td>Text</td></tr>
<tr><td><code>bool</code></td><td><code>true</code></td><td>Wahrheitswert</td></tr>
<tr><td><code>array</code></td><td><code>[1, 2]</code></td><td>Liste oder Map</td></tr>
<tr><td><code>null</code></td><td><code>null</code></td><td>Kein Wert</td></tr>
</table>
<p>Den Typ einer Variable findest du mit <code class="inline">gettype($x)</code> oder mit Funktionen wie <code class="inline">is_int($x)</code>, <code class="inline">is_string($x)</code>.</p>
<h2>Typumwandlung</h2>
<p>PHP wandelt Typen oft automatisch um das ist bequem, aber tückisch. Mit explizitem Cast bist du auf der sicheren Seite:</p>
<pre><span class="v">$input</span> = <span class="s">'42'</span>; <span class="c">// string</span>
<span class="v">$number</span> = (<span class="t">int</span>) <span class="v">$input</span>; <span class="c">// 42 als int</span>
<span class="v">$float</span> = (<span class="t">float</span>) <span class="s">'3.14'</span>; <span class="c">// 3.14</span>
<span class="v">$text</span> = (<span class="t">string</span>) <span class="s">42</span>; <span class="c">// "42"</span>
<span class="c">// Boolean-Cast: 0, "", "0", null, [] sind false</span>
<span class="k">if</span> (<span class="v">$value</span>) { <span class="c">// implizit zu bool</span>
<span class="k">echo</span> <span class="s">'truthy'</span>;
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body">
<b>Vergleich mit == ist tückisch</b>
<code class="inline">'0' == false</code> ist <code class="inline">true</code>. <code class="inline">'abc' == 0</code> war in alten PHP-Versionen auch true. Nutze immer <code class="inline">===</code> für strikten Vergleich, der auch den Typ prüft.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Wie erkennst du in PHP-Code, ob etwas eine Variable ist?</li>
<li>Was ist der Unterschied zwischen <code style="color:#777BB4">==</code> und <code style="color:#777BB4">===</code>?</li>
<li>Welche Werte werden bei Boolean-Cast zu <code style="color:#777BB4">false</code>?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 3 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">03</div>
<div class="chapter-title">
<h1>Strings und Formatierung</h1>
<div class="subtitle">Interpolation, Heredoc, String-Funktionen</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
In PHP gibt es einfache und doppelte Anführungszeichen für Strings. Beide funktionieren aber sie verhalten sich unterschiedlich. Warum gibt es überhaupt zwei, und welche solltest du wann nutzen?
</div>
<h2>Strings: '' vs ""</h2>
<p>Beide sind Strings, aber doppelte Anführungszeichen interpolieren Variablen, einfache nicht:</p>
<pre><span class="v">$name</span> = <span class="s">'Marek'</span>;
<span class="k">echo</span> <span class="s">"Hallo </span><span class="v">$name</span><span class="s">"</span>; <span class="c">// Hallo Marek</span>
<span class="k">echo</span> <span class="s">'Hallo $name'</span>; <span class="c">// Hallo $name (wörtlich)</span>
<span class="c">// Komplexere Ausdrücke: geschweifte Klammern</span>
<span class="v">$user</span> = [<span class="s">'name'</span> =&gt; <span class="s">'Marek'</span>];
<span class="k">echo</span> <span class="s">"Hallo {</span><span class="v">$user</span><span class="s">['name']}"</span>;
<span class="c">// Verkettung mit .</span>
<span class="k">echo</span> <span class="s">'Hallo '</span> . <span class="v">$name</span> . <span class="s">'!'</span>;</pre>
<p>Faustregel: <b>Doppelte Anführungszeichen</b> wenn du Variablen einsetzen willst, <b>einfache</b> sonst. Einfache sind minimal schneller, aber das ist heute praktisch egal.</p>
<h2>Heredoc und Nowdoc für Mehrzeiler</h2>
<p>Für längeren Text mit Variablen oder ohne gibt es <b>Heredoc</b> und <b>Nowdoc</b>:</p>
<pre><span class="v">$name</span> = <span class="s">'Marek'</span>;
<span class="v">$mail</span> = &lt;&lt;&lt;<span class="s">TEXT</span>
<span class="s">Hallo </span><span class="v">$name</span><span class="s">,
vielen Dank für deine Bestellung.
Grüße
TEXT</span>;
<span class="c">// Nowdoc (kein Interpolation, wörtlich)</span>
<span class="v">$tpl</span> = &lt;&lt;&lt;<span class="s">'TPL'</span>
<span class="s">Verwende {{ name }} für den Namen.
TPL</span>;</pre>
<h2>Wichtige String-Funktionen</h2>
<p>PHP hat eine riesige Auswahl an String-Funktionen. Diese kennst du nach einer Woche auswendig:</p>
<table>
<tr><th>Funktion</th><th>Effekt</th></tr>
<tr><td><code>strlen($s)</code></td><td>Länge des Strings</td></tr>
<tr><td><code>strtoupper / strtolower</code></td><td>Groß-/Kleinschreibung</td></tr>
<tr><td><code>trim($s)</code></td><td>Whitespace vorne/hinten entfernen</td></tr>
<tr><td><code>str_replace($a, $b, $s)</code></td><td>a durch b ersetzen</td></tr>
<tr><td><code>str_contains($s, $needle)</code></td><td>Prüft, ob enthalten</td></tr>
<tr><td><code>explode(',', $s)</code></td><td>String zu Array</td></tr>
<tr><td><code>implode(',', $arr)</code></td><td>Array zu String</td></tr>
<tr><td><code>sprintf('%05d', 42)</code></td><td>Formatiert "00042"</td></tr>
</table>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body">
<b>Methoden-Verkettung mit Pipes nicht möglich</b>
Anders als bei Objektmethoden gibt es bei String-Funktionen keine Verkettung. <code class="inline">trim(strtolower($s))</code> ist die Schreibweise von innen nach außen lesen.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Wann nutzt du <code style="color:#777BB4">""</code>, wann <code style="color:#777BB4">''</code>?</li>
<li>Was ist der Unterschied zwischen Heredoc und Nowdoc?</li>
<li>Wie prüfst du, ob ein String einen anderen enthält?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 4 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">04</div>
<div class="chapter-title">
<h1>Arrays: Liste und Map</h1>
<div class="subtitle">Indiziert, assoziativ, gemischt</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
In den meisten Sprachen sind Listen und Dictionaries zwei verschiedene Datenstrukturen. In PHP ist beides ein <code>array</code> das macht vieles flexibel, aber auch leicht verwirrend. Wie funktioniert das?
</div>
<h2>Indizierte Arrays (Listen)</h2>
<p>Eine Liste ohne explizite Keys die Indizes werden automatisch 0, 1, 2, ...:</p>
<pre><span class="v">$fruits</span> = [<span class="s">'Apfel'</span>, <span class="s">'Birne'</span>, <span class="s">'Kirsche'</span>];
<span class="v">$fruits</span>[<span class="s">0</span>]; <span class="c">// 'Apfel'</span>
<span class="v">$fruits</span>[<span class="s">2</span>]; <span class="c">// 'Kirsche'</span>
<span class="f">count</span>(<span class="v">$fruits</span>); <span class="c">// 3</span>
<span class="c">// Hinten anhängen</span>
<span class="v">$fruits</span>[] = <span class="s">'Banane'</span>; <span class="c">// jetzt 4 Elemente</span>
<span class="f">array_push</span>(<span class="v">$fruits</span>, <span class="s">'Mango'</span>); <span class="c">// alternative Schreibweise</span></pre>
<h2>Assoziative Arrays (Maps)</h2>
<p>Mit String-Keys wird das Array zu einer Map die häufigste Form in PHP-Code, weil viele APIs (JSON, Datenbank-Zeilen) so aussehen:</p>
<pre><span class="v">$user</span> = [
<span class="s">'name'</span> =&gt; <span class="s">'Marek'</span>,
<span class="s">'age'</span> =&gt; <span class="s">34</span>,
<span class="s">'roles'</span> =&gt; [<span class="s">'admin'</span>, <span class="s">'editor'</span>],
];
<span class="v">$user</span>[<span class="s">'name'</span>]; <span class="c">// 'Marek'</span>
<span class="v">$user</span>[<span class="s">'email'</span>] = <span class="s">'m@e.de'</span>; <span class="c">// neuer Key</span>
<span class="c">// Sicher: gibt null wenn nicht da, kein Fehler</span>
<span class="v">$phone</span> = <span class="v">$user</span>[<span class="s">'phone'</span>] ?? <span class="s">'unbekannt'</span>;</pre>
<p>Der <b>Null-Coalescing-Operator</b> <code class="inline">??</code> ist eine der nützlichsten PHP-Features. Er gibt den linken Wert zurück, wenn er existiert und nicht null ist, sonst den rechten.</p>
<h2>Über Arrays iterieren</h2>
<p><code class="inline">foreach</code> ist das Werkzeug der Wahl. Es funktioniert für beide Array-Typen:</p>
<pre><span class="c">// Nur Werte</span>
<span class="k">foreach</span> (<span class="v">$fruits</span> <span class="k">as</span> <span class="v">$fruit</span>) {
<span class="k">echo</span> <span class="v">$fruit</span>;
}
<span class="c">// Key und Wert</span>
<span class="k">foreach</span> (<span class="v">$user</span> <span class="k">as</span> <span class="v">$key</span> =&gt; <span class="v">$value</span>) {
<span class="k">echo</span> <span class="s">"</span><span class="v">$key</span><span class="s">: </span><span class="v">$value</span><span class="s">\n"</span>;
}</pre>
<h2>Array-Funktionen</h2>
<table>
<tr><th>Funktion</th><th>Wofür</th></tr>
<tr><td><code>array_map($fn, $arr)</code></td><td>Jedes Element transformieren</td></tr>
<tr><td><code>array_filter($arr, $fn)</code></td><td>Elemente mit Bedingung behalten</td></tr>
<tr><td><code>array_reduce($arr, $fn, $init)</code></td><td>Aggregieren zu einem Wert</td></tr>
<tr><td><code>array_keys / array_values</code></td><td>Keys oder Werte extrahieren</td></tr>
<tr><td><code>in_array($x, $arr)</code></td><td>Enthält Wert?</td></tr>
<tr><td><code>sort($arr)</code></td><td>Sortiert in-place</td></tr>
</table>
<div class="recall">
<b>Recall</b>
<ol>
<li>Was ist der Unterschied zwischen indizierten und assoziativen Arrays?</li>
<li>Was macht <code style="color:#777BB4">??</code> in PHP?</li>
<li>Wie iterierst du über ein assoziatives Array mit Keys und Werten?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 5 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">05</div>
<div class="chapter-title">
<h1>Control Flow</h1>
<div class="subtitle">if, match, foreach, while</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
Wie sagst du PHP "tu das nur, wenn X" oder "wiederhole das, bis Y"? Die klassischen Konstrukte kennst du aus jeder Sprache aber PHP 8 hat mit <code>match</code> ein modernes Werkzeug ergänzt, das vieles eleganter macht.
</div>
<h2>if, elseif, else</h2>
<pre><span class="k">if</span> (<span class="v">$score</span> &gt;= <span class="s">90</span>) {
<span class="v">$grade</span> = <span class="s">'A'</span>;
} <span class="k">elseif</span> (<span class="v">$score</span> &gt;= <span class="s">75</span>) {
<span class="v">$grade</span> = <span class="s">'B'</span>;
} <span class="k">else</span> {
<span class="v">$grade</span> = <span class="s">'C oder schlechter'</span>;
}
<span class="c">// Ternary für einfache Fälle</span>
<span class="v">$status</span> = <span class="v">$age</span> &gt;= <span class="s">18</span> ? <span class="s">'erwachsen'</span> : <span class="s">'minderjährig'</span>;</pre>
<h2>match: das moderne switch</h2>
<p>Seit PHP 8 gibt es <code class="inline">match</code> ähnlich wie <code class="inline">switch</code>, aber strikt im Vergleich, ein Ausdruck (also mit Rückgabewert), kein fallthrough:</p>
<pre><span class="v">$status</span> = <span class="k">match</span>(<span class="v">$code</span>) {
<span class="s">200</span>, <span class="s">201</span>, <span class="s">204</span> =&gt; <span class="s">'success'</span>,
<span class="s">301</span>, <span class="s">302</span> =&gt; <span class="s">'redirect'</span>,
<span class="s">404</span> =&gt; <span class="s">'not found'</span>,
<span class="s">500</span> =&gt; <span class="s">'server error'</span>,
<span class="k">default</span> =&gt; <span class="s">'unknown'</span>,
};</pre>
<p>Beachte die Kommas zwischen Werten sie bedeuten "oder". Und das <code class="inline">default</code> ist Pflicht (sonst Exception), das schützt vor vergessenen Fällen.</p>
<h2>Schleifen</h2>
<pre><span class="c">// for: bekannte Anzahl</span>
<span class="k">for</span> (<span class="v">$i</span> = <span class="s">0</span>; <span class="v">$i</span> &lt; <span class="s">10</span>; <span class="v">$i</span>++) {
<span class="k">echo</span> <span class="v">$i</span>;
}
<span class="c">// foreach: über Arrays</span>
<span class="k">foreach</span> (<span class="v">$items</span> <span class="k">as</span> <span class="v">$item</span>) {
<span class="k">echo</span> <span class="v">$item</span>;
}
<span class="c">// while: unbestimmte Anzahl</span>
<span class="k">while</span> (<span class="v">$line</span> = <span class="f">fgets</span>(<span class="v">$file</span>)) {
<span class="f">processLine</span>(<span class="v">$line</span>);
}
<span class="c">// break und continue</span>
<span class="k">foreach</span> (<span class="v">$items</span> <span class="k">as</span> <span class="v">$item</span>) {
<span class="k">if</span> (<span class="v">$item</span>-&gt;skip) <span class="k">continue</span>;
<span class="k">if</span> (<span class="v">$item</span>-&gt;stop) <span class="k">break</span>;
<span class="f">process</span>(<span class="v">$item</span>);
}</pre>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body">
<b>match statt switch</b>
In neuem Code: nimm <code class="inline">match</code>. Strikter Vergleich (===), kein fallthrough-Fehler, Ergebnis als Wert. <code class="inline">switch</code> nur noch in Legacy-Code oder wenn du komplexe Blöcke pro Fall brauchst.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Wann nutzt du <code style="color:#777BB4">foreach</code>, wann <code style="color:#777BB4">for</code>?</li>
<li>Was sind die wichtigsten Vorteile von <code style="color:#777BB4">match</code> gegenüber <code style="color:#777BB4">switch</code>?</li>
<li>Was ist der Unterschied zwischen <code style="color:#777BB4">break</code> und <code style="color:#777BB4">continue</code>?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 6 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">06</div>
<div class="chapter-title">
<h1>Funktionen mit Type-Hints</h1>
<div class="subtitle">Parameter, Rückgabewerte, Named Arguments</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
Eine Funktion ohne Type-Hints ist wie ein Brief ohne Adresse sie kommt vielleicht an, aber niemand weiß, was sie erwartet. Modernes PHP-Code hat Types überall. Wie schreibst du sie, und was kannst du damit?
</div>
<h2>Funktion deklarieren</h2>
<pre><span class="k">function</span> <span class="f">add</span>(<span class="t">int</span> <span class="v">$a</span>, <span class="t">int</span> <span class="v">$b</span>): <span class="t">int</span> {
<span class="k">return</span> <span class="v">$a</span> + <span class="v">$b</span>;
}
<span class="k">function</span> <span class="f">greet</span>(<span class="t">string</span> <span class="v">$name</span>, <span class="t">string</span> <span class="v">$greeting</span> = <span class="s">'Hallo'</span>): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"</span><span class="v">$greeting</span><span class="s">, </span><span class="v">$name</span><span class="s">!"</span>;
}
<span class="k">echo</span> <span class="f">add</span>(<span class="s">3</span>, <span class="s">4</span>); <span class="c">// 7</span>
<span class="k">echo</span> <span class="f">greet</span>(<span class="s">'Marek'</span>); <span class="c">// Hallo, Marek!</span></pre>
<p>Die Syntax: <code class="inline">function name(typ $param): rückgabetyp</code>. Beides ist optional, aber heute Standard.</p>
<h2>Named Arguments</h2>
<p>Seit PHP 8 kannst du Argumente beim Aufruf mit Namen übergeben. Praktisch bei Funktionen mit vielen Defaults:</p>
<pre><span class="k">function</span> <span class="f">createUser</span>(
<span class="t">string</span> <span class="v">$name</span>,
<span class="t">int</span> <span class="v">$age</span> = <span class="s">0</span>,
<span class="t">bool</span> <span class="v">$isAdmin</span> = <span class="k">false</span>,
?<span class="t">string</span> <span class="v">$email</span> = <span class="k">null</span>,
): <span class="t">User</span> { <span class="c">/* ... */</span> }
<span class="c">// Positional (alt)</span>
<span class="f">createUser</span>(<span class="s">'Marek'</span>, <span class="s">34</span>, <span class="k">false</span>, <span class="s">'m@e.de'</span>);
<span class="c">// Named (modern, lesbarer)</span>
<span class="f">createUser</span>(
name: <span class="s">'Marek'</span>,
email: <span class="s">'m@e.de'</span>,
isAdmin: <span class="k">true</span>,
);</pre>
<h2>Nullable und Union Types</h2>
<p>Mit <code class="inline">?</code> wird ein Typ nullable, mit <code class="inline">|</code> sind mehrere Typen erlaubt:</p>
<pre><span class="c">// Darf string oder null sein</span>
<span class="k">function</span> <span class="f">find</span>(<span class="t">int</span> <span class="v">$id</span>): ?<span class="t">User</span> {
<span class="k">return</span> <span class="v">$id</span> &gt; <span class="s">0</span> ? <span class="k">new</span> <span class="t">User</span>(<span class="v">$id</span>) : <span class="k">null</span>;
}
<span class="c">// Union: int ODER string</span>
<span class="k">function</span> <span class="f">parse</span>(<span class="t">int</span>|<span class="t">string</span> <span class="v">$input</span>): <span class="t">int</span> {
<span class="k">return</span> (<span class="t">int</span>) <span class="v">$input</span>;
}
<span class="c">// void: gibt nichts zurück</span>
<span class="k">function</span> <span class="f">log</span>(<span class="t">string</span> <span class="v">$msg</span>): <span class="t">void</span> {
<span class="f">file_put_contents</span>(<span class="s">'app.log'</span>, <span class="v">$msg</span>);
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body">
<b>Closures vs. globale Variablen</b>
Funktionen sehen <i>keine</i> Variablen aus dem umgebenden Code. Du musst sie explizit übergeben. Closures (anonyme Funktionen) können mit <code class="inline">use ($var)</code> Variablen importieren aber Globals mit <code class="inline">global $x</code> sind tabu.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Wie deklarierst du eine Funktion, die <code style="color:#777BB4">string</code> zurückgibt?</li>
<li>Was ist der Vorteil von Named Arguments?</li>
<li>Was bedeutet <code style="color:#777BB4">?string</code> als Typ?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 7 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">07</div>
<div class="chapter-title">
<h1>Klassen und Objekte</h1>
<div class="subtitle">Promoted Constructor, readonly, Methoden</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
In PHP 5 brauchten Klassen viel Boilerplate: Properties, Konstruktor, Getter, Setter seitenweise Code für simple Daten. PHP 8 hat das drastisch reduziert. Was sieht eine moderne Klasse heute aus?
</div>
<h2>Klasse mit Promoted Constructor</h2>
<p>Das wichtigste PHP-8-Feature für Klassen: Konstruktor-Parameter werden automatisch zu Properties, wenn du sie mit einem Sichtbarkeits-Modifier markierst:</p>
<pre><span class="k">class</span> <span class="t">User</span> {
<span class="k">public function</span> <span class="f">__construct</span>(
<span class="k">public readonly</span> <span class="t">string</span> <span class="v">$name</span>,
<span class="k">public readonly</span> <span class="t">int</span> <span class="v">$age</span>,
<span class="k">private</span> ?<span class="t">string</span> <span class="v">$email</span> = <span class="k">null</span>,
) {}
<span class="k">public function</span> <span class="f">isAdult</span>(): <span class="t">bool</span> {
<span class="k">return</span> <span class="v">$this</span>-&gt;age &gt;= <span class="s">18</span>;
}
<span class="k">public function</span> <span class="f">getEmail</span>(): ?<span class="t">string</span> {
<span class="k">return</span> <span class="v">$this</span>-&gt;email;
}
}
<span class="v">$user</span> = <span class="k">new</span> <span class="t">User</span>(name: <span class="s">'Marek'</span>, age: <span class="s">34</span>);
<span class="k">echo</span> <span class="v">$user</span>-&gt;name; <span class="c">// 'Marek'</span>
<span class="k">echo</span> <span class="v">$user</span>-&gt;<span class="f">isAdult</span>() ? <span class="s">'ja'</span> : <span class="s">'nein'</span>;</pre>
<p><code class="inline">readonly</code> verhindert nachträgliche Änderungen. <code class="inline">public</code>, <code class="inline">private</code>, <code class="inline">protected</code> steuern die Sichtbarkeit von außen.</p>
<h2>Vererbung und Interfaces</h2>
<pre><span class="k">interface</span> <span class="t">Greetable</span> {
<span class="k">public function</span> <span class="f">greet</span>(): <span class="t">string</span>;
}
<span class="k">class</span> <span class="t">User</span> <span class="k">implements</span> <span class="t">Greetable</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public readonly</span> <span class="t">string</span> <span class="v">$name</span>) {}
<span class="k">public function</span> <span class="f">greet</span>(): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"Hallo, </span><span class="v">$this</span><span class="s">-&gt;name"</span>;
}
}
<span class="k">class</span> <span class="t">Admin</span> <span class="k">extends</span> <span class="t">User</span> {
<span class="k">public function</span> <span class="f">greet</span>(): <span class="t">string</span> {
<span class="k">return</span> <span class="s">"Hallo Admin </span><span class="v">$this</span><span class="s">-&gt;name"</span>;
}
}</pre>
<h2>Static Methods und Konstanten</h2>
<pre><span class="k">class</span> <span class="t">Math</span> {
<span class="k">public const</span> PI = <span class="s">3.14159</span>;
<span class="k">public static function</span> <span class="f">square</span>(<span class="t">int</span> <span class="v">$x</span>): <span class="t">int</span> {
<span class="k">return</span> <span class="v">$x</span> * <span class="v">$x</span>;
}
}
<span class="k">echo</span> <span class="t">Math</span>::PI; <span class="c">// 3.14159</span>
<span class="k">echo</span> <span class="t">Math</span>::<span class="f">square</span>(<span class="s">5</span>); <span class="c">// 25</span></pre>
<div class="callout note">
<div class="callout-icon">i</div>
<div class="callout-body">
<b>Konstruktor-Promotion sparsam einsetzen</b>
Bei mehr als 5-6 Parametern wird die Konstruktor-Liste unübersichtlich. Dann lieber DTOs oder Builder-Pattern. Aber für die meisten Cases mit 2-4 Properties ist Promotion ein riesiger Gewinn an Lesbarkeit.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Was generiert <code style="color:#777BB4">public readonly string $name</code> im Konstruktor?</li>
<li>Was ist der Unterschied zwischen <code style="color:#777BB4">extends</code> und <code style="color:#777BB4">implements</code>?</li>
<li>Wie greifst du auf eine static-Konstante zu?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 8 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">08</div>
<div class="chapter-title">
<h1>Namespaces und Autoloading</h1>
<div class="subtitle">PSR-4, use-Statements, Datei-Organisation</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
Bei zwei Klassen mit dem Namen <code>User</code> eine für Admins, eine für Kunden kracht es. Wie verhindert PHP solche Konflikte? Namespaces sind die Antwort, und sie hängen eng mit Composers Autoloading zusammen.
</div>
<h2>Namespace deklarieren</h2>
<p>Ein Namespace gibt deinen Klassen einen "Pfad". Konvention: ein Namespace pro Datei, am Anfang deklariert:</p>
<pre><span class="t">&lt;?php</span>
<span class="k">declare</span>(strict_types=<span class="s">1</span>);
<span class="k">namespace</span> App\Domain\User;
<span class="k">class</span> <span class="t">User</span> {
<span class="k">public function</span> <span class="f">__construct</span>(<span class="k">public readonly</span> <span class="t">string</span> <span class="v">$name</span>) {}
}</pre>
<p>Die volle Bezeichnung dieser Klasse ist jetzt <code class="inline">App\Domain\User\User</code>. Eine andere Klasse mit demselben Namen in einem anderen Namespace kollidiert nicht.</p>
<h2>Klassen importieren mit use</h2>
<pre><span class="t">&lt;?php</span>
<span class="k">namespace</span> App\Controller;
<span class="k">use</span> App\Domain\User\User;
<span class="k">use</span> App\Domain\Order\Order;
<span class="k">class</span> <span class="t">UserController</span> {
<span class="k">public function</span> <span class="f">show</span>(<span class="t">int</span> <span class="v">$id</span>): <span class="t">User</span> {
<span class="k">return new</span> <span class="t">User</span>(<span class="s">'Marek'</span>); <span class="c">// kein voller Pfad nötig</span>
}
}</pre>
<p>Mit Aliassen kannst du Namen umbenennen, wenn es Konflikte gibt:</p>
<pre><span class="k">use</span> App\Domain\User\User <span class="k">as</span> DomainUser;
<span class="k">use</span> App\Http\User <span class="k">as</span> HttpUser;</pre>
<h2>PSR-4: Datei = Namespace</h2>
<p><b>PSR-4</b> ist die Konvention, wie Namespaces auf Dateipfade abgebildet werden. Composer nutzt das für Autoloading. Eine typische Struktur:</p>
<pre>my-project/
├── composer.json
├── vendor/ <span class="c"># installierte Pakete</span>
└── src/
├── Controller/
│ └── UserController.php <span class="c"># App\Controller\UserController</span>
└── Domain/
└── User/
└── User.php <span class="c"># App\Domain\User\User</span></pre>
<p>In <code class="inline">composer.json</code> definierst du das Mapping:</p>
<pre>{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}</pre>
<p>Nach <code class="inline">composer dump-autoload</code> findet PHP jede Klasse anhand ihres Namespaces automatisch kein manuelles <code class="inline">require</code> mehr.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body">
<b>Eine Klasse, eine Datei</b>
Konvention: pro Datei genau eine Klasse, Datei-Name gleich Klassen-Name (case-sensitive!). <code class="inline">User.php</code> enthält <code class="inline">class User</code>. Die meisten Frameworks und Tools verlassen sich darauf.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Wozu sind Namespaces da?</li>
<li>Was bedeutet PSR-4 in einem Satz?</li>
<li>Wie importierst du eine Klasse aus einem anderen Namespace?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 9 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">09</div>
<div class="chapter-title">
<h1>Fehlerbehandlung</h1>
<div class="subtitle">Exceptions, try/catch, eigene Fehler</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
Was passiert, wenn eine Datei nicht existiert, eine Datenbankverbindung scheitert oder eine API einen unerwarteten Wert liefert? In PHP gibt es zwei Welten: alte <code>false</code>-Rückgaben und moderne Exceptions. Wie navigierst du beide?
</div>
<h2>Exceptions verstehen</h2>
<p>Eine <b>Exception</b> ist PHPs moderne Art, Fehler zu signalisieren. Wird sie nicht gefangen, bricht das Script ab. Mit <code class="inline">try/catch</code> reagierst du gezielt:</p>
<pre><span class="k">try</span> {
<span class="v">$data</span> = <span class="f">json_decode</span>(<span class="v">$json</span>, flags: JSON_THROW_ON_ERROR);
<span class="v">$user</span> = <span class="f">processData</span>(<span class="v">$data</span>);
} <span class="k">catch</span> (\<span class="t">JsonException</span> <span class="v">$e</span>) {
<span class="k">echo</span> <span class="s">"Ungültiges JSON: "</span> . <span class="v">$e</span>-&gt;<span class="f">getMessage</span>();
} <span class="k">catch</span> (\<span class="t">Exception</span> <span class="v">$e</span>) {
<span class="k">echo</span> <span class="s">"Anderer Fehler: "</span> . <span class="v">$e</span>-&gt;<span class="f">getMessage</span>();
} <span class="k">finally</span> {
<span class="f">cleanup</span>();
}</pre>
<p>Der <code class="inline">finally</code>-Block läuft immer auch wenn die Exception nicht gefangen wurde. Praktisch für Cleanup wie Datei schließen oder Locks freigeben.</p>
<h2>Mehrere Exception-Typen</h2>
<p>PHP kennt eine ganze Hierarchie von Exception-Typen. Du kannst gezielt darauf reagieren:</p>
<table>
<tr><th>Exception</th><th>Wann</th></tr>
<tr><td><code>InvalidArgumentException</code></td><td>Falsche Funktionsargumente</td></tr>
<tr><td><code>RuntimeException</code></td><td>Fehler zur Laufzeit</td></tr>
<tr><td><code>TypeError</code></td><td>Type-Hint verletzt</td></tr>
<tr><td><code>ValueError</code></td><td>Wert außerhalb erlaubtem Bereich</td></tr>
<tr><td><code>JsonException</code></td><td>JSON-Parsing fehlgeschlagen</td></tr>
<tr><td><code>PDOException</code></td><td>Datenbankfehler</td></tr>
</table>
<h2>Eigene Exceptions</h2>
<p>Für domänenspezifische Fehler definierst du eigene Exception-Klassen. Sie erben von <code class="inline">\Exception</code>:</p>
<pre><span class="k">class</span> <span class="t">InsufficientFundsException</span> <span class="k">extends</span> \<span class="t">Exception</span> {}
<span class="k">function</span> <span class="f">withdraw</span>(<span class="t">int</span> <span class="v">$amount</span>): <span class="t">void</span> {
<span class="k">if</span> (<span class="v">$amount</span> &gt; <span class="v">$this</span>-&gt;balance) {
<span class="k">throw new</span> <span class="t">InsufficientFundsException</span>(
<span class="s">"Brauche </span><span class="v">$amount</span><span class="s">, habe nur </span><span class="v">$this</span><span class="s">-&gt;balance"</span>
);
}
<span class="v">$this</span>-&gt;balance -= <span class="v">$amount</span>;
}</pre>
<div class="callout warn">
<div class="callout-icon">!</div>
<div class="callout-body">
<b>Niemals @-Suppression</b>
Mit <code class="inline">@functionCall()</code> kannst du Warnings unterdrücken. Tu's nicht du versteckst nur Probleme, die später schwer zu finden sind. Wenn du weißt, dass etwas schiefgehen kann, behandle es explizit mit try/catch.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Was passiert, wenn eine Exception nicht gefangen wird?</li>
<li>Wann läuft der <code style="color:#777BB4">finally</code>-Block?</li>
<li>Warum solltest du eigene Exception-Klassen schreiben?</li>
</ol>
</div>
</section>
<!-- ===== KAPITEL 10 ===== -->
<section class="chapter">
<div class="chapter-head">
<div class="chapter-num">10</div>
<div class="chapter-title">
<h1>Composer und erste Anwendung</h1>
<div class="subtitle">Pakete installieren, kleines CLI-Tool bauen</div>
</div>
</div>
<div class="gap">
<b>Frage zum Einstieg</b>
Du hast PHP gelernt aber wie kommst du jetzt zu einem echten Projekt? <b>Composer</b> ist die Brücke: er installiert Pakete aus dem riesigen PHP-Ökosystem, lädt Klassen automatisch und gibt deinem Projekt Struktur. Lass uns ein kleines Tool bauen.
</div>
<h2>Projekt aufsetzen</h2>
<p>Composer ist der Standard-Paketmanager für PHP. Installation einmal global, dann pro Projekt:</p>
<pre><span class="c"># Neues Projekt</span>
mkdir hello-cli
cd hello-cli
composer init <span class="c"># interaktiver Wizard</span>
<span class="c"># Paket hinzufügen</span>
composer require symfony/console
<span class="c"># Projektstruktur anlegen</span>
mkdir src
mkdir bin</pre>
<p>Composer erzeugt <code class="inline">composer.json</code> mit deinen Abhängigkeiten und den Ordner <code class="inline">vendor/</code> mit den installierten Paketen.</p>
<h2>composer.json mit Autoload</h2>
<pre>{
"name": "marek/hello-cli",
"type": "project",
"require": {
"php": "^8.4",
"symfony/console": "^7.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}</pre>
<p>Nach Änderungen am Autoload: <code class="inline">composer dump-autoload</code> ausführen, damit PHP die neuen Pfade kennt.</p>
<h2>Kleines CLI-Tool bauen</h2>
<p>Mit Symfony Console schreibst du in wenigen Zeilen ein professionelles CLI-Programm:</p>
<pre><span class="c">// src/GreetCommand.php</span>
<span class="t">&lt;?php</span>
<span class="k">namespace</span> App;
<span class="k">use</span> Symfony\Component\Console\Command\Command;
<span class="k">use</span> Symfony\Component\Console\Input\InputInterface;
<span class="k">use</span> Symfony\Component\Console\Input\InputArgument;
<span class="k">use</span> Symfony\Component\Console\Output\OutputInterface;
<span class="k">class</span> <span class="t">GreetCommand</span> <span class="k">extends</span> <span class="t">Command</span> {
<span class="k">protected static</span> <span class="v">$defaultName</span> = <span class="s">'greet'</span>;
<span class="k">protected function</span> <span class="f">configure</span>(): <span class="t">void</span> {
<span class="v">$this</span>-&gt;<span class="f">addArgument</span>(<span class="s">'name'</span>, InputArgument::REQUIRED);
}
<span class="k">protected function</span> <span class="f">execute</span>(InputInterface <span class="v">$in</span>, OutputInterface <span class="v">$out</span>): <span class="t">int</span> {
<span class="v">$name</span> = <span class="v">$in</span>-&gt;<span class="f">getArgument</span>(<span class="s">'name'</span>);
<span class="v">$out</span>-&gt;<span class="f">writeln</span>(<span class="s">"&lt;info&gt;Hallo, </span><span class="v">$name</span><span class="s">!&lt;/info&gt;"</span>);
<span class="k">return</span> Command::SUCCESS;
}
}</pre>
<pre><span class="c">// bin/app.php</span>
<span class="t">&lt;?php</span>
<span class="k">require</span> __DIR__ . <span class="s">'/../vendor/autoload.php'</span>;
<span class="k">use</span> Symfony\Component\Console\Application;
<span class="k">use</span> App\GreetCommand;
<span class="v">$app</span> = <span class="k">new</span> Application();
<span class="v">$app</span>-&gt;<span class="f">add</span>(<span class="k">new</span> <span class="t">GreetCommand</span>());
<span class="v">$app</span>-&gt;<span class="f">run</span>();</pre>
<p>Im Terminal: <code class="inline">php bin/app.php greet Marek</code>. Du siehst "Hallo, Marek!" in Grün. Das ist ein vollständiges CLI-Tool mit Argument-Parsing, Help-Output und Exit-Codes mit weniger als 50 Zeilen Code.</p>
<div class="callout tip">
<div class="callout-icon">✓</div>
<div class="callout-body">
<b>Erst Tools, dann Framework</b>
Mit Composer und einzelnen Symfony-Komponenten kommst du bei kleinen Projekten weit. Erst wenn du Routing, Templates, ORM, Auth zusammen brauchst, lohnt sich der Sprung zu einem vollen Framework wie <b>Laravel</b> oder <b>Symfony</b>.
</div>
</div>
<div class="recall">
<b>Recall</b>
<ol>
<li>Welche Datei steuert Composers Abhängigkeiten und Autoload?</li>
<li>Wozu ist <code style="color:#777BB4">composer dump-autoload</code>?</li>
<li>Welche Library nimmst du für CLI-Tools in PHP?</li>
</ol>
</div>
</section>
<!-- ===== ENDING ===== -->
<section class="ending">
<h1>Wie es weitergeht</h1>
<p>Du hast PHP jetzt in 10 Kapiteln vom ersten Script bis zur Composer-Anwendung durchlaufen. Aber Wissen verblasst ohne Wiederholung. Plane <b>aktive Wiederholung</b> ein effektiver als jedes Re-Reading.</p>
<h2>Spaced-Repetition-Plan</h2>
<div class="spaced-plan">
<div class="spaced-day">
<b>Heute</b>
<p>Guide gelesen, Recall-Fragen aus jedem Kapitel beantwortet.</p>
</div>
<div class="spaced-day">
<b>+1 Tag</b>
<p>OnePager überfliegen, alle Recall-Fragen aus dem Kopf beantworten.</p>
</div>
<div class="spaced-day">
<b>+7 Tage</b>
<p>Mini-Projekt: ein eigenes CLI-Tool mit Composer und Symfony Console.</p>
</div>
<div class="spaced-day">
<b>+30 Tage</b>
<p>Cheatsheet als Referenz ein neues Paket aus Packagist einbinden.</p>
</div>
</div>
<h2>Was als nächstes lernen</h2>
<p>Mit diesen Grundlagen kannst du in jede Spezialisierung einsteigen. Empfehlungen je nach Interesse:</p>
<ul>
<li><b>Web-Frameworks</b> Laravel für schnellen Einstieg, Symfony für modulare Apps</li>
<li><b>Datenbanken</b> PDO für SQL direkt, Doctrine als ORM für komplexe Domänen</li>
<li><b>Testing</b> PHPUnit für Unit-Tests, Mockery für Mocks</li>
<li><b>Static Analysis</b> PHPStan und Psalm finden Bugs ohne Ausführung</li>
<li><b>E-Commerce</b> Shopware oder Magento für Online-Shops</li>
<li><b>CMS</b> WordPress, Drupal oder TYPO3 für Content-Sites</li>
</ul>
<h2>Begleitmaterial</h2>
<p>Dieser Guide ist Teil eines Sets:</p>
<ul>
<li><b>PHP OnePager</b> die visuelle Übersicht für den Schreibtisch</li>
<li><b>PHP Cheatsheet</b> die dichte Referenz beim Programmieren</li>
<li><b>PHP Mini-Guide</b> der 15-Min-Schnelleinstieg</li>
<li><b>PHP Anfänger-Guide</b> (dieses Dokument) die Grundlagen tief</li>
</ul>
</section>
</body>
</html>
```