diff --git a/backend/config.py b/backend/config.py
index b29a9c9..38aec00 100644
--- a/backend/config.py
+++ b/backend/config.py
@@ -1,7 +1,7 @@
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
-DOC_DIR = PROJECT_ROOT / "doc"
+TEMPLATES_DIR = PROJECT_ROOT / "templates"
STORAGE_DIR = PROJECT_ROOT / "storage"
DB_PATH = PROJECT_ROOT / "guides.db"
diff --git a/backend/generator.py b/backend/generator.py
index 7c3e9e6..afced1b 100644
--- a/backend/generator.py
+++ b/backend/generator.py
@@ -6,7 +6,7 @@ from pathlib import Path
from config import (
CLAUDE_CLI,
- DOC_DIR,
+ TEMPLATES_DIR,
GENERATION_TIMEOUTS,
MAX_CONCURRENT_GENERATIONS,
MAX_ITERATIONS,
@@ -80,8 +80,8 @@ async def _render_pngs(pdf_path: Path, preview_dir: Path) -> list[Path]:
def _build_generator_prompt(topic: str, format_name: str, html_path: Path) -> str:
- spec = (DOC_DIR / "Format" / f"{format_name}.md").read_text(encoding="utf-8")
- reference = (DOC_DIR / "Referenz" / f"{format_name}.md").read_text(encoding="utf-8")
+ spec = (TEMPLATES_DIR / "Format" / f"{format_name}.md").read_text(encoding="utf-8")
+ reference = (TEMPLATES_DIR / "Referenz" / f"{format_name}.md").read_text(encoding="utf-8")
return f"""Erstelle einen Lern-Guide zum Thema "{topic}" im Format "{format_name}".
@@ -111,7 +111,7 @@ Führe KEIN weasyprint aus, erzeuge KEINE PDF.
def _build_review_prompt(format_name: str, png_paths: list[Path], page_count: int) -> str:
- spec = (DOC_DIR / "Format" / f"{format_name}.md").read_text(encoding="utf-8")
+ spec = (TEMPLATES_DIR / "Format" / f"{format_name}.md").read_text(encoding="utf-8")
png_list = "\n".join(str(p) for p in png_paths)
diff --git a/templates/Format/BeginnerGuide.md b/templates/Format/BeginnerGuide.md
new file mode 100644
index 0000000..6622d78
--- /dev/null
+++ b/templates/Format/BeginnerGuide.md
@@ -0,0 +1,147 @@
+```
+ANFÄNGER-GUIDE-STIL (HTML/CSS → PDF via WeasyPrint)
+
+FORMAT
+- A4 Hochformat, mehrseitig
+- @page { size: A4; margin: 22mm 20mm 20mm 20mm; }
+- @page :first { margin: 0; } für Cover
+- Footer: Seitenzahl Mitte, Guide-Titel rechts (außer Cover)
+
+UMFANG
+- 8-12 Kapitel à ~15 Min Lesezeit
+- ~1500 Wörter Fließtext pro Kapitel
+- 2-3 Code-Beispiele pro Kapitel (kurz, 5-15 Zeilen)
+- 15-25 Seiten gesamt
+- ~2-3h Lesezeit gesamt
+
+EINSTIEGSNIVEAU
+- Setzt nur voraus, dass der Leser weiß, WAS das Thema ist
+- Kapitel 1 erklärt Setup und erste Schritte von Null
+- Keine Vorkenntnisse im Thema selbst
+- Erklärt Begriffe beim ersten Auftreten
+
+KAPITEL-PROGRESSION
+- Aufgeteilt in 3 Teile mit eigenen TOC-Sektionen:
+ - Teil 1: Grundlagen — Was ist es, wie startet man, erste Konzepte
+ - Teil 2: Strukturen/Bausteine — Die wichtigsten Mechanismen
+ - Teil 3: Echte Anwendungen — Integration, Tooling, kleine Projekte
+- Jedes Kapitel baut auf vorherigen auf
+- Tiefere Konzepte erst nach den Grundlagen
+- Letztes Kapitel idealerweise mit kleiner echter Anwendung
+
+STRUKTUR
+1. Cover: vollflächiger Hintergrund, Hero-Aussage, Outcome-Versprechen
+2. Inhaltsverzeichnis: 3 Teile, nummeriert, mit Zeit-Markern (15 Min)
+3. Kapitel 1-N
+4. Ending: Spaced-Repetition-Plan, nächste Schritte, Begleitmaterial
+
+KAPITEL-AUFBAU
+1. Kapitel-Head: große Nummer + Titel + Subtitle, Trennlinie
+2. Gap-Opener: kursiv eingerahmt, Information-Gap explizit
+3. 2-4 H2-Sektionen
+4. Pro Sektion: Erklärtext + Code-Beispiel + ggf. Callout
+5. Recall-Box am Ende (3 Fragen)
+
+ELEMENTE
+- Fließtext: justify mit Silbentrennung
+- Codeblöcke: dunkler Hintergrund, syntax highlighting
+- Inline-Code: heller Hintergrund, Hauptfarbe
+- Tabellen: Header farbig (Hauptfarbe-Dunkel), Zeilen mit dünner Trennlinie
+- Callouts in 3 Varianten: tip (grün), warn (rot), note (Hauptfarbe)
+- Recall-Box: dunkler Hintergrund mit Akzentfarbe
+
+TYPOGRAFIE
+- Body: 10.5pt Serif (Charter), line-height 1.55
+- H1 Kapitel: 22pt Sans-Serif bold, Hauptfarbe-Dunkel
+- H2 Sektion: 14pt Sans-Serif bold
+- H3 Subsektion: 11pt Sans-Serif bold
+- Code: 8.5pt Monospace, line-height 1.5
+- Inline-Code: 9pt Monospace
+- Recall/Callout-Labels: 8pt uppercase, letter-spacing 1pt
+- Cover-H1: 56pt Sans-Serif bold, letter-spacing -2pt
+
+FARBEN (max 3 + Neutrals)
+- Hauptfarbe: kräftig, an offizielle Farbe des Themas anlehnen
+- Hauptfarbe-Dunkel: dunklere Variante für Headings und Akzente
+- Hauptfarbe-Darker: noch dunkler für Cover-Verlauf und Recall-Box
+- Hintergrund-Soft: helle Variante der Hauptfarbe
+- Code-Hintergrund: #1e2a3a (dunkel)
+- Text: #1a1a1a / muted #5a6470 / Linie #d8dde3
+- Callout-Farben: grün/rot/Hauptfarbe
+
+INFORMATION-GAP-OPENER (PFLICHT pro Kapitel)
+- Kursiv, eingerahmt mit Hauptfarbe-Border
+- Stellt konkrete Frage, die das Kapitel beantwortet
+- Erzeugt Spannung (Information-Gap)
+- Niveau passend zum Kapitel:
+ - Kapitel 1: setzt nur Grundverständnis voraus
+ - Letztes Kapitel: darf auf alle vorherigen Kapitel aufbauen
+
+RECALL-BOX (PFLICHT pro Kapitel)
+- Am Kapitel-Ende
+- Dunkler Hintergrund mit Akzentfarbe
+- 3 nummerierte Fragen
+- Direkt auf Kapitel-Inhalt bezogen
+- Code-Snippets in Fragen mit Akzentfarbe hervorgehoben
+
+ENDING (PFLICHT)
+- Spaced-Repetition-Plan: 4 Karten (Heute, +1 Tag, +7 Tage, +30 Tage)
+- "Was als nächstes lernen" mit Spezialisierungs-Vorschlägen
+- Verweis auf Begleitmaterial (OnePager, Cheatsheet, Mini-Guide)
+
+CALLOUT-NUTZUNG
+- tip (grün): Best Practice, idiomatische Lösung
+- warn (rot): Fallen, häufige Bugs, Anti-Patterns
+- note (Hauptfarbe): Hintergrund-Info, Querverweis
+
+CALLOUT-CSS WICHTIG
+- .callout-body > b:first-child mit display:block für Label
+- NICHT .callout-body b global mit display:block (zerstört Inline-Bold)
+- Mehrzeiliger Body-Text in
wrappen wenn Inline-Bolds drin sind
+
+THEMENSPEZIFISCHE ANPASSUNGEN (vor Generierung wählen)
+- Hauptfarbe: offizielle Farbe des Themas
+- Logo-Buchstabe(n): erstes Zeichen oder Kürzel
+- Version + Stand-Datum
+- 8-12 Kapitel-Titel mit progressivem Aufbau
+
+PFLICHT-ELEMENTE PRO KAPITEL
+- 1 Gap-Opener am Anfang
+- 2-3 Code-Beispiele (kurz, 5-15 Zeilen)
+- Mindestens 1 Callout
+- 1 Recall-Box am Ende
+
+VERMEIDEN
+- Einleitungs-Floskeln ("In diesem Kapitel lernen wir...")
+- Wiederholungen aus vorherigem Kapitel
+- Übersichts-Inhalt (steht im OnePager)
+- Reine Referenz-Tabellen (stehen im Cheatsheet)
+- Konzepte vorwegnehmen, die später dran sind
+- Vorausgesetztes Wissen über das Thema
+- page-break mitten in Codeblock oder Callout (page-break-inside: avoid)
+- Mehr als 3 Schriftgrößen pro Sektion
+- Floats oder absolute positioning (bricht in WeasyPrint)
+- Subscript/Superscript via Unicode (nicht alle Fonts unterstützen das)
+
+GENERIERUNG MIT FEEDBACK-LOOP (max 3 Iterationen)
+1. HTML schreiben
+2. weasyprint file.html file.pdf (Timeout 240s)
+3. PDF zu PNGs: alle Seiten konvertieren
+4. Mehrere Seiten ansehen (Cover, TOC, Kapitel 1, mittlere Seite, Ending)
+5. Prüfen:
+ - Cover randlos und ohne Footer?
+ - TOC zeigt alle 3 Teile?
+ - Kapitel 1 fängt wirklich bei Null an?
+ - Kapitel beginnen auf neuer Seite?
+ - Code-Blöcke nicht über Seitenumbruch zerrissen?
+ - Recall-Boxen vollständig sichtbar?
+ - Footer mit Seitenzahl korrekt?
+ - Inline-Bolds in Callouts korrekt (nicht als Blöcke)?
+ - Steigt der Schwierigkeitsgrad spürbar von Kapitel zu Kapitel?
+6. Bei Problemen: fixen, ab Schritt 2 wiederholen
+7. Nach max 3 Iterationen ausgeben
+
+INSTALLATION
+- pip install weasyprint pdf2image
+- apt install poppler-utils
+```
\ No newline at end of file
diff --git a/templates/Format/Cheatsheet.md b/templates/Format/Cheatsheet.md
new file mode 100644
index 0000000..ed39a65
--- /dev/null
+++ b/templates/Format/Cheatsheet.md
@@ -0,0 +1,104 @@
+```
+CHEATSHEET-STIL QUERFORMAT (HTML/CSS → PDF via WeasyPrint)
+
+FORMAT
+- A4 Querformat (297mm × 210mm)
+- @page { size: A4 landscape; margin: 0; }
+- Padding: 7mm 9mm 18mm 9mm (unten Platz für Footer)
+- Position relative für absoluten Footer
+
+LAYOUT
+- Grid: hero (auto) / main (4 Spalten) / footer (absolute)
+- Main: 4 Spalten gleich breit, gap 3mm
+- Spalten intern: flex-column, gap 3mm
+- 8-12 thematische Blöcke verteilt (2-3 pro Spalte)
+
+STRUKTUR (in dieser Reihenfolge)
+1. Hero: Logo links (15mm), Titel + Untertitel mittig, Version + Stand rechts
+2. Main-Grid: 8-12 Blöcke in 4 Spalten
+3. Footer: farbige Box mit Quick-Commands + Tag (absolute, unten)
+
+UNTERSCHIEDE ZU HOCHFORMAT-CHEATSHEET
+- 4 Spalten statt 3 (mehr horizontaler Platz)
+- 9-12 Blöcke statt 8-10
+- Footer mit 4 Quick-Commands statt 3
+- Kleinere Schriftgrößen (8pt Body statt 8.5pt)
+- Kompaktere Code-Blöcke (6.5pt statt 6.8pt)
+
+BLOCK-AUFBAU
+- Block-Head: Hauptfarbe-Hintergrund, weißer Text, Icon links (3.5mm)
+- Block-Body: weißer Hintergrund, dünner Rand, abgerundet 2mm
+- Inhalt: dichte Referenz, nicht Erklärung
+- Varianten: Referenz-Tabelle, Code-Block, Kachel-Grid, Plus/Minus
+
+FARBEN (max 3 + Neutrals)
+- Hauptfarbe: an offizielle Farbe des Themas anlehnen
+- Hauptfarbe-Dunkel: dunklere Variante für Headings
+- Hauptfarbe-Darker: noch dunkler für Footer
+- Akzentfarbe für Plus/Minus: grün/rot
+- Hintergrund-Soft: helle Variante der Hauptfarbe
+- Code-Hintergrund: #1e2a3a (dunkel)
+- Text: #1a1a1a / muted #5a6470 / Linie #d8dde3
+
+TYPOGRAFIE
+- Body: 8pt, line-height 1.35
+- Hero h1: 18pt bold
+- Block-Head: 8pt bold uppercase, letter-spacing 0.5pt
+- Tabellen: 7.5pt, Keys 7pt monospace
+- Code: 6.5pt monospace, line-height 1.4
+- Inline-Code: 7pt monospace, Hauptfarbe
+- Max 3 Schriftgrößen pro Block
+
+ICONS
+- SVG inline, stroke statt fill
+- 3.5mm in Block-Heads
+- 2mm in Tile-Icons (kleiner als Hochformat)
+- currentColor für automatische Anpassung
+
+BLOCK-TYPEN (für Variation nutzen)
+- Referenz-Tabelle: 2-spaltig (Befehl/Methode → Bedeutung)
+- Code-Block: vollständiges Beispiel mit Syntax-Highlighting
+- Kachel-Grid: 2x4 mit Icons (z.B. Ökosystem)
+- Direktiven-Grid: 2-spaltig kompakte Begriffe + Kurzbeschreibung
+- Plus/Minus-Split: 2-spaltig (idiomatisch vs vermeiden)
+
+THEMENSPEZIFISCHE ANPASSUNGEN (vor Generierung wählen)
+- Hauptfarbe: offizielle Farbe des Themas
+- Logo-Buchstabe(n) oder Kürzel
+- Version + Stand-Datum
+- Block-Auswahl: 8-12 wichtigste Referenz-Themen
+- Quick-Commands im Footer: 4 wichtigste Kommandos
+
+VISUELLE ELEMENTE PFLICHT
+- Mindestens 1 Code-Block (oft mehrere im Querformat)
+- Mindestens 1 Kachel-Grid mit Icons
+- Mindestens 1 Plus/Minus-Split
+- Footer mit Quick-Commands
+- Versionsbadge in Hero
+
+VERMEIDEN
+- Reine Bullet-Listen ohne Struktur
+- Erklärtext (gehört in Guide, nicht Cheatsheet)
+- Mehr als 12 Blöcke (überfüllt)
+- Mehr als 3 Schriftgrößen
+- Vertikal sehr lange Blöcke (Spalten unbalanciert)
+- Floats oder absolute positioning (außer für Footer)
+
+GENERIERUNG MIT FEEDBACK-LOOP (max 3 Iterationen)
+1. HTML schreiben
+2. weasyprint file.html file.pdf
+3. PDF zu PNG: python -c "from pdf2image import convert_from_path; convert_from_path('file.pdf', dpi=120)[0].save('preview.png')"
+4. Preview ansehen mit Read-Tool
+5. Prüfen:
+ - Footer überlappt nicht mit Inhalt?
+ - 4 Spalten balanciert (ähnliche Höhe)?
+ - Alle Blöcke vollständig sichtbar?
+ - Code-Blöcke nicht abgeschnitten?
+ - Icons rendern?
+6. Bei Problemen: Inhalt straffen oder padding-bottom erhöhen, ab Schritt 2 wiederholen
+7. Nach max 3 Iterationen ausgeben
+
+INSTALLATION
+- pip install weasyprint pdf2image
+- apt install poppler-utils
+```
\ No newline at end of file
diff --git a/templates/Format/ExtendedGuide.md b/templates/Format/ExtendedGuide.md
new file mode 100644
index 0000000..e226c78
--- /dev/null
+++ b/templates/Format/ExtendedGuide.md
@@ -0,0 +1,171 @@
+```
+EXTENDED-GUIDE-STIL (HTML/CSS → PDF via WeasyPrint)
+
+FORMAT
+- A4 Hochformat, mehrseitig
+- @page { size: A4; margin: 22mm 20mm 20mm 20mm; }
+- @page :first { margin: 0; } für Cover
+- Footer: Seitenzahl Mitte, Guide-Titel rechts (außer Cover)
+
+UMFANG
+- 15-20 Kapitel à ~15 Min Lesezeit
+- ~1500 Wörter Fließtext pro Kapitel (gleich wie alle anderen Stufen)
+- 2-3 Code-Beispiele pro Kapitel (gleich wie alle anderen, 5-15 Zeilen)
+- 50-70 Seiten gesamt
+- ~4-5h Lesezeit gesamt
+
+EINSTIEGSNIVEAU
+- Setzt Anfänger- UND Fortgeschritten-Guide voraus
+- Grundbegriffe und fortgeschrittene Patterns werden NICHT mehr erklärt
+- Verweist bei Bedarf auf vorherige Guides
+- Geht direkt in Internals, Spezial-Themen und Edge-Cases
+
+UNTERSCHIED ZU FORTGESCHRITTEN-GUIDE
+- Kapitel-Größe IDENTISCH (1500 Wörter, 2-3 Code-Beispiele, 15 Min)
+- Unterschied liegt nur in:
+ - THEMEN (Experten/Nischen, Internals, Sprach-Mechanismen)
+ - VORAUSGESETZTEM WISSEN (alle Patterns aus Fortgeschritten)
+ - KAPITEL-ANZAHL (15-20 statt 12-15)
+ - "WARUM" über "WIE" (mehr Trade-Offs, mehr Design-Entscheidungen)
+
+KAPITEL-PROGRESSION
+- Aufgeteilt in 3 Teile mit eigenen TOC-Sektionen
+- Beispiele für PHP:
+ - Teil 1: Sprach-Internals — Reflection, SPL, Stream-Wrapper, GC, FFI
+ - Teil 2: Performance & Async — OpCache, Fibers, ReactPHP, Profiling, Caching
+ - Teil 3: Architektur & Patterns — DI-Container, Event-Dispatcher, CQRS, DDD, Hexagonal, Event Sourcing
+- Jedes Kapitel geht in Bereiche, wo die meisten Entwickler nicht hingehen
+- Bewusst dort, wo Mainstream-Tutorials aufhören
+
+STRUKTUR
+1. Cover: vollflächiger Hintergrund, Hero-Aussage mit "an der Grenze" oder ähnlichem Tone
+2. Inhaltsverzeichnis: 3 Teile, nummeriert, Zeit-Marker (15 Min) - kann auf zwei Seiten brechen
+3. Kapitel 1-N
+4. Ending: Spaced-Repetition-Plan, nächste Schritte (außerhalb des offiziellen Lernpfads), Begleitmaterial
+
+KAPITEL-AUFBAU
+1. Kapitel-Head: große Nummer + Titel + Subtitle, Trennlinie
+2. Gap-Opener: kursiv eingerahmt, anspruchsvolles Problem oder Tief-Frage
+3. 3-5 H2-Sektionen (mehr als im Fortgeschritten-Guide)
+4. Pro Sektion: Erklärtext + Code-Beispiel + ggf. Callout
+5. Recall-Box am Ende (3 Fragen, anspruchsvoller als alle Stufen davor)
+
+CODE-BEISPIELE
+- Production-Code-Niveau, keine Demos
+- Echte Library-Namen (Symfony, Doctrine, ReactPHP, EventSauce)
+- Internals-Code zur Erklärung von PHP-Mechanismen
+- Vereinfachte Implementierungen echter Frameworks
+- Länge bleibt 5-15 Zeilen (wie alle anderen Stufen)
+
+ELEMENTE
+- Fließtext: justify mit Silbentrennung
+- Codeblöcke: dunkler Hintergrund, syntax highlighting
+- Inline-Code: heller Hintergrund, Hauptfarbe
+- Tabellen: Header farbig (Hauptfarbe-Dunkel), Vergleichstabellen mit Trade-Offs
+- Callouts in 3 Varianten: tip (grün), warn (rot), note (Hauptfarbe)
+- Recall-Box: dunkler Hintergrund mit Akzentfarbe
+
+TYPOGRAFIE
+- Body: 10.5pt Serif (Charter), line-height 1.55
+- H1 Kapitel: 22pt Sans-Serif bold, Hauptfarbe-Dunkel
+- H2 Sektion: 14pt Sans-Serif bold
+- H3 Subsektion: 11pt Sans-Serif bold
+- Code: 8.5pt Monospace, line-height 1.5
+- Inline-Code: 9pt Monospace
+- Recall/Callout-Labels: 8pt uppercase, letter-spacing 1pt
+- Cover-H1: 56pt Sans-Serif bold, letter-spacing -2pt
+
+FARBEN (max 3 + Neutrals)
+- Hauptfarbe: kräftig, an offizielle Farbe des Themas anlehnen
+- Hauptfarbe-Dunkel: dunklere Variante für Headings und Akzente
+- Hauptfarbe-Darker: noch dunkler für Cover-Verlauf und Recall-Box
+- Hintergrund-Soft: helle Variante der Hauptfarbe
+- Code-Hintergrund: #1e2a3a (dunkel)
+- Text: #1a1a1a / muted #5a6470 / Linie #d8dde3
+- Callout-Farben: grün/rot/Hauptfarbe
+
+INFORMATION-GAP-OPENER (PFLICHT pro Kapitel)
+- Kursiv, eingerahmt mit Hauptfarbe-Border
+- Anspruchsvolles Problem oder Internals-Frage als Aufhänger
+- Niveau: setzt fortgeschrittene Praxis voraus
+- Beispiele:
+ - "Wie weiß Symfony zur Laufzeit, welche Routen in deinem Controller stecken?"
+ - "PHP räumt Speicher automatisch auf – meistens. Aber in Long-Running-Prozessen..."
+ - "Anämische Entities sind in PHP weit verbreitet..."
+
+RECALL-BOX (PFLICHT pro Kapitel)
+- Am Kapitel-Ende
+- 3 Fragen, anspruchsvoller als Fortgeschritten
+- Fragen nach Warum/Wofür/Wann genau statt Was/Wie
+- Code-Snippets in Fragen mit Akzentfarbe hervorgehoben
+
+ENDING (PFLICHT)
+- Spaced-Repetition-Plan: 4 Karten (Heute, +7 Tage, +30 Tage, +90 Tage)
+ - Sehr langfristige Spacing-Abstände
+ - Aufgaben anspruchsvoll (eigenes Spezialprojekt, Source-Code lesen)
+- "Was als nächstes lernen" — bewusst außerhalb offizieller Lernpfade
+ (Source-Code, eigene Extensions, Sprach-Design, RFCs)
+- Verweis auf ALLE Begleitmaterialien (komplette Reihe)
+
+CALLOUT-NUTZUNG
+- tip (grün): Best Practice für Spezial-Cases, Library-Empfehlung
+- warn (rot): subtile Fallen, Architektur-Anti-Patterns, Komplexitäts-Warnungen
+- note (Hauptfarbe): Hintergrund-Info, alternative Lösung, "wann lohnt sich das"
+
+CALLOUT-CSS WICHTIG
+- .callout-body > b:first-child mit display:block für Label
+- NICHT .callout-body b global mit display:block (zerstört Inline-Bold)
+- Mehrzeiliger Body-Text in
wrappen wenn Inline-Bolds drin sind
+
+GAP-CSS WICHTIG
+- .gap > b:first-child mit display:block für "FRAGE ZUM EINSTIEG"-Label
+- NICHT .gap b global mit display:block (zerstört Inline-Bold im Frage-Text)
+- Bei Inline-Bolds im Gap-Text wird sonst jedes zum Block
+
+THEMENSPEZIFISCHE ANPASSUNGEN (vor Generierung wählen)
+- Hauptfarbe: offizielle Farbe des Themas
+- Logo-Buchstabe(n) oder Kürzel
+- Version + Stand-Datum
+- 15-20 Kapitel-Titel: Internals, Spezial-Themen, Production-Edge
+- Themen, die der Mainstream-Entwickler nicht täglich braucht
+
+PFLICHT-ELEMENTE PRO KAPITEL
+- 1 Gap-Opener am Anfang
+- 2-3 Code-Beispiele (5-15 Zeilen, gleich wie alle anderen Stufen)
+- Mindestens 1 Callout
+- 1 Recall-Box am Ende
+
+VERMEIDEN
+- Wiederholung von Anfänger- und Fortgeschritten-Themen
+- Einleitungs-Floskeln ("In diesem Kapitel lernen wir...")
+- Übersichts-Inhalt (steht im OnePager)
+- Reine Referenz-Tabellen (stehen im Cheatsheet)
+- Toy-Beispiele (Production-Niveau zeigen)
+- Themen, die der Mainstream-Entwickler täglich braucht
+ (gehören in Anfänger oder Fortgeschritten)
+- page-break mitten in Codeblock oder Callout (page-break-inside: avoid)
+- Mehr als 3 Schriftgrößen pro Sektion
+- Floats oder absolute positioning (bricht in WeasyPrint)
+
+GENERIERUNG MIT FEEDBACK-LOOP (max 3 Iterationen)
+1. HTML schreiben (sehr langes Dokument, ~3500-5000 Zeilen typisch)
+2. weasyprint file.html file.pdf (Timeout 300s, große Datei)
+3. PDF zu PNGs: alle Seiten konvertieren (dpi=90 für Memory-Effizienz)
+4. Schlüsselseiten ansehen: Cover, TOC, Kapitel 1, mittlere Seite, Ending
+5. Prüfen:
+ - Cover randlos und ohne Footer?
+ - TOC zeigt alle 3 Teile? (kann auf 2 Seiten brechen bei 15+ Kapiteln, OK)
+ - Kapitel beginnen auf neuer Seite?
+ - Code-Blöcke nicht über Seitenumbruch zerrissen?
+ - Recall-Boxen vollständig sichtbar?
+ - Footer mit Seitenzahl korrekt?
+ - Setzt der Guide spürbar Anfänger+Fortgeschritten-Wissen voraus?
+ - Sind Themen wirklich Experten-/Nischen-Niveau?
+ - Inline-Bolds in Gap-Openers und Callouts korrekt (nicht als Blöcke)?
+6. Bei Problemen: fixen, ab Schritt 2 wiederholen
+7. Nach max 3 Iterationen ausgeben
+
+INSTALLATION
+- pip install weasyprint pdf2image
+- apt install poppler-utils
+```
\ No newline at end of file
diff --git a/templates/Format/IntermediateGuide.md b/templates/Format/IntermediateGuide.md
new file mode 100644
index 0000000..f8f28f1
--- /dev/null
+++ b/templates/Format/IntermediateGuide.md
@@ -0,0 +1,166 @@
+```
+FORTGESCHRITTEN-GUIDE-STIL (HTML/CSS → PDF via WeasyPrint)
+
+FORMAT
+- A4 Hochformat, mehrseitig
+- @page { size: A4; margin: 22mm 20mm 20mm 20mm; }
+- @page :first { margin: 0; } für Cover
+- Footer: Seitenzahl Mitte, Guide-Titel rechts (außer Cover)
+
+UMFANG
+- 12-15 Kapitel à ~15 Min Lesezeit
+- ~1500 Wörter Fließtext pro Kapitel (gleich wie Anfänger)
+- 2-3 Code-Beispiele pro Kapitel (gleich wie Anfänger, 5-15 Zeilen)
+- 30-50 Seiten gesamt
+- ~3-4h Lesezeit gesamt
+
+EINSTIEGSNIVEAU
+- Setzt Anfänger-Guide oder gleichwertiges Vorwissen voraus
+- Grundbegriffe werden NICHT mehr erklärt
+- Verweist bei Bedarf auf Anfänger-Guide
+- Geht direkt in fortgeschrittene Patterns und Production-Tools
+
+UNTERSCHIED ZU ANFÄNGER-GUIDE
+- Kapitel-Größe IDENTISCH (1500 Wörter, 2-3 Code-Beispiele, 15 Min)
+- Unterschied liegt nur in:
+ - THEMEN (anspruchsvoller, weniger bekannt)
+ - VORAUSGESETZTEM WISSEN (Grundlagen werden nicht wiederholt)
+ - REIFE der Code-Beispiele (Production-nah statt Demo)
+ - KAPITEL-ANZAHL (12-15 statt 8-12)
+
+KAPITEL-PROGRESSION
+- Aufgeteilt in 3 Teile mit eigenen TOC-Sektionen
+- Beispiele für PHP:
+ - Teil 1: OOP-Patterns — Interfaces, Traits, Enums, Attribute
+ - Teil 2: Funktional & Generators — Closures, Higher-Order, Generators, Generics
+ - Teil 3: Production-Tools — PDO, HTTP-Clients, Static Analysis, Tests
+- Jedes Kapitel ist tiefer als ein Anfänger-Kapitel
+- Mehr Edge Cases, mehr "warum genau so"
+- Production-Code-Niveau
+
+STRUKTUR
+1. Cover: vollflächiger Hintergrund, Hero-Aussage mit "tiefer" oder ähnlichem Tone
+2. Inhaltsverzeichnis: 3 Teile, nummeriert, mit Zeit-Markern (15 Min)
+3. Kapitel 1-12
+4. Ending: Spaced-Repetition-Plan, nächste Schritte, Begleitmaterial
+
+KAPITEL-AUFBAU
+1. Kapitel-Head: große Nummer + Titel + Subtitle, Trennlinie
+2. Gap-Opener: kursiv eingerahmt, konkretes Praxis-Problem
+3. 2-4 H2-Sektionen
+4. Pro Sektion: Erklärtext + Code-Beispiel + ggf. Callout
+5. Recall-Box am Ende (3 Fragen, anspruchsvoller als im Anfänger)
+
+CODE-BEISPIELE
+- Realistischer als im Anfänger-Guide (kein "foo/bar")
+- Production-nahe Patterns
+- TypeScript/Type-Hints idiomatisch
+- Echte Library-Namen (Guzzle, Doctrine, PHPStan, Symfony)
+- Mehr Edge Cases zeigen
+- Länge bleibt 5-15 Zeilen (wie Anfänger)
+
+ELEMENTE
+- Fließtext: justify mit Silbentrennung
+- Codeblöcke: dunkler Hintergrund, syntax highlighting
+- Inline-Code: heller Hintergrund, Hauptfarbe
+- Tabellen: Header farbig (Hauptfarbe-Dunkel)
+- Callouts in 3 Varianten: tip (grün), warn (rot), note (Hauptfarbe)
+- Recall-Box: dunkler Hintergrund mit Akzentfarbe
+
+TYPOGRAFIE
+- Body: 10.5pt Serif (Charter), line-height 1.55
+- H1 Kapitel: 22pt Sans-Serif bold, Hauptfarbe-Dunkel
+- H2 Sektion: 14pt Sans-Serif bold
+- H3 Subsektion: 11pt Sans-Serif bold
+- Code: 8.5pt Monospace, line-height 1.5
+- Inline-Code: 9pt Monospace
+- Recall/Callout-Labels: 8pt uppercase, letter-spacing 1pt
+- Cover-H1: 56pt Sans-Serif bold, letter-spacing -2pt
+
+FARBEN (max 3 + Neutrals)
+- Hauptfarbe: kräftig, an offizielle Farbe des Themas anlehnen
+- Hauptfarbe-Dunkel: dunklere Variante für Headings und Akzente
+- Hauptfarbe-Darker: noch dunkler für Cover-Verlauf und Recall-Box
+- Hintergrund-Soft: helle Variante der Hauptfarbe
+- Code-Hintergrund: #1e2a3a (dunkel)
+- Text: #1a1a1a / muted #5a6470 / Linie #d8dde3
+- Callout-Farben: grün/rot/Hauptfarbe
+
+INFORMATION-GAP-OPENER (PFLICHT pro Kapitel)
+- Kursiv, eingerahmt mit Hauptfarbe-Border
+- Konkretes Praxis-Problem als Aufhänger
+- Niveau: setzt Anfänger-Wissen voraus
+- Beispiele:
+ - "Du hast drei verschiedene Logger – Datei, Datenbank, Sentry..."
+ - "Du sollst die Zeilen einer 5-GB-Logdatei verarbeiten..."
+ - "SQL-Injection ist seit 20 Jahren die häufigste Web-Sicherheitslücke..."
+
+RECALL-BOX (PFLICHT pro Kapitel)
+- Am Kapitel-Ende
+- 3 Fragen, anspruchsvoller als im Anfänger-Guide
+- Fragen nach Wann/Warum/Wofür statt Was/Wie
+- Code-Snippets in Fragen mit Akzentfarbe hervorgehoben
+
+ENDING (PFLICHT)
+- Spaced-Repetition-Plan: 4 Karten (Heute, +3 Tage, +14 Tage, +60 Tage)
+ - Anspruchsvollere Spacing-Abstände als im Anfänger-Guide
+ - Aufgaben anspruchsvoller (echtes Projekt aufsetzen)
+- "Was als nächstes lernen" mit Spezialisierungs-Vorschlägen Richtung Extended
+- Verweis auf alle Begleitmaterialien (OnePager, Cheatsheet, Mini, Anfänger)
+
+CALLOUT-NUTZUNG
+- tip (grün): Best Practice, idiomatische Lösung, Library-Empfehlung
+- warn (rot): Fallen, häufige Anti-Patterns, Sicherheits-Risiken
+- note (Hauptfarbe): Hintergrund-Info, alternative Lösung, Querverweis
+
+CALLOUT-CSS WICHTIG
+- .callout-body > b:first-child mit display:block für Label
+- NICHT .callout-body b global mit display:block (zerstört Inline-Bold)
+- Mehrzeiliger Body-Text in
wrappen wenn Inline-Bolds drin sind
+
+THEMENSPEZIFISCHE ANPASSUNGEN (vor Generierung wählen)
+- Hauptfarbe: offizielle Farbe des Themas
+- Logo-Buchstabe(n) oder Kürzel
+- Version + Stand-Datum
+- 12-15 Kapitel-Titel: fortgeschrittene Patterns, Tools, Production-Aspekte
+- Keine Wiederholung der Anfänger-Grundlagen
+
+PFLICHT-ELEMENTE PRO KAPITEL
+- 1 Gap-Opener am Anfang
+- 2-3 Code-Beispiele (5-15 Zeilen, gleich wie Anfänger)
+- Mindestens 1 Callout
+- 1 Recall-Box am Ende
+
+VERMEIDEN
+- Wiederholung von Grundlagen aus dem Anfänger-Guide
+- Einleitungs-Floskeln ("In diesem Kapitel lernen wir...")
+- Übersichts-Inhalt (steht im OnePager)
+- Reine Referenz-Tabellen (stehen im Cheatsheet)
+- Toy-Beispiele wie foo/bar (Production-Code zeigen)
+- Themen, die in Anfänger oder Extended besser passen
+- page-break mitten in Codeblock oder Callout (page-break-inside: avoid)
+- Mehr als 3 Schriftgrößen pro Sektion
+- Floats oder absolute positioning (bricht in WeasyPrint)
+
+GENERIERUNG MIT FEEDBACK-LOOP (max 3 Iterationen)
+1. HTML schreiben
+2. weasyprint file.html file.pdf (Timeout 300s)
+3. PDF zu PNGs: alle Seiten konvertieren
+4. Schlüsselseiten ansehen: Cover, TOC, Kapitel 1, mittlere Seite, Ending
+5. Prüfen:
+ - Cover randlos und ohne Footer?
+ - TOC zeigt alle 3 Teile?
+ - Kapitel beginnen auf neuer Seite?
+ - Code-Blöcke nicht über Seitenumbruch zerrissen?
+ - Recall-Boxen vollständig sichtbar?
+ - Footer mit Seitenzahl korrekt?
+ - Setzt der Guide spürbar Anfänger-Wissen voraus?
+ - Sind Code-Beispiele realistisch (kein foo/bar)?
+ - Inline-Bolds in Callouts korrekt (nicht als Blöcke)?
+6. Bei Problemen: fixen, ab Schritt 2 wiederholen
+7. Nach max 3 Iterationen ausgeben
+
+INSTALLATION
+- pip install weasyprint pdf2image
+- apt install poppler-utils
+```
\ No newline at end of file
diff --git a/templates/Format/MiniGuide.md b/templates/Format/MiniGuide.md
new file mode 100644
index 0000000..b3c878b
--- /dev/null
+++ b/templates/Format/MiniGuide.md
@@ -0,0 +1,148 @@
+```
+MINI-GUIDE-STIL (HTML/CSS → PDF via WeasyPrint)
+
+FORMAT
+- A4 Hochformat, 3-4 Seiten
+- @page { size: A4; margin: 18mm 18mm 16mm 18mm; }
+- Footer: Seitenzahl Mitte, Guide-Titel rechts
+
+UMFANG (einheitlich mit allen Guide-Stufen)
+- 1 Kapitel (oder besser: 4-6 Sektionen ohne Kapitel-Struktur)
+- ~1500 Wörter Fließtext
+- 5-7 Code-Beispiele (sehr kurz, 2-7 Zeilen)
+- ~15 Min Lesezeit
+- 3-4 Seiten
+
+ZIELGRUPPE — KOMPAKTER ANFÄNGER-EINSTIEG
+- Echte Anfänger ohne Programmier-Vorwissen im Thema
+- Setzt nur allgemeines Verständnis voraus ("was ist Programmieren")
+- Begriffe werden bei erstem Auftreten erklärt
+- KEIN Sprach-Charakter-Überblick für erfahrene Entwickler
+- KEINE fortgeschrittenen Features (auch wenn cool und kurz)
+
+INHALTLICHE PRINZIPIEN
+- Nur absolute Basics zeigen
+- Themen, die jemand nach 15 Min selbst nachmachen kann
+- Keine Tooling-Komplexität (Paketmanager, Build-Systeme, Compiler)
+- Keine Sprach-Spezialitäten (Type-Systeme, Decorators, Generics)
+- Keine OOP, wenn möglich (oder nur trivialste Form)
+- Erklär-Tiefe vor Feature-Breite
+- Lieber 5 Konzepte gründlich als 15 oberflächlich
+
+TYPISCHE 5-SEKTIONEN-STRUKTUR
+1. Sprache starten — Installation, erste Datei, erstes Programm
+2. Variablen — Konzept + 2-3 Basis-Typen
+3. Kontrollfluss — if/else mit einfachem Beispiel
+4. Listen + Iteration — Array + Schleife
+5. Funktionen — Deklaration + Aufruf + Rückgabe
+
+(Diese Reihenfolge baut aufeinander auf und endet mit etwas Sinnvollem.)
+
+STRUKTUR
+1. Kompakter Head: Logo links (16mm), Titel + Subtitle mittig, Badge + Zeit rechts
+2. Gap-Opener: Frage zum Einstieg, kursiv eingerahmt, niedrigschwellig
+3. 4-6 H2-Sektionen mit Erklärtext + Code-Beispiel + ggf. Callout
+
+ELEMENTE
+- Fließtext: justify mit Silbentrennung
+- Codeblöcke: dunkler Hintergrund, syntax highlighting, sehr kurz (2-7 Zeilen)
+- Inline-Code: heller Hintergrund, Hauptfarbe
+- Tabellen: nur wenn wirklich nötig (Vergleichs-Operatoren o.ä.)
+- Callouts in 3 Varianten: tip (grün), warn (rot), note (Hauptfarbe)
+
+TYPOGRAFIE
+- Body: 10.5pt Serif (Charter), line-height 1.55
+- Head h1: 20pt Sans-Serif bold
+- H2 Sektion: 13pt Sans-Serif bold
+- Code: 8.5pt Monospace, line-height 1.5
+- Inline-Code: 9pt Monospace
+- Callout-Labels: 8pt uppercase, letter-spacing 1pt
+
+FARBEN (max 3 + Neutrals)
+- Hauptfarbe: kräftig, an offizielle Farbe des Themas anlehnen
+- Hauptfarbe-Dunkel: dunklere Variante für Akzente
+- Hintergrund-Soft: helle Variante der Hauptfarbe
+- Code-Hintergrund: #1e2a3a
+- Text: #1a1a1a / muted #5a6470 / Linie #d8dde3
+- Callout-Farben: grün/rot/Hauptfarbe
+
+GAP-OPENER (PFLICHT)
+- Kursiv, eingerahmt mit Hauptfarbe-Border
+- Niedrigschwellige Frage, die der Guide beantwortet
+- Begeistert mit relevanter Statistik oder Praxis-Bezug
+- KEINE Fachbegriffe, die noch nicht erklärt sind
+- Beispiele:
+ - "PHP läuft hinter rund drei Viertel aller Webseiten..."
+ - "JavaScript ist die Sprache des Webs – aber wie schreibt man das eigentliche Code..."
+ - "Python ist die beliebteste Anfänger-Sprache..."
+
+ERKLÄR-TIEFE PRO KONZEPT
+- Konzept benennen (z.B. "Variable")
+- In einem Satz erklären, was es ist
+- Code-Beispiel mit Kommentaren
+- Sprach-Eigenheiten erwähnen (z.B. "in PHP beginnen Variablen mit $")
+- KEIN Verweis auf andere Konzepte, die noch kommen
+
+CALLOUT-NUTZUNG
+- tip (grün): Übungs-Anregung am Ende, ermutigend
+- warn (rot): Anfänger-Stolperfallen ("= vs ==", "vergessenes Semikolon")
+- note (Hauptfarbe): Hintergrund-Info, Erklärung einer Sprach-Eigenheit
+
+CALLOUT-CSS WICHTIG
+- .callout-body > b:first-child mit display:block für Label
+- NICHT .callout-body b global mit display:block (zerstört Inline-Bold)
+
+GAP-CSS WICHTIG
+- .gap > b:first-child mit display:block für "FRAGE ZUM EINSTIEG"-Label
+- NICHT .gap b global mit display:block (zerstört Inline-Bold im Frage-Text)
+
+THEMENSPEZIFISCHE ANPASSUNGEN (vor Generierung wählen)
+- Hauptfarbe: offizielle Farbe des Themas
+- Logo-Buchstabe(n) oder Kürzel
+- Begrüßungs-Statistik im Gap-Opener
+- 4-6 Anfänger-Themen wählen (siehe Standard-Struktur)
+
+PFLICHT-ELEMENTE
+- 1 Gap-Opener am Anfang
+- 5-7 Code-Beispiele (kurz, 2-7 Zeilen, anfänger-tauglich)
+- Mindestens 1 Callout (oft: warn für Stolperfalle, tip für Übung am Ende)
+- Inline-Codes für Fachbegriffe
+
+VERMEIDEN
+- TOC oder Cover (überdimensioniert für 15 Min)
+- Einleitungs-Floskeln ("In diesem Mini-Guide lernen wir...")
+- Vollständigkeitsanspruch (gehört in größeren Guide)
+- Referenz-Tabellen ohne Erklärtext (gehört in Cheatsheet)
+- Recall oder Next-Step am Ende (Mini-Guide endet mit Inhalt)
+- Themen, die fortgeschritten sind (auch wenn cool):
+ - Type-Systems, Type-Hints, Generics
+ - OOP-Features (außer trivialster Form)
+ - Tooling (Paketmanager, Build, Linting)
+ - Sprach-Spezialitäten (PHP: strict_types, readonly, Composer, PSR-4)
+- page-break mitten in Codeblock oder Callout (page-break-inside: avoid)
+- Mehr als 3 Schriftgrößen pro Sektion
+- Floats oder absolute positioning (bricht in WeasyPrint)
+- Fachbegriffe ohne Erklärung
+- Verweise auf andere Konzepte, die noch kommen
+- Edge Cases und "aber"-Sätze
+
+GENERIERUNG MIT FEEDBACK-LOOP (max 3 Iterationen)
+1. HTML schreiben
+2. weasyprint file.html file.pdf
+3. PDF zu PNGs: alle Seiten konvertieren
+4. Alle Seiten ansehen
+5. Prüfen:
+ - Head sauber (Logo überlappt nicht mit Titel)?
+ - Code-Blöcke nicht über Seitenumbruch zerrissen?
+ - Callouts vollständig sichtbar?
+ - Inline-Bolds in Callouts/Gap korrekt (nicht als Blöcke)?
+ - Footer mit Seitenzahl korrekt?
+ - Würde ein echter Anfänger das verstehen?
+ - Wurden alle Fachbegriffe beim ersten Auftreten erklärt?
+6. Bei Problemen: fixen, ab Schritt 2 wiederholen
+7. Nach max 3 Iterationen ausgeben
+
+INSTALLATION
+- pip install weasyprint pdf2image
+- apt install poppler-utils
+```
\ No newline at end of file
diff --git a/templates/Format/OnePager.md b/templates/Format/OnePager.md
new file mode 100644
index 0000000..3c6a2b1
--- /dev/null
+++ b/templates/Format/OnePager.md
@@ -0,0 +1,96 @@
+```
+ONEPAGER-STIL QUERFORMAT (HTML/CSS → PDF via WeasyPrint)
+
+FORMAT
+- A4 Querformat (297mm × 210mm)
+- @page { size: A4 landscape; margin: 0; }
+- Padding: 9mm 11mm 9mm 11mm
+
+LAYOUT
+- Grid: hero+stats (auto) / divider / main (1fr, 3 Spalten) / footer (absolute)
+- Stats-Bar in Hero integriert (rechts), spart vertikalen Platz
+- Main: 3 Spalten gleich breit, gap 4mm
+- Spalten intern: flex-column, gap 4mm
+- 6 thematische Blöcke verteilt (2 pro Spalte)
+
+STRUKTUR (in dieser Reihenfolge)
+1. Hero: Logo links (22mm), Titel + Untertitel mittig, 4 Stats rechts
+2. Divider: 1.5pt schwarze Linie
+3. Main-Grid: 6 Blöcke in 3 Spalten
+4. Footer: farbige Box mit Kernaussage + Tag (absolute, unten)
+
+UNTERSCHIEDE ZU HOCHFORMAT
+- 3 Spalten statt 2 (mehr horizontaler Platz)
+- 6 Blöcke statt 4-5
+- Stats integriert in Hero statt eigene Zeile
+- Kleinere Schriftgrößen (9.5pt Body statt 10pt)
+- Kompaktere Code-Blöcke (7pt statt 7.5pt)
+
+BLOCK-AUFBAU
+- Titel: 9.5pt bold uppercase, Icon links, Hauptfarbe-Unterstreichung 2pt
+- Inhalt: visuell, nicht reine Textbullets
+- Varianten: Icon-Liste, Code-Block, Kachel-Grid, Plus/Minus-Spalten, Type-Grid
+
+FARBEN (max 3 + Neutrals)
+- Hauptfarbe: an offizielle Farbe des Themas anlehnen
+- Hauptfarbe-Dunkel: für Headings
+- Hauptfarbe-Darker: für Footer
+- Akzentfarbe: kontrastierend
+- Hintergrund-Soft: helle Variante der Hauptfarbe
+- Code-Hintergrund: #1e2a3a
+- Text: #1a1a1a / muted #5a6470 / Linie #e5e5e5
+
+TYPOGRAFIE
+- Body: 9.5pt, line-height 1.4
+- Hero h1: 20pt bold
+- Block-Titel: 9.5pt bold uppercase, letter-spacing 0.5pt
+- Stats-Zahl: 14pt bold, Label 6.5pt uppercase
+- Code: 7pt monospace, dunkler Hintergrund
+- Feature-Text: 8.5pt
+- Max 3 Schriftgrößen pro Block
+
+ICONS
+- SVG inline, stroke statt fill
+- 4mm in Block-Titeln (kleiner als Hochformat wegen kompakter Layout)
+- 2.8mm in Kachel-Icons
+- currentColor für automatische Anpassung
+
+THEMENSPEZIFISCHE ANPASSUNGEN (vor Generierung wählen)
+- Hauptfarbe: offizielle Farbe des Themas
+- Logo-Buchstabe(n) oder Kürzel
+- 4 Stats: themen-relevante Zahlen
+- Block-Auswahl: 6 wichtigste Aspekte für Erstübersicht
+
+VISUELLE ELEMENTE PFLICHT
+- Mindestens 1 Code-Block
+- Mindestens 1 Kachel-Grid mit Icons (Ökosystem)
+- Mindestens 1 Plus/Minus-Split (Modern vs Legacy o.ä.)
+- Footer als farbige Box (visueller Anker)
+- Stats-Bar im Hero
+
+VERMEIDEN
+- Reine Bullet-Listen in jedem Block
+- Mehr als 6 Hauptblöcke (Querformat hat eh schon mehr Platz)
+- Mehr als 3 Schriftgrößen
+- Marketing-Floskeln in Hero
+- Floats oder absolute positioning (außer für Footer)
+- Vertikal sehr lange Blöcke (würden Spalten unbalanciert machen)
+
+GENERIERUNG MIT FEEDBACK-LOOP (max 3 Iterationen)
+1. HTML schreiben
+2. weasyprint file.html file.pdf
+3. PDF zu PNG: python -c "from pdf2image import convert_from_path; convert_from_path('file.pdf', dpi=120)[0].save('preview.png')"
+4. Preview ansehen mit Read-Tool
+5. Prüfen:
+ - Stats-Bar überlappt nicht mit Titel?
+ - 3 Spalten balanciert (ähnliche Höhe)?
+ - Footer nicht abgeschnitten?
+ - Code-Block lesbar?
+ - Alle Icons rendern?
+6. Bei Problemen: fixen, ab Schritt 2 wiederholen
+7. Nach max 3 Iterationen ausgeben
+
+INSTALLATION
+- pip install weasyprint pdf2image
+- apt install poppler-utils
+```
\ No newline at end of file
diff --git a/templates/Referenz/BeginnerGuide.md b/templates/Referenz/BeginnerGuide.md
new file mode 100644
index 0000000..a4662b0
--- /dev/null
+++ b/templates/Referenz/BeginnerGuide.md
@@ -0,0 +1,1438 @@
+```
+
+
+
+
+PHP Anfänger-Guide
+
+
+
+
+
+
+
+
php
+
Anfänger-Guide · 2,5h · Stand 2026
+
+
+
+
PHP in 10 Kapiteln.
+
Von der Installation bis zur ersten Composer-Anwendung – modern und idiomatisch.
+
+
+
+
+ Was du danach kannst
+ 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.
+
+
PHP 8.4
+
+
+
+
+
+
Inhalt
+
+
Teil 1 · Grundlagen
+
+
+
1
+
+
PHP installieren und starten
+
Setup mit Docker, DDEV oder lokal, erstes Script
+
+
15 Min
+
+
+
+
2
+
+
Variablen und Typen
+
$-Variablen, strict_types, Typumwandlung
+
+
15 Min
+
+
+
+
3
+
+
Strings und Formatierung
+
Interpolation, Heredoc, String-Funktionen
+
+
15 Min
+
+
+
Teil 2 · Strukturen
+
+
+
4
+
+
Arrays: Liste und Map
+
Indiziert, assoziativ, gemischt
+
+
15 Min
+
+
+
+
5
+
+
Control Flow
+
if, match, foreach, while
+
+
15 Min
+
+
+
+
6
+
+
Funktionen mit Type-Hints
+
Parameter, Rückgabewerte, Named Arguments
+
+
15 Min
+
+
+
Teil 3 · Objekte und echte Anwendungen
+
+
+
7
+
+
Klassen und Objekte
+
Promoted Constructor, readonly, Methoden
+
+
15 Min
+
+
+
+
8
+
+
Namespaces und Autoloading
+
PSR-4, use-Statements, Datei-Organisation
+
+
15 Min
+
+
+
+
9
+
+
Fehlerbehandlung
+
Exceptions, try/catch, eigene Fehler
+
+
15 Min
+
+
+
+
10
+
+
Composer und erste Anwendung
+
Pakete installieren, kleines CLI-Tool bauen
+
+
15 Min
+
+
+
+
+
+
+
01
+
+
PHP installieren und starten
+
Setup mit Docker, DDEV oder lokal
+
+
+
+
+ Frage zum Einstieg
+ Du willst PHP-Code schreiben und ausführen. Aber wie kommt PHP auf deinen Rechner? Klassische XAMPP-Installation, modernes Docker, oder einfach php -S für schnelle Tests? Die richtige Wahl spart dir später Stunden.
+
+
+
Drei Wege, PHP zu starten
+
Für lokale Entwicklung gibt es drei sinnvolle Optionen, jede mit ihrem Anwendungsfall:
+
+
+
Methode
Wann
+
Lokal (brew, apt)
Kleine Scripts, schneller Test
+
DDEV
Mehrere Projekte mit DB
+
Docker direkt
Custom-Setup, CI/CD
+
+
+
Für den Einstieg reicht eine lokale Installation. Auf macOS mit Homebrew: brew install php. Auf Ubuntu: apt install php8.4-cli. Auf Windows: am einfachsten WSL2 mit Ubuntu darin.
+
+
Erstes Script
+
Lege eine Datei hello.php an mit folgendem Inhalt:
Im Terminal: php hello.php. Du siehst die Ausgabe. Glückwunsch – das ist alles, was PHP zum Start braucht.
+
+
Die Zeile declare(strict_types=1) gehört in jede moderne PHP-Datei. Sie aktiviert strenge Typprüfung und verhindert, dass PHP heimlich Strings in Zahlen umwandelt. Verlässlicher Code.
+
+
Eingebauter Web-Server
+
Für Web-Entwicklung braucht PHP normalerweise einen Webserver (Apache, nginx). Für lokales Testen gibt es einen eingebauten Server:
+
+
# Im Verzeichnis mit deinen .php-Dateien
+php -S localhost:8000
+
+
Öffne http://localhost:8000/hello.php im Browser. Der Server ist nicht für Production gedacht, aber für lokale Entwicklung perfekt.
+
+
+
✓
+
+ DDEV für echte Projekte
+ Sobald du eine Datenbank brauchst, mehrere Projekte gleichzeitig, oder ein konkretes PHP-Framework wie Shopware oder Laravel, lohnt sich DDEV – wraps Docker mit sinnvollen Defaults. ddev config in deinem Projektordner reicht meist.
+
+
+
+
+ Recall
+
+
Wie führst du eine PHP-Datei im Terminal aus?
+
Wozu ist declare(strict_types=1) gut?
+
Wann nutzt du php -S, wann DDEV?
+
+
+
+
+
+
+
+
02
+
+
Variablen und Typen
+
$-Variablen, strict_types, Typumwandlung
+
+
+
+
+ Frage zum Einstieg
+ PHP-Variablen beginnen mit einem $. 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.
+
+
+
Variablen mit $
+
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:
PHP ist dynamisch typisiert: 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).
+
+
Die wichtigsten Typen
+
+
+
Typ
Beispiel
Wofür
+
int
42
Ganze Zahlen
+
float
3.14
Kommazahlen
+
string
'Hi'
Text
+
bool
true
Wahrheitswert
+
array
[1, 2]
Liste oder Map
+
null
null
Kein Wert
+
+
+
Den Typ einer Variable findest du mit gettype($x) oder mit Funktionen wie is_int($x), is_string($x).
+
+
Typumwandlung
+
PHP wandelt Typen oft automatisch um – das ist bequem, aber tückisch. Mit explizitem Cast bist du auf der sicheren Seite:
+ Vergleich mit == ist tückisch
+ '0' == false ist true. 'abc' == 0 war in alten PHP-Versionen auch true. Nutze immer === für strikten Vergleich, der auch den Typ prüft.
+
+
+
+
+ Recall
+
+
Wie erkennst du in PHP-Code, ob etwas eine Variable ist?
+
Was ist der Unterschied zwischen == und ===?
+
Welche Werte werden bei Boolean-Cast zu false?
+
+
+
+
+
+
+
+
03
+
+
Strings und Formatierung
+
Interpolation, Heredoc, String-Funktionen
+
+
+
+
+ Frage zum Einstieg
+ 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?
+
+
+
Strings: '' vs ""
+
Beide sind Strings, aber doppelte Anführungszeichen interpolieren Variablen, einfache nicht:
Faustregel: Doppelte Anführungszeichen wenn du Variablen einsetzen willst, einfache sonst. Einfache sind minimal schneller, aber das ist heute praktisch egal.
+
+
Heredoc und Nowdoc für Mehrzeiler
+
Für längeren Text mit Variablen oder ohne gibt es Heredoc und Nowdoc:
+
+
$name = 'Marek';
+
+$mail = <<<TEXT
+Hallo $name,
+
+vielen Dank für deine Bestellung.
+
+Grüße
+TEXT;
+
+// Nowdoc (kein Interpolation, wörtlich)
+$tpl = <<<'TPL'
+Verwende {{ name }} für den Namen.
+TPL;
+
+
Wichtige String-Funktionen
+
PHP hat eine riesige Auswahl an String-Funktionen. Diese kennst du nach einer Woche auswendig:
+
+
+
Funktion
Effekt
+
strlen($s)
Länge des Strings
+
strtoupper / strtolower
Groß-/Kleinschreibung
+
trim($s)
Whitespace vorne/hinten entfernen
+
str_replace($a, $b, $s)
a durch b ersetzen
+
str_contains($s, $needle)
Prüft, ob enthalten
+
explode(',', $s)
String zu Array
+
implode(',', $arr)
Array zu String
+
sprintf('%05d', 42)
Formatiert "00042"
+
+
+
+
✓
+
+ Methoden-Verkettung mit Pipes nicht möglich
+ Anders als bei Objektmethoden gibt es bei String-Funktionen keine Verkettung. trim(strtolower($s)) ist die Schreibweise – von innen nach außen lesen.
+
+
+
+
+ Recall
+
+
Wann nutzt du "", wann ''?
+
Was ist der Unterschied zwischen Heredoc und Nowdoc?
+
Wie prüfst du, ob ein String einen anderen enthält?
+
+
+
+
+
+
+
+
04
+
+
Arrays: Liste und Map
+
Indiziert, assoziativ, gemischt
+
+
+
+
+ Frage zum Einstieg
+ In den meisten Sprachen sind Listen und Dictionaries zwei verschiedene Datenstrukturen. In PHP ist beides ein array – das macht vieles flexibel, aber auch leicht verwirrend. Wie funktioniert das?
+
+
+
Indizierte Arrays (Listen)
+
Eine Liste ohne explizite Keys – die Indizes werden automatisch 0, 1, 2, ...:
Mit String-Keys wird das Array zu einer Map – die häufigste Form in PHP-Code, weil viele APIs (JSON, Datenbank-Zeilen) so aussehen:
+
+
$user = [
+ 'name' => 'Marek',
+ 'age' => 34,
+ 'roles' => ['admin', 'editor'],
+];
+
+$user['name']; // 'Marek'
+$user['email'] = 'm@e.de'; // neuer Key
+
+// Sicher: gibt null wenn nicht da, kein Fehler
+$phone = $user['phone'] ?? 'unbekannt';
+
+
Der Null-Coalescing-Operator?? ist eine der nützlichsten PHP-Features. Er gibt den linken Wert zurück, wenn er existiert und nicht null ist, sonst den rechten.
+
+
Über Arrays iterieren
+
foreach ist das Werkzeug der Wahl. Es funktioniert für beide Array-Typen:
Was ist der Unterschied zwischen indizierten und assoziativen Arrays?
+
Was macht ?? in PHP?
+
Wie iterierst du über ein assoziatives Array mit Keys und Werten?
+
+
+
+
+
+
+
+
05
+
+
Control Flow
+
if, match, foreach, while
+
+
+
+
+ Frage zum Einstieg
+ 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 match ein modernes Werkzeug ergänzt, das vieles eleganter macht.
+
+ match statt switch
+ In neuem Code: nimm match. Strikter Vergleich (===), kein fallthrough-Fehler, Ergebnis als Wert. switch nur noch in Legacy-Code oder wenn du komplexe Blöcke pro Fall brauchst.
+
+
+
+
+ Recall
+
+
Wann nutzt du foreach, wann for?
+
Was sind die wichtigsten Vorteile von match gegenüber switch?
+
Was ist der Unterschied zwischen break und continue?
+
+
+
+
+
+
+
+
06
+
+
Funktionen mit Type-Hints
+
Parameter, Rückgabewerte, Named Arguments
+
+
+
+
+ Frage zum Einstieg
+ 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?
+
Mit ? wird ein Typ nullable, mit | sind mehrere Typen erlaubt:
+
+
// Darf string oder null sein
+functionfind(int$id): ?User {
+ return$id > 0 ? newUser($id) : null;
+}
+
+// Union: int ODER string
+functionparse(int|string$input): int {
+ return (int) $input;
+}
+
+// void: gibt nichts zurück
+functionlog(string$msg): void {
+ file_put_contents('app.log', $msg);
+}
+
+
+
!
+
+ Closures vs. globale Variablen
+ Funktionen sehen keine Variablen aus dem umgebenden Code. Du musst sie explizit übergeben. Closures (anonyme Funktionen) können mit use ($var) Variablen importieren – aber Globals mit global $x sind tabu.
+
+
+
+
+ Recall
+
+
Wie deklarierst du eine Funktion, die string zurückgibt?
+
Was ist der Vorteil von Named Arguments?
+
Was bedeutet ?string als Typ?
+
+
+
+
+
+
+
+
07
+
+
Klassen und Objekte
+
Promoted Constructor, readonly, Methoden
+
+
+
+
+ Frage zum Einstieg
+ 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?
+
+
+
Klasse mit Promoted Constructor
+
Das wichtigste PHP-8-Feature für Klassen: Konstruktor-Parameter werden automatisch zu Properties, wenn du sie mit einem Sichtbarkeits-Modifier markierst:
classMath {
+ public const PI = 3.14159;
+
+ public static functionsquare(int$x): int {
+ return$x * $x;
+ }
+}
+
+echoMath::PI; // 3.14159
+echoMath::square(5); // 25
+
+
+
i
+
+ Konstruktor-Promotion sparsam einsetzen
+ 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.
+
+
+
+
+ Recall
+
+
Was generiert public readonly string $name im Konstruktor?
+
Was ist der Unterschied zwischen extends und implements?
+
Wie greifst du auf eine static-Konstante zu?
+
+
+
+
+
+
+
+
08
+
+
Namespaces und Autoloading
+
PSR-4, use-Statements, Datei-Organisation
+
+
+
+
+ Frage zum Einstieg
+ Bei zwei Klassen mit dem Namen User – 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.
+
+
+
Namespace deklarieren
+
Ein Namespace gibt deinen Klassen einen "Pfad". Konvention: ein Namespace pro Datei, am Anfang deklariert:
Die volle Bezeichnung dieser Klasse ist jetzt App\Domain\User\User. Eine andere Klasse mit demselben Namen in einem anderen Namespace kollidiert nicht.
+
+
Klassen importieren mit use
+
+
<?php
+namespace App\Controller;
+
+use App\Domain\User\User;
+use App\Domain\Order\Order;
+
+classUserController {
+ public functionshow(int$id): User {
+ return newUser('Marek'); // kein voller Pfad nötig
+ }
+}
+
+
Mit Aliassen kannst du Namen umbenennen, wenn es Konflikte gibt:
+
+
use App\Domain\User\User as DomainUser;
+use App\Http\User as HttpUser;
+
+
PSR-4: Datei = Namespace
+
PSR-4 ist die Konvention, wie Namespaces auf Dateipfade abgebildet werden. Composer nutzt das für Autoloading. Eine typische Struktur:
Nach composer dump-autoload findet PHP jede Klasse anhand ihres Namespaces automatisch – kein manuelles require mehr.
+
+
+
✓
+
+ Eine Klasse, eine Datei
+ Konvention: pro Datei genau eine Klasse, Datei-Name gleich Klassen-Name (case-sensitive!). User.php enthält class User. Die meisten Frameworks und Tools verlassen sich darauf.
+
+
+
+
+ Recall
+
+
Wozu sind Namespaces da?
+
Was bedeutet PSR-4 in einem Satz?
+
Wie importierst du eine Klasse aus einem anderen Namespace?
+
+
+
+
+
+
+
+
09
+
+
Fehlerbehandlung
+
Exceptions, try/catch, eigene Fehler
+
+
+
+
+ Frage zum Einstieg
+ Was passiert, wenn eine Datei nicht existiert, eine Datenbankverbindung scheitert oder eine API einen unerwarteten Wert liefert? In PHP gibt es zwei Welten: alte false-Rückgaben und moderne Exceptions. Wie navigierst du beide?
+
+
+
Exceptions verstehen
+
Eine Exception ist PHPs moderne Art, Fehler zu signalisieren. Wird sie nicht gefangen, bricht das Script ab. Mit try/catch reagierst du gezielt:
+ Niemals @-Suppression
+ Mit @functionCall() 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.
+
+
+
+
+ Recall
+
+
Was passiert, wenn eine Exception nicht gefangen wird?
+
Wann läuft der finally-Block?
+
Warum solltest du eigene Exception-Klassen schreiben?
+
+
+
+
+
+
+
+
10
+
+
Composer und erste Anwendung
+
Pakete installieren, kleines CLI-Tool bauen
+
+
+
+
+ Frage zum Einstieg
+ Du hast PHP gelernt – aber wie kommst du jetzt zu einem echten Projekt? Composer 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.
+
+
+
Projekt aufsetzen
+
Composer ist der Standard-Paketmanager für PHP. Installation einmal global, dann pro Projekt:
Im Terminal: php bin/app.php greet Marek. 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.
+
+
+
✓
+
+ Erst Tools, dann Framework
+ 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 Laravel oder Symfony.
+
+
+
+
+ Recall
+
+
Welche Datei steuert Composers Abhängigkeiten und Autoload?
+
Wozu ist composer dump-autoload?
+
Welche Library nimmst du für CLI-Tools in PHP?
+
+
+
+
+
+
+
Wie es weitergeht
+
+
Du hast PHP jetzt in 10 Kapiteln vom ersten Script bis zur Composer-Anwendung durchlaufen. Aber Wissen verblasst ohne Wiederholung. Plane aktive Wiederholung ein – effektiver als jedes Re-Reading.
+
+
Spaced-Repetition-Plan
+
+
+ Heute
+
Guide gelesen, Recall-Fragen aus jedem Kapitel beantwortet.
+
+
+ +1 Tag
+
OnePager überfliegen, alle Recall-Fragen aus dem Kopf beantworten.
+
+
+ +7 Tage
+
Mini-Projekt: ein eigenes CLI-Tool mit Composer und Symfony Console.
+
+
+ +30 Tage
+
Cheatsheet als Referenz – ein neues Paket aus Packagist einbinden.
+
+
+
+
Was als nächstes lernen
+
Mit diesen Grundlagen kannst du in jede Spezialisierung einsteigen. Empfehlungen je nach Interesse:
+
+
+
Web-Frameworks – Laravel für schnellen Einstieg, Symfony für modulare Apps
+
Datenbanken – PDO für SQL direkt, Doctrine als ORM für komplexe Domänen
+
Testing – PHPUnit für Unit-Tests, Mockery für Mocks
+
Static Analysis – PHPStan und Psalm finden Bugs ohne Ausführung
+
E-Commerce – Shopware oder Magento für Online-Shops
+
CMS – WordPress, Drupal oder TYPO3 für Content-Sites
+
+
+
Begleitmaterial
+
Dieser Guide ist Teil eines Sets:
+
+
PHP OnePager – die visuelle Übersicht für den Schreibtisch
+
PHP Cheatsheet – die dichte Referenz beim Programmieren
+
PHP Mini-Guide – der 15-Min-Schnelleinstieg
+
PHP Anfänger-Guide (dieses Dokument) – die Grundlagen tief
+
+
+
+
+
+```
\ No newline at end of file
diff --git a/templates/Referenz/Cheatsheet.md b/templates/Referenz/Cheatsheet.md
new file mode 100644
index 0000000..d976242
--- /dev/null
+++ b/templates/Referenz/Cheatsheet.md
@@ -0,0 +1,537 @@
+```
+
+
+
+
+PHP Cheatsheet
+
+
+
+
+
+
+
+
php
+
+
PHP Cheatsheet
+
Syntax, OOP, Type-System, Standard-Funktionen & Ökosystem auf einen Blick
+
+
+ 8.4
+ Stand 2026
+
+
+
+
+
+
+
+
+
+
+
+
+ Variablen & Typen
+
+
+
+
$x = 42
int
+
$x = 3.14
float
+
$x = "Hi"
string
+
$x = true
bool
+
$x = null
null
+
$x = [1,2,3]
array
+
$x = ['k'=>1]
assoc array
+
gettype($x)
Typ-Name
+
is_int($x)
Typ-Check
+
(int)$x
Cast
+
+
+
+
+
+
+
+ String-Funktionen
+
+
+
+
strlen($s)
Länge
+
strtolower
klein
+
strtoupper
groß
+
trim($s)
Whitespace weg
+
explode(',', $s)
→ array
+
implode(',', $a)
→ string
+
str_replace
a → b
+
str_contains
enthält?
+
sprintf("%d", 1)
format
+
"$name"
Interpolation
+
+
+
+
+
+
+
+
+
+
+
+
+ Moderne Klasse (PHP 8)
+
+
+
classUser {
+ public function__construct(
+ public readonlystring$name,
+ public readonlyint$age,
+ private ?string$email = null,
+ ) {}
+
+ public functionisAdult(): bool {
+ return$this->age >= 18;
+ }
+}
+
+$u = newUser(name: 'Marek', age: 34);
+
+
+```
\ No newline at end of file
diff --git a/templates/Referenz/ExtendedGuide.md b/templates/Referenz/ExtendedGuide.md
new file mode 100644
index 0000000..d121029
--- /dev/null
+++ b/templates/Referenz/ExtendedGuide.md
@@ -0,0 +1,2542 @@
+```
+
+
+
+
+PHP Extended-Guide
+
+
+
+
+
+
+
+
php
+
Extended-Guide · 4h · Stand 2026
+
+
+
+
PHP an der Grenze.
+
Internals, Fibers, FFI, DDD & Event Sourcing – PHP über das Übliche hinaus.
+
+
+
+
+ Was du danach kannst
+ Reflection und Stream-Wrapper produktiv einsetzen · mit Fibers asynchron programmieren · eigenen DI-Container schreiben · Performance gezielt profilen und optimieren · komplexe Domänen mit DDD, Hexagonal Architecture und Event Sourcing strukturieren.
+
+
PHP 8.4
+
+
+
+
+
+
Inhalt
+
+
Teil 1 · Sprach-Internals
+
+
+
1
+
+
Reflection-API tief
+
Klassen, Methoden, Attribute zur Laufzeit erkunden
+ Frage zum Einstieg
+ Wie weiß Symfony zur Laufzeit, welche Routen in deinem Controller stecken? Wie generiert Doctrine SQL-Schemas aus deinen Entity-Klassen? Beide nutzen Reflection – PHPs eingebauter Mechanismus, um Code-Struktur zu introspecten.
+
+
+
ReflectionClass: alles über eine Klasse
+
Mit ReflectionClass bekommst du alle Metadaten einer Klasse: Properties, Methoden, Attribute, Parent, Interfaces. Das ist die Basis fast aller Framework-Magie:
Manchmal willst du eine Klasse instanziieren, ohne den Konstruktor aufzurufen – z.B. zum Deserialisieren von Datenbank-Rows oder JSON. Reflection erlaubt das:
+
+
$reflection = new \ReflectionClass(User::class);
+$user = $reflection->newInstanceWithoutConstructor();
+
+// Jetzt Properties direkt setzen
+$nameProperty = $reflection->getProperty('name');
+$nameProperty->setValue($user, 'Marek');
+
+
Doctrine ORM tut genau das beim Laden: Entity ohne Konstruktor instanziieren, Properties aus DB-Row setzen. Sonst müsste jede Entity einen "leeren" Konstruktor haben.
+
+
Private Properties und Methoden
+
Reflection ignoriert Sichtbarkeitsregeln – essentiell für Testing-Frameworks und Serializer:
Reflection ist nicht gratis. Pro ReflectionClass-Instanz parst PHP intern viele Metadaten. Frameworks wie Symfony cachen Reflection-Ergebnisse aggressiv. Faustregel:
+
+
+
Use Case
OK
+
Boot-Zeit, einmal pro Request
ja
+
Dependency-Injection-Container Build
ja, aber cachen
+
In jedem Method-Call
nein, das ist langsam
+
Hot-Path-Loops
nein, niemals
+
+
+
+
i
+
+ Attribute via Reflection
+ Wie im Fortgeschritten-Guide gezeigt: $reflection->getAttributes() liest PHP 8 Attribute aus. Das ist die Brücke zwischen deinem deklarativen Markup (#[Route('/users')]) und dem Framework-Code, der es interpretiert.
+
+
+
+
+ Recall
+
+
Welche zwei großen Frameworks nutzen Reflection als Kern-Mechanismus?
+
Wie umgehst du den Konstruktor beim Instanziieren?
+
Warum ist Reflection in Hot-Paths problematisch?
+
+
+
+
+
+
+
+
02
+
+
SPL Datenstrukturen
+
Stack, Queue, Heap, ObjectStorage
+
+
+
+
+ Frage zum Einstieg
+ PHP-Arrays sind extrem flexibel – aber für spezielle Use Cases nicht optimal. Eine Priority Queue mit Array nachbauen kostet O(n log n) pro Insert. Die Standard PHP Library (SPL) liefert spezialisierte Datenstrukturen. Wann lohnt sich der Wechsel?
+
+
+
SplStack und SplQueue
+
Stack (LIFO) und Queue (FIFO) als typisierte Strukturen. Bei großen Mengen schneller als Array-Operationen mit array_push/array_shift:
+
+
$stack = new \SplStack();
+$stack->push('a');
+$stack->push('b');
+$stack->push('c');
+
+$stack->pop(); // 'c'
+$stack->top(); // 'b' (peek ohne entfernen)
+
+$queue = new \SplQueue();
+$queue->enqueue('first');
+$queue->enqueue('second');
+$queue->dequeue(); // 'first'
+
+
Wichtiger Vorteil gegenüber Array: O(1) für alle Operationen, auch dequeue. array_shift ist O(n) wegen Re-Indexing.
+
+
SplPriorityQueue
+
Bei Aufgaben mit Prioritäten – Job-Scheduler, A*-Pathfinding, Event-Loops – ist eine Priority Queue Standard:
+ SPL nur wenn nötig
+ Für die meisten Web-Apps reichen normale Arrays völlig. SPL lohnt sich, wenn du Performance-kritische Pfade mit großen Mengen hast oder spezielle Semantik brauchst (Priority, Object-Keys). Sonst macht es Code unnötig komplex.
+
+
+
+
+ Recall
+
+
Was ist der Performance-Vorteil von SplQueue gegenüber array_shift?
+
Wofür nutzt du SplObjectStorage?
+
Wann lohnt sich SplFixedArray?
+
+
+
+
+
+
+
+
03
+
+
Stream-Wrapper und Filter
+
Eigene Protokolle wie file:// schreiben
+
+
+
+
+ Frage zum Einstieg
+ Du kennst fopen('file:///pfad'), file_get_contents('http://...'), vielleicht fopen('php://memory'). All das funktioniert über Stream-Wrapper. Du kannst eigene schreiben – z.B. s3://bucket/key oder db://users/42. Wie geht das?
+
+
+
Was sind Stream-Wrapper?
+
Ein Stream-Wrapper definiert ein Pseudo-Protokoll, das wie eine Datei aussieht. Alle PHP-Funktionen, die mit Streams arbeiten (fopen, fread, file_get_contents, file_put_contents), funktionieren transparent damit.
+
+
PHP bringt mehrere eingebaute Wrapper mit:
+
+
+
Wrapper
Wofür
+
file://
Lokales Dateisystem (Default)
+
http:// / https://
HTTP-Requests
+
php://memory
In-Memory Stream
+
php://stdin / php://stdout
Standard-IO
+
php://input
Raw POST-Body
+
data://
Base64 oder URL-encoded Daten
+
compress.zlib://
gzip-Streams
+
+
+
Eigenen Wrapper schreiben
+
Ein Stream-Wrapper ist eine Klasse mit bestimmten Methoden. Minimal-Beispiel: ein Wrapper, der Strings aus einem statischen Array liefert:
+ Flysystem für Cloud-Storage
+ Für Production: nutze league/flysystem statt eigene Stream-Wrapper zu schreiben. Es abstrahiert S3, Azure, GCS, FTP, local mit einer einheitlichen API – stabiler und besser getestet als selbst gestrickte Wrapper.
+
+
+
+
+ Recall
+
+
Welche Methoden braucht ein Stream-Wrapper mindestens?
+
Wozu sind Stream-Filter da?
+
Welche Library nutzt du stattdessen für Cloud-Storage?
+
+
+
+
+
+
+
+
04
+
+
Garbage Collection
+
Zirkuläre Referenzen, Memory-Profiling
+
+
+
+
+ Frage zum Einstieg
+ PHP räumt Speicher automatisch auf – meistens. Aber in Long-Running-Prozessen (CLI-Tools, Workers, ReactPHP-Server) sammeln sich plötzlich GB an RAM. Was ist los? Die Antwort liegt in PHPs Garbage Collection und wie sie zirkuläre Referenzen behandelt.
+
+
+
Reference Counting
+
PHP nutzt primär Reference Counting: jede Variable hat einen Counter, wie oft auf sie verwiesen wird. Fällt der Counter auf 0, wird der Speicher sofort freigegeben:
In CLI-Workern oder ReactPHP-Servern ist Memory-Management kritisch – PHP-Scripts sterben sonst nach Stunden. Pattern:
+
+
while ($message = $queue->consume()) {
+ processMessage($message);
+
+ // Memory-Check
+ if (memory_get_usage() > 100 * 1024 * 1024) {
+ gc_collect_cycles();
+
+ if (memory_get_usage() > 200 * 1024 * 1024) {
+ exit(0); // Supervisor startet neu
+ }
+ }
+}
+
+
Symfony Messenger und Laravel Horizon nutzen genau dieses Pattern – sie killen Worker nach bestimmter Memory-Schwelle und lassen sie neu starten.
+
+
WeakReference und WeakMap
+
Seit PHP 7.4 (WeakReference) und 8.0 (WeakMap) gibt es einen offiziellen Mechanismus für Referenzen, die nicht in den Refcount zählen:
+
+
$user = newUser('Marek');
+$weak = \WeakReference::create($user);
+
+$weak->get(); // User-Objekt
+
+unset($user); // User wird sofort freigegeben
+$weak->get(); // null
+
+// WeakMap: nützlich für Caches, die nicht "festhalten"
+$cache = new \WeakMap();
+$cache[$user] = 'cached-value';
+// Sobald $user weg ist, ist auch der Cache-Eintrag weg
+
+
+ Recall
+
+
Was ist PHPs primäre GC-Strategie?
+
Warum reicht Reference Counting bei zirkulären Referenzen nicht?
+
Wofür nutzt du WeakReference?
+
+
+
+
+
+
+
+
05
+
+
FFI - C-Code direkt aufrufen
+
Foreign Function Interface
+
+
+
+
+ Frage zum Einstieg
+ Du brauchst eine C-Library – etwa für GPU-Berechnungen, Bildverarbeitung oder spezielle Algorithmen. Klassisch: PHP-Extension in C schreiben (großer Aufwand). Mit FFI seit PHP 7.4 rufst du C-Funktionen direkt aus PHP-Code auf, ohne Kompilieren.
+
+
+
FFI aktivieren
+
FFI ist standardmäßig installiert, muss aber in der php.ini aktiviert werden:
+
+
# php.ini
+extension=ffi
+ffi.enable=preload # sicher (nur für preloaded scripts)
+# oder: ffi.enable=true # offen, unsicher in Web-Context
+
+
C-Funktion aufrufen
+
FFI braucht zwei Dinge: die Signatur der C-Funktion und die Library-Datei. Klassisches Beispiel: die libc-Funktion getpid:
+
+
$ffi = \FFI::cdef("
+ int getpid(void);
+ unsigned int sleep(unsigned int seconds);
+", "libc.so.6");
+
+echo$ffi->getpid(); // 12345 (Process-ID)
+$ffi->sleep(2); // 2 Sekunden schlafen
+
+
Header-Datei laden
+
Für größere Libraries kannst du komplette C-Header parsen lassen. Beispiel: SDL für ein Spiele-Backend, oder libxml für XML-Verarbeitung:
+
+
$ffi = \FFI::load('/usr/include/sdl2/SDL.h');
+
+// Jetzt sind alle SDL-Funktionen verfügbar
+$ffi->SDL_Init(SDL_INIT_VIDEO);
+$window = $ffi->SDL_CreateWindow(...);
+
+
Structs und Pointer
+
C-Strukturen werden zu PHP-Objekten. Pointer und Memory-Allokation funktionieren auch:
FFI-Calls sind nicht gratis: jeder Aufruf hat Overhead durch Type-Conversion. Faustregel:
+
+
+
Lohnt sich: wenige Aufrufe mit viel C-Arbeit pro Call (Bildverarbeitung)
+
Lohnt sich nicht: viele kleine Aufrufe in Loops (klassische PHP-Logik schneller)
+
+
+
+
!
+
+ FFI ist nicht für Web-Apps gedacht
+ Mit ffi.enable=preload nur in CLI-Scripts oder Preload-Files nutzen. In Web-Context (Apache/php-fpm) bedeutet falsche FFI-Nutzung Memory-Corruption oder Crashes. Halte FFI auf CLI-Tools oder Daemon-Code beschränkt.
+
+
+
+
+ Recall
+
+
Welche zwei Dinge braucht FFI::cdef?
+
Wann lohnt sich FFI, wann nicht?
+
Warum solltest du FFI nicht in Web-Requests nutzen?
+
+
+
+
+
+
+
+
06
+
+
OpCache und Preloading
+
Code-Compilation einmal, nicht pro Request
+
+
+
+
+ Frage zum Einstieg
+ PHP parst und kompiliert standardmäßig deinen Code bei jedem Request neu. Bei einer Laravel-App mit hunderten Dateien sind das viele Millisekunden Overhead pro Request. OpCache und Preloading eliminieren das.
+
+
+
OpCache aktivieren
+
OpCache ist ab PHP 5.5 dabei, muss aber in php.ini aktiviert werden:
OpCache cached die kompilierten OpCodes im Shared Memory. Beim nächsten Request wird der gecachte OpCode direkt ausgeführt – ohne Parse und Compile.
+
+
Validate Timestamps in Production
+
In Development willst du Code-Änderungen sofort sehen (validate_timestamps=1). In Production sind File-Stat-Calls bei jedem Request unnötiger Overhead – deshalb validate_timestamps=0:
+
+
# Bei Deployment manuell reload
+service php-fpm reload
+# oder
+opcache_reset() # im PHP-Code
+# oder cache file/api
+curl http://localhost/opcache-reset.php
Seit PHP 7.4 gibt es Preloading: beim Start des PHP-Prozesses werden Klassen einmal komplett geladen und stehen dann in jedem Request sofort zur Verfügung – ohne OpCache-Lookup:
// preload.php
+<?php
+require'/var/www/vendor/autoload.php';
+
+// Lade alle Klassen aus src/
+foreach (new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator('/var/www/src')
+) as$file) {
+ if ($file->getExtension() === 'php') {
+ opcache_compile_file($file->getRealPath());
+ }
+}
+
+
Effekt: in Laravel- und Symfony-Apps oft 20-30% schnellere Requests, vor allem bei kleinen Aktionen.
+
+
JIT (Just-In-Time)
+
Mit PHP 8 kam der JIT. Er kompiliert OpCodes weiter zu nativem Maschinencode. In typischen Web-Apps bringt JIT wenig (I/O-bound), bei rechenintensiven Loops und CLI-Tools deutlich:
+
+
+
Use Case
Speedup mit JIT
+
Web (Database/Network-bound)
~5-10%
+
CLI-Tools, Compute-bound
~30-50%
+
Mandelbrot/Mathematik
~2-3x
+
+
+
+
i
+
+ Preloading-Limitationen
+ Geladene Klassen können zur Laufzeit nicht mehr geändert werden. Wenn dein Framework Klassen dynamisch erweitert (z.B. Symfony Cache-Container), führt das zu Konflikten. Reload bei Deployment ist Pflicht.
+
+
+
+
+ Recall
+
+
Was cached OpCache genau?
+
Was ist der Unterschied zwischen validate_timestamps=1 und 0?
+
Wann lohnt sich JIT besonders?
+
+
+
+
+
+
+
+
07
+
+
Fibers - Cooperative Concurrency
+
Async ohne Threading (PHP 8.1+)
+
+
+
+
+ Frage zum Einstieg
+ PHP ist klassisch single-threaded und blockierend. Aber moderne Apps brauchen oft parallel laufende HTTP-Calls, gleichzeitige DB-Queries, Streaming. Fibers in PHP 8.1 bringen kooperative Concurrency – die Basis für moderne async Libraries.
+
+
+
Was ist eine Fiber?
+
Eine Fiber ist eine "leichtgewichtige" Coroutine: sie hat ihren eigenen Stack, kann pausieren und später fortgesetzt werden. Anders als Threads laufen sie nicht parallel, sondern kooperativ – sie geben Kontrolle freiwillig ab:
Klassischer Use Case: HTTP-Requests parallel. Statt zu blockieren, suspendiert die Fiber während sie wartet:
+
+
functionfetchUrl(string$url): string {
+ returnnew \Fiber(function() use ($url) {
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+ // Während wir warten: andere Fibers können laufen
+ \Fiber::suspend();
+
+ returncurl_exec($ch);
+ });
+}
+
+
Das einfache Beispiel hier zeigt nur das Konzept – in der Praxis brauchst du eine Scheduler/Event-Loop, die mehrere Fibers verwaltet. Frameworks wie ReactPHP und Amp bieten das.
+
+
Amp 3.0 mit Fibers
+
Amp (revierschiff in PHP-Async-Welt) nutzt Fibers in Version 3.0 für eine viel saubere API als die alten Promise-basierten Patterns:
+
+
use function Amp\async;
+use function Amp\Future\await;
+
+// Drei parallele HTTP-Requests
+$results = await([
+ async(fetchUrl(...), 'https://api.a.com'),
+ async(fetchUrl(...), 'https://api.b.com'),
+ async(fetchUrl(...), 'https://api.c.com'),
+]);
+
+// Hier sind alle drei Responses verfügbar
+foreach ($resultsas$response) {
+ // ...
+}
+
+
Im Hintergrund läuft eine Event-Loop, die alle Fibers verwaltet und sie aufweckt, wenn ihre I/O fertig ist.
+
+
Fibers vs. echte Threads
+
+
+
Aspekt
Fibers
Threads (parallel)
+
Parallelität
Nein, kooperativ
Ja, true parallel
+
Memory pro Unit
~8 KB
~MB
+
Context-Switch
Sehr schnell
OS-Overhead
+
Shared State
Direkt zugreifbar
Locking nötig
+
Use Case
I/O-bound
CPU-bound
+
+
+
Für klassische Web-Apps (viele I/O-Calls, wenig CPU) sind Fibers fast immer die richtige Wahl. Wenn du echtes Parallel-Processing brauchst (Bildverarbeitung, Berechnungen), nutzt du PHP parallel-Extension oder lagerst es in separate Prozesse aus.
+
+
+
✓
+
+ Symfony 7 mit Fibers
+ Symfony 7+ nutzt intern Fibers für HttpClient (Multi-Request) und Messenger (parallele Message-Verarbeitung). Auch ohne explizit Fibers zu schreiben profitierst du dadurch.
+
+
+
+
+ Recall
+
+
Was ist der Hauptunterschied zwischen Fiber und Thread?
+
Wann gibt eine Fiber Kontrolle ab?
+
Wofür braucht es zusätzlich eine Event-Loop wie in Amp?
+
+
+
+
+
+
+
+
08
+
+
ReactPHP & Event-Loops
+
Long-Running Server, non-blocking I/O
+
+
+
+
+ Frage zum Einstieg
+ PHP klassisch: ein Request, ein Prozess, Sterben am Ende. Aber WebSocket-Server, Pub/Sub-Workers oder Streaming-APIs brauchen Long-Running-Prozesse. ReactPHP bringt das Event-Loop-Modell (wie Node.js) in die PHP-Welt.
+
+
+
Event-Loop-Grundprinzip
+
Eine Event-Loop ist eine Endlos-Schleife, die Events aus verschiedenen Quellen (Sockets, Timer, Streams) liest und Handler aufruft. I/O-Operationen blockieren nicht – sie geben Promises/Callbacks zurück.
+
+
use React\EventLoop\Loop;
+
+// Timer: nach 2 Sekunden, einmal
+Loop::addTimer(2.0, function() {
+ echo"Nach 2 Sekunden\n";
+});
+
+// Periodisch alle 5 Sekunden
+Loop::addPeriodicTimer(5.0, function() {
+ echo"Alle 5 Sekunden\n";
+});
+
+// Event-Loop starten (blockierend bis stop)
+Loop::run();
+
+
HTTP-Server mit ReactPHP
+
Ein vollständiger HTTP-Server in wenigen Zeilen – ohne Apache/nginx davor:
+
+
use React\Http\HttpServer;
+use React\Http\Message\Response;
+use React\Socket\SocketServer;
+use Psr\Http\Message\ServerRequestInterface;
+
+$http = new HttpServer(function(ServerRequestInterface $request) {
+ return Response::plaintext("Hello, " . $request->getUri()->getPath());
+});
+
+$socket = new SocketServer('0.0.0.0:8080');
+$http->listen($socket);
+
+echo"Server läuft auf http://localhost:8080\n";
+// Event-Loop läuft implizit weiter
+
+
Vorteil gegenüber php-fpm: State bleibt zwischen Requests erhalten. Datenbankverbindungen, Caches, geladene Klassen werden einmal aufgebaut. Bei vielen kleinen Requests ist das deutlich schneller.
+
+
Streams und Pipes
+
+
use React\Stream\ReadableResourceStream;
+use React\Stream\WritableResourceStream;
+
+$stdin = new ReadableResourceStream(STDIN);
+$stdout = new WritableResourceStream(STDOUT);
+
+$stdin->on('data', function($chunk) use ($stdout) {
+ $stdout->write(strtoupper($chunk));
+});
+
+$stdin->on('end', function() {
+ echo"\nInput beendet\n";
+});
+
+
WebSocket-Server mit Ratchet
+
Ratchet baut auf ReactPHP auf und bringt WebSocket-Support:
+ Blockierender Code killt die Event-Loop
+ Ein einziger sleep(10), file_get_contents() auf eine langsame URL, oder ein synchrones PDO::query() blockiert die gesamte Event-Loop. Alle Connections frieren ein. In ReactPHP musst du konsequent non-blocking I/O nutzen.
+
+
+
+
+ Recall
+
+
Was ist der Hauptunterschied zwischen php-fpm und ReactPHP?
+
Welches Pattern wäre für einen WebSocket-Chat geeignet?
+
Warum sind blockierende Calls in ReactPHP tabu?
+
+
+
+
+
+
+
+
09
+
+
Profiling mit Blackfire
+
CPU- und Memory-Hotspots finden
+
+
+
+
+ Frage zum Einstieg
+ Deine App ist langsam – aber wo genau? Der Controller? Die DB-Query? Die View-Rendering? Raten endet im Polieren der falschen Stellen. Blackfire und Xdebug Profiler zeigen dir Call-Graphs mit echten Messzahlen.
+
+
+
Xdebug-Profiler (gratis)
+
Xdebug bringt einen einfachen Profiler mit. In php.ini:
Mit ?XDEBUG_TRIGGER=1 als Query-Parameter aktivierst du das Profiling für einen Request. Ergebnis: eine Cachegrind-Datei in /tmp/xdebug/, die du mit KCacheGrind (Linux) oder QCacheGrind (macOS) öffnest.
+
+
Blackfire (professionell)
+
Blackfire ist ein kommerzieller Profiler mit Web-UI, Vergleichen, Regressions-Detection und Production-Profiling. Setup: Agent installieren, Probe als PHP-Extension, dann profilen:
+
+
# CLI-Script profilen
+blackfire run php script.php
+
+# Eine bestimmte URL
+blackfire curl https://example.com/slow-page
+
+# Mit Iterationen (Mittelwert)
+blackfire --samples=10 curl https://example.com/page
+
+
Das Ergebnis ist ein Call-Graph: jede Funktion mit ihrer Zeit, wer sie aufruft, wie oft, welcher Anteil an der Gesamtzeit.
+
+
Im Code instrumentieren
+
+
use Blackfire\Client;
+use Blackfire\Profile\Configuration;
+
+$blackfire = new Client();
+$config = (new Configuration())->setTitle('Order Processing');
+$probe = $blackfire->createProbe($config);
+
+// Code, der gemessen werden soll
+$service->processOrders($orders);
+
+$blackfire->endProbe($probe);
+
+
Typische Findings
+
+
+
Pattern
Wie oft
Lösung
+
N+1 Query
fast jede App
Eager-Loading
+
JSON-Encoding mehrfach
oft
Cache
+
Twig ohne Cache
häufig
opcache.preload
+
Doctrine: gleiche Entity 100x geladen
häufig
Identity Map
+
Composer Autoload langsam
oft
composer dump-autoload -o
+
+
+
Memory-Profiling
+
Memory-Leaks sind oft schlimmer als CPU-Probleme. memory_get_peak_usage() gibt dir die maximale Speichernutzung eines Requests:
Für tieferes Memory-Profiling: php-meminfo (Extension) oder Blackfire's Memory-Profile.
+
+
+
✓
+
+ Misst, was zählt – nicht alles
+ Optimiere nicht blind nach Profiler-Ausgabe. Eine Funktion, die 30% der Zeit braucht, aber nur einmal pro Tag läuft, ist weniger wichtig als eine, die 5% braucht, aber 1000 mal pro Minute. Schau dir absolute Zahlen × Frequenz an, nicht nur Prozent.
+
+
+
+
+ Recall
+
+
Welcher Profiler ist gratis dabei?
+
Was ist ein N+1 Query und warum häufig?
+
Wofür ist memory_get_peak_usage()?
+
+
+
+
+
+
+
+
10
+
+
Caching-Strategien
+
APCu, Redis, HTTP-Cache, ESI
+
+
+
+
+ Frage zum Einstieg
+ Eine Datenbank-Query, die 200ms dauert. Eine Berechnung, die immer wieder dasselbe Ergebnis liefert. Ein externer API-Call, der teuer ist. Caching ist meist die größte Performance-Optimierung – aber wo cachen und wie invalidieren?
+
+
+
Die Caching-Schichten
+
+
+
Schicht
Latenz
Tech
+
OpCache (kompilierter Code)
~0ms
eingebaut
+
Request-Cache (in-memory)
~0ms
Arrays
+
APCu (shared memory)
~0.01ms
APCu Extension
+
Redis (lokal)
~0.5ms
Redis-Server
+
Redis (Netzwerk)
~1-5ms
remote Redis
+
Datenbank-Query
~5-100ms
MySQL/Postgres
+
Externe API
~50-1000ms
HTTP
+
+
+
Jede Schicht hinunter ist 10-100x langsamer. Die Kunst: so weit oben wie möglich cachen, ohne stale Daten zu liefern.
+
+
PSR-6 / PSR-16 Cache
+
Die PHP-FIG hat Cache-Interfaces standardisiert. Symfony Cache, Laravel Cache und Doctrine Cache implementieren sie. Dein Code bleibt agnostisch:
+
+
use Symfony\Contracts\Cache\CacheInterface;
+use Symfony\Contracts\Cache\ItemInterface;
+
+classUserRepository {
+ public function__construct(private CacheInterface $cache) {}
+
+ public functionfind(int$id): ?User {
+ return$this->cache->get("user.$id", function(ItemInterface $item) use ($id) {
+ $item->expiresAfter(3600); // 1 Stunde
+ return$this->loadFromDb($id);
+ });
+ }
+}
+
+
APCu für Request-übergreifende Caches
+
APCu ist Shared Memory pro PHP-Server-Prozess. Sehr schnell, aber:
+
+
+
Pro Server-Maschine – nicht über mehrere Server verteilt
+
Daten gehen bei PHP-FPM-Reload verloren
+
Begrenzte Größe (Standard: 32MB)
+
+
+
// Direkte API
+apcu_store('key', $data, 300); // 5 Min TTL
+$data = apcu_fetch('key');
+
+// Atomic increment für Counter
+$hits = apcu_inc('hits:home');
+
+
Redis für verteilte Caches
+
+
$redis = new \Redis();
+$redis->connect('redis-server', 6379);
+
+// Einfacher Key-Value
+$redis->set('user:42', json_encode($user), 3600);
+$cached = json_decode($redis->get('user:42'), true);
+
+// Pipeline für Batch-Operationen
+$pipe = $redis->multi(Redis::PIPELINE);
+foreach ($idsas$id) {
+ $pipe->get("user:$id");
+}
+$results = $pipe->exec(); // alle in einem Roundtrip
+
+
HTTP-Cache und ESI
+
Für komplette Seiten oder Fragmente bietet sich HTTP-Caching an. Cache-Control und ETag sagen Browser und CDNs, wann sie nicht neu fragen müssen:
ESI (Edge Side Includes) erlaubt, dass CDN/Varnish Teile einer Seite separat cachen – Header bleibt 1 Stunde gecached, der User-spezifische Mini-Cart nur 1 Minute. Symfony unterstützt ESI nativ.
+
+
Cache-Invalidierung
+
Das schwerste Problem. Strategien:
+
+
+
TTL – einfach, aber kann stale sein
+
Manuell bei Schreib-Operationen – fehlt leicht eine Stelle
+
Tags – Cache-Items haben Labels, du invalidierst per Label
+
Event-driven – Listener auf Domain-Events räumen Cache auf
+
+
+
+
i
+
+ "Es gibt zwei schwere Probleme in der Informatik..."
+ Cache-Invalidierung und Naming. Beide passieren immer. Akzeptiere, dass dein Cache manchmal stale ist, baue Monitoring ein, und plane Strategien für Invalidierung von Anfang an mit.
+
+
+
+
+ Recall
+
+
Wann nutzt du APCu, wann Redis?
+
Was macht ESI in Varnish?
+
Welche Strategien für Cache-Invalidierung kennst du?
+
+
+
+
+
+
+
+
11
+
+
Dependency Injection Container
+
Eigener Container in 100 Zeilen
+
+
+
+
+ Frage zum Einstieg
+ Symfony Container, Laravel Container, PHP-DI – alle lösen dasselbe Problem: Klassen automatisch instanziieren mit den richtigen Abhängigkeiten. Wie funktioniert das intern? Ein eigener DI-Container in 100 Zeilen Code zeigt das Prinzip.
+
+
+
Was ein DI-Container tut
+
Du sagst dem Container: "Gib mir eine Instanz von OrderService". Er liest die Konstruktor-Signatur, sieht "braucht PaymentGateway und EmailSender", instanziiert die auch automatisch und übergibt sie:
+
+
classOrderService {
+ public function__construct(
+ private PaymentGateway $gateway,
+ private EmailSender $mailer,
+ ) {}
+}
+
+// Container kümmert sich um alles
+$container = newContainer();
+$service = $container->get(OrderService::class);
+
+
Minimaler Container
+
Hier ein vereinfachter Container, der Klassen über Reflection auflöst. Production-Container haben mehr Features, das Kern-Prinzip bleibt:
Compile-Time-Optimierung – Bindings werden zu generiertem PHP-Code (Symfony)
+
Auto-Wiring – kein explizites Binding nötig, wenn Class-Name = Interface
+
Tags – Services mit Tags wie "event_listener" automatisch sammeln
+
Lazy-Loading – Services werden erst beim ersten Zugriff erzeugt
+
Parameter – Konfigurations-Werte (Strings, Arrays) als Konstanten injizieren
+
+
+
+
✓
+
+ Nutze einen ausgereiften Container
+ Für eigene Projekte: PHP-DI oder Symfony DependencyInjection. Eigene Container bauen ist lehrreich, aber Production-Container haben Edge Cases und Performance-Optimierung, die du selbst nicht erreichst.
+
+
+
+
+ Recall
+
+
Was ist die Hauptaufgabe eines DI-Containers?
+
Welcher Reflection-Mechanismus ist Basis dafür?
+
Was machen "Tags" in Symfony's Container?
+
+
+
+
+
+
+
+
12
+
+
Event-Dispatcher Pattern
+
Lose Kopplung zwischen Bounded Contexts
+
+
+
+
+ Frage zum Einstieg
+ Eine Bestellung wird platziert. Folge: E-Mail an Kunde, Lagerplatz reservieren, Statistik aktualisieren, Webhook an Partner senden. Sollst du das alles in OrderService::place() reincoden? Das Event-Dispatcher-Pattern trennt das sauber.
+
OrderService kennt alle anderen Services. Jeder neue Listener bedeutet eine Code-Änderung. Tests brauchen alle Mocks. Tight Coupling.
+
+
Mit Event-Dispatcher
+
Der Service publiziert nur ein Event. Wer darauf reagiert, ist ihm egal. Andere Bounded Contexts subscriben sich:
+
+
classOrderPlaced {
+ public function__construct(public readonlyOrder$order) {}
+}
+
+classOrderService {
+ public function__construct(private EventDispatcherInterface $events) {}
+
+ public functionplace(Order$order): void {
+ $this->save($order);
+ $this->events->dispatch(newOrderPlaced($order));
+ }
+}
+
+// Listener leben in ihren eigenen Modulen
+classSendConfirmationListener {
+ public function__invoke(OrderPlaced$event): void {
+ $this->mailer->send($event->order->customer, 'Bestätigung');
+ }
+}
+
+classReserveInventoryListener {
+ public function__invoke(OrderPlaced$event): void {
+ $this->inventory->reserve($event->order);
+ }
+}
+
+
Jetzt kann ein neues Team einen neuen Listener hinzufügen, ohne OrderService anzufassen. Tests des Services brauchen nur einen Mock-Dispatcher.
+
+
Symfony EventDispatcher
+
Symfony hat einen ausgereiften Dispatcher (PSR-14-konform). Listener werden über Tags oder Attribute registriert:
+
+
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
+
+#[AsEventListener(event: OrderPlaced::class)]
+classSendConfirmationListener {
+ public function__invoke(OrderPlaced$event): void {
+ // ...
+ }
+}
+
+
Symfony scannt beim Container-Build alle Klassen mit dem Attribut und verdrahtet sie automatisch.
+
+
Synchron vs. Asynchron
+
Standardmäßig laufen Listener synchron – im selben Request, hintereinander. Für langsame Operationen (E-Mail, Webhook) willst du sie async machen – sonst friert der User-Request ein:
+
+
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+
+// Statt EventListener: MessageHandler in Symfony Messenger
+#[AsMessageHandler]
+classSendConfirmationHandler {
+ public function__invoke(OrderPlaced$message): void {
+ $this->mailer->send(...);
+ }
+}
+
+# config/messenger.yaml
+framework:
+ messenger:
+ routing:
+ App\Event\OrderPlaced: 'async'# landet in Queue
+
+
Der User-Request kehrt sofort zurück, der Mail-Versand passiert im Worker-Prozess. Wichtig für UX und Resilienz.
+
+
+
i
+
+ Domain Events vs. Application Events
+ Domain Events (OrderPlaced) sind Teil der Geschäftslogik, beschreiben "was passiert ist". Application Events sind technisch (kernel.request in Symfony). Mische sie nicht – Domain Events leben im Domain-Layer, Application Events im Framework.
+
+
+
+
+ Recall
+
+
Was ist das Hauptproblem von direkten Service-Aufrufen ohne Events?
+
Wann nutzt du sync, wann async Event-Handling?
+
Was ist der Unterschied zwischen Domain Event und Application Event?
+
+
+
+
+
+
+
+
13
+
+
CQRS und Command Bus
+
Lese- und Schreibmodelle trennen
+
+
+
+
+ Frage zum Einstieg
+ Deine User-Liste braucht 12 verschiedene Filter, Pagination, Aggregate. Deine User-Updates haben komplexe Business-Regeln. Beide in einer Klasse abzubilden wird unhandlich. CQRS trennt Reads und Writes radikal.
+
+
+
Was ist CQRS?
+
Command Query Responsibility Segregation – ein Pattern, das Lese-Operationen (Queries) und Schreib-Operationen (Commands) auf separate Modelle teilt:
+
+
+
Commands ändern State, geben nichts zurück (oder nur ID)
+
Queries lesen State, ändern nichts
+
+
+
// Command: Intention "tu das"
+classCreateUserCommand {
+ public function__construct(
+ public readonlystring$name,
+ public readonlystring$email,
+ ) {}
+}
+
+// Query: Frage "wie ist das"
+classFindUsersByCountryQuery {
+ public function__construct(
+ public readonlystring$country,
+ public readonlyint$page = 1,
+ ) {}
+}
+
+
Command Bus
+
Statt Controller-Code, der direkt Repository-Aufrufe macht, dispatched er einen Command an den Bus. Der Bus findet den richtigen Handler:
+
+
classUserController {
+ public functioncreate(Request$request, CommandBus $bus): Response {
+ $command = newCreateUserCommand(
+ name: $request->getString('name'),
+ email: $request->getString('email'),
+ );
+
+ $bus->dispatch($command);
+
+ return new Response('Created', 201);
+ }
+}
+
+// Handler – die einzige Stelle mit Business-Logik
+classCreateUserCommandHandler {
+ public function__construct(private UserRepository $repo) {}
+
+ public function__invoke(CreateUserCommand$cmd): void {
+ $user = newUser($cmd->name, $cmd->email);
+ $this->repo->save($user);
+ }
+}
+
+
Query Bus
+
Analog für Reads – aber mit Rückgabewert. Queries lesen oft aus optimierten Read-Modellen (denormalisierte Views, Search-Index):
+
+
classFindUsersByCountryQueryHandler {
+ public function__construct(private \PDO$db) {}
+
+ public function__invoke(FindUsersByCountryQuery$q): array {
+ // Direktes SQL, optimiert für die View
+ $stmt = $this->db->prepare('
+ SELECT id, name, email, created_at, order_count
+ FROM users_with_stats
+ WHERE country = ?
+ LIMIT ? OFFSET ?
+ ');
+ $stmt->execute([$q->country, 50, ($q->page - 1) * 50]);
+
+ return$stmt->fetchAll();
+ }
+}
+
+
Beachte: keine Entities, kein Repository – direkter DB-Zugriff auf eine speziell für diese Query optimierte View. Performance über Domain-Sauberkeit, weil Queries nichts ändern.
+
+
Symfony Messenger als Bus
+
Symfony Messenger ist nicht nur für async – auch als Command/Query Bus nutzbar:
+ CQRS ist keine Silver Bullet
+ Es bringt mehr Code, mehr Klassen, mehr Indirection. Bei einer Standard-CRUD-App ist es Overkill. Wende es gezielt an: nur dort wo Komplexität es rechtfertigt. Innerhalb einer App kannst du auch nur einen Bounded Context mit CQRS bauen, den Rest mit klassischen Services.
+
+
+
+
+ Recall
+
+
Was ist der Hauptunterschied zwischen Command und Query?
+ Frage zum Einstieg
+ Anämische Entities sind in PHP weit verbreitet: Klassen mit nur Properties und Getter/Setter, alle Business-Logik in Service-Klassen verstreut. Domain-Driven Design dreht das um – Logik lebt im Domain-Modell, nicht außerhalb.
+
+
+
Value Objects
+
Ein Value Object ist ein Wert ohne eigene Identität, unveränderlich. Statt string $email nimm Email $email – mit eingebauter Validierung:
+
+
final classEmail {
+ public function__construct(public readonlystring$value) {
+ if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
+ throw new \InvalidArgumentException("Ungültige Email: $value");
+ }
+ }
+
+ public functiondomain(): string {
+ returnsubstr($this->value, strpos($this->value, '@') + 1);
+ }
+
+ public functionequals(Email$other): bool {
+ returnstrtolower($this->value) === strtolower($other->value);
+ }
+}
+
+$email = newEmail('marek@example.com'); // validiert automatisch
+$email->domain(); // 'example.com'
+
+
Wo immer eine Email durch deinen Code wandert, ist sie garantiert valide. Keine "ist das ein gültiges Format?"-Checks mehr überall.
+
+
Entities mit Geschäftslogik
+
Eine Entity hat eine Identität (z.B. ID), kann sich über die Zeit verändern, aber bleibt dasselbe Objekt. Klassisches Anti-Pattern: anämische Entity mit nur Gettern/Settern. DDD-Ansatz: Logik lebt in der Entity:
+
+
classOrder {
+ private array$items = [];
+ private OrderStatus $status;
+
+ public function__construct(public readonly OrderId $id, public readonly CustomerId $customerId) {
+ $this->status = OrderStatus::Draft;
+ }
+
+ public functionaddItem(Product $product, int$quantity): void {
+ if ($this->status !== OrderStatus::Draft) {
+ throw new CannotModifyConfirmedOrder();
+ }
+ $this->items[] = new OrderItem($product, $quantity);
+ }
+
+ public functionconfirm(): void {
+ if (empty($this->items)) throw new CannotConfirmEmptyOrder();
+ $this->status = OrderStatus::Confirmed;
+ }
+
+ public functiontotal(): Money {
+ returnarray_reduce(
+ $this->items,
+ fn(Money $sum, OrderItem $item) => $sum->add($item->subtotal()),
+ Money::zero()
+ );
+ }
+}
+
+
Die Order schützt ihre eigenen Invarianten – sie lässt nicht zu, dass jemand sie in einen inkonsistenten Zustand bringt.
+
+
Aggregates und Aggregate Root
+
Ein Aggregate ist eine Gruppe von Objekten, die zusammen konsistent bleiben müssen. Der Aggregate Root ist der einzige Einstiegspunkt:
+
+
// Order ist Aggregate Root
+// OrderItem ist Teil des Aggregates, NICHT direkt zugreifbar
+
+// Schlecht: OrderItem direkt aus Repository holen
+$item = $itemRepo->find(123);
+$item->quantity = 5;
+$itemRepo->save($item); // Order weiß nichts davon → Inkonsistenz
+
+// Gut: Immer durch Aggregate Root
+$order = $orderRepo->find($orderId);
+$order->changeItemQuantity($itemId, 5); // Order prüft Invarianten
+$orderRepo->save($order);
+
+
Repositories
+
Ein Repository ist die Schnittstelle zur Persistenz für ein Aggregate. Er sieht aus wie eine Collection:
+
+
interfaceOrderRepository {
+ public functionfind(OrderId $id): ?Order;
+ public functionsave(Order$order): void;
+ public functionremove(Order$order): void;
+}
+
+// Implementierung mit Doctrine
+classDoctrineOrderRepositoryimplementsOrderRepository {
+ public function__construct(private EntityManagerInterface $em) {}
+
+ public functionfind(OrderId $id): ?Order {
+ return$this->em->find(Order::class, $id->value);
+ }
+
+ public functionsave(Order$order): void {
+ $this->em->persist($order);
+ $this->em->flush();
+ }
+}
+
+
+
i
+
+ DDD ist mehr als nur Klassen-Struktur
+ DDD beinhaltet auch strategisches Design: Bounded Contexts, Ubiquitous Language, Context Maps. Die taktischen Patterns (Value Object, Entity, Aggregate) sind nur das Werkzeug für die strategische Trennung von Geschäftsbereichen.
+
+
+
+
+ Recall
+
+
Was unterscheidet Value Object von Entity?
+
Was ist die Regel für Zugriff auf Aggregate-Member?
+
Wofür ist ein Repository da?
+
+
+
+
+
+
+
+
15
+
+
Hexagonal Architecture
+
Ports und Adapter, Domain im Zentrum
+
+
+
+
+ Frage zum Einstieg
+ Deine App hängt überall an Symfony, an Doctrine, an Redis. Refactor auf ein anderes Framework? Praktisch unmöglich. Tests, die echte DB brauchen? Langsam. Hexagonal Architecture (auch "Ports und Adapter") löst das mit konsequenter Inversion of Control.
+
+
+
Das Prinzip
+
Im Zentrum steht die Domain (Geschäftslogik). Drumherum sind Ports (Interfaces) und Adapter (Implementierungen). Die Domain weiß nichts von Symfony, Doctrine oder HTTP – sie kennt nur ihre eigenen Interfaces:
Die Domain definiert, was sie braucht (Port), die Infrastruktur liefert wie (Adapter):
+
+
// Outbound Port (in der Domain)
+// Liegt im src/Domain/
+interfacePaymentGateway {
+ public functioncharge(Order$order): PaymentReceipt;
+}
+
+// Outbound Adapter (in der Infrastructure)
+// Liegt im src/Infrastructure/Payment/
+classStripeGatewayimplementsPaymentGateway {
+ public function__construct(private StripeClient $stripe) {}
+
+ public functioncharge(Order$order): PaymentReceipt {
+ $charge = $this->stripe->charges->create([...]);
+ return new PaymentReceipt($charge->id);
+ }
+}
+
+// Alternative für Tests
+classFakePaymentGatewayimplementsPaymentGateway {
+ public functioncharge(Order$order): PaymentReceipt {
+ return new PaymentReceipt('fake-receipt');
+ }
+}
+
+
Vorteile
+
+
+
Tests ohne Infrastruktur – Domain mit Fakes statt echter DB/API
+
Framework-Wechsel möglich – Domain unabhängig von Symfony
+
Klare Verantwortung – jede Schicht hat ihren Job
+
Adapter parallel – HTTP, CLI und Worker rufen denselben Use Case
+ Nicht jede App braucht Hexagonal
+ Die Architektur kostet: mehr Interfaces, mehr Klassen, mehr Indirection. Für CRUD-Apps Overkill. Lohnt sich, wenn die Geschäftslogik komplex und langlebig ist, Tests wichtig sind und die App über Jahre weiterleben soll.
+
+
+
+
+ Recall
+
+
Wo lebt die Geschäftslogik in Hexagonal Architecture?
+
Was ist der Unterschied zwischen Port und Adapter?
+
Warum kann man die Domain ohne Datenbank testen?
+
+
+
+
+
+
+
+
16
+
+
Event Sourcing
+
State als Sequenz von Events
+
+
+
+
+ Frage zum Einstieg
+ Klassische DB speichert den aktuellen Zustand: "Bestellung X hat 3 Items, Status 'paid'". Wer hat wann was geändert? Verloren. Event Sourcing dreht das um: speichere die Events, nicht den Zustand. Der aktuelle Zustand ist die Summe aller Events.
+
+
+
Das Grundprinzip
+
Statt: UPDATE orders SET status='paid' WHERE id=42 speicherst du: OrderPaid(orderId: 42, at: 2026-01-15) in einer Event-Store-Tabelle. Der aktuelle Zustand entsteht durch Replay aller Events:
Beachte die zwei Phasen: recordEvent erstellt das Event und ruft apply auf. apply ändert nur State, niemals Validierung – sonst kann beim Replay nichts schiefgehen.
+
+
Rekonstruieren aus Events
+
Beim Laden eines Aggregates: alle Events aus dem Store holen, neuen Aggregate-Instanz erstellen, jedes Event apply'en:
Komplette Historie – jede Änderung ist nachvollziehbar, "wer hat wann was?"
+
Audit-Trail – ideal für Banking, Healthcare, Compliance
+
Time-Travel-Debugging – State zu einem beliebigen Zeitpunkt rekonstruieren
+
Multiple Projections – verschiedene Read-Modelle aus denselben Events bauen
+
Natürlich mit CQRS kombinierbar
+
+
+
Nachteile
+
+
+
Komplexer als CRUD – nicht für alles geeignet
+
Event-Schema-Evolution – Events sind ewig, Schema-Änderungen schwierig
+
Performance – Replay vieler Events kostet → Snapshots als Optimierung
+
Eventual Consistency – Reads kommen aus Projektionen, nicht aus Events direkt
+
+
+
+
i
+
+ Libraries für Event Sourcing in PHP
+ EventSauce ist die populärste Library im PHP-Ökosystem – ausgereift, mit Snapshot-Support, Upcasting für Schema-Evolution und CQRS-Integration. Broadway und Prooph sind ältere Alternativen.
+
+
+
+
+ Recall
+
+
Was speichert ein Event Store statt aktuellem State?
+
Warum trennt man recordEvent und apply?
+
Welche Library nutzt man typischerweise in PHP?
+
+
+
+
+
+
+
Wie es weitergeht
+
+
Du hast PHP jetzt von Sprach-Internals über Performance-Optimierung bis zu fortgeschrittenen Architektur-Patterns durchlaufen. Damit kennst du PHP an seinen Grenzen.
+
+
Spaced-Repetition-Plan
+
+
+ Heute
+
Guide gelesen, Recall-Fragen aus jedem Kapitel beantwortet.
+
+
+ +7 Tage
+
Zwei Kapitel auswählen, vertiefen, eigene Implementierung versuchen.
+
+
+ +30 Tage
+
Spezialthema umsetzen: ReactPHP-Server, eigener DI-Container oder Hexagonal Architecture in echtem Projekt.
+
+
+ +90 Tage
+
Source-Code von Symfony, Doctrine oder EventSauce lesen – Patterns erkennen.
+
+
+
+
Was als nächstes lernen
+
Du bist jetzt jenseits des offiziellen PHP-Lernpfads. Empfehlungen für Tiefenexpertise:
+
+
+
PHP-Source-Code – Zend Engine in C, internal-Verzeichnis auf GitHub
PHP Fortgeschritten-Guide – Patterns und Production
+
PHP Extended-Guide (dieses Dokument) – Internals und Architektur
+
+
+
+
+
+```
\ No newline at end of file
diff --git a/templates/Referenz/IntermediateGuide.md b/templates/Referenz/IntermediateGuide.md
new file mode 100644
index 0000000..b50e449
--- /dev/null
+++ b/templates/Referenz/IntermediateGuide.md
@@ -0,0 +1,1874 @@
+```
+
+
+
+
+PHP Fortgeschritten-Guide
+
+
+
+
+
+
+
+
php
+
Fortgeschritten-Guide · 3h · Stand 2026
+
+
+
+
PHP tiefer.
+
Von Interfaces und Generators bis Tests – Production-PHP, das hält.
+
+
+
+
+ Was du danach kannst
+ OOP-Patterns idiomatisch einsetzen · funktional mit Closures und Generators arbeiten · Attribute für saubere Metadaten nutzen · Datenbanken mit PDO sicher anbinden · APIs konsumieren · mit PHPStan und PHPUnit produktionsreifen Code schreiben.
+
+
PHP 8.4
+
+
+
+
+
+
Inhalt
+
+
Teil 1 · OOP-Patterns
+
+
+
1
+
+
Interfaces und abstrakte Klassen
+
Kontrakte definieren, Implementierungen trennen
+
+
15 Min
+
+
+
+
2
+
+
Traits
+
Code-Reuse jenseits klassischer Vererbung
+
+
15 Min
+
+
+
+
3
+
+
Enums richtig nutzen
+
Backed Enums, Methods, Pattern-Matching
+
+
15 Min
+
+
+
+
4
+
+
Attribute (PHP 8)
+
Metadaten typensicher statt Annotations
+
+
15 Min
+
+
+
Teil 2 · Funktional & Generators
+
+
+
5
+
+
Closures und Arrow Functions
+
First-Class Callables, use-Capture
+
+
15 Min
+
+
+
+
6
+
+
Higher-Order Functions
+
array_map, array_filter, array_reduce in der Praxis
+
+
15 Min
+
+
+
+
7
+
+
Iterators und Generators
+
Lazy Evaluation mit yield
+
+
15 Min
+
+
+
+
8
+
+
Generics via PHPDoc
+
Templates ohne native Generics
+
+
15 Min
+
+
+
Teil 3 · Production-Tools
+
+
+
9
+
+
PDO und sichere Datenbanken
+
Prepared Statements, Transaktionen
+
+
15 Min
+
+
+
+
10
+
+
HTTP-Requests mit Guzzle
+
APIs konsumieren, async Requests
+
+
15 Min
+
+
+
+
11
+
+
PHPStan und Static Analysis
+
Bugs vor der Ausführung finden
+
+
15 Min
+
+
+
+
12
+
+
Testen mit PHPUnit
+
Unit-Tests, Mocks, Data Providers
+
+
15 Min
+
+
+
+
+
+
+
01
+
+
Interfaces und abstrakte Klassen
+
Kontrakte definieren, Implementierungen trennen
+
+
+
+
+ Frage zum Einstieg
+ Du hast drei verschiedene Logger – Datei, Datenbank, Sentry. Sie sollen austauschbar sein, ohne dass der aufrufende Code etwas merkt. Wie zwingst du sie zu einer gemeinsamen API, ohne sie zu Geschwistern in einer Klassen-Hierarchie zu machen? Interfaces sind die Antwort.
+
+
+
Interface als reiner Kontrakt
+
Ein Interface definiert nur, was Methoden tun, nicht wie. Klassen, die das Interface implementieren, müssen alle Methoden bereitstellen:
Der Gewinn: jede Funktion kann den Typ LoggerInterface erwarten und akzeptiert dadurch jede Implementierung – ohne deren Klasse zu kennen.
+
+
Dependency Injection in Aktion
+
+
classOrderService {
+ public function__construct(
+ private readonlyLoggerInterface$logger
+ ) {}
+
+ public functionplace(Order$order): void {
+ $this->logger->info("Order $order->id placed");
+ // ...
+ }
+}
+
+// In Tests: Fake-Logger reinreichen
+// In Production: FileLogger oder SentryLogger
+$service = newOrderService(newFileLogger('/var/log/app.log'));
+
+
Abstrakte Klassen
+
Wenn du gemeinsamen Code teilen willst, aber bestimmte Methoden offen lassen, nimmst du eine abstrakte Klasse. Sie kann Properties, fertige Methoden und abstrakte Methoden mischen:
+
+
abstract classNotification {
+ public function__construct(protected readonlystring$recipient) {}
+
+ // Fertige Methode
+ public functionsend(string$message): void {
+ $formatted = $this->format($message);
+ $this->deliver($formatted);
+ }
+
+ // Muss von Subklassen implementiert werden
+ abstract protected functionformat(string$msg): string;
+ abstract protected functiondeliver(string$msg): void;
+}
+
+
Interface oder abstrakte Klasse?
+
+
+
Use Case
Wähle
+
Reiner Kontrakt
Interface
+
Mehrere "Verträge" erfüllen
Interface (mehrfach implementierbar)
+
Gemeinsame Code-Basis teilen
Abstrakte Klasse
+
Template Method Pattern
Abstrakte Klasse
+
+
+
+
✓
+
+ PSR-Interfaces nutzen
+ Die PHP-FIG hat Standard-Interfaces für Logger, Container, HTTP und mehr definiert: Psr\Log\LoggerInterface, Psr\Container\ContainerInterface. Wenn du deine Klassen daran ausrichtest, passen sie zu jedem Framework.
+
+
+
+
+ Recall
+
+
Was ist der Hauptunterschied zwischen Interface und abstrakter Klasse?
+
Wie viele Interfaces darf eine Klasse implementieren?
+
Was ist Dependency Injection in einem Satz?
+
+
+
+
+
+
+
+
02
+
+
Traits
+
Code-Reuse jenseits klassischer Vererbung
+
+
+
+
+ Frage zum Einstieg
+ PHP unterstützt nur Einfach-Vererbung – eine Klasse hat genau eine Eltern-Klasse. Aber was, wenn zwei nicht verwandte Klassen denselben Code brauchen? Logging-Methoden in User und Order kopieren? Traits lösen das eleganter.
+
+
+
Trait: wiederverwendbarer Code-Block
+
Ein Trait ist wie eine Klasse, aber du erstellst keine Instanz davon. Stattdessen wirst du in andere Klassen "einkopiert". Mehrere Traits pro Klasse sind erlaubt:
traitSoftDeletable {
+ private ?\DateTimeImmutable $deletedAt = null;
+
+ public functiondelete(): void {
+ $this->deletedAt = new \DateTimeImmutable();
+ }
+
+ public functionisDeleted(): bool {
+ return$this->deletedAt !== null;
+ }
+}
+
+classArticle {
+ useTimestampable, SoftDeletable; // beide Sets von Methoden
+}
+
+
Konflikt-Auflösung
+
Wenn zwei Traits Methoden mit gleichem Namen haben, gibt es einen Konflikt. PHP zwingt dich, explizit zu wählen:
+
+
traitA { public functionhello(): string { return'A'; } }
+traitB { public functionhello(): string { return'B'; } }
+
+classX {
+ useA, B {
+ A::hello insteadof B; // nimm A's hello, ignoriere B's
+ B::hello as helloFromB; // B's hello als 'helloFromB' verfügbar
+ }
+}
+
+
+
!
+
+ Traits sind kein Multi-Inheritance-Ersatz
+ Traits machen Code-Reuse, keinen Typ-Polymorphismus. Eine Klasse mit Trait ist nicht "vom Typ Trait" – nutze dafür Interfaces. Faustregel: Trait für Implementierung, Interface für Vertrag.
+
+
+
+
Wann Trait, wann Komposition?
+
Traits sind verlockend, aber sie binden Code statisch in Klassen ein. Eine moderne Alternative ist Komposition: ein Service-Objekt als Property, das die Logik kapselt. Faustregel:
+
+
+
Trait, wenn die Logik Teil des Objekts ist (Timestamps, Soft-Delete)
+
Komposition, wenn die Logik austauschbar sein soll (verschiedene Logger, Strategien)
+
+
+
+ Recall
+
+
Was ist der Unterschied zwischen Trait und abstrakter Klasse?
+
Wie löst du Methoden-Konflikte zwischen zwei Traits?
+
Wann nutzt du Komposition statt Trait?
+
+
+
+
+
+
+
+
03
+
+
Enums richtig nutzen
+
Backed Enums, Methods, Pattern-Matching
+
+
+
+
+ Frage zum Einstieg
+ Früher waren Status-Werte oft Strings ("draft", "published") oder Klassen-Konstanten – fehleranfällig und ohne Typ-Sicherheit. PHP 8.1 hat echte Enums gebracht. Wie nutzt du sie idiomatisch?
+
Praktisch für Dropdowns, Validierung oder Migration zwischen Status.
+
+
+
✓
+
+ Statt String-Constants immer Enum
+ Wenn du heute const STATUS_DRAFT = 'draft' schreibst, ist es fast immer besser, ein Enum zu nehmen. Typ-Sicherheit, Methoden direkt am Wert, IDE-Vervollständigung, exhaustive match-Checks.
+
+
+
+
+ Recall
+
+
Was ist der Unterschied zwischen einfachem Enum und Backed Enum?
+
Wie konvertierst du einen String sicher zu einem Enum-Case?
+
Was gibt Status::cases() zurück?
+
+
+
+
+
+
+
+
04
+
+
Attribute (PHP 8)
+
Metadaten typensicher statt Annotations
+
+
+
+
+ Frage zum Einstieg
+ Bevor PHP 8 nutzten Frameworks wie Symfony und Doctrine Doc-Block-Annotations für Metadaten: /** @Route("/users") */. Kommentare, die geparst werden – brüchig, ohne Typ-Check. PHP 8 brachte native Attribute. Wie schreibst und nutzt du sie?
+
+
+
Attribut definieren
+
Ein Attribut ist eine Klasse, die mit #[Attribute] markiert ist. Sie kann Konstruktor-Parameter haben wie jede andere Klasse:
+
+
#[\Attribute(\Attribute::TARGET_METHOD)]
+classRoute {
+ public function__construct(
+ public readonlystring$path,
+ public readonlystring$method = 'GET',
+ ) {}
+}
+
+
Das TARGET_METHOD sagt: dieses Attribut darf nur auf Methoden. Andere Optionen: TARGET_CLASS, TARGET_PROPERTY, TARGET_PARAMETER.
Mit der Reflection-API liest du Attribute aus Klassen, Methoden oder Properties aus – das ist die Grundlage für Frameworks wie Symfony, Doctrine oder ORM-Libraries:
+
+
$reflection = new \ReflectionClass(UserController::class);
+
+foreach ($reflection->getMethods() as$method) {
+ foreach ($method->getAttributes(Route::class) as$attr) {
+ $route = $attr->newInstance(); // Route-Instanz
+ echo"$route->method $route->path → " . $method->name . "\n";
+ }
+}
+// Output:
+// GET /users → list
+// GET /users/{id} → show
+// POST /users → create
+
+
Typische Anwendungsfälle
+
+
+
Framework
Beispiel-Attribute
+
Symfony
#[Route], #[AsCommand]
+
Doctrine
#[ORM\Entity], #[ORM\Column]
+
PHPUnit
#[DataProvider], #[Test]
+
Validator
#[Assert\NotBlank]
+
+
+
+
i
+
+ Attribute statt Doc-Block-Annotations
+ Alter Code mit /** @Route("/users") */ funktioniert noch, aber das Doctrine/Symfony-Ökosystem migriert auf native Attribute. Bei Neuentwicklungen: immer Attribute, nie Annotations.
+
+
+
+
+ Recall
+
+
Was ist der Vorteil von Attributen gegenüber Doc-Block-Annotations?
+
Wie liest du Attribute zur Laufzeit aus?
+
Welche Frameworks nutzen Attribute intensiv?
+
+
+
+
+
+
+
+
05
+
+
Closures und Arrow Functions
+
First-Class Callables, use-Capture
+
+
+
+
+ Frage zum Einstieg
+ Funktionen als Werte – das ist seit Jahren in JavaScript Standard. Auch PHP kann das, mit Closures, Arrow Functions und seit PHP 8.1 First-Class Callable Syntax. Wann nutzt du was?
+
+
+
Closures: anonyme Funktionen
+
Eine Closure ist eine Funktion ohne Namen, die in einer Variable lebt. Du übergibst sie als Argument oder gibst sie zurück:
+ Arrow für einfach, Closure für komplex
+ Faustregel: Arrow Function (fn) wenn der Body ein einzelner Ausdruck ist. Volle Closure (function) wenn du mehrere Statements brauchst oder explizites Variablen-Capturing willst.
+
+
+
+
+ Recall
+
+
Was ist der Unterschied zwischen use ($x) und use (&$x)?
+
Was capturen Arrow Functions automatisch?
+
Wofür nutzt du First-Class Callable Syntax strtoupper(...)?
+
+
+
+
+
+
+
+
06
+
+
Higher-Order Functions
+
array_map, array_filter, array_reduce in der Praxis
+
+
+
+
+ Frage zum Einstieg
+ Du willst aus einer Liste von Bestellungen die Beträge aller bezahlten zusammenrechnen. Klassisch mit foreach, Zwischenvariable, Bedingung – fünf Zeilen. Mit Higher-Order Functions wird daraus ein lesbarer Einzeiler. Wie?
+
+
+
array_map: transformieren
+
array_map wendet eine Funktion auf jedes Element an und gibt ein neues Array zurück:
Letzteres ist oft lesbarer als verschachtelte Aufrufe – nicht immer ist die kompakteste Version die beste.
+
+
+
i
+
+ illuminate/collections für komplexere Fälle
+ Wenn du oft mit verketteten Operationen arbeitest, lohnt sich Laravels Collection-Library (auch standalone nutzbar): collect($users)->filter(...)->map(...)->sum(). Lesbarer als verschachtelte PHP-Standardfunktionen.
+
+
+
+
+ Recall
+
+
Was ist die Reihenfolge der Argumente bei array_map vs. array_filter?
+
Welches Detail bei array_filter überrascht oft?
+
Wann ist eine verschachtelte Verkettung weniger lesbar als Zwischenvariablen?
+
+
+
+
+
+
+
+
07
+
+
Iterators und Generators
+
Lazy Evaluation mit yield
+
+
+
+
+ Frage zum Einstieg
+ Du sollst die Zeilen einer 5-GB-Logdatei verarbeiten. Komplett ins Array laden? Speicher reicht nicht. Klassische File-Funktionen mit Schleife? Funktioniert, aber Code wird unhandlich, wenn er weiterverarbeitet werden soll. Generators lösen das elegant.
+
+
+
yield: Werte häppchenweise produzieren
+
Ein Generator ist eine Funktion, die Werte mit yield ausgibt statt mit return. Sie pausiert zwischen den Werten – Speicher bleibt frei:
+
+
functionreadLines(string$path): \Generator {
+ $handle = fopen($path, 'r');
+ while (($line = fgets($handle)) !== false) {
+ yieldrtrim($line);
+ }
+ fclose($handle);
+}
+
+// Aufruf liefert sofort einen Generator – Datei wird noch nicht gelesen
+$lines = readLines('/var/log/huge.log');
+
+// Erst die Iteration liest tatsächlich
+foreach ($linesas$line) {
+ if (str_contains($line, 'ERROR')) {
+ echo$line;
+ }
+}
+
+
Der Speicher-Vorteil: zu jedem Zeitpunkt ist nur eine Zeile geladen, egal wie groß die Datei ist.
+ Generator nur einmal durchlaufen
+ Generatoren sind nicht rewindable. Wenn du sie zweimal mit foreach durchläufst, ist der zweite Durchlauf leer. Brauchst du Mehrfach-Zugriff, konvertiere mit iterator_to_array($gen) zu einem Array.
+
+
+
+
+ Recall
+
+
Welcher Vorteil von Generators bei großen Datenmengen?
+
Was passiert, wenn du einen Generator zweimal mit foreach durchläufst?
+
Wofür ist yield from da?
+
+
+
+
+
+
+
+
08
+
+
Generics via PHPDoc
+
Templates ohne native Generics
+
+
+
+
+ Frage zum Einstieg
+ PHP hat keine nativen Generics wie TypeScript oder Java. Wenn du eine Collection-Klasse schreibst, weiß die IDE nicht, ob da User oder Order drin sind – jeder Zugriff wird zu mixed. Die Lösung: PHPDoc-Generics mit PHPStan oder Psalm.
+
+
+
Das Problem ohne Generics
+
+
classCollection {
+ private array$items = [];
+
+ public functionadd(mixed$item): void {
+ $this->items[] = $item;
+ }
+
+ public functionfirst(): mixed {
+ return$this->items[0] ?? null;
+ }
+}
+
+$users = newCollection();
+$users->add(newUser('Marek'));
+
+$user = $users->first(); // IDE: mixed, keine Autocompletion
+$user->name; // keine Hilfe von IDE oder PHPStan
+
+
Generics in PHPDoc
+
Mit PHPDoc-Annotationen kannst du Templates definieren. PHPStan und Psalm verstehen sie und prüfen Typen statisch:
+ Native Generics kommen vielleicht
+ Es gibt seit Jahren Diskussionen um native Generics in PHP. Ein RFC ist mehrfach gescheitert wegen Implementierungs-Komplexität (Runtime-Type-Erasure vs. Reified). Bis dahin sind PHPDoc-Generics mit PHPStan/Psalm der Standard.
+
+
+
+
+ Recall
+
+
Welche Tools verstehen PHPDoc-Generics?
+
Wie deklarierst du eine generische Klasse?
+
Wofür ist array{name: string, age: int}?
+
+
+
+
+
+
+
+
09
+
+
PDO und sichere Datenbanken
+
Prepared Statements, Transaktionen
+
+
+
+
+ Frage zum Einstieg
+ SQL-Injection ist seit 20 Jahren die häufigste Web-Sicherheitslücke. Sie ist trivial zu verhindern – mit Prepared Statements. PDO ist PHPs eingebaute, datenbank-agnostische Schnittstelle dafür. Wie nutzt du sie richtig?
+
+
+
PDO-Verbindung aufbauen
+
PDO unterstützt MySQL, PostgreSQL, SQLite und mehr über dieselbe API. Der DSN-String unterscheidet sich, der Rest ist gleich:
Die drei Options sind Pflicht in jedem Projekt: Exceptions bei Fehlern, assoziative Arrays beim Fetch, echte Prepared Statements (statt emulierter Client-Side).
+
+
Prepared Statements (immer!)
+
Niemals User-Input direkt in SQL-Strings konkatenieren. Stattdessen: Placeholder mit ? oder Named Parameters:
+
+
// Falsch: SQL-Injection-Lücke
+$id = $_GET['id'];
+$result = $pdo->query("SELECT * FROM users WHERE id = $id");
+// $_GET['id'] = "1; DROP TABLE users" → katastrophal
+
+// Richtig: Prepared Statement mit Positional Parameters
+$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
+$stmt->execute([$id]);
+$user = $stmt->fetch();
+
+// Oder mit Named Parameters
+$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id AND active = :active');
+$stmt->execute([':id' => $id, ':active' => true]);
+
+
Ergebnisse abrufen
+
+
// Einzelne Zeile
+$user = $stmt->fetch(); // assoc array oder false
+
+// Alle Zeilen
+$users = $stmt->fetchAll(); // Array von Zeilen
+
+// Iterativ für große Result-Sets
+while ($row = $stmt->fetch()) {
+ processRow($row);
+}
+
+// Direkt in Objekte mappen (PDO::FETCH_CLASS)
+$stmt->setFetchMode(PDO::FETCH_CLASS, User::class);
+$users = $stmt->fetchAll();
+
+
Transaktionen
+
Wenn mehrere Operationen atomar sein müssen (alle oder keine), nutzt du Transaktionen:
+
+
$pdo->beginTransaction();
+
+try {
+ $pdo->prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?')
+ ->execute([100, $fromId]);
+
+ $pdo->prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?')
+ ->execute([100, $toId]);
+
+ $pdo->commit();
+} catch (\PDOException$e) {
+ $pdo->rollBack();
+ throw$e;
+}
+
+
+
!
+
+ Niemals mysql_* oder mysqli ohne Prepared Statements
+ Die alten mysql_*-Funktionen sind seit PHP 7 entfernt. mysqli existiert noch, aber nur mit Prepared Statements nutzen. Standard heute: PDO für direkten DB-Zugriff, Doctrine DBAL für Query-Builder, Doctrine ORM für komplexe Domänen.
+
+
+
+
+ Recall
+
+
Welche drei Options gehören in jeden PDO-Konstruktor?
+
Warum sind Prepared Statements sicherer als String-Konkatenation?
+
Wann nutzt du eine Transaktion?
+
+
+
+
+
+
+
+
10
+
+
HTTP-Requests mit Guzzle
+
APIs konsumieren, async Requests
+
+
+
+
+ Frage zum Einstieg
+ Du musst eine externe API ansprechen – REST mit JSON, Authentifizierung, vielleicht Retry bei 503. Mit nativen Funktionen wie file_get_contents oder cURL geht das, aber wird schnell hässlich. Guzzle ist Standard-Library für HTTP in PHP.
+
Drei API-Calls in der Zeit eines einzelnen – wenn die API es zulässt, ist das ein riesiger Performance-Gewinn.
+
+
+
✓
+
+ PSR-18 als alternative
+ Wenn du framework-agnostisch bleiben willst, programmiere gegen Psr\Http\Client\ClientInterface. Guzzle implementiert das, aber auch andere Clients (z.B. Symfony HttpClient). Du tauschst sie dann ohne Code-Änderung aus.
+
+
+
+
+ Recall
+
+
Wofür ist base_uri in der Client-Konfiguration?
+
Welche Exception fängst du für HTTP 4xx?
+
Wann lohnen sich async Requests?
+
+
+
+
+
+
+
+
11
+
+
PHPStan und Static Analysis
+
Bugs vor der Ausführung finden
+
+
+
+
+ Frage zum Einstieg
+ Ein Tippfehler im Property-Namen, ein null-Wert, der zu einer Method-Aufruf-Exception führt, eine Funktion mit falscher Argument-Anzahl – all das passiert in PHP zur Laufzeit, oft erst in Production. PHPStan findet diese Bugs ohne den Code auszuführen.
+
Je präziser deine PHPDoc-Annotationen, desto mehr findet PHPStan. Besonders mächtig bei Arrays:
+
+
/**
+ * @param array<User> $users
+ * @return array<string>
+ */
+functiongetNames(array$users): array {
+ returnarray_map(fn($u) => $u->name, $users);
+}
+
+// Ohne PHPDoc würde PHPStan nicht wissen, was $users enthält
+// Mit PHPDoc kann es fehlerhafte Aufrufe finden:
+getNames([newUser('M'), 'string']); // PHPStan: erwartet User, nicht string
+
+
Baseline für Legacy-Code
+
Wenn du PHPStan zu einem bestehenden Projekt hinzufügst, sind oft hunderte Fehler in altem Code. Eine Baseline ignoriert bestehende Fehler – neue müssen gefixt werden:
+
+
vendor/bin/phpstan analyse --generate-baseline
+
+
Das erzeugt phpstan-baseline.neon. Bestehende Fehler werden eingefroren, neuer Code wird streng geprüft. Über die Zeit räumst du die Baseline ab.
+
+
+
i
+
+ Psalm als Alternative
+ Psalm ist ein vergleichbares Tool von Vimeo. Die Features überschneiden sich zu 95% – PHPStan ist verbreiteter, Psalm geht bei bestimmten Edge Cases tiefer. In Symfony und Laravel-Welt: meist PHPStan.
+
+
+
+
+ Recall
+
+
Was tut PHPStan, was PHP-Runtime nicht tut?
+
Welche Level-Stufe sollte langfristig das Ziel sein?
+
Wofür ist eine PHPStan-Baseline?
+
+
+
+
+
+
+
+
12
+
+
Testen mit PHPUnit
+
Unit-Tests, Mocks, Data Providers
+
+
+
+
+ Frage zum Einstieg
+ Tests fühlen sich als zusätzliche Arbeit an – bis du das erste Mal eine Änderung machst und in Sekunden weißt, dass nichts kaputt ist. PHPUnit ist Standard für PHP-Tests. Wie schreibst du erste Tests und welche Patterns lohnen sich?
+
Bug Fixes – jeder gefixte Bug bekommt einen Test, damit er nicht wiederkehrt
+
+
+
Was du nicht testen musst: Getter/Setter, fremde Library-Funktionen, Framework-Code.
+
+
+
✓
+
+ Test-Pyramid: viele Unit, wenige E2E
+ Schreibe viele Unit-Tests (schnell, isoliert), weniger Integration-Tests (echte DB, API), und wenige End-to-End-Tests (gesamte App). Eine Pyramide, keine Eisbecher-Form. Schnelles Feedback ist wichtiger als 100% Test-Coverage.
+
+
+
+
+ Recall
+
+
Was prüft assertSame im Gegensatz zu assertEquals?
+
Wofür sind Data Providers?
+
Warum nutzt du Mocks statt echter Abhängigkeiten in Tests?
+
+
+
+
+
+
+
Wie es weitergeht
+
+
Du hast PHP jetzt von OOP-Patterns über funktionale Werkzeuge bis zu Production-Tools durchlaufen. Damit baust du wartbare, getestete Applikationen. Aber Praxis schlägt Theorie – setze diese Patterns in echtem Code ein.
+
+
Spaced-Repetition-Plan
+
+
+ Heute
+
Guide gelesen, Recall-Fragen aus jedem Kapitel beantwortet.
+
+
+ +3 Tage
+
Drei beliebige Kapitel überfliegen, Recall-Fragen aus dem Kopf.
+
+
+ +14 Tage
+
Mini-Projekt: REST-API mit PDO, Tests und PHPStan Level 8.
+
+
+ +60 Tage
+
Bestehendes Projekt um Tests und Static Analysis erweitern.
+
+
+
+
Was als nächstes lernen
+
Mit diesen Werkzeugen kannst du in jede Spezialisierung tiefer einsteigen:
+
+
+
Frameworks – Symfony oder Laravel mit voller Tiefe (DI, Events, Forms, Security)
+
Doctrine ORM – Domain-Modelle mit komplexen Beziehungen abbilden
Performance – Profiling mit Blackfire, OpCache, Caching-Strategien
+
Security – Authentication, OWASP Top 10, Code-Review
+
Microservices – kleine PHP-Services, API-Gateways, Service-Discovery
+
+
+
Begleitmaterial
+
Dieser Guide ist Teil eines Sets:
+
+
PHP OnePager – die visuelle Übersicht
+
PHP Cheatsheet – die dichte Referenz
+
PHP Mini-Guide – der 15-Min-Schnelleinstieg
+
PHP Anfänger-Guide – die Grundlagen
+
PHP Fortgeschritten-Guide (dieses Dokument) – Patterns und Production
+
+
+
+
+
+```
\ No newline at end of file
diff --git a/templates/Referenz/MiniGuide.md b/templates/Referenz/MiniGuide.md
new file mode 100644
index 0000000..cdef1f8
--- /dev/null
+++ b/templates/Referenz/MiniGuide.md
@@ -0,0 +1,383 @@
+```
+
+
+
+
+PHP Mini-Guide
+
+
+
+
+
+
+
php
+
+
PHP in 15 Minuten
+
Dein erstes PHP-Programm – Schritt für Schritt
+
+
+ Mini-Guide
+ 15 Min · von Null
+
+
+
+
+
+ Frage zum Einstieg
+ PHP läuft hinter rund drei Viertel aller Webseiten – inklusive WordPress, Wikipedia und Facebook. Aber wie sieht PHP-Code überhaupt aus, und wie startet man? In 15 Minuten kannst du dein erstes Programm schreiben.
+
+
+
+
PHP starten
+
+
PHP ist eine Programmiersprache, die auf deinem Computer oder einem Webserver läuft. Im Gegensatz zu HTML oder CSS wird PHP nicht im Browser angezeigt – es erzeugt Ausgaben (zum Beispiel HTML), die dann ausgeliefert werden.
+
+
Um anzufangen, brauchst du PHP auf deinem Computer. Auf Mac: brew install php. Auf Ubuntu: apt install php8.4-cli. Auf Windows: am einfachsten WSL2 mit Ubuntu darin nutzen.
+
+
PHP-Code lebt in Dateien mit der Endung .php. Lege eine Datei hallo.php an mit diesem Inhalt:
+
+
<?php
+
+echo"Hallo Welt!";
+
+
Die erste Zeile <?php sagt PHP: "ab hier kommt mein Code". Das Wort echo heißt: "gib das aus, was danach kommt". Strings (also Text) stehen in Anführungszeichen. Jede Anweisung endet mit einem Semikolon.
+
+
Im Terminal ausführen mit:
+
+
php hallo.php
+
+
Du siehst "Hallo Welt!" – dein erstes PHP-Programm läuft.
+
+
+
Variablen
+
+
Eine Variable ist ein benannter Platz, an dem du einen Wert speicherst. In PHP beginnen Variablen immer mit einem Dollar-Zeichen $. Das macht sie im Code sofort erkennbar:
Die Klammer hinter if enthält die Bedingung. Die geschweiften Klammern { } umschließen den Code, der ausgeführt wird, wenn die Bedingung wahr ist. else ist der Block, wenn sie falsch ist.
+
+
Wichtige Vergleichs-Operatoren:
+
+
+
== gleich
+
!= ungleich
+
<, > kleiner, größer
+
<=, >= kleiner-gleich, größer-gleich
+
+
+
+
i
+
+ Ein Gleichheitszeichen reicht nicht
+ Zum Zuweisen nutzt du = (ein Gleichheitszeichen). Zum Vergleichen brauchst du == (zwei). Das ist eine häufige Verwechslung bei Anfängern.
+
+
+
+
+
Listen und Schleifen
+
+
Mehrere Werte fasst du in einer Liste zusammen. In PHP heißen Listen array:
+
+
$obst = ["Apfel", "Birne", "Kirsche"];
+
+
Über jede Liste kannst du mit foreach Schritt für Schritt gehen:
Das gibt "Apfel", "Birne", "Kirsche" untereinander aus. Das \n ist ein Zeilenumbruch. Die Variable $frucht bekommt bei jedem Durchlauf den nächsten Wert aus der Liste.
+
+
Listen müssen nicht aus Strings bestehen. Zahlen gehen genauso:
Die Funktion macht ihre Berechnung und liefert das Ergebnis zurück. Du fängst es in einer Variable auf und kannst damit weiterarbeiten.
+
+
+
✓
+
+ Übung macht den Meister
+ Schreibe jetzt selbst ein kleines PHP-Programm. Zum Beispiel: eine Liste deiner Lieblings-Filme, die mit foreach ausgegeben werden. Oder eine Funktion, die das Doppelte einer Zahl zurückgibt. Praktisch ausprobieren ist der schnellste Weg, PHP zu lernen.
+
+
+
+
+
+```
\ No newline at end of file
diff --git a/templates/Referenz/OnePager.md b/templates/Referenz/OnePager.md
new file mode 100644
index 0000000..bea56eb
--- /dev/null
+++ b/templates/Referenz/OnePager.md
@@ -0,0 +1,526 @@
+```
+
+
+
+
+PHP OnePager
+
+
+
+
+
+
+
+
php
+
+
PHP – Server-Sprache des Web
+
Dynamische Webseiten · seit 1995 · 75% aller Websites · objektorientiert & funktional
+
+
+
+
+
8.4
+
Aktuelle Version
+
+
+
1995
+
Erstes Release
+
+
+
75%
+
Aller Websites
+
+
+
300k+
+
Composer-Pakete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Kernkonzepte
+
+
+
+
1
+
Server-seitig – läuft auf Webservern, generiert HTML pro Request
+
+
+
2
+
Dynamisch typisiert – Typen zur Laufzeit, optional mit Type-Hints prüfbar