1438 lines
56 KiB
Markdown
1438 lines
56 KiB
Markdown
```
|
||
<!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"><?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> => <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> = <<<<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> = <<<<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> => <span class="s">'Marek'</span>,
|
||
<span class="s">'age'</span> => <span class="s">34</span>,
|
||
<span class="s">'roles'</span> => [<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> => <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> >= <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> >= <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> >= <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> => <span class="s">'success'</span>,
|
||
<span class="s">301</span>, <span class="s">302</span> => <span class="s">'redirect'</span>,
|
||
<span class="s">404</span> => <span class="s">'not found'</span>,
|
||
<span class="s">500</span> => <span class="s">'server error'</span>,
|
||
<span class="k">default</span> => <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> < <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>->skip) <span class="k">continue</span>;
|
||
<span class="k">if</span> (<span class="v">$item</span>->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> > <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>->age >= <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>->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>->name; <span class="c">// 'Marek'</span>
|
||
<span class="k">echo</span> <span class="v">$user</span>-><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">->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">->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"><?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"><?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>-><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>-><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> > <span class="v">$this</span>->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">->balance"</span>
|
||
);
|
||
}
|
||
<span class="v">$this</span>->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"><?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>-><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>-><span class="f">getArgument</span>(<span class="s">'name'</span>);
|
||
<span class="v">$out</span>-><span class="f">writeln</span>(<span class="s">"<info>Hallo, </span><span class="v">$name</span><span class="s">!</info>"</span>);
|
||
<span class="k">return</span> Command::SUCCESS;
|
||
}
|
||
}</pre>
|
||
|
||
<pre><span class="c">// bin/app.php</span>
|
||
<span class="t"><?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>-><span class="f">add</span>(<span class="k">new</span> <span class="t">GreetCommand</span>());
|
||
<span class="v">$app</span>-><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>
|
||
``` |