@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.
- package/assets/logo.png +0 -0
- package/assets/logo.svg +22 -0
- package/assets/logo.webp +0 -0
- package/assets/logo@0.5x.png +0 -0
- package/assets/logo@0.75x.png +0 -0
- package/assets/logo@1.5x.png +0 -0
- package/assets/logo@2x.png +0 -0
- package/assets/logo@3x.png +0 -0
- package/assets/logo@4x.png +0 -0
- package/bin/artiframe.js +57 -0
- package/bin/artiframe.php +45 -0
- package/core-stubs/app/ApiControl.php +81 -0
- package/core-stubs/app/Database.php +69 -0
- package/core-stubs/app/DotEnv.php +63 -0
- package/core-stubs/app/R2Manager.php +130 -0
- package/core-stubs/app/ViewControl.php +24 -0
- package/core-stubs/bin/SystemMethod.php +271 -0
- package/core-stubs/bin/ViewMethod.php +354 -0
- package/core-stubs/docs/de.html +951 -0
- package/core-stubs/docs/en.html +952 -0
- package/core-stubs/docs/es.html +947 -0
- package/core-stubs/docs/fr.html +850 -0
- package/core-stubs/docs/tr.html +951 -0
- package/core-stubs/public/assets/css/components/footer.css +0 -0
- package/core-stubs/public/assets/css/components/header.css +0 -0
- package/core-stubs/public/assets/css/components/mobilenav.css +0 -0
- package/core-stubs/public/assets/css/components/sidebar.css +0 -0
- package/core-stubs/public/assets/css/components/theme-modal.css +0 -0
- package/core-stubs/public/assets/css/root/app.css +61 -0
- package/core-stubs/public/assets/js/components/header.js +0 -0
- package/core-stubs/public/assets/js/components/mobilenav.js +0 -0
- package/core-stubs/public/assets/js/components/sidebar.js +0 -0
- package/core-stubs/public/assets/js/components/theme-modal.js +0 -0
- package/core-stubs/public/assets/js/root/app.js +30 -0
- package/core-stubs/public/includes/footer.php +5 -0
- package/core-stubs/public/includes/head.php +26 -0
- package/core-stubs/public/includes/header.php +5 -0
- package/core-stubs/public/includes/mobilenav.php +5 -0
- package/core-stubs/public/includes/sidebar.php +5 -0
- package/core-stubs/public/includes/theme-modal.php +5 -0
- package/core-stubs/readme/de.md +29 -0
- package/core-stubs/readme/en.md +29 -0
- package/core-stubs/readme/es.md +29 -0
- package/core-stubs/readme/fr.md +29 -0
- package/core-stubs/readme/tr.md +29 -0
- package/package.json +49 -0
- package/scripts/postinstall.js +21 -0
- package/src/App.php +266 -0
- package/src/Commands/MakeApiCommand.php +71 -0
- package/src/Commands/MakeClassCommand.php +88 -0
- package/src/Commands/MakeViewCommand.php +95 -0
- package/src/Commands/NewProjectCommand.php +633 -0
- package/src/Commands/VersionCommand.php +92 -0
- package/src/Lang/de.php +53 -0
- package/src/Lang/en.php +53 -0
- package/src/Lang/es.php +53 -0
- package/src/Lang/fr.php +53 -0
- package/src/Lang/tr.php +53 -0
- package/src/Services/Safeguard.php +48 -0
- package/src/Services/Translator.php +52 -0
|
@@ -0,0 +1,951 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="tr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>ArtiFrame | Resmi Geliştirici Dokümantasyonu</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>Geliştirici Dokümantasyonu</span>
|
|
225
|
+
</div>
|
|
226
|
+
<nav>
|
|
227
|
+
<div class="sb-cat">Başlangıç</div>
|
|
228
|
+
<a href="#giris">Giriş & Felsefe</a>
|
|
229
|
+
<a href="#kurulum">Kurulum</a>
|
|
230
|
+
<a href="#dizin">Dizin Yapısı</a>
|
|
231
|
+
|
|
232
|
+
<div class="sb-cat">Mimari</div>
|
|
233
|
+
<a href="#bootstrapper">Bootstrapper Mimarisi</a>
|
|
234
|
+
<a href="#kurallar">Kural Seti</a>
|
|
235
|
+
|
|
236
|
+
<div class="sb-cat">CLI Aracı</div>
|
|
237
|
+
<a href="#cli-giris">CLI'ye Giriş</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">Tarih Fonksiyonları</a>
|
|
248
|
+
<a href="#vh-format">Formatlama</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 & İstek</a>
|
|
256
|
+
<a href="#sh-security">Güvenlik</a>
|
|
257
|
+
|
|
258
|
+
<div class="sb-cat">API Güvenliği</div>
|
|
259
|
+
<a href="#api-methods">HTTP Metod Kontrolü</a>
|
|
260
|
+
<a href="#api-cors">CORS</a>
|
|
261
|
+
<a href="#api-rate">Rate Limiting</a>
|
|
262
|
+
|
|
263
|
+
<div class="sb-cat">Pratik</div>
|
|
264
|
+
<a href="#workflow">Tam İş Akışı</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>Dokümantasyonu</span></h1>
|
|
274
|
+
<p class="lead">Sıfır dış bağımlılık, katı kurallar ve güçlü bir CLI ile yönetilen, ölçeklenebilir native PHP projelerinin çerçevesi. Dependency yok, composer paket çorbasından arınmış, iş mantığınıza odaklanmanızı sağlayan, hafif ve hızlı bir ekosistem.</p>
|
|
275
|
+
</header>
|
|
276
|
+
|
|
277
|
+
<!-- ====== GİRİŞ & FELSEFE ====== -->
|
|
278
|
+
<section id="giris">
|
|
279
|
+
<h2>Giriş & Felsefe</h2>
|
|
280
|
+
<p>ArtiFrame, <strong>"Convention over Configuration"</strong> (Yapılandırmadan Önce Kurallar) ilkesine dayanır. Bir framework'ün gücü, sunduğu araçların zenginliğinden değil, kurduğu düzenin tutarlılığından gelir.</p>
|
|
281
|
+
|
|
282
|
+
<div class="grid-2">
|
|
283
|
+
<div class="card highlight">
|
|
284
|
+
<h3>Zero Overhead</h3>
|
|
285
|
+
<p>Composer paketlerine, framework çekirdeğine veya 3. taraf kütüphanelere sıfır bağımlılık. Her satır kod sizindir; şişirmeden, gereksiz soyutlama katmanlarından arınmış.</p>
|
|
286
|
+
</div>
|
|
287
|
+
<div class="card highlight">
|
|
288
|
+
<h3>Güvenlik Önce Gelir</h3>
|
|
289
|
+
<p>XSS koruması, CSRF doğrulama, SQL injection'a karşı sanitizasyon ve HTTP metod kontrolü varsayılan olarak yerleşik gelir. Güvenlik bir seçenek değil, bir standart.</p>
|
|
290
|
+
</div>
|
|
291
|
+
<div class="card highlight">
|
|
292
|
+
<h3>Katı Kural Seti</h3>
|
|
293
|
+
<p>Projeye yeni dahil olan bir junior geliştirici, <code class="ic">data-js</code> mimarisini ve dizin yapısını dakikalar içinde kavrar. Takım uyumu, framework seviyesinde garanti altındadır.</p>
|
|
294
|
+
</div>
|
|
295
|
+
<div class="card highlight">
|
|
296
|
+
<h3>CLI Öncelikli</h3>
|
|
297
|
+
<p>View, API, class dosyalarını elle oluşturmak yok. CLI, stub dosyalarından üretir, asset bağlantılarını kurar ve projeyi tutarlı tutar.</p>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<div class="alert a-info">
|
|
302
|
+
<strong>ℹ️ AGPLv3 Lisans:</strong> ArtiFrame açık kaynaklıdır. Ürettiğiniz türev çalışmalar, kaynak kodun açık kalması koşuluyla özgürce kullanılabilir. Telif bildirimi kaldırılamaz.
|
|
303
|
+
</div>
|
|
304
|
+
</section>
|
|
305
|
+
|
|
306
|
+
<!-- ====== KURULUM ====== -->
|
|
307
|
+
<section id="kurulum">
|
|
308
|
+
<h2>Kurulum</h2>
|
|
309
|
+
<p>ArtiFrame CLI, global bir PHP aracı olarak kurulur. Bir kez kurulur, her projede kullanılır.</p>
|
|
310
|
+
|
|
311
|
+
<h3>1. CLI Aracını Global Olarak Kur</h3>
|
|
312
|
+
<pre><code><span class="cm"># NPM üzerinden global kurulum</span>
|
|
313
|
+
npm install -g @artilingo/artiframe-cli
|
|
314
|
+
|
|
315
|
+
<span class="cm"># Kurulumu doğrula</span>
|
|
316
|
+
artiframe</code></pre>
|
|
317
|
+
|
|
318
|
+
<h3>2. İnteraktif Kabuk</h3>
|
|
319
|
+
<p>Terminale sadece <code class="ic">artiframe</code> yazıp Enter'a basın. CLI kapanmaz; sürekli komut dinleyen interaktif bir kabuk açılır:</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> </code></pre>
|
|
326
|
+
|
|
327
|
+
<h3>3. Yeni Proje Başlat</h3>
|
|
328
|
+
<pre><code>artiframe> new benim-projem</code></pre>
|
|
329
|
+
<p>Bu komut <code class="ic">benim-projem/</code> dizinini oluşturur ve tüm iskelet yapıyı içine kopyalar: <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> ve ilk <code class="ic">index.php</code> sayfası.</p>
|
|
330
|
+
|
|
331
|
+
<h3>4. Ortam Ayarları</h3>
|
|
332
|
+
<pre><code>cp .env.example .env</code></pre>
|
|
333
|
+
<p><code class="ic">.env</code> dosyanızı açıp veritabanı ve uygulama bilgilerini doldurun. Bu dosya asla versiyon kontrolüne girmez.</p>
|
|
334
|
+
|
|
335
|
+
<div class="alert a-warn">
|
|
336
|
+
<strong>⚠️ Web Sunucusu Ayarı:</strong> Apache/Nginx'in document root'unu <code class="ic">/public/</code> klasörüne yönlendirin. Diğer dizinler dışarıya asla açık olmamalıdır.
|
|
337
|
+
</div>
|
|
338
|
+
</section>
|
|
339
|
+
|
|
340
|
+
<!-- ====== DİZİN YAPISI ====== -->
|
|
341
|
+
<section id="dizin">
|
|
342
|
+
<h2>Dizin Yapısı</h2>
|
|
343
|
+
<p>Proje başlatıldığında oluşan mimari; sorumlulukların (SoC) net biçimde ayrılmasını sağlar.</p>
|
|
344
|
+
|
|
345
|
+
<div class="tree">
|
|
346
|
+
<span class="dir">proje-adi/</span>
|
|
347
|
+
├── <span class="dir">app/</span> <span class="note"># Altyapı katmanı</span>
|
|
348
|
+
│ ├── <span class="file">ViewControl.php</span> <span class="note"># View (HTML sayfa) bootstrapper'ı</span>
|
|
349
|
+
│ ├── <span class="file">ApiControl.php</span> <span class="note"># API endpoint bootstrapper'ı</span>
|
|
350
|
+
│ ├── <span class="file">Database.php</span> <span class="note"># PDO tabanlı veritabanı bağlantısı</span>
|
|
351
|
+
│ ├── <span class="file">DotEnv.php</span> <span class="note"># .env okuyucu</span>
|
|
352
|
+
│ └── <span class="file">R2Manager.php</span> <span class="note"># Cloudflare R2 dosya yöneticisi</span>
|
|
353
|
+
│
|
|
354
|
+
├── <span class="dir warn">bin/</span> <span class="note"># ⚠️ Çekirdek sistem — doğrudan düzenlemeyin</span>
|
|
355
|
+
│ ├── <span class="file">SystemMethod.php</span> <span class="note"># API/Backend global yardımcıları</span>
|
|
356
|
+
│ ├── <span class="file">ViewMethod.php</span> <span class="note"># View/Frontend global yardımcıları</span>
|
|
357
|
+
│ └── <span class="dir">stubs/</span> <span class="note"># CLI'ın kullandığı şablon dosyaları</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"># Yapılandırma dosyaları</span>
|
|
364
|
+
│ └── <span class="file">app-version.php</span> <span class="note"># APP_VERSION ve APP_ENV sabitleri</span>
|
|
365
|
+
│
|
|
366
|
+
├── <span class="dir">public/</span> <span class="note"># ← Web sunucusunun tek açık dizini</span>
|
|
367
|
+
│ ├── <span class="dir safe">assets/</span>
|
|
368
|
+
│ │ ├── <span class="dir">css/</span> <span class="note"># View'a özel CSS dosyaları</span>
|
|
369
|
+
│ │ └── <span class="dir">js/</span> <span class="note"># View'a özel JS dosyaları</span>
|
|
370
|
+
│ ├── <span class="dir">includes/</span> <span class="note"># Paylaşılan bileşenler</span>
|
|
371
|
+
│ │ ├── head.php
|
|
372
|
+
│ │ ├── header.php
|
|
373
|
+
│ │ └── footer.php
|
|
374
|
+
│ ├── <span class="dir">api/</span> <span class="note"># API endpoint dosyaları</span>
|
|
375
|
+
│ └── <span class="file">index.php</span> <span class="note"># Ana sayfa</span>
|
|
376
|
+
│
|
|
377
|
+
├── <span class="dir">src/</span> <span class="note"># İş mantığı ve sınıflar</span>
|
|
378
|
+
├── <span class="file">.env</span> <span class="note"># Ortam değişkenleri (git'e girmiyor)</span>
|
|
379
|
+
├── <span class="file">.env.example</span> <span class="note"># Şablon — git'e giriyor</span>
|
|
380
|
+
└── <span class="file">kilavuz.html</span> <span class="note"># Bu doküman</span>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<div class="alert a-danger">
|
|
384
|
+
<strong>🚫 bin/ dizinine dokunmayın:</strong> <code class="ic">bin/</code> içindeki dosyalar çerçevenin çekirdeğidir. Projeye özel iş mantığı buraya eklenmez. Ekleyeceğiniz sınıflar ve servisler <code class="ic">src/</code> altında, altyapı bileşenleri <code class="ic">app/</code> altında yer alır.
|
|
385
|
+
</div>
|
|
386
|
+
</section>
|
|
387
|
+
|
|
388
|
+
<!-- ====== BOOTSTRAPPER ====== -->
|
|
389
|
+
<section id="bootstrapper">
|
|
390
|
+
<h2>Bootstrapper Mimarisi <span class="tag b-core">Kritik</span></h2>
|
|
391
|
+
<p>ArtiFrame, iki tamamen bağımsız bootstrapper kullanır. Bu mimari, <strong>HTML header sorunlarını ve güvenlik açıklarını</strong> kökten önler.</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>HTML sayfaları (view dosyaları) için kullanılır. Session başlatır, <code class="ic">ViewMethod</code>'u yükler.</p>
|
|
397
|
+
<pre><code><span class="cm">// public/profil.php — EN ÜSTE, HTML basmadan önce</span>
|
|
398
|
+
<span class="kw"><?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">?></span>
|
|
403
|
+
<span class="kw"><!DOCTYPE html></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>API endpoint dosyaları için kullanılır. JSON header'ı ayarlar, HTTP metod kontrolü yapar, <code class="ic">SystemMethod</code>'u yükler.</p>
|
|
409
|
+
<pre><code><span class="cm">// public/api/kullanici/getir.php</span>
|
|
410
|
+
<span class="kw"><?php</span>
|
|
411
|
+
<span class="cm">// $allowedMethods MUTLAKA require'dan önce tanımlanmalı</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 ve ApiControl asla karıştırılmaz:</strong> ViewControl bir API dosyasına require edilirse JSON header yerine HTML header dönebilir ve tüm API yanıtı bozulur. ApiControl bir HTML sayfasına require edilirse session başlamaz ve sayfa kırılır.
|
|
423
|
+
</div>
|
|
424
|
+
</section>
|
|
425
|
+
|
|
426
|
+
<!-- ====== KURALLAR ====== -->
|
|
427
|
+
<section id="kurallar">
|
|
428
|
+
<h2>Kural Seti <span class="tag b-core">Standart</span></h2>
|
|
429
|
+
|
|
430
|
+
<div class="card">
|
|
431
|
+
<h3>Kural 1: data-js Mimarisi <span class="badge b-sec">Kritik</span></h3>
|
|
432
|
+
<p>JavaScript olayları asla bir <code class="ic">class</code> veya <code class="ic">id</code> üzerinden dinlenemez. Bunlar görsel / stil kimliklerdir. Tüm JS etkileşimleri <code class="ic">data-js</code> özniteliği ile yönetilir. CSS bir class sildiğinde JavaScript asla çökmez.</p>
|
|
433
|
+
<pre><code><span class="cm"><!-- ❌ Anti-Pattern — desteklenmez --></span>
|
|
434
|
+
<button id="submitBtn" class="btn">Gönder</button>
|
|
435
|
+
<span class="cm">// JS: document.getElementById('submitBtn').addEventListener(...)</span>
|
|
436
|
+
|
|
437
|
+
<span class="cm"><!-- ✅ ArtiFrame Standardı --></span>
|
|
438
|
+
<button class="btn btn-primary" <span class="var">data-js</span>="login-submit">Gönder</button>
|
|
439
|
+
<span class="cm">// JS: document.querySelector('[data-js="login-submit"]').addEventListener(...)</span></code></pre>
|
|
440
|
+
</div>
|
|
441
|
+
|
|
442
|
+
<div class="card">
|
|
443
|
+
<h3>Kural 2: Tema Mimarisi</h3>
|
|
444
|
+
<p>Koyu/Açık mod ve temalar, <code class="ic"><html></code> etiketinin <code class="ic">data-theme</code> ve <code class="ic">data-mode</code> öznitelikleri üzerinden yönetilir. Gövde sınıfları kullanılmaz.</p>
|
|
445
|
+
<pre><code><span class="cm"><!-- view.stub şablonundan gelen HTML açılış etiketi --></span>
|
|
446
|
+
<html lang="tr" data-theme="default" data-mode="light">
|
|
447
|
+
|
|
448
|
+
<span class="cm">/* app.css içindeki tema tanımı */</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>Kural 3: Güvenli Veri Akışı</h3>
|
|
461
|
+
<p>Veritabanından gelen her veri, DOM'a basılmadan önce <code class="ic">display()</code> ile sarılır. API'ye gelen her veri, işlenmeden önce <code class="ic">sanitizeString()</code> veya <code class="ic">sanitizeInt()</code> ile temizlenir.</p>
|
|
462
|
+
</div>
|
|
463
|
+
|
|
464
|
+
<div class="card">
|
|
465
|
+
<h3>Kural 4: APP_ENV ile Hata Yönetimi</h3>
|
|
466
|
+
<p><code class="ic">.env</code> dosyasındaki <code class="ic">APP_ENV</code> değeri hata görünürlüğünü belirler. Production'da kullanıcıya hata mesajı gösterilmez.</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 Ekosistemi</h2>
|
|
482
|
+
<p>ArtiFrame CLI, terminale <code class="ic">artiframe</code> yazıldığında interaktif bir kabuk açar. Tüm komutlar bu kabuk içinde çalışır. Komutlar tek seferlik de çalıştırılabilir.</p>
|
|
483
|
+
<pre><code><span class="cm"># İnteraktif mod (önerilen)</span>
|
|
484
|
+
artiframe
|
|
485
|
+
artiframe> make:view admin/kullanicilar.php
|
|
486
|
+
|
|
487
|
+
<span class="cm"># Tek seferlik mod</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 Komutu</span></h2>
|
|
494
|
+
<p>Yeni bir ArtiFrame projesi oluşturur. Tüm dizin iskeletini, bootstrapper dosyalarını ve ilk index sayfasını oluşturur.</p>
|
|
495
|
+
<pre><code>artiframe> <span class="fn">new</span> <span class="st">proje-adi</span></code></pre>
|
|
496
|
+
<p>Oluşturulan yapı:</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 Komutu</span></h2>
|
|
512
|
+
<p>Yeni bir sayfa (view) dosyası ve ona özel CSS/JS varlıklarını oluşturur. Varlıklar otomatik olarak sayfaya bağlanır ve <code class="ic">?v=APP_VERSION</code> ile önbellek temizleme (cache-busting) uygulanır.</p>
|
|
513
|
+
<pre><code>artiframe> <span class="fn">make:view</span> <span class="st">admin/kullanicilar.php</span></code></pre>
|
|
514
|
+
<p>Oluşturulan dosyalar:</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>Oluşturulan view dosyasının başında ViewControl require edilmiş, head/header/footer include'ları eklenmiş ve CSS/JS linkleri cache-busting ile bağlanmış olarak gelir.</p>
|
|
519
|
+
</section>
|
|
520
|
+
|
|
521
|
+
<!-- make:api -->
|
|
522
|
+
<section id="cli-api">
|
|
523
|
+
<h2><code>make:api</code> <span class="tag b-cli">CLI Komutu</span></h2>
|
|
524
|
+
<p>İki farklı şablondan birini seçerek API endpoint dosyası oluşturur. Her yeni API dosyasında <code class="ic">$allowedMethods</code> değişkeni ve <code class="ic">ApiControl.php</code> require'ı hazır gelir.</p>
|
|
525
|
+
|
|
526
|
+
<div class="card">
|
|
527
|
+
<h3><span class="badge b-api">standart</span> — Tekil Eylemli API</h3>
|
|
528
|
+
<p>Tek bir iş yapan (login, gönder, sil) endpoint'ler için. Doğrudan iş mantığı yazılır.</p>
|
|
529
|
+
<pre><code>artiframe> <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"><?php</span>
|
|
531
|
+
<span class="var">$allowedMethods</span> = [<span class="st">'POST'</span>]; <span class="cm">// Sadece POST kabul et</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">// İş mantığı buraya...</span>
|
|
537
|
+
<span class="fn">jsonResponse</span>([<span class="st">'status'</span> => <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> — Çok Eylemli API</h3>
|
|
542
|
+
<p>Bir modül için CRUD işlemlerini tek endpoint'te yöneten yapı. <code class="ic">action</code> parametresiyle hangi işlemin yapılacağı belirlenir.</p>
|
|
543
|
+
<pre><code>artiframe> <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"><?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> => <span class="st">'success'</span>, <span class="st">'message'</span> => <span class="st">'Oluşturuldu.'</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> => <span class="st">'success'</span>, <span class="st">'message'</span> => <span class="st">'Güncellendi.'</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> => <span class="st">'success'</span>, <span class="st">'message'</span> => <span class="st">'Silindi.'</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> => <span class="st">'error'</span>, <span class="st">'message'</span> => <span class="st">'Geçersiz action.'</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 Komutu</span></h2>
|
|
571
|
+
<p>Namespace ve sınıf boilerplate'i hazır gelecek şekilde yeni bir PHP sınıf dosyası oluşturur.</p>
|
|
572
|
+
<pre><code>artiframe> <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 Komutu</span></h2>
|
|
578
|
+
<p><code class="ic">config/app-version.php</code> içindeki sürüm numarasını semantik versiyonlama (SemVer) kurallarına göre günceller. Sürüm formatı: <strong>MAJOR.MINOR.PATCH</strong></p>
|
|
579
|
+
|
|
580
|
+
<table class="sig-table">
|
|
581
|
+
<thead>
|
|
582
|
+
<tr><th>Komut</th><th>Açıklama</th><th>Örnek</th></tr>
|
|
583
|
+
</thead>
|
|
584
|
+
<tbody>
|
|
585
|
+
<tr><td>version upgrade patch</td><td>Hata düzeltme, küçük iyileştirme</td><td>1.2.3 → 1.2.4</td></tr>
|
|
586
|
+
<tr><td>version upgrade minor</td><td>Geriye uyumlu yeni özellik</td><td>1.2.3 → 1.3.0</td></tr>
|
|
587
|
+
<tr><td>version upgrade major</td><td>Kırıcı değişiklik</td><td>1.2.3 → 2.0.0</td></tr>
|
|
588
|
+
<tr><td>version downgrade patch</td><td>Son patch'i geri al</td><td>1.2.4 → 1.2.3</td></tr>
|
|
589
|
+
<tr><td>version downgrade minor</td><td>Son minor'ı geri al</td><td>1.3.0 → 1.2.0</td></tr>
|
|
590
|
+
<tr><td>version downgrade major</td><td>Son major'ı geri al</td><td>2.0.0 → 1.0.0</td></tr>
|
|
591
|
+
</tbody>
|
|
592
|
+
</table>
|
|
593
|
+
|
|
594
|
+
<pre><code>artiframe> <span class="fn">version</span> upgrade minor
|
|
595
|
+
<span class="cm">✔ Sürüm 1.2.0 → 1.3.0 olarak güncellendi.</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>Veritabanından veya kullanıcı girdisinden gelen veriyi DOM'a basarken <strong>zorunlu XSS koruması</strong> uygular. Null veya boş değerlerde hata fırlatmak yerine varsayılan değer döner.</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>💡 İpucu: <code>$default</code> Kullanımı</strong><br>
|
|
605
|
+
İkinci parametre (<code>$default</code>), veri tabanından gelen veri boş (null, false, empty string) olduğunda ekrana basılacak kurtarıcı metindir. Örneğin isim verisi girilmemiş bir kullanıcı için <code>display($isim, 'İsimsiz')</code> kullanırsanız sayfanız çirkin boşluklar yerine anlamlı bir içerik sunar.
|
|
606
|
+
</div>
|
|
607
|
+
<pre><code><span class="cm"><!-- ❌ Güvensiz — XSS açığı oluşturur --></span>
|
|
608
|
+
<h1><?= <span class="var">$user</span>[<span class="st">'isim'</span>] ?></h1>
|
|
609
|
+
|
|
610
|
+
<span class="cm"><!-- ✅ ArtiFrame Güvenli Kullanımı --></span>
|
|
611
|
+
<h1><?= <span class="fn">display</span>(<span class="var">$user</span>[<span class="st">'isim'</span>], <span class="st">'İsimsiz Kullanıcı'</span>) ?></h1></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>Kullanıcılardan alınan linkleri (örneğin profil web siteleri) <code><a href="..."></code> veya <code><img src="..."></code> içlerine basarken kullanılır. <code>javascript:alert(1)</code> gibi (Stored XSS) tehlikeli payload'ları zararsız hale getirerek bağlantı üzerinden kod çalıştırılmasını önler.</p>
|
|
617
|
+
<span class="ret">string escapeUrl(string $url)</span>
|
|
618
|
+
<pre><code><span class="cm"><!-- ❌ Güvensiz — URL üzerinden JS kodu sızdırılabilir --></span>
|
|
619
|
+
<a href="<?= <span class="var">$user</span>[<span class="st">'website'</span>] ?>">Siteyi Ziyaret Et</a>
|
|
620
|
+
|
|
621
|
+
<span class="cm"><!-- ✅ ArtiFrame Güvenli Kullanımı --></span>
|
|
622
|
+
<a href="<?= <span class="fn">escapeUrl</span>(<span class="var">$user</span>[<span class="st">'website'</span>]) ?>">Siteyi Ziyaret Et</a></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>HTML formlarına CSRF saldırılarına karşı gizli token alanı ekler. <strong>POST form içeren her sayfada kullanılması zorunludur.</strong></p>
|
|
628
|
+
<span class="ret">string csrfField()</span>
|
|
629
|
+
<pre><code><form action="/api/kaydet.php" method="POST">
|
|
630
|
+
<?= <span class="fn">csrfField</span>() ?> <span class="cm"><!-- Güvenlik için zorunludur --></span>
|
|
631
|
+
<input type="text" name="ad">
|
|
632
|
+
<button type="submit" data-js="kaydet-btn">Kaydet</button>
|
|
633
|
+
</form></code></pre>
|
|
634
|
+
</section>
|
|
635
|
+
|
|
636
|
+
<section id="vh-dates">
|
|
637
|
+
<h2>View Helpers — Tarih Fonksiyonları <span class="tag b-view">ViewMethod</span></h2>
|
|
638
|
+
<p>Veritabanındaki <code class="ic">Y-m-d H:i:s</code> formatındaki tarih stringleri veya UNIX timestamp değerlerini işler. Tüm metinsel çıktılar dile göre (<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>) yerelleştirilir.</p>
|
|
639
|
+
|
|
640
|
+
<table class="sig-table">
|
|
641
|
+
<thead><tr><th>Fonksiyon</th><th>Çıktı Örneği</th><th>Açıklama</th></tr></thead>
|
|
642
|
+
<tbody>
|
|
643
|
+
<tr><td>day($date)</td><td>24</td><td>Sadece gün</td></tr>
|
|
644
|
+
<tr><td>month($date)</td><td>07</td><td>Sadece ay (rakam)</td></tr>
|
|
645
|
+
<tr><td>year($date)</td><td>2026</td><td>Yıl</td></tr>
|
|
646
|
+
<tr><td>timeOnly($date)</td><td>14:30</td><td>Saat:Dakika</td></tr>
|
|
647
|
+
<tr><td>fulldate($date)</td><td>24.07.2026</td><td>Tam tarih</td></tr>
|
|
648
|
+
<tr><td>formatDate($date, $format)</td><td>24.07.2026 14:30</td><td>Özel format</td></tr>
|
|
649
|
+
<tr><td>monthName($date, $lang)</td><td>Temmuz / July / Juli</td><td>Ay adı (dile göre)</td></tr>
|
|
650
|
+
<tr><td>fulldateName($date, $lang)</td><td>24 Temmuz 2026 / July 24, 2026</td><td>Tam tarih (ay adıyla, dile göre)</td></tr>
|
|
651
|
+
<tr><td>timeAgo($date, $lang)</td><td>5 dakika önce / 5 minutes ago</td><td>Sosyal medya tarzı (dile göre)</td></tr>
|
|
652
|
+
</tbody>
|
|
653
|
+
</table>
|
|
654
|
+
|
|
655
|
+
<div class="alert a-info" style="margin-top: 0; margin-bottom: 24px;">
|
|
656
|
+
<strong>💡 Parametreleri Anlamak: <code>$format</code> ve <code>$lang</code></strong>
|
|
657
|
+
<ul style="margin-top: 10px; margin-bottom: 0;">
|
|
658
|
+
<li style="margin-bottom: 8px;"><strong><code>$format</code> (Biçim):</strong> Sadece <code>formatDate()</code> fonksiyonu ile kullanılır. Standart PHP tarih harflerini kabul eder. (Örneğin: <code>'d/m/Y'</code> ➔ 24/07/2026, veya <code>'H:i'</code> ➔ 15:30). Diğer hazır fonksiyonlar (day, year vb.) yetersiz kaldığında kendi tarih kalıbınızı oluşturmanızı sağlar.</li>
|
|
659
|
+
<li style="margin-bottom: 0;"><strong><code>$lang</code> (Dil):</strong> Çıktısında metin (ay adı, "önce" kelimesi) barındıran fonksiyonlarda kullanılır. Bu parametreyi boş bırakırsanız sistem <strong>varsayılan olarak <code>'tr'</code> (Türkçe)</strong> çalışır. Çok dilli bir proje yapıyorsanız ikinci parametreye dil kodunu (<code>tr, en, de, fr, es</code>) girmeniz yeterlidir. (Örn: <code>timeAgo($tarih, 'en')</code> ➔ 5 minutes ago)</li>
|
|
660
|
+
</ul>
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
<pre><code><span class="cm"><!-- time() fonksiyonu ile alınmış bir UNIX timestamp olduğunu varsayalım --></span>
|
|
664
|
+
<span class="kw"><?php</span> <span class="var">$tarih</span> = <span class="fn">time</span>(); <span class="kw">?></span>
|
|
665
|
+
|
|
666
|
+
<span class="cm"><!-- Dile göre tam tarih --></span>
|
|
667
|
+
<span><?= <span class="fn">fulldateName</span>(<span class="var">$tarih</span>, <span class="st">'tr'</span>) ?></span>
|
|
668
|
+
<span class="cm"><!-- Çıktı: 24 Temmuz 2026 --></span>
|
|
669
|
+
|
|
670
|
+
<span class="cm"><!-- Sosyal medya tarzı zaman gösterimi --></span>
|
|
671
|
+
<span><?= <span class="fn">timeAgo</span>(<span class="var">$tarih</span>, <span class="st">'en'</span>) ?></span>
|
|
672
|
+
<span class="cm"><!-- Çıktı: 5 minutes ago --></span>
|
|
673
|
+
|
|
674
|
+
<span><?= <span class="fn">timeAgo</span>(<span class="var">$tarih</span>, <span class="st">'de'</span>) ?></span>
|
|
675
|
+
<span class="cm"><!-- Çıktı: vor 5 Minuten --></span></code></pre>
|
|
676
|
+
</section>
|
|
677
|
+
|
|
678
|
+
<section id="vh-format">
|
|
679
|
+
<h2>View Helpers — Metin Formatlama <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>Uzun metinleri (örneğin blog özetlerini) son kelimeyi kesmeden istenilen karakter limitinde kırpar ve sonuna belirtilen ek ekler.</p>
|
|
685
|
+
<pre><code><p><?= <span class="fn">truncate</span>(<span class="var">$post</span>[<span class="st">'icerik'</span>], <span class="nu">160</span>, <span class="st">'...'</span>) ?></p></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>Tutarı para birimi sembolüyle formatlar. Para birimi kodu (ISO), sembol yerleşimini otomatik belirler. Varsayılan para birimi <code class="ic">usd</code>'dir.</p>
|
|
692
|
+
<span class="ret">string money(float $amount, string $currency = 'usd')</span>
|
|
693
|
+
|
|
694
|
+
<table class="sig-table">
|
|
695
|
+
<thead><tr><th>Kod</th><th>Çıktı</th><th>Para Birimi</th></tr></thead>
|
|
696
|
+
<tbody>
|
|
697
|
+
<tr><td>usd</td><td>$1.250,00</td><td>ABD Doları</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ürk Lirası</td></tr>
|
|
700
|
+
<tr><td>gbp</td><td>£1.250,00</td><td>İngiliz Sterlini</td></tr>
|
|
701
|
+
<tr><td>jpy</td><td>1.250,00 ¥</td><td>Japon Yeni</td></tr>
|
|
702
|
+
<tr><td>inr</td><td>1.250,00 ₹</td><td>Hindistan Rupisi</td></tr>
|
|
703
|
+
<tr><td>rub</td><td>1.250,00 ₽</td><td>Rus Rublesi</td></tr>
|
|
704
|
+
<tr><td>krw</td><td>1.250,00 ₩</td><td>Güney Kore Wonu</td></tr>
|
|
705
|
+
<tr><td>brl</td><td>R$1.250,00</td><td>Brezilya Reali</td></tr>
|
|
706
|
+
<tr><td>aed</td><td>1.250,00 د.إ</td><td>BAE Dirhemi</td></tr>
|
|
707
|
+
</tbody>
|
|
708
|
+
</table>
|
|
709
|
+
|
|
710
|
+
<pre><code><span><?= <span class="fn">money</span>(<span class="var">$urun</span>[<span class="st">'fiyat'</span>], <span class="st">'try'</span>) ?></span>
|
|
711
|
+
<span class="cm"><!-- Çıktı: 1.250,00 ₺ --></span>
|
|
712
|
+
|
|
713
|
+
<span><?= <span class="fn">money</span>(<span class="var">$urun</span>[<span class="st">'fiyat'</span>], <span class="st">'usd'</span>) ?></span>
|
|
714
|
+
<span class="cm"><!-- Çıktı: $1.250,00 --></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>JSON header'ını ayarlar, HTTP durum kodunu verir ve json_encode ile çıktıyı güvenli biçimde basar, ardından betiği sonlandırır. <strong>Tüm API yanıtları bu fonksiyon üzerinden verilmek zorundadır.</strong></p>
|
|
721
|
+
<span class="ret">void jsonResponse(array $data, int $statusCode = 200)</span>
|
|
722
|
+
<pre><code><span class="cm">// Başarılı yanıt</span>
|
|
723
|
+
<span class="fn">jsonResponse</span>([<span class="st">'status'</span> => <span class="st">'success'</span>, <span class="st">'data'</span> => <span class="var">$kullanici</span>], <span class="nu">200</span>);
|
|
724
|
+
|
|
725
|
+
<span class="cm">// Hata yanıtı</span>
|
|
726
|
+
<span class="fn">jsonResponse</span>([<span class="st">'status'</span> => <span class="st">'error'</span>, <span class="st">'message'</span> => <span class="st">'Yetkisiz erişim.'</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>Gelen POST isteğinin meşru bir formdan geldiğini doğrular. Formun içine <code class="ic">csrfField()</code> eklenmemişse veya token geçersizse false döner.</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> => <span class="st">'error'</span>, <span class="st">'message'</span> => <span class="st">'Geçersiz CSRF token.'</span>], <span class="nu">403</span>);
|
|
735
|
+
}</code></pre>
|
|
736
|
+
</section>
|
|
737
|
+
|
|
738
|
+
<section id="sh-sanitize">
|
|
739
|
+
<h2>System Helpers — Sanitize Fonksiyonları <span class="tag b-api">SystemMethod</span></h2>
|
|
740
|
+
|
|
741
|
+
<table class="sig-table">
|
|
742
|
+
<thead><tr><th>Fonksiyon</th><th>Açıklama</th></tr></thead>
|
|
743
|
+
<tbody>
|
|
744
|
+
<tr><td>sanitizeInt($value)</td><td>İçerisindeki tüm harfleri, sembolleri ve virgülleri uçurur, <strong>sadece tam sayı</strong> rakamlarını bırakır. ID veya limitler için kullanılır.</td></tr>
|
|
745
|
+
<tr><td>sanitizeFloat($value)</td><td>Küsüratlı (ondalıklı) sayılar dışındaki her şeyi temizler. Para tutarları veya metrikler için kullanılır.</td></tr>
|
|
746
|
+
<tr><td>sanitizeString($value)</td><td>XSS ve türevi saldırıları engellemek için tüm <strong>HTML ve PHP etiketlerini (<code><script></code>, <code><iframe></code> vb.) yok eder</strong>. Güvenli düz metin bırakır.</td></tr>
|
|
747
|
+
<tr><td>sanitizeEmail($email)</td><td>E-posta formatına uymayan geçersiz ve tehlikeli tüm karakterleri filtreler.</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 & İstek <span class="tag b-api">SystemMethod</span></h2>
|
|
758
|
+
|
|
759
|
+
<table class="sig-table">
|
|
760
|
+
<thead><tr><th>Fonksiyon</th><th>Açıklama</th></tr></thead>
|
|
761
|
+
<tbody>
|
|
762
|
+
<tr><td>isPost()</td><td>İstek POST mu?</td></tr>
|
|
763
|
+
<tr><td>isGet()</td><td>İstek GET mi?</td></tr>
|
|
764
|
+
<tr><td>isPut()</td><td>İstek PUT mu?</td></tr>
|
|
765
|
+
<tr><td>isDelete()</td><td>İstek DELETE mi?</td></tr>
|
|
766
|
+
<tr><td>isAjax()</td><td>İstek XHR/Fetch API üzerinden mi?</td></tr>
|
|
767
|
+
<tr><td>getClientIp()</td><td>Gerçek IP adresi (Cloudflare & Proxy destekli)</td></tr>
|
|
768
|
+
<tr><td>redirect($url)</td><td>Verilen URL'ye yönlendirip betiği sonlandırır</td></tr>
|
|
769
|
+
</tbody>
|
|
770
|
+
</table>
|
|
771
|
+
|
|
772
|
+
<pre><code><span class="cm">// Doğrudan tarayıcı erişimini engelle</span>
|
|
773
|
+
<span class="kw">if</span> (!<span class="fn">isAjax</span>()) {
|
|
774
|
+
<span class="fn">jsonResponse</span>([<span class="st">'error'</span> => <span class="st">'Doğrudan erişim kısıtlı.'</span>], <span class="nu">403</span>);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
<span class="cm">// Oturum yoksa yönlendir</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">// Cloudflare arkasında da gerçek IP</span></code></pre>
|
|
783
|
+
</section>
|
|
784
|
+
|
|
785
|
+
<section id="sh-security">
|
|
786
|
+
<h2>System Helpers — Güvenlik <span class="tag b-sec">SystemMethod</span></h2>
|
|
787
|
+
|
|
788
|
+
<table class="sig-table">
|
|
789
|
+
<thead><tr><th>Fonksiyon</th><th>Açıklama</th></tr></thead>
|
|
790
|
+
<tbody>
|
|
791
|
+
<tr><td>generateCsrf()</td><td>Yeni CSRF token üretir ve session'a kaydeder</td></tr>
|
|
792
|
+
<tr><td>generateToken($length)</td><td>Kriptografik güvenli rastgele hex string (parola sıfırlama, API key vb.)</td></tr>
|
|
793
|
+
<tr><td>hashPassword($password)</td><td>bcrypt ile şifre hash'ler</td></tr>
|
|
794
|
+
<tr><td>verifyPassword($password, $hash)</td><td>Hash ile şifreyi doğrular</td></tr>
|
|
795
|
+
</tbody>
|
|
796
|
+
</table>
|
|
797
|
+
|
|
798
|
+
<pre><code><span class="cm">// Güvenli token üretimi (API key, e-posta doğrulama linki vb.)</span>
|
|
799
|
+
<span class="var">$token</span> = <span class="fn">generateToken</span>(<span class="nu">32</span>);
|
|
800
|
+
<span class="cm">// ⚠️ Not: 32 byte veri üretilir ancak hex formatına (16'lık taban)</span>
|
|
801
|
+
<span class="cm">// çevrildiği için çıktı tam 2 katı, yani 64 karakter uzunluğunda bir string olur.</span>
|
|
802
|
+
|
|
803
|
+
<span class="cm">// Şifre kaydı</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">// Şifre doğrulama</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> => <span class="st">'Hatalı şifre.'</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 Metod Kontrolü <span class="tag b-sec">ApiControl</span></h2>
|
|
815
|
+
<p><code class="ic">$allowedMethods</code> dizisi, ApiControl require'ından <strong>önce</strong> tanımlanmalıdır. ApiControl bu diziyi okuyarak izin verilmeyen metodlardan gelen istekleri otomatik olarak <code class="ic">405 Method Not Allowed</code> ile reddeder.</p>
|
|
816
|
+
<pre><code><span class="cm">// Sadece GET ve POST kabul eden bir endpoint</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">// Sadece DELETE kabul eden bir endpoint</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> istekleri CORS için otomatik <code class="ic">200</code> döner ve betik sonlanır — elle müdahale gerekmez.</p>
|
|
824
|
+
</section>
|
|
825
|
+
|
|
826
|
+
<section id="api-cors">
|
|
827
|
+
<h2>API — CORS <span class="tag b-api">ApiControl (Yorum Satırı)</span></h2>
|
|
828
|
+
<p>Başka domainlerden veya mobil uygulamalardan API erişimine izin vermek istiyorsanız <code class="ic">ApiControl.php</code> içindeki CORS bloğunu aktifleştirin.</p>
|
|
829
|
+
<pre><code><span class="cm">// ApiControl.php içinde — açıklama kaldırılarak aktifleştirilir</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> Production ortamında <code class="ic">*</code> (tüm domainler) kullanmayın. Domain adresini açıkça belirtin.
|
|
835
|
+
</div>
|
|
836
|
+
</section>
|
|
837
|
+
|
|
838
|
+
<section id="api-rate">
|
|
839
|
+
<h2>API — Rate Limiting <span class="tag b-api">ApiControl (Yorum Satırı)</span></h2>
|
|
840
|
+
<p>Kötü niyetli kullanıcıların veya botların API'yi flood etmesini önlemek için <code class="ic">ApiControl.php</code> içindeki Rate Limiting bloğunu aktifleştirebilirsiniz. Varsayılan kural: <strong>60 saniyede 60 istek / IP</strong>.</p>
|
|
841
|
+
<div class="alert a-warn" style="margin-top: 10px; margin-bottom: 20px;">
|
|
842
|
+
<strong>⚠️ Redis Sunucusu Gereklidir:</strong> Rate Limiting (hız sınırlandırma) özelliği <code>\Src\Service\RedisService::getInstance()</code> sınıfı üzerinden Redis kullanarak çalışır. Bu kod bloğunu açmadan önce projenizde bir Redis servisi kurduğunuzdan ve <code>src/Service/</code> altında bir bağlantı sınıfı oluşturduğunuzdan emin olun.
|
|
843
|
+
</div>
|
|
844
|
+
<pre><code><span class="cm">// Aynı IP'den 60 saniyede 60'tan fazla istek gelirse:</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> => <span class="st">'Çok fazla istek. Lütfen bekleyin.'</span>]);</code></pre>
|
|
847
|
+
</section>
|
|
848
|
+
|
|
849
|
+
<!-- ====== WORKFLOW ====== -->
|
|
850
|
+
<section id="workflow">
|
|
851
|
+
<h2>Tam İş Akışı: İletişim Formu (End-to-End)</h2>
|
|
852
|
+
<p>Bir <strong>İletişim Formu</strong> özelliğini ArtiFrame standartlarında baştan sona nasıl geliştireceğinizi adım adım inceleyelim.</p>
|
|
853
|
+
|
|
854
|
+
<ol class="steps">
|
|
855
|
+
<li>
|
|
856
|
+
<div>
|
|
857
|
+
<strong>View Dosyasını Oluşturun</strong>
|
|
858
|
+
<pre><code>artiframe> <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>API Endpoint Oluşturun</strong>
|
|
867
|
+
<pre><code>artiframe> <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>HTML Formunu Kodlayın</strong><br>
|
|
873
|
+
<code class="ic">public/iletisim.php</code> dosyasına aşağıdaki formu ekleyin:
|
|
874
|
+
<pre><code><form action="/api/iletisim/gonder.php" method="POST">
|
|
875
|
+
<?= <span class="fn">csrfField</span>() ?>
|
|
876
|
+
<input type="text" name="ad" placeholder="Adınız">
|
|
877
|
+
<input type="email" name="eposta" placeholder="E-posta">
|
|
878
|
+
<textarea name="mesaj" placeholder="Mesajınız"></textarea>
|
|
879
|
+
<button type="submit" data-js="<span class="st">iletisim-gonder</span>">Gönder</button>
|
|
880
|
+
</form></code></pre>
|
|
881
|
+
</div>
|
|
882
|
+
</li>
|
|
883
|
+
<li>
|
|
884
|
+
<div>
|
|
885
|
+
<strong>JavaScript ile Fetch API'yi Bağlayın</strong><br>
|
|
886
|
+
<code class="ic">public/assets/js/iletisim.js</code> dosyasına:
|
|
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) => {
|
|
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>Sunucu Mantığını (Backend) Tamamlayın</strong><br>
|
|
902
|
+
<code class="ic">public/api/iletisim/gonder.php</code> dosyasında:
|
|
903
|
+
<pre><code><span class="kw"><?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 Doğrulama</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> => <span class="st">'Geçersiz token.'</span>], <span class="nu">403</span>);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
<span class="cm">// 2. Veriyi Temizle</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. İş Mantığı (Mail gönder, DB'ye kaydet vb.)</span>
|
|
919
|
+
<span class="cm">// ...</span>
|
|
920
|
+
|
|
921
|
+
<span class="cm">// 4. Yanıt Ver</span>
|
|
922
|
+
<span class="fn">jsonResponse</span>([<span class="st">'status'</span> => <span class="st">'success'</span>, <span class="st">'message'</span> => <span class="st">'Mesajınız alındı.'</span>], <span class="nu">200</span>);</code></pre>
|
|
923
|
+
</div>
|
|
924
|
+
</li>
|
|
925
|
+
</ol>
|
|
926
|
+
|
|
927
|
+
<div class="alert a-success">
|
|
928
|
+
<strong>✅ Tamamlandı!</strong> XSS koruması, CSRF doğrulama, HTTP metod kısıtlaması ve data-js mimarisi ile tam güvenli ve standart uyumlu bir form akışı oluşturdunuz.
|
|
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>
|