@artilingo/artiframe-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/assets/logo.png +0 -0
  2. package/assets/logo.svg +22 -0
  3. package/assets/logo.webp +0 -0
  4. package/assets/logo@0.5x.png +0 -0
  5. package/assets/logo@0.75x.png +0 -0
  6. package/assets/logo@1.5x.png +0 -0
  7. package/assets/logo@2x.png +0 -0
  8. package/assets/logo@3x.png +0 -0
  9. package/assets/logo@4x.png +0 -0
  10. package/bin/artiframe.js +57 -0
  11. package/bin/artiframe.php +45 -0
  12. package/core-stubs/app/ApiControl.php +81 -0
  13. package/core-stubs/app/Database.php +69 -0
  14. package/core-stubs/app/DotEnv.php +63 -0
  15. package/core-stubs/app/R2Manager.php +130 -0
  16. package/core-stubs/app/ViewControl.php +24 -0
  17. package/core-stubs/bin/SystemMethod.php +271 -0
  18. package/core-stubs/bin/ViewMethod.php +354 -0
  19. package/core-stubs/docs/de.html +951 -0
  20. package/core-stubs/docs/en.html +952 -0
  21. package/core-stubs/docs/es.html +947 -0
  22. package/core-stubs/docs/fr.html +850 -0
  23. package/core-stubs/docs/tr.html +951 -0
  24. package/core-stubs/public/assets/css/components/footer.css +0 -0
  25. package/core-stubs/public/assets/css/components/header.css +0 -0
  26. package/core-stubs/public/assets/css/components/mobilenav.css +0 -0
  27. package/core-stubs/public/assets/css/components/sidebar.css +0 -0
  28. package/core-stubs/public/assets/css/components/theme-modal.css +0 -0
  29. package/core-stubs/public/assets/css/root/app.css +61 -0
  30. package/core-stubs/public/assets/js/components/header.js +0 -0
  31. package/core-stubs/public/assets/js/components/mobilenav.js +0 -0
  32. package/core-stubs/public/assets/js/components/sidebar.js +0 -0
  33. package/core-stubs/public/assets/js/components/theme-modal.js +0 -0
  34. package/core-stubs/public/assets/js/root/app.js +30 -0
  35. package/core-stubs/public/includes/footer.php +5 -0
  36. package/core-stubs/public/includes/head.php +26 -0
  37. package/core-stubs/public/includes/header.php +5 -0
  38. package/core-stubs/public/includes/mobilenav.php +5 -0
  39. package/core-stubs/public/includes/sidebar.php +5 -0
  40. package/core-stubs/public/includes/theme-modal.php +5 -0
  41. package/core-stubs/readme/de.md +29 -0
  42. package/core-stubs/readme/en.md +29 -0
  43. package/core-stubs/readme/es.md +29 -0
  44. package/core-stubs/readme/fr.md +29 -0
  45. package/core-stubs/readme/tr.md +29 -0
  46. package/package.json +49 -0
  47. package/scripts/postinstall.js +21 -0
  48. package/src/App.php +266 -0
  49. package/src/Commands/MakeApiCommand.php +71 -0
  50. package/src/Commands/MakeClassCommand.php +88 -0
  51. package/src/Commands/MakeViewCommand.php +95 -0
  52. package/src/Commands/NewProjectCommand.php +633 -0
  53. package/src/Commands/VersionCommand.php +92 -0
  54. package/src/Lang/de.php +53 -0
  55. package/src/Lang/en.php +53 -0
  56. package/src/Lang/es.php +53 -0
  57. package/src/Lang/fr.php +53 -0
  58. package/src/Lang/tr.php +53 -0
  59. package/src/Services/Safeguard.php +48 -0
  60. package/src/Services/Translator.php +52 -0
@@ -0,0 +1,951 @@
1
+ <!DOCTYPE html>
2
+ <html lang="de">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ArtiFrame | Offizielle Entwicklerdokumentation</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --bg: #0d1117;
11
+ --surface: #161b22;
12
+ --surface-2: #1c2128;
13
+ --border: #30363d;
14
+ --text: #e6edf3;
15
+ --muted: #7d8590;
16
+ --green: #009d6c;
17
+ --green-light: #00c88c;
18
+ --green-dim: rgba(0,157,108,0.12);
19
+ --blue: #58a6ff;
20
+ --yellow: #e3b341;
21
+ --red: #f85149;
22
+ --purple: #bc8cff;
23
+ --code-bg: #0d1117;
24
+ --sidebar-w: 280px;
25
+ }
26
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
27
+
28
+ html { scroll-behavior: smooth; }
29
+
30
+ body {
31
+ font-family: 'Inter', sans-serif;
32
+ background: var(--bg);
33
+ color: var(--text);
34
+ line-height: 1.75;
35
+ display: flex;
36
+ }
37
+
38
+ /* ---- SIDEBAR ---- */
39
+ .sidebar {
40
+ width: var(--sidebar-w);
41
+ background: var(--surface);
42
+ height: 100vh;
43
+ position: fixed;
44
+ top: 0; left: 0;
45
+ border-right: 1px solid var(--border);
46
+ overflow-y: auto;
47
+ padding: 28px 0 40px;
48
+ display: flex;
49
+ flex-direction: column;
50
+ }
51
+
52
+ .sb-logo {
53
+ padding: 0 22px 24px;
54
+ border-bottom: 1px solid var(--border);
55
+ margin-bottom: 16px;
56
+ }
57
+ .sb-logo strong {
58
+ font-size: 1.35rem;
59
+ font-weight: 700;
60
+ color: var(--green);
61
+ letter-spacing: -0.3px;
62
+ }
63
+ .sb-logo span {
64
+ display: block;
65
+ font-size: 0.75rem;
66
+ color: var(--muted);
67
+ margin-top: 3px;
68
+ }
69
+
70
+ .sb-cat {
71
+ padding: 16px 22px 6px;
72
+ font-size: 0.68rem;
73
+ text-transform: uppercase;
74
+ letter-spacing: 1.2px;
75
+ color: var(--green);
76
+ font-weight: 700;
77
+ }
78
+ .sidebar nav a {
79
+ display: block;
80
+ padding: 7px 22px;
81
+ font-size: 0.88rem;
82
+ color: var(--muted);
83
+ text-decoration: none;
84
+ border-left: 2px solid transparent;
85
+ transition: all .15s;
86
+ }
87
+ .sidebar nav a:hover,
88
+ .sidebar nav a.active {
89
+ color: var(--text);
90
+ background: var(--green-dim);
91
+ border-left-color: var(--green);
92
+ }
93
+
94
+ /* ---- MAIN ---- */
95
+ .content {
96
+ margin-left: var(--sidebar-w);
97
+ padding: 60px 70px 100px;
98
+ max-width: 1100px;
99
+ width: 100%;
100
+ }
101
+
102
+ /* ---- TYPOGRAPHY ---- */
103
+ h1 { font-size: 2.6rem; font-weight: 700; letter-spacing: -0.5px; margin-bottom: 16px; }
104
+ h1 span { color: var(--green); }
105
+ h2 {
106
+ font-size: 1.7rem; font-weight: 700;
107
+ margin: 72px 0 20px;
108
+ padding-bottom: 14px;
109
+ border-bottom: 1px solid var(--border);
110
+ }
111
+ h2 .tag { font-size: 0.65rem; padding: 3px 8px; border-radius: 4px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; vertical-align: middle; margin-left: 10px; }
112
+ h3 { font-size: 1.1rem; font-weight: 600; margin: 32px 0 12px; color: var(--green-light); display: flex; align-items: center; gap: 10px; }
113
+ p { color: var(--muted); margin-bottom: 16px; font-size: 0.97rem; }
114
+ strong { color: var(--text); }
115
+ ul, ol { margin: 0 0 16px 22px; color: var(--muted); font-size: 0.97rem; }
116
+ li { margin-bottom: 8px; }
117
+ a { color: var(--green-light); text-decoration: none; }
118
+ a:hover { text-decoration: underline; }
119
+
120
+ /* ---- LEAD ---- */
121
+ .lead { font-size: 1.05rem; color: var(--muted); margin-bottom: 0; max-width: 720px; }
122
+
123
+ /* ---- BADGES ---- */
124
+ .badge { font-size: 0.68rem; padding: 3px 8px; border-radius: 4px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; white-space: nowrap; }
125
+ .b-view { background: rgba(88,166,255,.12); color: #58a6ff; border: 1px solid rgba(88,166,255,.3); }
126
+ .b-api { background: rgba(188,140,255,.12); color: #bc8cff; border: 1px solid rgba(188,140,255,.3); }
127
+ .b-cli { background: rgba(0,200,140,.12); color: #00c88c; border: 1px solid rgba(0,200,140,.3); }
128
+ .b-core { background: rgba(227,179,65,.12); color: #e3b341; border: 1px solid rgba(227,179,65,.3); }
129
+ .b-sec { background: rgba(248,81,73,.12); color: #f85149; border: 1px solid rgba(248,81,73,.3); }
130
+
131
+ /* ---- CODE ---- */
132
+ pre {
133
+ background: var(--code-bg);
134
+ border: 1px solid var(--border);
135
+ border-radius: 8px;
136
+ padding: 22px 24px;
137
+ overflow-x: auto;
138
+ margin: 18px 0 24px;
139
+ font-size: 0.875rem;
140
+ line-height: 1.6;
141
+ }
142
+ code { font-family: 'JetBrains Mono', 'Consolas', monospace; color: #e6edf3; }
143
+ .ic { /* inline code */
144
+ background: var(--surface-2);
145
+ padding: 2px 7px;
146
+ border-radius: 4px;
147
+ font-family: 'JetBrains Mono', monospace;
148
+ font-size: 0.85em;
149
+ color: var(--yellow);
150
+ border: 1px solid var(--border);
151
+ }
152
+ .kw { color: #ff7b72; } /* keyword / php tag */
153
+ .fn { color: #d2a8ff; } /* function name */
154
+ .st { color: #a5d6ff; } /* string */
155
+ .cm { color: #8b949e; } /* comment */
156
+ .nu { color: #79c0ff; } /* number / bool */
157
+ .var { color: #ffa657; } /* variable */
158
+
159
+ /* ---- CARDS ---- */
160
+ .card {
161
+ background: var(--surface);
162
+ border: 1px solid var(--border);
163
+ border-radius: 10px;
164
+ padding: 28px 32px;
165
+ margin-bottom: 20px;
166
+ }
167
+ .card.highlight { border-left: 3px solid var(--green); }
168
+
169
+ /* ---- ALERTS ---- */
170
+ .alert { padding: 18px 22px; border-radius: 8px; margin: 24px 0; border-left: 3px solid; font-size: 0.95rem; }
171
+ .a-info { background: rgba(88,166,255,.08); border-color: var(--blue); color: #93c5fd; }
172
+ .a-warn { background: rgba(227,179,65,.08); border-color: var(--yellow); color: #fbbf24; }
173
+ .a-danger { background: rgba(248,81,73,.08); border-color: var(--red); color: #fca5a5; }
174
+ .a-success { background: rgba(0,157,108,.08); border-color: var(--green); color: #6ee7b7; }
175
+
176
+ /* ---- RETURN TYPE ---- */
177
+ .ret { display: inline-block; font-size: 0.78rem; color: var(--green); font-family: 'JetBrains Mono', monospace; background: rgba(0,157,108,.12); padding: 3px 10px; border-radius: 4px; border: 1px solid rgba(0,157,108,.25); margin-bottom: 12px; }
178
+
179
+ /* ---- GRID ---- */
180
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 24px; }
181
+ @media(max-width: 900px) { .grid-2 { grid-template-columns: 1fr; } }
182
+
183
+ /* ---- DIR TREE ---- */
184
+ .tree { background: var(--code-bg); border: 1px solid var(--border); border-radius: 8px; padding: 22px 24px; font-family: 'JetBrains Mono', monospace; font-size: 0.875rem; line-height: 1.9; white-space: pre; overflow-x: auto; }
185
+ .tree .dir { color: var(--blue); font-weight: 600; }
186
+ .tree .file { color: #e6edf3; }
187
+ .tree .note { color: #8b949e; }
188
+ .tree .warn { color: var(--yellow); }
189
+ .tree .safe { color: var(--green); }
190
+
191
+ /* ---- STEP LIST ---- */
192
+ .steps { counter-reset: step; list-style: none; margin: 0 0 24px; padding: 0; }
193
+ .steps li { counter-increment: step; display: flex; gap: 18px; margin-bottom: 20px; }
194
+ .steps li::before {
195
+ content: counter(step);
196
+ flex-shrink: 0;
197
+ width: 30px; height: 30px;
198
+ background: var(--green-dim);
199
+ border: 1px solid rgba(0,157,108,.3);
200
+ color: var(--green);
201
+ border-radius: 50%;
202
+ display: flex; align-items: center; justify-content: center;
203
+ font-weight: 700; font-size: 0.85rem;
204
+ margin-top: 2px;
205
+ }
206
+
207
+ /* ---- SIGNATURE TABLE ---- */
208
+ .sig-table { width: 100%; border-collapse: collapse; margin: 16px 0 24px; font-size: 0.87rem; }
209
+ .sig-table th { text-align: left; padding: 10px 14px; background: var(--surface-2); color: var(--muted); font-weight: 600; border-bottom: 1px solid var(--border); }
210
+ .sig-table td { padding: 10px 14px; border-bottom: 1px solid rgba(48,54,61,.5); vertical-align: top; }
211
+ .sig-table td:first-child { font-family: 'JetBrains Mono', monospace; color: var(--yellow); white-space: nowrap; }
212
+ .sig-table td:last-child { color: var(--muted); }
213
+
214
+ /* ---- SCROLL SPY ---- */
215
+ header#page-header { margin-bottom: 52px; }
216
+ </style>
217
+ </head>
218
+ <body>
219
+
220
+ <!-- SIDEBAR -->
221
+ <aside class="sidebar">
222
+ <div class="sb-logo">
223
+ <strong>ArtiFrame</strong>
224
+ <span>Entwicklerdokumentation</span>
225
+ </div>
226
+ <nav>
227
+ <div class="sb-cat">Erste Schritte</div>
228
+ <a href="#giris">Einführung & Philosophie</a>
229
+ <a href="#kurulum">Installation</a>
230
+ <a href="#dizin">Verzeichnisstruktur</a>
231
+
232
+ <div class="sb-cat">Architektur</div>
233
+ <a href="#bootstrapper">Bootstrapper-Architektur</a>
234
+ <a href="#kurallar">Regelsatz</a>
235
+
236
+ <div class="sb-cat">CLI-Tool</div>
237
+ <a href="#cli-giris">Einführung in CLI</a>
238
+ <a href="#cli-new">new</a>
239
+ <a href="#cli-view">make:view</a>
240
+ <a href="#cli-api">make:api</a>
241
+ <a href="#cli-class">make:class</a>
242
+ <a href="#cli-version">version</a>
243
+
244
+ <div class="sb-cat">View Helpers</div>
245
+ <a href="#vh-display">display()</a>
246
+ <a href="#vh-csrf">csrfField()</a>
247
+ <a href="#vh-dates">Datumsfunktionen</a>
248
+ <a href="#vh-format">Formatierung</a>
249
+ <a href="#vh-money">money()</a>
250
+
251
+ <div class="sb-cat">System Helpers</div>
252
+ <a href="#sh-json">jsonResponse()</a>
253
+ <a href="#sh-csrf">verifyCsrf()</a>
254
+ <a href="#sh-sanitize">Sanitize</a>
255
+ <a href="#sh-request">HTTP & Anfragen</a>
256
+ <a href="#sh-security">Sicherheit</a>
257
+
258
+ <div class="sb-cat">API-Sicherheit</div>
259
+ <a href="#api-methods">HTTP-Methoden-Kontrolle</a>
260
+ <a href="#api-cors">CORS</a>
261
+ <a href="#api-rate">Rate Limiting</a>
262
+
263
+ <div class="sb-cat">Praxis</div>
264
+ <a href="#workflow">Vollständiger Arbeitsablauf</a>
265
+ </nav>
266
+ </aside>
267
+
268
+ <!-- MAIN CONTENT -->
269
+ <main class="content">
270
+
271
+ <!-- ====== GİRİŞ ====== -->
272
+ <header id="page-header">
273
+ <h1>ArtiFrame <span>Dokumentation</span></h1>
274
+ <p class="lead">Ein Framework für skalierbare native PHP-Projekte, verwaltet ohne externe Abhängigkeiten, mit strikten Regeln und einer leistungsstarken CLI. Keine Abhängigkeiten, befreit vom Composer-Paket-Chaos – ein leichtes und schnelles Ökosystem, das es Ihnen ermöglicht, sich auf Ihre Geschäftslogik zu konzentrieren.</p>
275
+ </header>
276
+
277
+ <!-- ====== GİRİŞ & FELSEFE ====== -->
278
+ <section id="giris">
279
+ <h2>Einführung & Philosophie</h2>
280
+ <p>ArtiFrame basiert auf dem Prinzip <strong>"Convention over Configuration"</strong> (Konvention vor Konfiguration). Die Stärke eines Frameworks ergibt sich nicht aus dem Reichtum der angebotenen Tools, sondern aus der Konsistenz der aufgebauten Ordnung.</p>
281
+
282
+ <div class="grid-2">
283
+ <div class="card highlight">
284
+ <h3>Zero Overhead</h3>
285
+ <p>Null Abhängigkeit von Composer-Paketen, Framework-Kernen oder Drittanbieter-Bibliotheken. Jede Zeile Code gehört Ihnen – ohne Bloat, frei von unnötigen Abstraktionsschichten.</p>
286
+ </div>
287
+ <div class="card highlight">
288
+ <h3>Sicherheit an erster Stelle</h3>
289
+ <p>XSS-Schutz, CSRF-Validierung, Bereinigung gegen SQL-Injection und HTTP-Methoden-Kontrolle sind standardmäßig integriert. Sicherheit ist keine Option, sondern ein Standard.</p>
290
+ </div>
291
+ <div class="card highlight">
292
+ <h3>Strikter Regelsatz</h3>
293
+ <p>Ein Junior-Entwickler, der neu im Projekt ist, versteht die <code class="ic">data-js</code>-Architektur und die Verzeichnisstruktur in wenigen Minuten. Team-Konsistenz ist auf Framework-Ebene garantiert.</p>
294
+ </div>
295
+ <div class="card highlight">
296
+ <h3>CLI-Fokussiert</h3>
297
+ <p>Kein manuelles Erstellen von View-, API- oder Class-Dateien. Die CLI generiert sie aus Stub-Dateien, richtet Asset-Verbindungen ein und hält das Projekt konsistent.</p>
298
+ </div>
299
+ </div>
300
+
301
+ <div class="alert a-info">
302
+ <strong>ℹ️ AGPLv3-Lizenz:</strong> ArtiFrame ist Open-Source. Abgeleitete Werke können frei verwendet werden, solange der Quellcode offen bleibt. Der Urheberrechtshinweis darf nicht entfernt werden.
303
+ </div>
304
+ </section>
305
+
306
+ <!-- ====== KURULUM ====== -->
307
+ <section id="kurulum">
308
+ <h2>Installation</h2>
309
+ <p>ArtiFrame CLI wird als globales PHP-Tool installiert. Es wird einmal installiert und in jedem Projekt verwendet.</p>
310
+
311
+ <h3>1. CLI-Tool global installieren</h3>
312
+ <pre><code><span class="cm"># Globale Installation über NPM</span>
313
+ npm install -g @artilingo/artiframe-cli
314
+
315
+ <span class="cm"># Installation überprüfen</span>
316
+ artiframe</code></pre>
317
+
318
+ <h3>2. Interaktive Shell</h3>
319
+ <p>Geben Sie einfach <code class="ic">artiframe</code> im Terminal ein und drücken Sie Eingabe. Die CLI schließt sich nicht; es öffnet sich eine interaktive Shell, die kontinuierlich auf Befehle hört:</p>
320
+ <pre><code>==================================================
321
+ ArtiFrame CLI Interactive Shell v1.0.0
322
+ ==================================================
323
+ Type 'help' for commands, or 'exit' to quit.
324
+
325
+ artiframe&gt; </code></pre>
326
+
327
+ <h3>3. Neues Projekt starten</h3>
328
+ <pre><code>artiframe&gt; new benim-projem</code></pre>
329
+ <p>Dieser Befehl erstellt das Verzeichnis <code class="ic">benim-projem/</code> und kopiert die gesamte Skelettstruktur hinein: <code class="ic">app/</code>, <code class="ic">bin/</code>, <code class="ic">config/</code>, <code class="ic">public/</code>, <code class="ic">src/</code>, <code class="ic">.env.example</code> und die erste <code class="ic">index.php</code>-Seite.</p>
330
+
331
+ <h3>4. Umgebungseinstellungen</h3>
332
+ <pre><code>cp .env.example .env</code></pre>
333
+ <p>Öffnen Sie Ihre <code class="ic">.env</code>-Datei und füllen Sie Datenbank- und Anwendungsinformationen aus. Diese Datei wird niemals in die Versionskontrolle aufgenommen.</p>
334
+
335
+ <div class="alert a-warn">
336
+ <strong>⚠️ Webserver-Einstellung:</strong> Leiten Sie das Document Root von Apache/Nginx auf den Ordner <code class="ic">/public/</code> um. Andere Verzeichnisse dürfen niemals nach außen hin zugänglich sein.
337
+ </div>
338
+ </section>
339
+
340
+ <!-- ====== DİZİN YAPISI ====== -->
341
+ <section id="dizin">
342
+ <h2>Verzeichnisstruktur</h2>
343
+ <p>Die beim Projektstart erstellte Architektur sorgt für eine klare Trennung der Zuständigkeiten (SoC).</p>
344
+
345
+ <div class="tree">
346
+ <span class="dir">proje-adi/</span>
347
+ ├── <span class="dir">app/</span> <span class="note"># Infrastrukturschicht</span>
348
+ │ ├── <span class="file">ViewControl.php</span> <span class="note"># View (HTML-Seite) Bootstrapper</span>
349
+ │ ├── <span class="file">ApiControl.php</span> <span class="note"># API-Endpunkt Bootstrapper</span>
350
+ │ ├── <span class="file">Database.php</span> <span class="note"># PDO-basierte Datenbankverbindung</span>
351
+ │ ├── <span class="file">DotEnv.php</span> <span class="note"># .env-Reader</span>
352
+ │ └── <span class="file">R2Manager.php</span> <span class="note"># Cloudflare R2 Dateimanager</span>
353
+
354
+ ├── <span class="dir warn">bin/</span> <span class="note"># ⚠️ Systemkern — nicht direkt bearbeiten</span>
355
+ │ ├── <span class="file">SystemMethod.php</span> <span class="note"># API/Backend globale Helfer</span>
356
+ │ ├── <span class="file">ViewMethod.php</span> <span class="note"># View/Frontend globale Helfer</span>
357
+ │ └── <span class="dir">stubs/</span> <span class="note"># Vorlagendateien für die CLI</span>
358
+ │ ├── view.stub
359
+ │ ├── api-standart.stub
360
+ │ ├── api-switch-case.stub
361
+ │ └── class.stub
362
+
363
+ ├── <span class="dir">config/</span> <span class="note"># Konfigurationsdateien</span>
364
+ │ └── <span class="file">app-version.php</span> <span class="note"># APP_VERSION- und APP_ENV-Konstanten</span>
365
+
366
+ ├── <span class="dir">public/</span> <span class="note"># ← Das einzige öffentlich zugängliche Verzeichnis des Webservers</span>
367
+ │ ├── <span class="dir safe">assets/</span>
368
+ │ │ ├── <span class="dir">css/</span> <span class="note"># View-spezifische CSS-Dateien</span>
369
+ │ │ └── <span class="dir">js/</span> <span class="note"># View-spezifische JS-Dateien</span>
370
+ │ ├── <span class="dir">includes/</span> <span class="note"># Geteilte Komponenten</span>
371
+ │ │ ├── head.php
372
+ │ │ ├── header.php
373
+ │ │ └── footer.php
374
+ │ ├── <span class="dir">api/</span> <span class="note"># API-Endpunkt-Dateien</span>
375
+ │ └── <span class="file">index.php</span> <span class="note"># Hauptseite</span>
376
+
377
+ ├── <span class="dir">src/</span> <span class="note"># Geschäftslogik und Klassen</span>
378
+ ├── <span class="file">.env</span> <span class="note"># Umgebungsvariablen (nicht in Git)</span>
379
+ ├── <span class="file">.env.example</span> <span class="note"># Vorlage — in Git enthalten</span>
380
+ └── <span class="file">kilavuz.html</span> <span class="note"># Dieses Dokument</span>
381
+ </div>
382
+
383
+ <div class="alert a-danger">
384
+ <strong>🚫 Fass den bin/-Ordner nicht an:</strong> Dateien in <code class="ic">bin/</code> sind der Kern des Frameworks. Projektspezifische Geschäftslogik wird hier nicht hinzugefügt. Klassen und Dienste gehören unter <code class="ic">src/</code>, Infrastrukturkomponenten unter <code class="ic">app/</code>.
385
+ </div>
386
+ </section>
387
+
388
+ <!-- ====== BOOTSTRAPPER ====== -->
389
+ <section id="bootstrapper">
390
+ <h2>Bootstrapper-Architektur <span class="tag b-core">Kritisch</span></h2>
391
+ <p>ArtiFrame verwendet zwei völlig unabhängige Bootstrapper. Diese Architektur verhindert von Grund auf <strong>HTML-Header-Probleme und Sicherheitslücken</strong>.</p>
392
+
393
+ <div class="grid-2">
394
+ <div class="card highlight">
395
+ <h3><span class="badge b-view">View</span> ViewControl.php</h3>
396
+ <p>Wird für HTML-Seiten (View-Dateien) verwendet. Startet eine Session und lädt <code class="ic">ViewMethod</code>.</p>
397
+ <pre><code><span class="cm">// public/profil.php — GANZ OBEN, bevor HTML ausgegeben wird</span>
398
+ <span class="kw">&lt;?php</span>
399
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>]
400
+ . <span class="st">'/../app/ViewControl.php'</span>;
401
+ <span class="kw">use</span> Bin\ViewMethod;
402
+ <span class="kw">?&gt;</span>
403
+ <span class="kw">&lt;!DOCTYPE html&gt;</span>
404
+ ...</code></pre>
405
+ </div>
406
+ <div class="card highlight">
407
+ <h3><span class="badge b-api">API</span> ApiControl.php</h3>
408
+ <p>Wird für API-Endpunkt-Dateien verwendet. Setzt den JSON-Header, führt HTTP-Methodenkontrolle durch und lädt <code class="ic">SystemMethod</code>.</p>
409
+ <pre><code><span class="cm">// public/api/kullanici/getir.php</span>
410
+ <span class="kw">&lt;?php</span>
411
+ <span class="cm">// $allowedMethods MUSS VOR require definiert werden</span>
412
+ <span class="var">$allowedMethods</span> = [<span class="st">'GET'</span>];
413
+
414
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>]
415
+ . <span class="st">'/../app/ApiControl.php'</span>;
416
+
417
+ <span class="kw">use</span> Bin\SystemMethod;</code></pre>
418
+ </div>
419
+ </div>
420
+
421
+ <div class="alert a-danger">
422
+ <strong>🚫 ViewControl und ApiControl dürfen niemals verwechselt werden:</strong> Wenn ViewControl in einer API-Datei eingebunden wird, wird möglicherweise ein HTML-Header statt eines JSON-Headers zurückgegeben und die gesamte API-Antwort schlägt fehl. Wenn ApiControl in einer HTML-Seite eingebunden wird, startet die Session nicht und die Seite bricht ab.
423
+ </div>
424
+ </section>
425
+
426
+ <!-- ====== KURALLAR ====== -->
427
+ <section id="kurallar">
428
+ <h2>Regelsatz <span class="tag b-core">Standard</span></h2>
429
+
430
+ <div class="card">
431
+ <h3>Regel 1: data-js-Architektur <span class="badge b-sec">Kritisch</span></h3>
432
+ <p>JavaScript-Events dürfen niemals über eine <code class="ic">class</code> oder <code class="ic">id</code> abgehört werden. Diese sind visuelle / Styling-Identifikatoren. Alle JS-Interaktionen werden über das Attribut <code class="ic">data-js</code> verwaltet. Wenn CSS eine Klasse löscht, stürzt JavaScript niemals ab.</p>
433
+ <pre><code><span class="cm">&lt;!-- ❌ Anti-Pattern — nicht unterstützt --&gt;</span>
434
+ &lt;button id="submitBtn" class="btn"&gt;Senden&lt;/button&gt;
435
+ <span class="cm">// JS: document.getElementById('submitBtn').addEventListener(...)</span>
436
+
437
+ <span class="cm">&lt;!-- ✅ ArtiFrame-Standard --&gt;</span>
438
+ &lt;button class="btn btn-primary" <span class="var">data-js</span>="login-submit"&gt;Senden&lt;/button&gt;
439
+ <span class="cm">// JS: document.querySelector('[data-js="login-submit"]').addEventListener(...)</span></code></pre>
440
+ </div>
441
+
442
+ <div class="card">
443
+ <h3>Regel 2: Theme-Architektur</h3>
444
+ <p>Dunkel-/Hell-Modus und Themes werden über die Attribute <code class="ic">data-theme</code> und <code class="ic">data-mode</code> des <code class="ic">&lt;html&gt;</code>-Tags verwaltet. Body-Klassen werden nicht verwendet.</p>
445
+ <pre><code><span class="cm">&lt;!-- HTML-Eröffnungstag aus der view.stub-Vorlage --&gt;</span>
446
+ &lt;html lang="de" data-theme="default" data-mode="light"&gt;
447
+
448
+ <span class="cm">/* Theme-Definition in app.css */</span>
449
+ html[data-theme="default"][data-mode="dark"] {
450
+ --bg-color: #0b0c0e;
451
+ --text-main: #ffffff;
452
+ }
453
+ html[data-theme="default"][data-mode="light"] {
454
+ --bg-color: #ffffff;
455
+ --text-main: #111111;
456
+ }</code></pre>
457
+ </div>
458
+
459
+ <div class="card">
460
+ <h3>Regel 3: Sicherer Datenfluss</h3>
461
+ <p>Alle aus der Datenbank stammenden Daten werden mit <code class="ic">display()</code> umschlossen, bevor sie im DOM ausgegeben werden. Alle an die API gesendeten Daten werden mit <code class="ic">sanitizeString()</code> oder <code class="ic">sanitizeInt()</code> bereinigt, bevor sie verarbeitet werden.</p>
462
+ </div>
463
+
464
+ <div class="card">
465
+ <h3>Regel 4: Fehlerbehandlung mit APP_ENV</h3>
466
+ <p>Der Wert von <code class="ic">APP_ENV</code> in der <code class="ic">.env</code>-Datei bestimmt die Sichtbarkeit von Fehlern. In der Production-Umgebung werden dem Benutzer keine Fehlermeldungen angezeigt.</p>
467
+ <pre><code><span class="cm">// config/app-version.php</span>
468
+ define(<span class="st">'APP_ENV'</span>, (int)<span class="var">$_ENV</span>[<span class="st">'APP_ENV'</span>]); <span class="cm">// 1=Debug, 0=Production</span>
469
+
470
+ <span class="kw">if</span> (APP_ENV === <span class="nu">1</span>) {
471
+ ini_set(<span class="st">'display_errors'</span>, <span class="nu">1</span>);
472
+ error_reporting(E_ALL);
473
+ } <span class="kw">else</span> {
474
+ ini_set(<span class="st">'display_errors'</span>, <span class="nu">0</span>);
475
+ }</code></pre>
476
+ </div>
477
+ </section>
478
+
479
+ <!-- ====== CLI GİRİŞ ====== -->
480
+ <section id="cli-giris">
481
+ <h2>CLI-Ökosystem</h2>
482
+ <p>Die ArtiFrame CLI öffnet eine interaktive Shell, wenn <code class="ic">artiframe</code> im Terminal eingegeben wird. Alle Befehle werden in dieser Shell ausgeführt. Befehle können auch einmalig ausgeführt werden.</p>
483
+ <pre><code><span class="cm"># Interaktiver Modus (empfohlen)</span>
484
+ artiframe
485
+ artiframe&gt; make:view admin/kullanicilar.php
486
+
487
+ <span class="cm"># Einmaliger Modus</span>
488
+ artiframe make:view admin/kullanicilar.php</code></pre>
489
+ </section>
490
+
491
+ <!-- new -->
492
+ <section id="cli-new">
493
+ <h2><code>new</code> <span class="tag b-cli">CLI-Befehl</span></h2>
494
+ <p>Erstellt ein neues ArtiFrame-Projekt. Generiert die gesamte Verzeichnisskelettstruktur, Bootstrapper-Dateien und die erste Index-Seite.</p>
495
+ <pre><code>artiframe&gt; <span class="fn">new</span> <span class="st">proje-adi</span></code></pre>
496
+ <p>Erstellte Struktur:</p>
497
+ <div class="tree" style="font-size:0.82rem; line-height:1.7">
498
+ proje-adi/
499
+ ├── app/ (ViewControl.php, ApiControl.php, Database.php, DotEnv.php)
500
+ ├── bin/ (SystemMethod.php, ViewMethod.php, stubs/)
501
+ ├── config/ (app-version.php)
502
+ ├── public/ (index.php, assets/, includes/, api/)
503
+ ├── src/
504
+ ├── .env.example
505
+ └── kilavuz.html
506
+ </div>
507
+ </section>
508
+
509
+ <!-- make:view -->
510
+ <section id="cli-view">
511
+ <h2><code>make:view</code> <span class="tag b-cli">CLI-Befehl</span></h2>
512
+ <p>Erstellt eine neue Seiten- (View-) Datei und deren spezifische CSS/JS-Assets. Assets werden automatisch mit der Seite verknüpft und Cache-Busting wird mit <code class="ic">?v=APP_VERSION</code> angewendet.</p>
513
+ <pre><code>artiframe&gt; <span class="fn">make:view</span> <span class="st">admin/kullanicilar.php</span></code></pre>
514
+ <p>Erstellte Dateien:</p>
515
+ <pre><code><span class="cm">✔ public/admin/kullanicilar.php</span>
516
+ <span class="cm">✔ public/assets/css/admin/kullanicilar.css</span>
517
+ <span class="cm">✔ public/assets/js/admin/kullanicilar.js</span></code></pre>
518
+ <p>Die erstellte View-Datei enthält am Anfang ein eingebundenes ViewControl, hinzugefügte Head/Header/Footer-Includes und per Cache-Busting verknüpfte CSS/JS-Links.</p>
519
+ </section>
520
+
521
+ <!-- make:api -->
522
+ <section id="cli-api">
523
+ <h2><code>make:api</code> <span class="tag b-cli">CLI-Befehl</span></h2>
524
+ <p>Erstellt eine API-Endpunkt-Dateien durch Auswahl aus zwei verschiedenen Vorlagen. Jede neue API-Datei enthält bereits die Variable <code class="ic">$allowedMethods</code> und die Einbindung von <code class="ic">ApiControl.php</code>.</p>
525
+
526
+ <div class="card">
527
+ <h3><span class="badge b-api">standart</span> — Einzelaktions-API</h3>
528
+ <p>Für Endpunkte, die eine einzelne Aufgabe ausführen (Login, Senden, Löschen). Die Geschäftslogik wird direkt geschrieben.</p>
529
+ <pre><code>artiframe&gt; <span class="fn">make:api</span> <span class="st">standart</span> <span class="st">api/auth/giris.php</span></code></pre>
530
+ <pre><code><span class="kw">&lt;?php</span>
531
+ <span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>]; <span class="cm">// Nur POST akzeptieren</span>
532
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>] . <span class="st">'/../app/ApiControl.php'</span>;
533
+
534
+ <span class="kw">use</span> Bin\SystemMethod;
535
+
536
+ <span class="cm">// Geschäftslogik hier...</span>
537
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>], <span class="nu">200</span>);</code></pre>
538
+ </div>
539
+
540
+ <div class="card">
541
+ <h3><span class="badge b-api">switch-case</span> — Multiaktions-API</h3>
542
+ <p>Eine Struktur, die CRUD-Operationen für ein Modul in einem einzigen Endpunkt verwaltet. Die auszuführende Aktion wird durch den Parameter <code class="ic">action</code> bestimmt.</p>
543
+ <pre><code>artiframe&gt; <span class="fn">make:api</span> <span class="st">switch-case</span> <span class="st">api/kullanici/yonet.php</span></code></pre>
544
+ <pre><code><span class="kw">&lt;?php</span>
545
+ <span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>];
546
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>] . <span class="st">'/../app/ApiControl.php'</span>;
547
+
548
+ <span class="kw">use</span> Bin\SystemMethod;
549
+
550
+ <span class="var">$action</span> = <span class="fn">sanitizeString</span>(<span class="var">$_POST</span>[<span class="st">'action'</span>] ?? <span class="st">''</span>);
551
+
552
+ <span class="kw">switch</span> (<span class="var">$action</span>) {
553
+ <span class="kw">case</span> <span class="st">'create'</span>:
554
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Erstellt.'</span>], <span class="nu">200</span>);
555
+ <span class="kw">break</span>;
556
+ <span class="kw">case</span> <span class="st">'update'</span>:
557
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Aktualisiert.'</span>], <span class="nu">200</span>);
558
+ <span class="kw">break</span>;
559
+ <span class="kw">case</span> <span class="st">'delete'</span>:
560
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Gelöscht.'</span>], <span class="nu">200</span>);
561
+ <span class="kw">break</span>;
562
+ <span class="kw">default</span>:
563
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'error'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Ungültige Aktion.'</span>], <span class="nu">400</span>);
564
+ }</code></pre>
565
+ </div>
566
+ </section>
567
+
568
+ <!-- make:class -->
569
+ <section id="cli-class">
570
+ <h2><code>make:class</code> <span class="tag b-cli">CLI-Befehl</span></h2>
571
+ <p>Erstellt eine neue PHP-Klassendatei mit vorbereitetem Namespace- und Klassen-Boilerplate.</p>
572
+ <pre><code>artiframe&gt; <span class="fn">make:class</span> <span class="st">classes/EmailService.php</span></code></pre>
573
+ </section>
574
+
575
+ <!-- version -->
576
+ <section id="cli-version">
577
+ <h2><code>version</code> <span class="tag b-cli">CLI-Befehl</span></h2>
578
+ <p>Aktualisiert die Versionsnummer in <code class="ic">config/app-version.php</code> gemäß den Regeln der semantischen Versionierung (SemVer). Versionsformat: <strong>MAJOR.MINOR.PATCH</strong></p>
579
+
580
+ <table class="sig-table">
581
+ <thead>
582
+ <tr><th>Befehl</th><th>Beschreibung</th><th>Beispiel</th></tr>
583
+ </thead>
584
+ <tbody>
585
+ <tr><td>version upgrade patch</td><td>Fehlerbehebung, kleine Verbesserung</td><td>1.2.3 → 1.2.4</td></tr>
586
+ <tr><td>version upgrade minor</td><td>Abwärtskompatible neue Funktion</td><td>1.2.3 → 1.3.0</td></tr>
587
+ <tr><td>version upgrade major</td><td>Breaking Change (brechende Änderung)</td><td>1.2.3 → 2.0.0</td></tr>
588
+ <tr><td>version downgrade patch</td><td>Letzten Patch rückgängig machen</td><td>1.2.4 → 1.2.3</td></tr>
589
+ <tr><td>version downgrade minor</td><td>Letztes Minor-Release rückgängig machen</td><td>1.3.0 → 1.2.0</td></tr>
590
+ <tr><td>version downgrade major</td><td>Letztes Major-Release rückgängig machen</td><td>2.0.0 → 1.0.0</td></tr>
591
+ </tbody>
592
+ </table>
593
+
594
+ <pre><code>artiframe&gt; <span class="fn">version</span> upgrade minor
595
+ <span class="cm">✔ Version von 1.2.0 auf 1.3.0 aktualisiert.</span></code></pre>
596
+ </section>
597
+
598
+ <!-- ====== VIEW HELPERS ====== -->
599
+ <section id="vh-display">
600
+ <h2>View Helpers — <code>display()</code> <span class="tag b-view">ViewMethod</span></h2>
601
+ <p>Wendet <strong>obligatorischen XSS-Schutz</strong> an, wenn Daten aus der Datenbank oder Benutzereingaben im DOM ausgegeben werden. Gibt bei Null- oder leeren Werten einen Standardwert zurück, anstatt einen Fehler zu werfen.</p>
602
+ <span class="ret">string display(mixed $data, string $default = '')</span>
603
+ <div class="alert a-info" style="margin-top: 10px; margin-bottom: 24px; padding: 12px 20px;">
604
+ <strong>💡 Tipp: Verwendung von <code>$default</code></strong><br>
605
+ Der zweite Parameter (<code>$default</code>) ist der Fallback-Text, der auf dem Bildschirm ausgegeben wird, wenn die aus der Datenbank stammenden Daten leer sind (null, false, leere Zeichenkette). Wenn Sie beispielsweise <code>display($isim, 'Unbenannter Benutzer')</code> für einen Benutzer verwenden, dessen Name nicht eingegeben wurde, bietet Ihre Seite sinnvolle Inhalte statt unschöner Lücken.
606
+ </div>
607
+ <pre><code><span class="cm">&lt;!-- ❌ Unsicher — erzeugt XSS-Lücke --&gt;</span>
608
+ &lt;h1&gt;&lt;?= <span class="var">$user</span>[<span class="st">'isim'</span>] ?&gt;&lt;/h1&gt;
609
+
610
+ <span class="cm">&lt;!-- ✅ Sichere ArtiFrame-Verwendung --&gt;</span>
611
+ &lt;h1&gt;&lt;?= <span class="fn">display</span>(<span class="var">$user</span>[<span class="st">'isim'</span>], <span class="st">'Unbenannter Benutzer'</span>) ?&gt;&lt;/h1&gt;</code></pre>
612
+ </section>
613
+
614
+ <section id="vh-escapeurl">
615
+ <h2>View Helpers — <code>escapeUrl()</code> <span class="tag b-view">ViewMethod</span></h2>
616
+ <p>Wird verwendet, wenn von Benutzern erhaltene Links (z. B. Profil-Websites) in <code>&lt;a href="..."&gt;</code> oder <code>&lt;img src="..."&gt;</code> ausgegeben werden. Macht gefährliche Payloads wie <code>javascript:alert(1)</code> (Stored XSS) unschädlich und verhindert die Ausführung von Code über den Link.</p>
617
+ <span class="ret">string escapeUrl(string $url)</span>
618
+ <pre><code><span class="cm">&lt;!-- ❌ Unsicher — JS-Code kann über URL eingeschleust werden --&gt;</span>
619
+ &lt;a href="&lt;?= <span class="var">$user</span>[<span class="st">'website'</span>] ?&gt;"&gt;Website besuchen&lt;/a&gt;
620
+
621
+ <span class="cm">&lt;!-- ✅ Sichere ArtiFrame-Verwendung --&gt;</span>
622
+ &lt;a href="&lt;?= <span class="fn">escapeUrl</span>(<span class="var">$user</span>[<span class="st">'website'</span>]) ?&gt;"&gt;Website besuchen&lt;/a&gt;</code></pre>
623
+ </section>
624
+
625
+ <section id="vh-csrf">
626
+ <h2>View Helpers — <code>csrfField()</code> <span class="tag b-view">ViewMethod</span></h2>
627
+ <p>Fügt HTML-Formularen ein verstecktes Token-Feld zum Schutz vor CSRF-Angriffen hinzu. <strong>Muss auf jeder Seite mit einem POST-Formular verwendet werden.</strong></p>
628
+ <span class="ret">string csrfField()</span>
629
+ <pre><code>&lt;form action="/api/kaydet.php" method="POST"&gt;
630
+ &lt;?= <span class="fn">csrfField</span>() ?&gt; <span class="cm">&lt;!-- Aus Sicherheitsgründen erforderlich --&gt;</span>
631
+ &lt;input type="text" name="ad"&gt;
632
+ &lt;button type="submit" data-js="kaydet-btn"&gt;Speichern&lt;/button&gt;
633
+ &lt;/form&gt;</code></pre>
634
+ </section>
635
+
636
+ <section id="vh-dates">
637
+ <h2>View Helpers — Datumsfunktionen <span class="tag b-view">ViewMethod</span></h2>
638
+ <p>Verarbeitet Datums-Strings im Format <code class="ic">Y-m-d H:i:s</code> oder UNIX-Timestamp-Werte aus der Datenbank. Alle Textausgaben werden je nach Sprache (<code class="ic">tr</code>, <code class="ic">en</code>, <code class="ic">de</code>, <code class="ic">fr</code>, <code class="ic">es</code>) lokalisiert.</p>
639
+
640
+ <table class="sig-table">
641
+ <thead><tr><th>Funktion</th><th>Ausgabebeispiel</th><th>Beschreibung</th></tr></thead>
642
+ <tbody>
643
+ <tr><td>day($date)</td><td>24</td><td>Nur Tag</td></tr>
644
+ <tr><td>month($date)</td><td>07</td><td>Nur Monat (Zahl)</td></tr>
645
+ <tr><td>year($date)</td><td>2026</td><td>Jahr</td></tr>
646
+ <tr><td>timeOnly($date)</td><td>14:30</td><td>Stunde:Minute</td></tr>
647
+ <tr><td>fulldate($date)</td><td>24.07.2026</td><td>Vollständiges Datum</td></tr>
648
+ <tr><td>formatDate($date, $format)</td><td>24.07.2026 14:30</td><td>Benutzerdefiniertes Format</td></tr>
649
+ <tr><td>monthName($date, $lang)</td><td>Temmuz / July / Juli</td><td>Monatsname (je nach Sprache)</td></tr>
650
+ <tr><td>fulldateName($date, $lang)</td><td>24 Temmuz 2026 / July 24, 2026</td><td>Vollständiges Datum (mit Monatsname, je nach Sprache)</td></tr>
651
+ <tr><td>timeAgo($date, $lang)</td><td>5 dakika önce / 5 minutes ago</td><td>Social-Media-Stil (je nach Sprache)</td></tr>
652
+ </tbody>
653
+ </table>
654
+
655
+ <div class="alert a-info" style="margin-top: 0; margin-bottom: 24px;">
656
+ <strong>💡 Parameter verstehen: <code>$format</code> und <code>$lang</code></strong>
657
+ <ul style="margin-top: 10px; margin-bottom: 0;">
658
+ <li style="margin-bottom: 8px;"><strong><code>$format</code> (Format):</strong> Wird nur mit der Funktion <code>formatDate()</code> verwendet. Akzeptiert Standard-PHP-Datumsbuchstaben. (Z. B.: <code>'d/m/Y'</code> ➔ 24/07/2026 oder <code>'H:i'</code> ➔ 15:30). Ermöglicht das Erstellen eigener Datums-Muster, wenn andere vordefinierte Funktionen (day, year usw.) unzureichend sind.</li>
659
+ <li style="margin-bottom: 0;"><strong><code>$lang</code> (Sprache):</strong> Wird in Funktionen verwendet, die Text in ihrer Ausgabe enthalten (Monatsname, Wort "vor"). Wenn Sie diesen Parameter leer lassen, arbeitet das System <strong>standardmäßig mit <code>'tr'</code> (Türkisch)</strong>. Wenn Sie ein mehrsprachiges Projekt erstellen, reicht es aus, den Sprachcode (<code>tr, en, de, fr, es</code>) als zweiten Parameter einzugeben. (Z. B.: <code>timeAgo($tarih, 'en')</code> ➔ 5 minutes ago)</li>
660
+ </ul>
661
+ </div>
662
+
663
+ <pre><code><span class="cm">&lt;!-- Angenommen, es ist ein UNIX-Timestamp von der Funktion time() --&gt;</span>
664
+ <span class="kw">&lt;?php</span> <span class="var">$tarih</span> = <span class="fn">time</span>(); <span class="kw">?&gt;</span>
665
+
666
+ <span class="cm">&lt;!-- Vollständiges Datum je nach Sprache --&gt;</span>
667
+ &lt;span&gt;&lt;?= <span class="fn">fulldateName</span>(<span class="var">$tarih</span>, <span class="st">'tr'</span>) ?&gt;&lt;/span&gt;
668
+ <span class="cm">&lt;!-- Ausgabe: 24 Temmuz 2026 --&gt;</span>
669
+
670
+ <span class="cm">&lt;!-- Zeitdarstellung im Social-Media-Stil --&gt;</span>
671
+ &lt;span&gt;&lt;?= <span class="fn">timeAgo</span>(<span class="var">$tarih</span>, <span class="st">'en'</span>) ?&gt;&lt;/span&gt;
672
+ <span class="cm">&lt;!-- Ausgabe: 5 minutes ago --&gt;</span>
673
+
674
+ &lt;span&gt;&lt;?= <span class="fn">timeAgo</span>(<span class="var">$tarih</span>, <span class="st">'de'</span>) ?&gt;&lt;/span&gt;
675
+ <span class="cm">&lt;!-- Ausgabe: vor 5 Minuten --&gt;</span></code></pre>
676
+ </section>
677
+
678
+ <section id="vh-format">
679
+ <h2>View Helpers — Textformatierung <span class="tag b-view">ViewMethod</span></h2>
680
+
681
+ <div class="card">
682
+ <h3><code>truncate($text, $length, $append)</code></h3>
683
+ <span class="ret">string truncate(string $text, int $length = 100, string $append = '...')</span>
684
+ <p>Kürzt lange Texte (z. B. Blog-Zusammenfassungen) auf das gewünschte Zeichenlimit, ohne das letzte Wort zu zerschneiden, und fügt das angegebene Suffix am Ende hinzu.</p>
685
+ <pre><code>&lt;p&gt;&lt;?= <span class="fn">truncate</span>(<span class="var">$post</span>[<span class="st">'icerik'</span>], <span class="nu">160</span>, <span class="st">'...'</span>) ?&gt;&lt;/p&gt;</code></pre>
686
+ </div>
687
+ </section>
688
+
689
+ <section id="vh-money">
690
+ <h2>View Helpers — <code>money()</code> <span class="tag b-view">ViewMethod</span></h2>
691
+ <p>Formatiert den Betrag mit dem Währungssymbol. Der Währungscode (ISO) bestimmt automatisch die Platzierung des Symbols. Die Standardwährung ist <code class="ic">usd</code>.</p>
692
+ <span class="ret">string money(float $amount, string $currency = 'usd')</span>
693
+
694
+ <table class="sig-table">
695
+ <thead><tr><th>Code</th><th>Ausgabe</th><th>Währung</th></tr></thead>
696
+ <tbody>
697
+ <tr><td>usd</td><td>$1.250,00</td><td>US-Dollar</td></tr>
698
+ <tr><td>eur</td><td>1.250,00 €</td><td>Euro</td></tr>
699
+ <tr><td>try / tl</td><td>1.250,00 ₺</td><td>Türkische Lira</td></tr>
700
+ <tr><td>gbp</td><td>£1.250,00</td><td>Britisches Pfund</td></tr>
701
+ <tr><td>jpy</td><td>1.250,00 ¥</td><td>Japanischer Yen</td></tr>
702
+ <tr><td>inr</td><td>1.250,00 ₹</td><td>Indische Rupie</td></tr>
703
+ <tr><td>rub</td><td>1.250,00 ₽</td><td>Russischer Rubel</td></tr>
704
+ <tr><td>krw</td><td>1.250,00 ₩</td><td>Südkoreanischer Won</td></tr>
705
+ <tr><td>brl</td><td>R$1.250,00</td><td>Brasilianischer Real</td></tr>
706
+ <tr><td>aed</td><td>1.250,00 د.إ</td><td>VAE-Dirham</td></tr>
707
+ </tbody>
708
+ </table>
709
+
710
+ <pre><code>&lt;span&gt;&lt;?= <span class="fn">money</span>(<span class="var">$urun</span>[<span class="st">'fiyat'</span>], <span class="st">'try'</span>) ?&gt;&lt;/span&gt;
711
+ <span class="cm">&lt;!-- Ausgabe: 1.250,00 ₺ --&gt;</span>
712
+
713
+ &lt;span&gt;&lt;?= <span class="fn">money</span>(<span class="var">$urun</span>[<span class="st">'fiyat'</span>], <span class="st">'usd'</span>) ?&gt;&lt;/span&gt;
714
+ <span class="cm">&lt;!-- Ausgabe: $1.250,00 --&gt;</span></code></pre>
715
+ </section>
716
+
717
+ <!-- ====== SYSTEM HELPERS ====== -->
718
+ <section id="sh-json">
719
+ <h2>System Helpers — <code>jsonResponse()</code> <span class="tag b-api">SystemMethod</span></h2>
720
+ <p>Setzt den JSON-Header, übergibt den HTTP-Statuscode, gibt die Ausgabe sicher mit json_encode aus und beendet anschließend das Skript. <strong>Alle API-Antworten müssen über diese Funktion gegeben werden.</strong></p>
721
+ <span class="ret">void jsonResponse(array $data, int $statusCode = 200)</span>
722
+ <pre><code><span class="cm">// Erfolgreiche Antwort</span>
723
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>, <span class="st">'data'</span> =&gt; <span class="var">$kullanici</span>], <span class="nu">200</span>);
724
+
725
+ <span class="cm">// Fehlerantwort</span>
726
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'error'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Unbefugter Zugriff.'</span>], <span class="nu">401</span>);</code></pre>
727
+ </section>
728
+
729
+ <section id="sh-csrf">
730
+ <h2>System Helpers — <code>verifyCsrf()</code> <span class="tag b-api">SystemMethod</span></h2>
731
+ <p>Überprüft, ob die eingehende POST-Anfrage von einem legitimen Formular stammt. Gibt false zurück, wenn <code class="ic">csrfField()</code> nicht im Formular enthalten ist oder das Token ungültig ist.</p>
732
+ <span class="ret">bool verifyCsrf(string $token)</span>
733
+ <pre><code><span class="kw">if</span> (!<span class="fn">verifyCsrf</span>(<span class="var">$_POST</span>[<span class="st">'csrf_token'</span>] ?? <span class="st">''</span>)) {
734
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'error'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Ungültiges CSRF-Token.'</span>], <span class="nu">403</span>);
735
+ }</code></pre>
736
+ </section>
737
+
738
+ <section id="sh-sanitize">
739
+ <h2>System Helpers — Bereinigungsfunktionen <span class="tag b-api">SystemMethod</span></h2>
740
+
741
+ <table class="sig-table">
742
+ <thead><tr><th>Funktion</th><th>Beschreibung</th></tr></thead>
743
+ <tbody>
744
+ <tr><td>sanitizeInt($value)</td><td>Entfernt alle Buchstaben, Symbole und Kommas und lässt <strong>nur Ganzzahlen</strong> übrig. Wird für IDs oder Limits verwendet.</td></tr>
745
+ <tr><td>sanitizeFloat($value)</td><td>Bereinigt alles außer Dezimalzahlen. Wird für Geldbeträge oder Metriken verwendet.</td></tr>
746
+ <tr><td>sanitizeString($value)</td><td>Eliminiert alle <strong>HTML- und PHP-Tags (<code>&lt;script&gt;</code>, <code>&lt;iframe&gt;</code> usw.)</strong>, um XSS- und ähnliche Angriffe zu verhindern. Hinterlässt sicheren Klartext.</td></tr>
747
+ <tr><td>sanitizeEmail($email)</td><td>Filtert alle ungültigen und gefährlichen Zeichen heraus, die nicht dem E-Mail-Format entsprechen.</td></tr>
748
+ </tbody>
749
+ </table>
750
+
751
+ <pre><code><span class="var">$id</span> = <span class="fn">sanitizeInt</span>(<span class="var">$_POST</span>[<span class="st">'id'</span>] ?? <span class="nu">0</span>);
752
+ <span class="var">$ad</span> = <span class="fn">sanitizeString</span>(<span class="var">$_POST</span>[<span class="st">'ad'</span>] ?? <span class="st">''</span>);
753
+ <span class="var">$email</span> = <span class="fn">sanitizeEmail</span>(<span class="var">$_POST</span>[<span class="st">'email'</span>] ?? <span class="st">''</span>);</code></pre>
754
+ </section>
755
+
756
+ <section id="sh-request">
757
+ <h2>System Helpers — HTTP & Anfragen <span class="tag b-api">SystemMethod</span></h2>
758
+
759
+ <table class="sig-table">
760
+ <thead><tr><th>Funktion</th><th>Beschreibung</th></tr></thead>
761
+ <tbody>
762
+ <tr><td>isPost()</td><td>Anfrage POST?</td></tr>
763
+ <tr><td>isGet()</td><td>Anfrage GET?</td></tr>
764
+ <tr><td>isPut()</td><td>Anfrage PUT?</td></tr>
765
+ <tr><td>isDelete()</td><td>Anfrage DELETE?</td></tr>
766
+ <tr><td>isAjax()</td><td>Anfrage über XHR/Fetch-API?</td></tr>
767
+ <tr><td>getClientIp()</td><td>Echte IP-Adresse (mit Cloudflare- & Proxy-Unterstützung)</td></tr>
768
+ <tr><td>redirect($url)</td><td>Leitet zur angegebenen URL weiter und beendet das Skript</td></tr>
769
+ </tbody>
770
+ </table>
771
+
772
+ <pre><code><span class="cm">// Direktzugriff über den Browser verhindern</span>
773
+ <span class="kw">if</span> (!<span class="fn">isAjax</span>()) {
774
+ <span class="fn">jsonResponse</span>([<span class="st">'error'</span> =&gt; <span class="st">'Direkter Zugriff eingeschränkt.'</span>], <span class="nu">403</span>);
775
+ }
776
+
777
+ <span class="cm">// Weiterleiten, wenn keine Sitzung vorhanden ist</span>
778
+ <span class="kw">if</span> (!isset(<span class="var">$_SESSION</span>[<span class="st">'user'</span>])) {
779
+ <span class="fn">redirect</span>(<span class="st">'/giris.php'</span>);
780
+ }
781
+
782
+ <span class="var">$ip</span> = <span class="fn">getClientIp</span>(); <span class="cm">// Echte IP auch hinter Cloudflare</span></code></pre>
783
+ </section>
784
+
785
+ <section id="sh-security">
786
+ <h2>System Helpers — Sicherheit <span class="tag b-sec">SystemMethod</span></h2>
787
+
788
+ <table class="sig-table">
789
+ <thead><tr><th>Funktion</th><th>Beschreibung</th></tr></thead>
790
+ <tbody>
791
+ <tr><td>generateCsrf()</td><td>Generiert ein neues CSRF-Token und speichert es in der Session</td></tr>
792
+ <tr><td>generateToken($length)</td><td>Kryptografisch sichere zufällige Hex-Zeichenkette (Passwort-Reset, API-Key usw.)</td></tr>
793
+ <tr><td>hashPassword($password)</td><td>Hasht das Passwort mit bcrypt</td></tr>
794
+ <tr><td>verifyPassword($password, $hash)</td><td>Überprüft das Passwort anhand des Hashes</td></tr>
795
+ </tbody>
796
+ </table>
797
+
798
+ <pre><code><span class="cm">// Sichere Token-Generierung (API-Key, E-Mail-Bestätigungslink usw.)</span>
799
+ <span class="var">$token</span> = <span class="fn">generateToken</span>(<span class="nu">32</span>);
800
+ <span class="cm">// ⚠️ Hinweis: 32 Byte Daten werden generiert, aber da sie ins Hex-Format (Basis 16)</span>
801
+ <span class="cm">// umgewandelt werden, ist die Ausgabe genau doppelt so lang, d. h. eine 64 Zeichen lange Zeichenkette.</span>
802
+
803
+ <span class="cm">// Passwortspeicherung</span>
804
+ <span class="var">$hash</span> = <span class="fn">hashPassword</span>(<span class="var">$_POST</span>[<span class="st">'sifre'</span>]);
805
+
806
+ <span class="cm">// Passwortüberprüfung</span>
807
+ <span class="kw">if</span> (!<span class="fn">verifyPassword</span>(<span class="var">$_POST</span>[<span class="st">'sifre'</span>], <span class="var">$kullanici</span>[<span class="st">'sifre_hash'</span>])) {
808
+ <span class="fn">jsonResponse</span>([<span class="st">'error'</span> =&gt; <span class="st">'Falsches Passwort.'</span>], <span class="nu">401</span>);
809
+ }</code></pre>
810
+ </section>
811
+
812
+ <!-- ====== API GÜVENLİĞİ ====== -->
813
+ <section id="api-methods">
814
+ <h2>API — HTTP-Methoden-Kontrolle <span class="tag b-sec">ApiControl</span></h2>
815
+ <p>Das Array <code class="ic">$allowedMethods</code> muss <strong>vor</strong> dem Einbinden von ApiControl definiert werden. ApiControl liest dieses Array und lehnt Anfragen von nicht zugelassenen Methoden automatisch mit <code class="ic">405 Method Not Allowed</code> ab.</p>
816
+ <pre><code><span class="cm">// Ein Endpunkt, der nur GET und POST akzeptiert</span>
817
+ <span class="var">$allowedMethods</span> = [<span class="st">'GET'</span>, <span class="st">'POST'</span>];
818
+ <span class="fn">require_once</span> ... . <span class="st">'/../app/ApiControl.php'</span>;
819
+
820
+ <span class="cm">// Ein Endpunkt, der nur DELETE akzeptiert</span>
821
+ <span class="var">$allowedMethods</span> = [<span class="st">'DELETE'</span>];
822
+ <span class="fn">require_once</span> ... . <span class="st">'/../app/ApiControl.php'</span>;</code></pre>
823
+ <p>Preflight-<code class="ic">OPTIONS</code>-Anfragen geben für CORS automatisch <code class="ic">200</code> zurück und das Skript beendet sich — kein manueller Eingriff erforderlich.</p>
824
+ </section>
825
+
826
+ <section id="api-cors">
827
+ <h2>API — CORS <span class="tag b-api">ApiControl (Auskommentiert)</span></h2>
828
+ <p>Wenn Sie den API-Zugriff von anderen Domains oder mobilen Anwendungen erlauben möchten, aktivieren Sie den CORS-Block in <code class="ic">ApiControl.php</code>.</p>
829
+ <pre><code><span class="cm">// In ApiControl.php — wird durch Einkommentieren aktiviert</span>
830
+ header(<span class="st">"Access-Control-Allow-Origin: https://sizin-domaininiz.com"</span>);
831
+ header(<span class="st">"Access-Control-Allow-Methods: GET, POST, OPTIONS"</span>);
832
+ header(<span class="st">"Access-Control-Allow-Headers: Content-Type, Authorization"</span>);</code></pre>
833
+ <div class="alert a-warn">
834
+ <strong>⚠️</strong> Verwenden Sie in der Production-Umgebung kein <code class="ic">*</code> (alle Domains). Geben Sie die Domainadresse explizit an.
835
+ </div>
836
+ </section>
837
+
838
+ <section id="api-rate">
839
+ <h2>API — Rate Limiting <span class="tag b-api">ApiControl (Auskommentiert)</span></h2>
840
+ <p>Sie können den Rate-Limiting-Block in <code class="ic">ApiControl.php</code> aktivieren, um zu verhindern, dass bösartige Benutzer oder Bots die API überfluten. Standardregel: <strong>60 Anfragen in 60 Sekunden / IP</strong>.</p>
841
+ <div class="alert a-warn" style="margin-top: 10px; margin-bottom: 20px;">
842
+ <strong>⚠️ Redis-Server erforderlich:</strong> Die Rate-Limiting-Funktion (Ratenbegrenzung) funktioniert über die Klasse <code>\Src\Service\RedisService::getInstance()</code> unter Verwendung von Redis. Stellen Sie vor dem Aktivieren dieses Codeblocks sicher, dass Sie einen Redis-Dienst in Ihrem Projekt eingerichtet und eine Verbindungsklasse unter <code>src/Service/</code> erstellt haben.
843
+ </div>
844
+ <pre><code><span class="cm">// Wenn von derselben IP mehr als 60 Anfragen in 60 Sekunden eingehen:</span>
845
+ http_response_code(<span class="nu">429</span>); <span class="cm">// Too Many Requests</span>
846
+ echo json_encode([<span class="st">'error'</span> =&gt; <span class="st">'Zu viele Anfragen. Bitte warten Sie.'</span>]);</code></pre>
847
+ </section>
848
+
849
+ <!-- ====== WORKFLOW ====== -->
850
+ <section id="workflow">
851
+ <h2>Vollständiger Arbeitsablauf: Kontaktformular (End-to-End)</h2>
852
+ <p>Schauen wir uns Schritt für Schritt an, wie Sie eine <strong>Kontaktformular</strong>-Funktion nach ArtiFrame-Standards von Anfang bis Ende entwickeln.</p>
853
+
854
+ <ol class="steps">
855
+ <li>
856
+ <div>
857
+ <strong>Erstellen Sie die View-Datei</strong>
858
+ <pre><code>artiframe&gt; <span class="fn">make:view</span> <span class="st">iletisim.php</span>
859
+ <span class="cm">✔ public/iletisim.php</span>
860
+ <span class="cm">✔ public/assets/css/iletisim.css</span>
861
+ <span class="cm">✔ public/assets/js/iletisim.js</span></code></pre>
862
+ </div>
863
+ </li>
864
+ <li>
865
+ <div>
866
+ <strong>Erstellen Sie den API-Endpunkt</strong>
867
+ <pre><code>artiframe&gt; <span class="fn">make:api</span> <span class="st">standart</span> <span class="st">api/iletisim/gonder.php</span></code></pre>
868
+ </div>
869
+ </li>
870
+ <li>
871
+ <div>
872
+ <strong>Codieren Sie das HTML-Formular</strong><br>
873
+ Fügen Sie das folgende Formular zur Datei <code class="ic">public/iletisim.php</code> hinzu:
874
+ <pre><code>&lt;form action="/api/iletisim/gonder.php" method="POST"&gt;
875
+ &lt;?= <span class="fn">csrfField</span>() ?&gt;
876
+ &lt;input type="text" name="ad" placeholder="Ihr Name"&gt;
877
+ &lt;input type="email" name="eposta" placeholder="E-Mail"&gt;
878
+ &lt;textarea name="mesaj" placeholder="Ihre Nachricht"&gt;&lt;/textarea&gt;
879
+ &lt;button type="submit" data-js="<span class="st">iletisim-gonder</span>"&gt;Senden&lt;/button&gt;
880
+ &lt;/form&gt;</code></pre>
881
+ </div>
882
+ </li>
883
+ <li>
884
+ <div>
885
+ <strong>Verbinden Sie die Fetch-API mit JavaScript</strong><br>
886
+ In der Datei <code class="ic">public/assets/js/iletisim.js</code>:
887
+ <pre><code>document.<span class="fn">querySelector</span>(<span class="st">'[data-js="iletisim-gonder"]'</span>).<span class="fn">addEventListener</span>(<span class="st">'click'</span>, async (e) =&gt; {
888
+ e.<span class="fn">preventDefault</span>();
889
+ <span class="kw">const</span> formData = <span class="kw">new</span> <span class="fn">FormData</span>(e.target.<span class="fn">closest</span>(<span class="st">'form'</span>));
890
+ <span class="kw">const</span> res = <span class="kw">await</span> <span class="fn">fetch</span>(<span class="st">'/api/iletisim/gonder.php'</span>, {
891
+ method: <span class="st">'POST'</span>,
892
+ body: formData
893
+ });
894
+ <span class="kw">const</span> data = <span class="kw">await</span> res.<span class="fn">json</span>();
895
+ <span class="fn">console</span>.<span class="fn">log</span>(data);
896
+ });</code></pre>
897
+ </div>
898
+ </li>
899
+ <li>
900
+ <div>
901
+ <strong>Vervollständigen Sie die Server-Logik (Backend)</strong><br>
902
+ In der Datei <code class="ic">public/api/iletisim/gonder.php</code>:
903
+ <pre><code><span class="kw">&lt;?php</span>
904
+ <span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>];
905
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>] . <span class="st">'/../app/ApiControl.php'</span>;
906
+ <span class="kw">use</span> Bin\SystemMethod;
907
+
908
+ <span class="cm">// 1. CSRF-Validierung</span>
909
+ <span class="kw">if</span> (!<span class="fn">verifyCsrf</span>(<span class="var">$_POST</span>[<span class="st">'csrf_token'</span>] ?? <span class="st">''</span>)) {
910
+ <span class="fn">jsonResponse</span>([<span class="st">'error'</span> =&gt; <span class="st">'Ungültiges Token.'</span>], <span class="nu">403</span>);
911
+ }
912
+
913
+ <span class="cm">// 2. Daten bereinigen</span>
914
+ <span class="var">$ad</span> = <span class="fn">sanitizeString</span>(<span class="var">$_POST</span>[<span class="st">'ad'</span>] ?? <span class="st">''</span>);
915
+ <span class="var">$eposta</span> = <span class="fn">sanitizeEmail</span>(<span class="var">$_POST</span>[<span class="st">'eposta'</span>] ?? <span class="st">''</span>);
916
+ <span class="var">$mesaj</span> = <span class="fn">sanitizeString</span>(<span class="var">$_POST</span>[<span class="st">'mesaj'</span>] ?? <span class="st">''</span>);
917
+
918
+ <span class="cm">// 3. Geschäftslogik (E-Mail senden, in DB speichern usw.)</span>
919
+ <span class="cm">// ...</span>
920
+
921
+ <span class="cm">// 4. Antwort geben</span>
922
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>, <span class="st">'message'</span> =&gt; <span class="st">'Ihre Nachricht wurde empfangen.'</span>], <span class="nu">200</span>);</code></pre>
923
+ </div>
924
+ </li>
925
+ </ol>
926
+
927
+ <div class="alert a-success">
928
+ <strong>✅ Abgeschlossen!</strong> Sie haben einen vollkommen sicheren und standardkonformen Formularablauf mit XSS-Schutz, CSRF-Validierung, HTTP-Methodeneinschränkung und data-js-Architektur erstellt.
929
+ </div>
930
+ </section>
931
+
932
+ </main>
933
+
934
+ <script>
935
+ const sections = document.querySelectorAll('section[id], header[id]');
936
+ const links = document.querySelectorAll('.sidebar nav a');
937
+
938
+ const observer = new IntersectionObserver((entries) => {
939
+ entries.forEach(entry => {
940
+ if (entry.isIntersecting) {
941
+ links.forEach(l => l.classList.remove('active'));
942
+ const active = document.querySelector(`.sidebar nav a[href="#${entry.target.id}"]`);
943
+ if (active) active.classList.add('active');
944
+ }
945
+ });
946
+ }, { rootMargin: '-20% 0px -70% 0px' });
947
+
948
+ sections.forEach(s => observer.observe(s));
949
+ </script>
950
+ </body>
951
+ </html>