@girardmedia/bootspring 3.3.2 → 3.4.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/agents/accessibility-auditor.md +39 -0
- package/assets/agents/api-designer.md +40 -0
- package/assets/agents/auth-implementer.md +64 -0
- package/assets/agents/bug-hunter.md +42 -0
- package/assets/agents/bundle-analyzer.md +40 -0
- package/assets/agents/cache-optimizer.md +55 -0
- package/assets/agents/changelog-writer.md +55 -0
- package/assets/agents/ci-cd-builder.md +40 -0
- package/assets/agents/code-explainer.md +39 -0
- package/assets/agents/code-reviewer.md +39 -0
- package/assets/agents/cost-optimizer.md +57 -0
- package/assets/agents/cron-scheduler.md +51 -0
- package/assets/agents/data-seeder.md +56 -0
- package/assets/agents/database-architect.md +40 -0
- package/assets/agents/dependency-updater.md +40 -0
- package/assets/agents/deploy-checker.md +40 -0
- package/assets/agents/docker-optimizer.md +40 -0
- package/assets/agents/documentation-writer.md +40 -0
- package/assets/agents/email-builder.md +55 -0
- package/assets/agents/env-setup.md +40 -0
- package/assets/agents/error-handler.md +40 -0
- package/assets/agents/eslint-fixer.md +46 -0
- package/assets/agents/feature-flagger.md +69 -0
- package/assets/agents/git-detective.md +39 -0
- package/assets/agents/graphql-builder.md +60 -0
- package/assets/agents/incident-responder.md +59 -0
- package/assets/agents/log-analyzer.md +39 -0
- package/assets/agents/migration-planner.md +41 -0
- package/assets/agents/monorepo-navigator.md +39 -0
- package/assets/agents/nextjs-expert.md +57 -0
- package/assets/agents/notification-builder.md +56 -0
- package/assets/agents/onboarding-guide.md +39 -0
- package/assets/agents/performance-profiler.md +40 -0
- package/assets/agents/prisma-expert.md +57 -0
- package/assets/agents/rate-limiter.md +58 -0
- package/assets/agents/react-expert.md +58 -0
- package/assets/agents/refactorer.md +42 -0
- package/assets/agents/regex-builder.md +46 -0
- package/assets/agents/release-manager.md +40 -0
- package/assets/agents/s3-manager.md +58 -0
- package/assets/agents/schema-validator.md +40 -0
- package/assets/agents/search-builder.md +62 -0
- package/assets/agents/security-auditor.md +39 -0
- package/assets/agents/sitemap-generator.md +53 -0
- package/assets/agents/stripe-integrator.md +59 -0
- package/assets/agents/tailwind-expert.md +55 -0
- package/assets/agents/tech-debt-tracker.md +39 -0
- package/assets/agents/test-writer.md +42 -0
- package/assets/agents/type-fixer.md +45 -0
- package/assets/agents/webhook-builder.md +54 -0
- package/assets/rules/cpp.md +53 -0
- package/assets/rules/css.md +52 -0
- package/assets/rules/go.md +50 -0
- package/assets/rules/html.md +52 -0
- package/assets/rules/java.md +51 -0
- package/assets/rules/kotlin.md +50 -0
- package/assets/rules/php.md +51 -0
- package/assets/rules/python.md +51 -0
- package/assets/rules/ruby.md +51 -0
- package/assets/rules/rust.md +49 -0
- package/assets/rules/shell.md +52 -0
- package/assets/rules/sql.md +49 -0
- package/assets/rules/swift.md +50 -0
- package/assets/rules/typescript.md +52 -0
- package/assets/rules/yaml-json.md +51 -0
- package/assets/skills/accessibility.md +210 -0
- package/assets/skills/agent-patterns.md +387 -0
- package/assets/skills/ai-integration.md +263 -0
- package/assets/skills/animation-patterns.md +224 -0
- package/assets/skills/api-design.md +218 -0
- package/assets/skills/api-gateway.md +341 -0
- package/assets/skills/api-versioning.md +226 -0
- package/assets/skills/astro-patterns.md +233 -0
- package/assets/skills/auth-patterns.md +248 -0
- package/assets/skills/aws-patterns.md +171 -0
- package/assets/skills/background-jobs.md +162 -0
- package/assets/skills/browser-extensions.md +309 -0
- package/assets/skills/caching-patterns.md +253 -0
- package/assets/skills/ci-cd.md +251 -0
- package/assets/skills/cli-development.md +296 -0
- package/assets/skills/code-review.md +185 -0
- package/assets/skills/cron-patterns.md +327 -0
- package/assets/skills/data-fetching.md +231 -0
- package/assets/skills/database-migrations.md +346 -0
- package/assets/skills/database-patterns.md +219 -0
- package/assets/skills/debugging.md +281 -0
- package/assets/skills/design-system.md +289 -0
- package/assets/skills/django-patterns.md +182 -0
- package/assets/skills/docker-patterns.md +235 -0
- package/assets/skills/e2e-testing.md +287 -0
- package/assets/skills/edge-computing.md +268 -0
- package/assets/skills/electron-patterns.md +266 -0
- package/assets/skills/email-templates.md +206 -0
- package/assets/skills/error-handling.md +265 -0
- package/assets/skills/event-driven.md +232 -0
- package/assets/skills/express-patterns.md +239 -0
- package/assets/skills/fastapi-patterns.md +198 -0
- package/assets/skills/feature-flags.md +212 -0
- package/assets/skills/figma-to-code.md +298 -0
- package/assets/skills/file-upload.md +228 -0
- package/assets/skills/forms-patterns.md +264 -0
- package/assets/skills/gcp-patterns.md +189 -0
- package/assets/skills/git-workflow.md +187 -0
- package/assets/skills/golang-patterns.md +185 -0
- package/assets/skills/graphql-patterns.md +244 -0
- package/assets/skills/i18n-patterns.md +172 -0
- package/assets/skills/image-processing.md +350 -0
- package/assets/skills/java-springboot.md +226 -0
- package/assets/skills/kotlin-patterns.md +207 -0
- package/assets/skills/kubernetes-patterns.md +326 -0
- package/assets/skills/laravel-patterns.md +261 -0
- package/assets/skills/llm-fine-tuning.md +335 -0
- package/assets/skills/load-testing.md +303 -0
- package/assets/skills/logging-observability.md +228 -0
- package/assets/skills/markdown-processing.md +318 -0
- package/assets/skills/mcp-server-patterns.md +292 -0
- package/assets/skills/microservices.md +272 -0
- package/assets/skills/migration-patterns.md +239 -0
- package/assets/skills/mongodb-patterns.md +189 -0
- package/assets/skills/monorepo-patterns.md +287 -0
- package/assets/skills/nextjs-app-router.md +237 -0
- package/assets/skills/notification-patterns.md +348 -0
- package/assets/skills/oauth-patterns.md +246 -0
- package/assets/skills/payment-integration.md +222 -0
- package/assets/skills/pdf-generation.md +307 -0
- package/assets/skills/performance-optimization.md +277 -0
- package/assets/skills/php-patterns.md +210 -0
- package/assets/skills/prisma-patterns.md +241 -0
- package/assets/skills/prompt-engineering.md +193 -0
- package/assets/skills/pwa-patterns.md +247 -0
- package/assets/skills/python-patterns.md +158 -0
- package/assets/skills/python-testing.md +172 -0
- package/assets/skills/queue-patterns.md +295 -0
- package/assets/skills/rag-patterns.md +159 -0
- package/assets/skills/rate-limiting.md +319 -0
- package/assets/skills/react-components.md +201 -0
- package/assets/skills/react-native-patterns.md +299 -0
- package/assets/skills/real-time-patterns.md +181 -0
- package/assets/skills/redis-patterns.md +188 -0
- package/assets/skills/refactoring.md +218 -0
- package/assets/skills/regex-patterns.md +191 -0
- package/assets/skills/remix-patterns.md +262 -0
- package/assets/skills/responsive-design.md +199 -0
- package/assets/skills/ruby-rails-patterns.md +178 -0
- package/assets/skills/rust-patterns.md +211 -0
- package/assets/skills/search-patterns.md +227 -0
- package/assets/skills/security-hardening.md +237 -0
- package/assets/skills/seo-patterns.md +179 -0
- package/assets/skills/serverless-patterns.md +223 -0
- package/assets/skills/sql-optimization.md +154 -0
- package/assets/skills/state-management.md +254 -0
- package/assets/skills/storybook-patterns.md +330 -0
- package/assets/skills/svelte-patterns.md +258 -0
- package/assets/skills/swift-patterns.md +227 -0
- package/assets/skills/tailwind-patterns.md +272 -0
- package/assets/skills/tdd-workflow.md +199 -0
- package/assets/skills/terraform-patterns.md +270 -0
- package/assets/skills/testing-react.md +240 -0
- package/assets/skills/testing-vitest.md +232 -0
- package/assets/skills/typescript-strict.md +159 -0
- package/assets/skills/video-processing.md +340 -0
- package/assets/skills/vue-patterns.md +247 -0
- package/assets/skills/web-workers.md +327 -0
- package/assets/skills/webhooks-patterns.md +283 -0
- package/assets/skills/websocket-patterns.md +306 -0
- package/dist/cli/index.js +941 -958
- package/dist/core/index.d.ts +341 -11
- package/dist/core.js +58 -95
- package/dist/mcp/index.d.ts +33 -1
- package/dist/mcp-server.js +177 -255
- package/package.json +4 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance-optimization
|
|
3
|
+
description: Optimize web performance with code splitting, lazy loading, image optimization, Web Vitals, and bundle analysis.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Performance Optimization
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
Apply these patterns when your application feels slow, when Lighthouse scores
|
|
11
|
+
drop below 90, or proactively when building new features. Performance is a
|
|
12
|
+
feature — every 100ms of load time costs measurable user engagement. Measure
|
|
13
|
+
first, optimize the bottleneck, measure again.
|
|
14
|
+
|
|
15
|
+
## How It Works
|
|
16
|
+
|
|
17
|
+
### 1. Code Splitting
|
|
18
|
+
|
|
19
|
+
Split your bundle so users download only the code they need for the current page.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Route-level splitting with React.lazy
|
|
23
|
+
import { lazy, Suspense } from 'react';
|
|
24
|
+
|
|
25
|
+
const Dashboard = lazy(() => import('./pages/Dashboard'));
|
|
26
|
+
const Settings = lazy(() => import('./pages/Settings'));
|
|
27
|
+
const Analytics = lazy(() => import('./pages/Analytics'));
|
|
28
|
+
|
|
29
|
+
function App() {
|
|
30
|
+
return (
|
|
31
|
+
<Suspense fallback={<PageSkeleton />}>
|
|
32
|
+
<Routes>
|
|
33
|
+
<Route path="/dashboard" element={<Dashboard />} />
|
|
34
|
+
<Route path="/settings" element={<Settings />} />
|
|
35
|
+
<Route path="/analytics" element={<Analytics />} />
|
|
36
|
+
</Routes>
|
|
37
|
+
</Suspense>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// Component-level splitting for heavy libraries
|
|
44
|
+
const CodeEditor = lazy(() => import('./components/CodeEditor'));
|
|
45
|
+
|
|
46
|
+
function TaskDetail({ task }: { task: Task }) {
|
|
47
|
+
return (
|
|
48
|
+
<div>
|
|
49
|
+
<TaskHeader task={task} />
|
|
50
|
+
{task.hasCode && (
|
|
51
|
+
<Suspense fallback={<div className="h-64 animate-pulse bg-gray-100" />}>
|
|
52
|
+
<CodeEditor value={task.code} />
|
|
53
|
+
</Suspense>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Dynamic Import for Libraries
|
|
61
|
+
|
|
62
|
+
Load heavy dependencies only when needed.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Bad — ships 500KB chart library to every page
|
|
66
|
+
import { Chart } from 'chart.js';
|
|
67
|
+
|
|
68
|
+
// Good — load only when user navigates to analytics
|
|
69
|
+
async function renderChart(canvas: HTMLCanvasElement, data: ChartData) {
|
|
70
|
+
const { Chart, registerables } = await import('chart.js');
|
|
71
|
+
Chart.register(...registerables);
|
|
72
|
+
return new Chart(canvas, { type: 'line', data });
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Image Optimization
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Next.js Image component — automatic optimization
|
|
80
|
+
import Image from 'next/image';
|
|
81
|
+
|
|
82
|
+
<Image
|
|
83
|
+
src="/hero.jpg"
|
|
84
|
+
alt="Dashboard preview"
|
|
85
|
+
width={1200}
|
|
86
|
+
height={630}
|
|
87
|
+
priority // preload above-the-fold images
|
|
88
|
+
placeholder="blur"
|
|
89
|
+
blurDataURL={blurHash}
|
|
90
|
+
/>
|
|
91
|
+
|
|
92
|
+
// Below the fold — lazy load (default behavior)
|
|
93
|
+
<Image
|
|
94
|
+
src="/feature.jpg"
|
|
95
|
+
alt="Feature screenshot"
|
|
96
|
+
width={800}
|
|
97
|
+
height={400}
|
|
98
|
+
loading="lazy"
|
|
99
|
+
sizes="(max-width: 768px) 100vw, 50vw"
|
|
100
|
+
/>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For non-Next.js projects:
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<!-- Native lazy loading + responsive images -->
|
|
107
|
+
<img
|
|
108
|
+
src="photo-800.webp"
|
|
109
|
+
srcset="photo-400.webp 400w, photo-800.webp 800w, photo-1200.webp 1200w"
|
|
110
|
+
sizes="(max-width: 600px) 100vw, 50vw"
|
|
111
|
+
loading="lazy"
|
|
112
|
+
decoding="async"
|
|
113
|
+
alt="Team photo"
|
|
114
|
+
/>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Serve WebP/AVIF format. Use `<picture>` for format fallbacks.
|
|
118
|
+
|
|
119
|
+
### 4. Web Vitals
|
|
120
|
+
|
|
121
|
+
The three Core Web Vitals that matter for user experience and SEO:
|
|
122
|
+
|
|
123
|
+
| Metric | Target | What it measures |
|
|
124
|
+
|--------|--------|-----------------|
|
|
125
|
+
| LCP (Largest Contentful Paint) | < 2.5s | When the main content becomes visible |
|
|
126
|
+
| INP (Interaction to Next Paint) | < 200ms | Response time to user interactions |
|
|
127
|
+
| CLS (Cumulative Layout Shift) | < 0.1 | Visual stability (things jumping around) |
|
|
128
|
+
|
|
129
|
+
Measure in production:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { onLCP, onINP, onCLS } from 'web-vitals';
|
|
133
|
+
|
|
134
|
+
function sendToAnalytics(metric: Metric) {
|
|
135
|
+
fetch('/api/vitals', {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
body: JSON.stringify({
|
|
138
|
+
name: metric.name,
|
|
139
|
+
value: metric.value,
|
|
140
|
+
rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
|
|
141
|
+
url: window.location.href,
|
|
142
|
+
}),
|
|
143
|
+
keepalive: true, // survives page navigation
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
onLCP(sendToAnalytics);
|
|
148
|
+
onINP(sendToAnalytics);
|
|
149
|
+
onCLS(sendToAnalytics);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 5. Fix Common LCP Problems
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Preload the LCP image
|
|
156
|
+
// In Next.js: priority prop on Image
|
|
157
|
+
// In HTML: <link rel="preload" as="image" href="/hero.webp" />
|
|
158
|
+
|
|
159
|
+
// Avoid render-blocking CSS
|
|
160
|
+
// Move non-critical CSS to async loading
|
|
161
|
+
<link rel="preload" href="/non-critical.css" as="style"
|
|
162
|
+
onload="this.onload=null;this.rel='stylesheet'" />
|
|
163
|
+
|
|
164
|
+
// Inline critical CSS for above-the-fold content
|
|
165
|
+
<style>{criticalCSS}</style>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 6. Fix Common CLS Problems
|
|
169
|
+
|
|
170
|
+
```css
|
|
171
|
+
/* Reserve space for images */
|
|
172
|
+
img, video {
|
|
173
|
+
max-width: 100%;
|
|
174
|
+
height: auto;
|
|
175
|
+
aspect-ratio: 16 / 9; /* prevent layout shift */
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Reserve space for dynamic content */
|
|
179
|
+
.ad-slot {
|
|
180
|
+
min-height: 250px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Use transform for animations, not width/height/top/left */
|
|
184
|
+
.toast-enter {
|
|
185
|
+
transform: translateY(100%);
|
|
186
|
+
transition: transform 0.3s ease;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 7. Profiling and Bundle Analysis
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Analyze bundle size
|
|
194
|
+
npx vite-bundle-visualizer # Vite
|
|
195
|
+
npx @next/bundle-analyzer # Next.js
|
|
196
|
+
|
|
197
|
+
# Find large dependencies
|
|
198
|
+
npx depcheck # unused dependencies
|
|
199
|
+
npx cost-of-modules # install size per package
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Vite config — manual chunks for better caching
|
|
204
|
+
export default defineConfig({
|
|
205
|
+
build: {
|
|
206
|
+
rollupOptions: {
|
|
207
|
+
output: {
|
|
208
|
+
manualChunks: {
|
|
209
|
+
vendor: ['react', 'react-dom'],
|
|
210
|
+
charts: ['chart.js', 'recharts'],
|
|
211
|
+
editor: ['monaco-editor'],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 8. Runtime Performance
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// Virtualize long lists — render only visible items
|
|
223
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
224
|
+
|
|
225
|
+
function VirtualList({ items }: { items: Item[] }) {
|
|
226
|
+
const parentRef = useRef<HTMLDivElement>(null);
|
|
227
|
+
const virtualizer = useVirtualizer({
|
|
228
|
+
count: items.length,
|
|
229
|
+
getScrollElement: () => parentRef.current,
|
|
230
|
+
estimateSize: () => 48,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
|
|
235
|
+
<div style={{ height: virtualizer.getTotalSize() }}>
|
|
236
|
+
{virtualizer.getVirtualItems().map((row) => (
|
|
237
|
+
<div key={row.key} style={{
|
|
238
|
+
position: 'absolute',
|
|
239
|
+
top: row.start,
|
|
240
|
+
height: row.size,
|
|
241
|
+
}}>
|
|
242
|
+
{items[row.index].name}
|
|
243
|
+
</div>
|
|
244
|
+
))}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Debounce expensive re-renders
|
|
251
|
+
const debouncedSearch = useMemo(
|
|
252
|
+
() => debounce((query: string) => setSearch(query), 300),
|
|
253
|
+
[],
|
|
254
|
+
);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Examples
|
|
258
|
+
|
|
259
|
+
| Problem | Solution | Impact |
|
|
260
|
+
|---------|----------|--------|
|
|
261
|
+
| 2MB JS bundle | Code splitting + lazy routes | 60-80% smaller initial load |
|
|
262
|
+
| 5s LCP | Preload hero image + inline critical CSS | LCP < 2s |
|
|
263
|
+
| Layout jumps | `aspect-ratio` on images + reserved ad slots | CLS < 0.05 |
|
|
264
|
+
| Slow list of 10,000 items | Virtualization | Renders 20 items instead of 10,000 |
|
|
265
|
+
| Heavy charting library | Dynamic import | Loaded only on analytics page |
|
|
266
|
+
|
|
267
|
+
## Checklist
|
|
268
|
+
|
|
269
|
+
- [ ] Routes are code-split with `React.lazy` or Next.js dynamic pages
|
|
270
|
+
- [ ] Heavy libraries are dynamically imported, not in the main bundle
|
|
271
|
+
- [ ] Images use modern formats (WebP/AVIF), responsive `srcset`, and `loading="lazy"`
|
|
272
|
+
- [ ] Above-the-fold images use `priority` (Next.js) or `<link rel="preload">`
|
|
273
|
+
- [ ] Web Vitals are measured in production and reported to analytics
|
|
274
|
+
- [ ] LCP is under 2.5s, INP under 200ms, CLS under 0.1
|
|
275
|
+
- [ ] Bundle analyzer is run in CI to catch size regressions
|
|
276
|
+
- [ ] Lists with 100+ items use virtualization
|
|
277
|
+
- [ ] `aspect-ratio` is set on images and media to prevent layout shift
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: php-patterns
|
|
3
|
+
description: PHP patterns with PSR standards, Composer, type hints, enums, fibers, and attributes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Modern PHP Patterns
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
Apply these patterns when writing PHP 8.1+ code. Modern PHP has strict typing, enums, attributes, fibers, and match expressions that eliminate whole categories of bugs. Use this skill when starting new projects, upgrading legacy codebases, or reviewing PHP code for type safety and PSR compliance.
|
|
10
|
+
|
|
11
|
+
## How It Works
|
|
12
|
+
|
|
13
|
+
### Strict Types and Type Declarations
|
|
14
|
+
|
|
15
|
+
Always declare strict types. Use union types, intersection types, and `never`:
|
|
16
|
+
|
|
17
|
+
```php
|
|
18
|
+
<?php
|
|
19
|
+
|
|
20
|
+
declare(strict_types=1);
|
|
21
|
+
|
|
22
|
+
function calculateDiscount(float $price, int $percent): float
|
|
23
|
+
{
|
|
24
|
+
if ($percent < 0 || $percent > 100) {
|
|
25
|
+
throw new \InvalidArgumentException("Percent must be 0-100, got {$percent}");
|
|
26
|
+
}
|
|
27
|
+
return $price * (1 - $percent / 100);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Union types (PHP 8.0+)
|
|
31
|
+
function findUser(int|string $identifier): ?User
|
|
32
|
+
{
|
|
33
|
+
return is_int($identifier)
|
|
34
|
+
? UserRepository::findById($identifier)
|
|
35
|
+
: UserRepository::findByEmail($identifier);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Intersection types (PHP 8.1+)
|
|
39
|
+
function process(Countable&Iterator $collection): void
|
|
40
|
+
{
|
|
41
|
+
foreach ($collection as $item) {
|
|
42
|
+
// $collection is guaranteed to be both Countable and Iterator
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// DNF types (PHP 8.2+)
|
|
47
|
+
function handle((Countable&Iterator)|null $items): void
|
|
48
|
+
{
|
|
49
|
+
// Accepts a collection that is both Countable and Iterator, or null
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Enums -- Replace Magic Strings
|
|
54
|
+
|
|
55
|
+
```php
|
|
56
|
+
enum OrderStatus: string
|
|
57
|
+
{
|
|
58
|
+
case Pending = 'pending';
|
|
59
|
+
case Paid = 'paid';
|
|
60
|
+
case Shipped = 'shipped';
|
|
61
|
+
case Cancelled = 'cancelled';
|
|
62
|
+
|
|
63
|
+
public function label(): string
|
|
64
|
+
{
|
|
65
|
+
return match ($this) {
|
|
66
|
+
self::Pending => 'Awaiting Payment',
|
|
67
|
+
self::Paid => 'Payment Received',
|
|
68
|
+
self::Shipped => 'In Transit',
|
|
69
|
+
self::Cancelled => 'Order Cancelled',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public function canTransitionTo(self $next): bool
|
|
74
|
+
{
|
|
75
|
+
return match ($this) {
|
|
76
|
+
self::Pending => in_array($next, [self::Paid, self::Cancelled]),
|
|
77
|
+
self::Paid => $next === self::Shipped,
|
|
78
|
+
self::Shipped => false,
|
|
79
|
+
self::Cancelled => false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Usage
|
|
85
|
+
$status = OrderStatus::from('pending');
|
|
86
|
+
echo $status->label(); // "Awaiting Payment"
|
|
87
|
+
$status->canTransitionTo(OrderStatus::Paid); // true
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Attributes -- Declarative Metadata
|
|
91
|
+
|
|
92
|
+
Replace docblock annotations with native attributes:
|
|
93
|
+
|
|
94
|
+
```php
|
|
95
|
+
#[Attribute(Attribute::TARGET_PROPERTY)]
|
|
96
|
+
class Validate
|
|
97
|
+
{
|
|
98
|
+
public function __construct(
|
|
99
|
+
public readonly string $rule,
|
|
100
|
+
public readonly string $message = '',
|
|
101
|
+
) {}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class CreateUserRequest
|
|
105
|
+
{
|
|
106
|
+
public function __construct(
|
|
107
|
+
#[Validate('email', 'Must be a valid email')]
|
|
108
|
+
public readonly string $email,
|
|
109
|
+
|
|
110
|
+
#[Validate('min:8', 'Must be at least 8 characters')]
|
|
111
|
+
public readonly string $password,
|
|
112
|
+
|
|
113
|
+
#[Validate('in:free,pro,enterprise', 'Invalid plan')]
|
|
114
|
+
public readonly string $plan = 'free',
|
|
115
|
+
) {}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Constructor Property Promotion and Readonly
|
|
120
|
+
|
|
121
|
+
```php
|
|
122
|
+
class Money
|
|
123
|
+
{
|
|
124
|
+
public function __construct(
|
|
125
|
+
public readonly int $amount,
|
|
126
|
+
public readonly string $currency = 'USD',
|
|
127
|
+
) {}
|
|
128
|
+
|
|
129
|
+
public function add(self $other): self
|
|
130
|
+
{
|
|
131
|
+
if ($this->currency !== $other->currency) {
|
|
132
|
+
throw new CurrencyMismatch($this->currency, $other->currency);
|
|
133
|
+
}
|
|
134
|
+
return new self($this->amount + $other->amount, $this->currency);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public function format(): string
|
|
138
|
+
{
|
|
139
|
+
$formatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY);
|
|
140
|
+
return $formatter->formatCurrency($this->amount / 100, $this->currency);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Fibers -- Cooperative Multitasking
|
|
146
|
+
|
|
147
|
+
```php
|
|
148
|
+
$fiber = new Fiber(function (): void {
|
|
149
|
+
$value = Fiber::suspend('waiting for input');
|
|
150
|
+
echo "Received: {$value}\n";
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
$result = $fiber->start(); // $result = 'waiting for input'
|
|
154
|
+
$fiber->resume('hello world'); // prints "Received: hello world"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Fibers power async frameworks like ReactPHP and Revolt without callbacks.
|
|
158
|
+
|
|
159
|
+
### Composer and PSR-4 Autoloading
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"autoload": {
|
|
164
|
+
"psr-4": {
|
|
165
|
+
"App\\": "src/",
|
|
166
|
+
"App\\Tests\\": "tests/"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"require": {
|
|
170
|
+
"php": ">=8.2"
|
|
171
|
+
},
|
|
172
|
+
"config": {
|
|
173
|
+
"sort-packages": true,
|
|
174
|
+
"optimize-autoloader": true
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Directory structure mirrors namespace: `src/Domain/User/UserRepository.php` maps to `App\Domain\User\UserRepository`.
|
|
180
|
+
|
|
181
|
+
### PSR Standards Reference
|
|
182
|
+
|
|
183
|
+
| PSR | Purpose | Tool |
|
|
184
|
+
|-----|---------|------|
|
|
185
|
+
| PSR-1/12 | Coding style | PHP-CS-Fixer, PHP_CodeSniffer |
|
|
186
|
+
| PSR-3 | Logger interface | Monolog |
|
|
187
|
+
| PSR-4 | Autoloading | Composer |
|
|
188
|
+
| PSR-7 | HTTP messages | Guzzle, Nyholm |
|
|
189
|
+
| PSR-11 | Container interface | PHP-DI, Symfony DI |
|
|
190
|
+
| PSR-15 | HTTP middleware | Slim, Mezzio |
|
|
191
|
+
|
|
192
|
+
## Examples
|
|
193
|
+
|
|
194
|
+
| Pattern | Before (Legacy) | After (Modern PHP 8.2+) |
|
|
195
|
+
|---------|-----------------|------------------------|
|
|
196
|
+
| Constants | `const STATUS_PAID = 'paid'` | `enum Status: string { case Paid = 'paid'; }` |
|
|
197
|
+
| Annotations | `@Route("/api")` docblock | `#[Route('/api')]` attribute |
|
|
198
|
+
| Constructors | Manual `$this->x = $x` | Constructor promotion `public readonly` |
|
|
199
|
+
| Null handling | `isset($x) ? $x : $default` | `$x ?? $default` or `?->` |
|
|
200
|
+
| Switch | `switch` with break | `match` expression (exhaustive) |
|
|
201
|
+
|
|
202
|
+
## Checklist
|
|
203
|
+
- [ ] `declare(strict_types=1)` at top of every PHP file
|
|
204
|
+
- [ ] All function parameters and return types declared
|
|
205
|
+
- [ ] Magic strings replaced with backed enums
|
|
206
|
+
- [ ] Docblock annotations migrated to native attributes
|
|
207
|
+
- [ ] Constructor property promotion used for value objects
|
|
208
|
+
- [ ] `readonly` on properties that should not change
|
|
209
|
+
- [ ] `match` used instead of `switch` for value expressions
|
|
210
|
+
- [ ] PSR-4 autoload with optimized autoloader enabled
|