@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,850 @@
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ArtiFrame | Documentation Officielle pour Développeurs</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>Documentation pour Développeurs</span>
225
+ </div>
226
+ <nav>
227
+ <div class="sb-cat">Commencer</div>
228
+ <a href="#giris">Introduction & Philosophie</a>
229
+ <a href="#kurulum">Installation</a>
230
+ <a href="#dizin">Structure du Répertoire</a>
231
+
232
+ <div class="sb-cat">Architecture</div>
233
+ <a href="#bootstrapper">Architecture Bootstrapper</a>
234
+ <a href="#kurallar">Ensemble de Règles</a>
235
+
236
+ <div class="sb-cat">Outil CLI</div>
237
+ <a href="#cli-giris">Introduction à la 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">Fonctions de Date</a>
248
+ <a href="#vh-format">Formatage</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 & Requête</a>
256
+ <a href="#sh-security">Sécurité</a>
257
+
258
+ <div class="sb-cat">Sécurité de l'API</div>
259
+ <a href="#api-methods">Contrôle de Méthode HTTP</a>
260
+ <a href="#api-cors">CORS</a>
261
+ <a href="#api-rate">Limitation de Débit</a>
262
+
263
+ <div class="sb-cat">Pratique</div>
264
+ <a href="#workflow">Flux de Travail Complet</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>Documentation</span></h1>
274
+ <p class="lead">Le framework pour des projets PHP natifs évolutifs, géré avec zéro dépendance externe, des règles strictes et une interface de ligne de commande puissante (CLI). Sans dépendances, libéré de la soupe de paquets Composer, un écosystème léger et rapide qui vous permet de vous concentrer sur votre logique métier.</p>
275
+ </header>
276
+
277
+ <!-- ====== GİRİŞ & FELSEFE ====== -->
278
+ <section id="giris">
279
+ <h2>Introduction & Philosophie</h2>
280
+ <p>ArtiFrame est basé sur le principe de <strong>"Convention over Configuration"</strong> (Convention plutôt que Configuration). La force d'un framework ne vient pas de la richesse des outils qu'il propose, mais de la cohérence de l'ordre qu'il établit.</p>
281
+
282
+ <div class="grid-2">
283
+ <div class="card highlight">
284
+ <h3>Zéro Surcharge (Zero Overhead)</h3>
285
+ <p>Zéro dépendance aux paquets Composer, au cœur du framework ou aux bibliothèques tierces. Chaque ligne de code est la vôtre ; sans gonflement, sans couches d'abstraction inutiles.</p>
286
+ </div>
287
+ <div class="card highlight">
288
+ <h3>La Sécurité d'Abord</h3>
289
+ <p>La protection XSS, la validation CSRF, l'assainissement contre l'injection SQL et le contrôle de la méthode HTTP sont intégrés par défaut. La sécurité n'est pas une option, c'est un standard.</p>
290
+ </div>
291
+ <div class="card highlight">
292
+ <h3>Ensemble de Règles Strict</h3>
293
+ <p>Un développeur junior nouvellement intégré au projet comprend l'architecture <code class="ic">data-js</code> et la structure des répertoires en quelques minutes. La cohésion de l'équipe est garantie au niveau du framework.</p>
294
+ </div>
295
+ <div class="card highlight">
296
+ <h3>Priorité à la CLI</h3>
297
+ <p>Pas de création manuelle de fichiers view, API ou class. La CLI génère à partir de fichiers stub, établit des liens d'assets et maintient le projet cohérent.</p>
298
+ </div>
299
+ </div>
300
+
301
+ <div class="alert a-info">
302
+ <strong>ℹ️ Licence AGPLv3 :</strong> ArtiFrame est open source. Les travaux dérivés que vous produisez peuvent être utilisés librement, à condition que le code source reste ouvert. L'avis de droit d'auteur ne peut être supprimé.
303
+ </div>
304
+ </section>
305
+
306
+ <!-- ====== KURULUM ====== -->
307
+ <section id="kurulum">
308
+ <h2>Installation</h2>
309
+ <p>ArtiFrame CLI est installé en tant qu'outil PHP global. Installé une fois, il est utilisé dans chaque projet.</p>
310
+
311
+ <h3>1. Installer l'Outil CLI Globalement</h3>
312
+ <pre><code><span class="cm"># Installation globale via NPM</span>
313
+ npm install -g @artilingo/artiframe-cli
314
+
315
+ <span class="cm"># Vérifier l'installation</span>
316
+ artiframe</code></pre>
317
+
318
+ <h3>2. Shell Interactif</h3>
319
+ <p>Tapez simplement <code class="ic">artiframe</code> dans le terminal et appuyez sur Entrée. La CLI ne se ferme pas ; un shell interactif s'ouvre, à l'écoute des commandes en continu :</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. Démarrer un Nouveau Projet</h3>
328
+ <pre><code>artiframe&gt; new mon-projet</code></pre>
329
+ <p>Cette commande crée le répertoire <code class="ic">mon-projet/</code> et copie toute la structure squelette à l'intérieur : <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> et la première page <code class="ic">index.php</code>.</p>
330
+
331
+ <h3>4. Paramètres d'Environnement</h3>
332
+ <pre><code>cp .env.example .env</code></pre>
333
+ <p>Ouvrez votre fichier <code class="ic">.env</code> et remplissez les informations de base de données et d'application. Ce fichier n'entre jamais dans le contrôle de version.</p>
334
+
335
+ <div class="alert a-warn">
336
+ <strong>⚠️ Paramètre du Serveur Web :</strong> Dirigez le document root d'Apache/Nginx vers le dossier <code class="ic">/public/</code>. Les autres répertoires ne doivent jamais être exposés à l'extérieur.
337
+ </div>
338
+ </section>
339
+
340
+ <!-- ====== DİZİN YAPISI ====== -->
341
+ <section id="dizin">
342
+ <h2>Structure du Répertoire</h2>
343
+ <p>L'architecture créée au démarrage du projet ; assure une séparation claire des responsabilités (SoC).</p>
344
+
345
+ <div class="tree">
346
+ <span class="dir">nom-du-projet/</span>
347
+ ├── <span class="dir">app/</span> <span class="note"># Couche d'infrastructure</span>
348
+ │ ├── <span class="file">ViewControl.php</span> <span class="note"># Bootstrapper pour les vues (pages HTML)</span>
349
+ │ ├── <span class="file">ApiControl.php</span> <span class="note"># Bootstrapper pour les endpoints API</span>
350
+ │ ├── <span class="file">Database.php</span> <span class="note"># Connexion à la base de données basée sur PDO</span>
351
+ │ ├── <span class="file">DotEnv.php</span> <span class="note"># Lecteur de .env</span>
352
+ │ └── <span class="file">R2Manager.php</span> <span class="note"># Gestionnaire de fichiers Cloudflare R2</span>
353
+
354
+ ├── <span class="dir warn">bin/</span> <span class="note"># ⚠️ Système de base — ne pas modifier directement</span>
355
+ │ ├── <span class="file">SystemMethod.php</span> <span class="note"># Utilitaires globaux API/Backend</span>
356
+ │ ├── <span class="file">ViewMethod.php</span> <span class="note"># Utilitaires globaux View/Frontend</span>
357
+ │ └── <span class="dir">stubs/</span> <span class="note"># Modèles utilisés par la 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"># Fichiers de configuration</span>
364
+ │ └── <span class="file">app-version.php</span> <span class="note"># Constantes APP_VERSION et APP_ENV</span>
365
+
366
+ ├── <span class="dir">public/</span> <span class="note"># ← Le seul répertoire ouvert du serveur web</span>
367
+ │ ├── <span class="dir safe">assets/</span>
368
+ │ │ ├── <span class="dir">css/</span> <span class="note"># Fichiers CSS spécifiques aux vues</span>
369
+ │ │ └── <span class="dir">js/</span> <span class="note"># Fichiers JS spécifiques aux vues</span>
370
+ │ ├── <span class="dir">includes/</span> <span class="note"># Composants partagés</span>
371
+ │ │ ├── head.php
372
+ │ │ ├── header.php
373
+ │ │ └── footer.php
374
+ │ ├── <span class="dir">api/</span> <span class="note"># Fichiers d'endpoints API</span>
375
+ │ └── <span class="file">index.php</span> <span class="note"># Page d'accueil</span>
376
+
377
+ ├── <span class="dir">src/</span> <span class="note"># Logique métier et classes</span>
378
+ ├── <span class="file">.env</span> <span class="note"># Variables d'environnement (non suivies par git)</span>
379
+ ├── <span class="file">.env.example</span> <span class="note"># Modèle — suivi par git</span>
380
+ └── <span class="file">guide_fr.html</span> <span class="note"># Ce document</span>
381
+ </div>
382
+
383
+ <div class="alert a-danger">
384
+ <strong>🚫 Ne touchez pas au répertoire bin/ :</strong> Les fichiers dans <code class="ic">bin/</code> constituent le cœur du framework. La logique métier spécifique au projet n'est pas ajoutée ici. Les classes et les services que vous ajouterez se trouvent sous <code class="ic">src/</code>, et les composants d'infrastructure se trouvent sous <code class="ic">app/</code>.
385
+ </div>
386
+ </section>
387
+
388
+ <!-- ====== BOOTSTRAPPER ====== -->
389
+ <section id="bootstrapper">
390
+ <h2>Architecture Bootstrapper <span class="tag b-core">Critique</span></h2>
391
+ <p>ArtiFrame utilise deux bootstrappers complètement indépendants. Cette architecture empêche fondamentalement les <strong>problèmes d'en-tête HTML et les failles de sécurité</strong>.</p>
392
+
393
+ <div class="grid-2">
394
+ <div class="card highlight">
395
+ <h3><span class="badge b-view">Vue</span> ViewControl.php</h3>
396
+ <p>Utilisé pour les pages HTML (fichiers de vue). Démarre la session, charge <code class="ic">ViewMethod</code>.</p>
397
+ <pre><code><span class="cm">// public/profil.php — TOUT EN HAUT, avant d'imprimer le HTML</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>Utilisé pour les fichiers d'endpoints API. Définit l'en-tête JSON, effectue le contrôle de la méthode HTTP, charge <code class="ic">SystemMethod</code>.</p>
409
+ <pre><code><span class="cm">// public/api/utilisateur/obtenir.php</span>
410
+ <span class="kw">&lt;?php</span>
411
+ <span class="cm">// $allowedMethods DOIT être défini avant require</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 et ApiControl ne sont jamais mélangés :</strong> Si ViewControl est requis dans un fichier API, il peut renvoyer un en-tête HTML au lieu d'un en-tête JSON, et toute la réponse de l'API sera corrompue. Si ApiControl est requis dans une page HTML, la session ne démarre pas et la page se casse.
423
+ </div>
424
+ </section>
425
+
426
+ <!-- ====== KURALLAR ====== -->
427
+ <section id="kurallar">
428
+ <h2>Ensemble de Règles <span class="tag b-core">Standard</span></h2>
429
+
430
+ <div class="card">
431
+ <h3>Règle 1 : Architecture data-js <span class="badge b-sec">Critique</span></h3>
432
+ <p>Les événements JavaScript ne peuvent jamais être écoutés via une <code class="ic">class</code> ou un <code class="ic">id</code>. Ce sont des identités visuelles/de style. Toutes les interactions JS sont gérées par l'attribut <code class="ic">data-js</code>. Si CSS supprime une classe, JavaScript ne plante jamais.</p>
433
+ <pre><code><span class="cm">&lt;!-- ❌ Anti-Pattern — non pris en charge --&gt;</span>
434
+ &lt;button id="submitBtn" class="btn"&gt;Envoyer&lt;/button&gt;
435
+ <span class="cm">// JS: document.getElementById('submitBtn').addEventListener(...)</span>
436
+
437
+ <span class="cm">&lt;!-- ✅ Standard ArtiFrame --&gt;</span>
438
+ &lt;button class="btn btn-primary" <span class="var">data-js</span>="login-submit"&gt;Envoyer&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>Règle 2 : Architecture de Thème</h3>
444
+ <p>Les modes Sombre/Clair et les thèmes sont gérés via les attributs <code class="ic">data-theme</code> et <code class="ic">data-mode</code> de la balise <code class="ic">&lt;html&gt;</code>. Les classes de corps ne sont pas utilisées.</p>
445
+ <pre><code><span class="cm">&lt;!-- Balise d'ouverture HTML provenant du modèle view.stub --&gt;</span>
446
+ &lt;html lang="fr" data-theme="default" data-mode="light"&gt;
447
+
448
+ <span class="cm">/* Définition du thème dans 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>Règle 3 : Flux de Données Sécurisé</h3>
461
+ <p>Toutes les données provenant de la base de données sont enveloppées avec <code class="ic">display()</code> avant d'être imprimées sur le DOM. Chaque donnée entrant dans l'API est nettoyée avec <code class="ic">sanitizeString()</code> ou <code class="ic">sanitizeInt()</code> avant d'être traitée.</p>
462
+ </div>
463
+
464
+ <div class="card">
465
+ <h3>Règle 4 : Gestion des Erreurs avec APP_ENV</h3>
466
+ <p>La valeur <code class="ic">APP_ENV</code> dans le fichier <code class="ic">.env</code> détermine la visibilité des erreurs. En production, aucun message d'erreur n'est affiché à l'utilisateur.</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>Écosystème CLI</h2>
482
+ <p>ArtiFrame CLI ouvre un shell interactif lorsque vous tapez <code class="ic">artiframe</code> dans le terminal. Toutes les commandes s'exécutent dans ce shell. Les commandes peuvent également être exécutées en une seule fois.</p>
483
+ <pre><code><span class="cm"># Mode interactif (recommandé)</span>
484
+ artiframe
485
+ artiframe&gt; make:view admin/utilisateurs.php
486
+
487
+ <span class="cm"># Mode à usage unique</span>
488
+ artiframe make:view admin/utilisateurs.php</code></pre>
489
+ </section>
490
+ <!-- new -->
491
+ <section id="cli-new">
492
+ <h2><code>new</code> <span class="tag b-cli">Commande CLI</span></h2>
493
+ <p>Crée un nouveau projet ArtiFrame. Génère le squelette complet du répertoire, les fichiers bootstrapper et la première page d'index.</p>
494
+ <pre><code>artiframe&gt; <span class="fn">new</span> <span class="st">nom-du-projet</span></code></pre>
495
+ <p>Structure créée :</p>
496
+ <div class="tree" style="font-size:0.82rem; line-height:1.7">
497
+ nom-du-projet/
498
+ ├── app/ (ViewControl.php, ApiControl.php, Database.php, DotEnv.php)
499
+ ├── bin/ (SystemMethod.php, ViewMethod.php, stubs/)
500
+ ├── config/ (app-version.php)
501
+ ├── public/ (index.php, assets/, includes/, api/)
502
+ ├── src/
503
+ ├── .env.example
504
+ └── guide_fr.html
505
+ </div>
506
+ </section>
507
+
508
+ <!-- make:view -->
509
+ <section id="cli-view">
510
+ <h2><code>make:view</code> <span class="tag b-cli">Commande CLI</span></h2>
511
+ <p>Crée un nouveau fichier de page (view) et ses ressources CSS/JS dédiées. Les ressources sont automatiquement liées à la page avec une invalidation du cache (cache-busting) via <code class="ic">?v=APP_VERSION</code>.</p>
512
+ <pre><code>artiframe&gt; <span class="fn">make:view</span> <span class="st">admin/utilisateurs.php</span></code></pre>
513
+ <p>Fichiers créés :</p>
514
+ <pre><code><span class="cm">✔ public/admin/utilisateurs.php</span>
515
+ <span class="cm">✔ public/assets/css/admin/utilisateurs.css</span>
516
+ <span class="cm">✔ public/assets/js/admin/utilisateurs.js</span></code></pre>
517
+ <p>Le fichier view généré inclut déjà `ViewControl` au début, comprend les inclusions de head/header/footer, et les liens CSS/JS sont connectés avec le système de cache-busting.</p>
518
+ </section>
519
+
520
+ <!-- make:api -->
521
+ <section id="cli-api">
522
+ <h2><code>make:api</code> <span class="tag b-cli">Commande CLI</span></h2>
523
+ <p>Crée un fichier endpoint API en sélectionnant l'un des deux modèles disponibles. Dans chaque nouveau fichier API, la variable <code class="ic">$allowedMethods</code> et l'inclusion de <code class="ic">ApiControl.php</code> sont déjà préparées.</p>
524
+
525
+ <div class="card">
526
+ <h3><span class="badge b-api">standart</span> — API à Action Unique</h3>
527
+ <p>Pour les endpoints qui effectuent une seule tâche (connexion, envoi, suppression). La logique métier y est écrite directement.</p>
528
+ <pre><code>artiframe&gt; <span class="fn">make:api</span> <span class="st">standart</span> <span class="st">api/auth/connexion.php</span></code></pre>
529
+ <pre><code><span class="kw">&lt;?php</span>
530
+ <span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>]; <span class="cm">// Accepte uniquement POST</span>
531
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>] . <span class="st">'/../app/ApiControl.php'</span>;
532
+
533
+ <span class="kw">use</span> Bin\SystemMethod;
534
+
535
+ <span class="cm">// Logique métier ici...</span>
536
+ <span class="fn">jsonResponse</span>([<span class="st">'status'</span> =&gt; <span class="st">'success'</span>], <span class="nu">200</span>);</code></pre>
537
+ </div>
538
+
539
+ <div class="card">
540
+ <h3><span class="badge b-api">switch-case</span> — API Multi-Actions</h3>
541
+ <p>Une structure qui gère les opérations CRUD pour un module dans un seul endpoint. L'opération à effectuer est déterminée par le paramètre <code class="ic">action</code>.</p>
542
+ <pre><code>artiframe&gt; <span class="fn">make:api</span> <span class="st">switch-case</span> <span class="st">api/utilisateur/gerer.php</span></code></pre>
543
+ <pre><code><span class="kw">&lt;?php</span>
544
+ <span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>];
545
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>] . <span class="st">'/../app/ApiControl.php'</span>;
546
+
547
+ <span class="kw">use</span> Bin\SystemMethod;
548
+
549
+ <span class="var">$action</span> = <span class="fn">sanitizeString</span>(<span class="var">$_POST</span>[<span class="st">'action'</span>] ?? <span class="st">''</span>);
550
+
551
+ <span class="kw">switch</span> (<span class="var">$action</span>) {
552
+ <span class="kw">case</span> <span class="st">'create'</span>:
553
+ <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">'Créé.'</span>], <span class="nu">200</span>);
554
+ <span class="kw">break</span>;
555
+ <span class="kw">case</span> <span class="st">'update'</span>:
556
+ <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">'Mis à jour.'</span>], <span class="nu">200</span>);
557
+ <span class="kw">break</span>;
558
+ <span class="kw">case</span> <span class="st">'delete'</span>:
559
+ <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">'Supprimé.'</span>], <span class="nu">200</span>);
560
+ <span class="kw">break</span>;
561
+ <span class="kw">default</span>:
562
+ <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">'Action non valide.'</span>], <span class="nu">400</span>);
563
+ }</code></pre>
564
+ </div>
565
+ </section>
566
+
567
+ <!-- make:class -->
568
+ <section id="cli-class">
569
+ <h2><code>make:class</code> <span class="tag b-cli">Commande CLI</span></h2>
570
+ <p>Crée un nouveau fichier de classe PHP avec son espace de noms (namespace) et son modèle de classe déjà préparés.</p>
571
+ <pre><code>artiframe&gt; <span class="fn">make:class</span> <span class="st">classes/EmailService.php</span></code></pre>
572
+ </section>
573
+
574
+ <!-- version -->
575
+ <section id="cli-version">
576
+ <h2><code>version</code> <span class="tag b-cli">Commande CLI</span></h2>
577
+ <p>Met à jour le numéro de version dans <code class="ic">config/app-version.php</code> selon les règles du versionnage sémantique (SemVer). Format de version : <strong>MAJOR.MINOR.PATCH</strong></p>
578
+
579
+ <table class="sig-table">
580
+ <thead>
581
+ <tr><th>Commande</th><th>Description</th><th>Exemple</th></tr>
582
+ </thead>
583
+ <tbody>
584
+ <tr><td>version upgrade patch</td><td>Correction de bogue, amélioration mineure</td><td>1.2.3 → 1.2.4</td></tr>
585
+ <tr><td>version upgrade minor</td><td>Nouvelle fonctionnalité rétrocompatible</td><td>1.2.3 → 1.3.0</td></tr>
586
+ <tr><td>version upgrade major</td><td>Changement majeur (rupture de compatibilité)</td><td>1.2.3 → 2.0.0</td></tr>
587
+ <tr><td>version downgrade patch</td><td>Annuler le dernier patch</td><td>1.2.4 → 1.2.3</td></tr>
588
+ <tr><td>version downgrade minor</td><td>Annuler la dernière version mineure</td><td>1.3.0 → 1.2.0</td></tr>
589
+ <tr><td>version downgrade major</td><td>Annuler la dernière version majeure</td><td>2.0.0 → 1.0.0</td></tr>
590
+ </tbody>
591
+ </table>
592
+
593
+ <pre><code>artiframe&gt; <span class="fn">version</span> upgrade minor
594
+ <span class="cm">✔ La version a été mise à jour de 1.2.0 → 1.3.0.</span></code></pre>
595
+ </section>
596
+
597
+ <!-- ====== VIEW HELPERS ====== -->
598
+ <section id="vh-display">
599
+ <h2>View Helpers — <code>display()</code> <span class="tag b-view">ViewMethod</span></h2>
600
+ <p>Applique une <strong>protection XSS obligatoire</strong> lors de l'impression de données provenant de la base de données ou de la saisie utilisateur sur le DOM. Au lieu de lancer une erreur sur des valeurs nulles ou vides, elle renvoie une valeur par défaut.</p>
601
+ <span class="ret">string display(mixed $data, string $default = '')</span>
602
+ <div class="alert a-info" style="margin-top: 10px; margin-bottom: 24px; padding: 12px 20px;">
603
+ <strong>💡 Astuce : Utilisation de <code>$default</code></strong><br>
604
+ Le deuxième paramètre (<code>$default</code>) est le texte de secours à afficher lorsque les données provenant de la base de données sont vides (null, false, chaîne vide). Par exemple, si vous utilisez <code>display($nom, 'Anonyme')</code> pour un utilisateur dont le nom n'a pas été saisi, votre page présentera un contenu significatif au lieu de vilains espaces vides.
605
+ </div>
606
+ <pre><code><span class="cm">&lt;!-- ❌ Non sécurisé — crée une vulnérabilité XSS --&gt;</span>
607
+ &lt;h1&gt;&lt;?= <span class="var">$user</span>[<span class="st">'nom'</span>] ?&gt;&lt;/h1&gt;
608
+
609
+ <span class="cm">&lt;!-- ✅ Utilisation sécurisée d'ArtiFrame --&gt;</span>
610
+ &lt;h1&gt;&lt;?= <span class="fn">display</span>(<span class="var">$user</span>[<span class="st">'nom'</span>], <span class="st">'Utilisateur Anonyme'</span>) ?&gt;&lt;/h1&gt;</code></pre>
611
+ </section>
612
+
613
+ <section id="vh-escapeurl">
614
+ <h2>View Helpers — <code>escapeUrl()</code> <span class="tag b-view">ViewMethod</span></h2>
615
+ <p>Utilisé lors de l'impression des liens reçus des utilisateurs (par exemple, les sites Web de profil) dans des balises <code>&lt;a href="..."&gt;</code> ou <code>&lt;img src="..."&gt;</code>. Il neutralise les charges utiles dangereuses comme <code>javascript:alert(1)</code> (XSS stocké), empêchant l'exécution de code via le lien.</p>
616
+ <span class="ret">string escapeUrl(string $url)</span>
617
+ <pre><code><span class="cm">&lt;!-- ❌ Non sécurisé — le code JS peut fuiter via l'URL --&gt;</span>
618
+ &lt;a href="&lt;?= <span class="var">$user</span>[<span class="st">'website'</span>] ?&gt;"&gt;Visiter le Site&lt;/a&gt;
619
+
620
+ <span class="cm">&lt;!-- ✅ Utilisation sécurisée d'ArtiFrame --&gt;</span>
621
+ &lt;a href="&lt;?= <span class="fn">escapeUrl</span>(<span class="var">$user</span>[<span class="st">'website'</span>]) ?&gt;"&gt;Visiter le Site&lt;/a&gt;</code></pre>
622
+ </section>
623
+
624
+ <section id="vh-csrf">
625
+ <h2>View Helpers — <code>csrfField()</code> <span class="tag b-view">ViewMethod</span></h2>
626
+ <p>Ajoute un champ de jeton caché aux formulaires HTML pour les protéger contre les attaques CSRF. <strong>Son utilisation est obligatoire sur chaque page contenant un formulaire POST.</strong></p>
627
+ <span class="ret">string csrfField()</span>
628
+ <pre><code>&lt;form action="/api/sauvegarder.php" method="POST"&gt;
629
+ &lt;?= <span class="fn">csrfField</span>() ?&gt; <span class="cm">&lt;!-- Obligatoire pour la sécurité --&gt;</span>
630
+ &lt;input type="text" name="nom"&gt;
631
+ &lt;button type="submit" data-js="save-btn"&gt;Sauvegarder&lt;/button&gt;
632
+ &lt;/form&gt;</code></pre>
633
+ </section>
634
+
635
+ <section id="vh-dates">
636
+ <h2>View Helpers — Fonctions de Date <span class="tag b-view">ViewMethod</span></h2>
637
+ <p>Traite les chaînes de date au format <code class="ic">Y-m-d H:i:s</code> provenant de la base de données ou les valeurs timestamp UNIX. Toutes les sorties textuelles sont localisées selon la langue (<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>).</p>
638
+
639
+ <table class="sig-table">
640
+ <thead><tr><th>Fonction</th><th>Exemple de Sortie</th><th>Description</th></tr></thead>
641
+ <tbody>
642
+ <tr><td>day($date)</td><td>24</td><td>Jour uniquement</td></tr>
643
+ <tr><td>month($date)</td><td>07</td><td>Mois (chiffre) uniquement</td></tr>
644
+ <tr><td>year($date)</td><td>2026</td><td>Année</td></tr>
645
+ <tr><td>timeOnly($date)</td><td>14:30</td><td>Heure:Minute</td></tr>
646
+ <tr><td>fulldate($date)</td><td>24.07.2026</td><td>Date complète</td></tr>
647
+ <tr><td>formatDate($date, $format)</td><td>24.07.2026 14:30</td><td>Format personnalisé</td></tr>
648
+ <tr><td>monthName($date, $lang)</td><td>Juillet / July / Juli</td><td>Nom du mois (selon la langue)</td></tr>
649
+ <tr><td>fulldateName($date, $lang)</td><td>24 Juillet 2026 / July 24, 2026</td><td>Date complète (avec nom du mois, selon la langue)</td></tr>
650
+ <tr><td>timeAgo($date, $lang)</td><td>il y a 5 minutes / 5 minutes ago</td><td>Style réseaux sociaux (selon la langue)</td></tr>
651
+ </tbody>
652
+ </table>
653
+
654
+ <div class="alert a-info" style="margin-top: 0; margin-bottom: 24px;">
655
+ <strong>💡 Comprendre les Paramètres : <code>$format</code> et <code>$lang</code></strong>
656
+ <ul style="margin-top: 10px; margin-bottom: 0;">
657
+ <li style="margin-bottom: 8px;"><strong><code>$format</code> (Format) :</strong> Utilisé uniquement avec la fonction <code>formatDate()</code>. Accepte les lettres de date standards de PHP. (Par exemple : <code>'d/m/Y'</code> ➔ 24/07/2026, ou <code>'H:i'</code> ➔ 15:30). Vous permet de créer votre propre modèle de date lorsque les fonctions prêtes à l'emploi (day, year, etc.) sont insuffisantes.</li>
658
+ <li style="margin-bottom: 0;"><strong><code>$lang</code> (Langue) :</strong> Utilisé dans les fonctions dont la sortie contient du texte (nom du mois, mot "il y a"). Si vous laissez ce paramètre vide, le système fonctionne <strong>par défaut en <code>'en'</code> (Anglais)</strong>. Si vous créez un projet multilingue, il vous suffit de saisir le code de langue (<code>tr, en, de, fr, es</code>) dans le deuxième paramètre. (Ex : <code>timeAgo($date, 'fr')</code> ➔ il y a 5 minutes)</li>
659
+ </ul>
660
+ </div>
661
+
662
+ <pre><code><span class="cm">&lt;!-- Supposons que nous ayons un timestamp UNIX obtenu via time() --&gt;</span>
663
+ <span class="kw">&lt;?php</span> <span class="var">$date</span> = <span class="fn">time</span>(); <span class="kw">?&gt;</span>
664
+
665
+ <span class="cm">&lt;!-- Date complète selon la langue --&gt;</span>
666
+ &lt;span&gt;&lt;?= <span class="fn">fulldateName</span>(<span class="var">$date</span>, <span class="st">'fr'</span>) ?&gt;&lt;/span&gt;
667
+ <span class="cm">&lt;!-- Sortie : 24 Juillet 2026 --&gt;</span>
668
+
669
+ <span class="cm">&lt;!-- Affichage de l'heure style réseaux sociaux --&gt;</span>
670
+ &lt;span&gt;&lt;?= <span class="fn">timeAgo</span>(<span class="var">$date</span>, <span class="st">'en'</span>) ?&gt;&lt;/span&gt;
671
+ <span class="cm">&lt;!-- Sortie : 5 minutes ago --&gt;</span>
672
+
673
+ &lt;span&gt;&lt;?= <span class="fn">timeAgo</span>(<span class="var">$date</span>, <span class="st">'fr'</span>) ?&gt;&lt;/span&gt;
674
+ <span class="cm">&lt;!-- Sortie : il y a 5 minutes --&gt;</span></code></pre>
675
+ </section>
676
+
677
+ <section id="vh-format">
678
+ <h2>View Helpers — Formatage de Texte <span class="tag b-view">ViewMethod</span></h2>
679
+
680
+ <div class="card">
681
+ <h3><code>truncate($text, $length, $append)</code></h3>
682
+ <span class="ret">string truncate(string $text, int $length = 100, string $append = '...')</span>
683
+ <p>Tronque les textes longs (comme les résumés de blog) à la limite de caractères souhaitée sans couper le dernier mot et ajoute le suffixe spécifié à la fin.</p>
684
+ <pre><code>&lt;p&gt;&lt;?= <span class="fn">truncate</span>(<span class="var">$article</span>[<span class="st">'contenu'</span>], <span class="nu">160</span>, <span class="st">'...'</span>) ?&gt;&lt;/p&gt;</code></pre>
685
+ </div>
686
+ </section>
687
+
688
+ <section id="vh-money">
689
+ <h2>View Helpers — <code>money()</code> <span class="tag b-view">ViewMethod</span></h2>
690
+ <p>Formate le montant avec le symbole de la devise. Le code de la devise (ISO) détermine automatiquement l'emplacement du symbole. La devise par défaut est <code class="ic">usd</code>.</p>
691
+ <span class="ret">string money(float $amount, string $currency = 'usd')</span>
692
+
693
+ <table class="sig-table">
694
+ <thead><tr><th>Code</th><th>Sortie</th><th>Devise</th></tr></thead>
695
+ <tbody>
696
+ <tr><td>usd</td><td>$1.250,00</td><td>Dollar Américain</td></tr>
697
+ <tr><td>eur</td><td>1.250,00 €</td><td>Euro</td></tr>
698
+ <tr><td>try / tl</td><td>1.250,00 ₺</td><td>Livre Turque</td></tr>
699
+ <tr><td>gbp</td><td>£1.250,00</td><td>Livre Sterling</td></tr>
700
+ <tr><td>jpy</td><td>1.250,00 ¥</td><td>Yen Japonais</td></tr>
701
+ <tr><td>inr</td><td>1.250,00 ₹</td><td>Roupie Indienne</td></tr>
702
+ <tr><td>rub</td><td>1.250,00 ₽</td><td>Rouble Russe</td></tr>
703
+ <tr><td>krw</td><td>1.250,00 ₩</td><td>Won Sud-Coréen</td></tr>
704
+ <tr><td>brl</td><td>R$1.250,00</td><td>Réal Brésilien</td></tr>
705
+ <tr><td>aed</td><td>1.250,00 د.إ</td><td>Dirham des EAU</td></tr>
706
+ </tbody>
707
+ </table>
708
+
709
+ <pre><code>&lt;span&gt;&lt;?= <span class="fn">money</span>(<span class="var">$produit</span>[<span class="st">'prix'</span>], <span class="st">'eur'</span>) ?&gt;&lt;/span&gt;
710
+ <span class="cm">&lt;!-- Sortie : 1.250,00 € --&gt;</span>
711
+
712
+ &lt;span&gt;&lt;?= <span class="fn">money</span>(<span class="var">$produit</span>[<span class="st">'prix'</span>], <span class="st">'usd'</span>) ?&gt;&lt;/span&gt;
713
+ <span class="cm">&lt;!-- Sortie : $1.250,00 --&gt;</span></code></pre>
714
+ </section>
715
+
716
+ <!-- ====== SYSTEM HELPERS ====== -->
717
+ <section id="sh-json">
718
+ <h2>System Helpers — <code>jsonResponse()</code> <span class="tag b-api">SystemMethod</span></h2>
719
+ <p>Définit l'en-tête JSON, fournit le code d'état HTTP et imprime la sortie de manière sécurisée avec json_encode, puis termine le script. <strong>Toutes les réponses de l'API doivent être données via cette fonction.</strong></p>
720
+ <span class="ret">void jsonResponse(array $data, int $statusCode = 200)</span>
721
+ <pre><code><span class="cm">// Réponse réussie</span>
722
+ <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">$utilisateur</span>], <span class="nu">200</span>);
723
+
724
+ <span class="cm">// Réponse d'erreur</span>
725
+ <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">'Accès non autorisé.'</span>], <span class="nu">401</span>);</code></pre>
726
+ </section>
727
+
728
+ <section id="sh-csrf">
729
+ <h2>System Helpers — <code>verifyCsrf()</code> <span class="tag b-api">SystemMethod</span></h2>
730
+ <p>Vérifie que la requête POST entrante provient d'un formulaire légitime. Renvoie false si <code class="ic">csrfField()</code> n'a pas été ajouté au formulaire ou si le jeton n'est pas valide.</p>
731
+ <span class="ret">bool verifyCsrf(string $token)</span>
732
+ <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>)) {
733
+ <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">'Jeton CSRF invalide.'</span>], <span class="nu">403</span>);
734
+ }</code></pre>
735
+ </section>
736
+
737
+ <section id="sh-sanitize">
738
+ <h2>System Helpers — Fonctions Sanitize (Nettoyage) <span class="tag b-api">SystemMethod</span></h2>
739
+
740
+ <table class="sig-table">
741
+ <thead><tr><th>Fonction</th><th>Description</th></tr></thead>
742
+ <tbody>
743
+ <tr><td>sanitizeInt($value)</td><td>Supprime toutes les lettres, symboles et virgules, en ne laissant <strong>que les chiffres entiers</strong>. Utilisé pour les ID ou les limites.</td></tr>
744
+ <tr><td>sanitizeFloat($value)</td><td>Nettoie tout sauf les nombres décimaux. Utilisé pour les montants monétaires ou les métriques.</td></tr>
745
+ <tr><td>sanitizeString($value)</td><td>Pour empêcher les attaques XSS et similaires, <strong>détruit toutes les balises HTML et PHP (<code>&lt;script&gt;</code>, <code>&lt;iframe&gt;</code>, etc.)</strong>. Laisse un texte brut sécurisé.</td></tr>
746
+ <tr><td>sanitizeEmail($email)</td><td>Filtre tous les caractères invalides et dangereux qui ne correspondent pas au format de l'e-mail.</td></tr>
747
+ </tbody>
748
+ </table>
749
+
750
+ <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>);
751
+ <span class="var">$nom</span> = <span class="fn">sanitizeString</span>(<span class="var">$_POST</span>[<span class="st">'nom'</span>] ?? <span class="st">''</span>);
752
+ <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>
753
+ </section>
754
+
755
+ <section id="sh-request">
756
+ <h2>System Helpers — HTTP & Requête <span class="tag b-api">SystemMethod</span></h2>
757
+
758
+ <div class="card">
759
+ <h3><code>getIP()</code></h3>
760
+ <span class="ret">string getIP()</span>
761
+ <p>Obtient la véritable adresse IP de l'utilisateur, même derrière Cloudflare ou un Proxy.</p>
762
+ </div>
763
+ <div class="card">
764
+ <h3><code>getMethod()</code></h3>
765
+ <span class="ret">string getMethod()</span>
766
+ <p>Renvoie la méthode de la requête HTTP active (GET, POST, PUT, DELETE).</p>
767
+ </div>
768
+ </section>
769
+
770
+ <section id="sh-security">
771
+ <h2>System Helpers — Sécurité <span class="tag b-api">SystemMethod</span></h2>
772
+
773
+ <div class="card">
774
+ <h3><code>generateToken($length)</code></h3>
775
+ <span class="ret">string generateToken(int $length = 32)</span>
776
+ <p>Crée une chaîne hexadécimale cryptographiquement sécurisée. Utilisé pour les réinitialisations de mot de passe ou les jetons d'accès API.</p>
777
+ </div>
778
+ </section>
779
+
780
+ <!-- ====== API GÜVENLİĞİ ====== -->
781
+ <section id="api-methods">
782
+ <h2>API — Contrôle de Méthode HTTP <span class="tag b-api">ApiControl</span></h2>
783
+ <p>Les fichiers d'API ArtiFrame n'autorisent aucune requête en dehors du tableau <code class="ic">$allowedMethods</code>. Si un pirate tente d'envoyer un POST à une API GET, la demande est bloquée avant même que le fichier ne soit lu, et une erreur HTTP 405 (Method Not Allowed) est renvoyée.</p>
784
+ <div class="alert a-success">
785
+ <strong>✅ Pratique Saine :</strong> Vous devez définir le tableau <code>$allowedMethods</code> tout en haut de votre page API (avant require).
786
+ </div>
787
+ </section>
788
+
789
+ <section id="api-cors">
790
+ <h2>API — Politique CORS <span class="tag b-api">ApiControl</span></h2>
791
+ <p>ArtiFrame est livré configuré par défaut pour n'accepter que les requêtes de la même origine (Same-Origin). Si vous écrivez une API pour une application mobile (ex: React Native) ou un frontend distant, vous pouvez facilement gérer cela avec la variable <code class="ic">$enableCors</code>.</p>
792
+ <pre><code><span class="kw">&lt;?php</span>
793
+ <span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>];
794
+ <span class="var">$enableCors</span> = <span class="nu">true</span>; <span class="cm">// Autorise l'accès de n'importe où</span>
795
+
796
+ <span class="fn">require_once</span> <span class="var">$_SERVER</span>[<span class="st">'DOCUMENT_ROOT'</span>] . <span class="st">'/../app/ApiControl.php'</span>;</code></pre>
797
+ </section>
798
+
799
+ <section id="api-rate">
800
+ <h2>API — Limitation de Débit (Rate Limiting)</h2>
801
+ <p>La protection contre les requêtes excessives n'est pas encore activée par défaut, mais une architecture qui limite l'adresse IP pour empêcher les requêtes par force brute ou DoS au niveau du contrôleur d'API est en cours de développement (à venir dans la version 1.1).</p>
802
+ </section>
803
+
804
+ <!-- ====== PRATİK ====== -->
805
+ <section id="workflow">
806
+ <h2>Flux de Travail Complet</h2>
807
+ <p>Voyons à quoi ressemble un flux de travail typique dans ArtiFrame. Supposons que nous voulions créer une page de connexion.</p>
808
+
809
+ <ul class="steps">
810
+ <li>
811
+ <div>
812
+ <strong>Créer la page (View)</strong>
813
+ <p>Ouvrez la CLI et tapez <code>make:view auth/connexion.php</code>. La page HTML, le fichier CSS et le fichier JS sont générés.</p>
814
+ </div>
815
+ </li>
816
+ <li>
817
+ <div>
818
+ <strong>Écrire l'interface</strong>
819
+ <p>Dans <code>public/auth/connexion.php</code>, créez votre balise form. Ajoutez l'action <code>action="/api/auth/login.php"</code>, <code>method="POST"</code>, et placez <code>&lt;?= csrfField() ?&gt;</code> à l'intérieur. Ajoutez la classe <code>data-js="login-btn"</code> à votre bouton d'envoi.</p>
820
+ </div>
821
+ </li>
822
+ <li>
823
+ <div>
824
+ <strong>Créer l'API</strong>
825
+ <p>Ouvrez la CLI et tapez <code>make:api standart api/auth/login.php</code>. Dans le fichier créé, assurez-vous que <code>$allowedMethods = ['POST'];</code> est défini.</p>
826
+ </div>
827
+ </li>
828
+ <li>
829
+ <div>
830
+ <strong>Gérer avec JavaScript (Optionnel)</strong>
831
+ <p>Dans <code>public/assets/js/auth/connexion.js</code>, écoutez le bouton en utilisant <code>[data-js="login-btn"]</code>. Effectuez la requête AJAX vers l'API. (Si vous n'utilisez pas JS, le formulaire fonctionnera quand même et redirigera classiquement via POST).</p>
832
+ </div>
833
+ </li>
834
+ <li>
835
+ <div>
836
+ <strong>Nettoyer et Répondre</strong>
837
+ <p>Dans votre fichier API, nettoyez les données POST entrantes : <code>$email = sanitizeEmail($_POST['email']);</code>. Interrogez la base de données. Si la vérification du mot de passe réussit, démarrez la session utilisateur et renvoyez le succès avec <code>jsonResponse()</code>.</p>
838
+ </div>
839
+ </li>
840
+ </ul>
841
+
842
+ <div class="alert a-success" style="margin-top: 40px;">
843
+ <strong>🎉 Prêt !</strong> Vous savez maintenant comment gérer en toute sécurité l'ensemble du flux de données avec ArtiFrame. Bonne création avec un code fluide et sans dépendances !
844
+ </div>
845
+ </section>
846
+
847
+ </main>
848
+
849
+ </body>
850
+ </html>