@baldurpan/create-ai-workflow 0.2.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 (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/agents-block.js +70 -0
  4. package/dist/bin.js +14 -0
  5. package/dist/check/markdown.js +47 -0
  6. package/dist/check/parse.js +113 -0
  7. package/dist/check/rules.js +245 -0
  8. package/dist/cli.js +109 -0
  9. package/dist/commands/check.js +41 -0
  10. package/dist/commands/install.js +76 -0
  11. package/dist/commands/standards-add.js +145 -0
  12. package/dist/commands/update.js +214 -0
  13. package/dist/layout.js +115 -0
  14. package/dist/log.js +20 -0
  15. package/dist/manifest.js +53 -0
  16. package/dist/paths.js +41 -0
  17. package/package.json +37 -0
  18. package/templates/blocks/agents-block.md +30 -0
  19. package/templates/claude/agents/planner.agent.md +60 -0
  20. package/templates/claude/agents/reviewer.agent.md +77 -0
  21. package/templates/context/README.md +81 -0
  22. package/templates/context/plan-template.md +69 -0
  23. package/templates/context/plan-template.notes.md +55 -0
  24. package/templates/context/roles/coder.md +76 -0
  25. package/templates/context/workflow.md +143 -0
  26. package/templates/skills/feature-close/SKILL.md +102 -0
  27. package/templates/skills/feature-implement/SKILL.md +142 -0
  28. package/templates/skills/feature-plan/SKILL.md +138 -0
  29. package/templates/skills/feature-status/SKILL.md +86 -0
  30. package/templates/skills/onboard/SKILL.md +122 -0
  31. package/templates/skills/orchestrate/SKILL.md +92 -0
  32. package/templates/skills/roadmap/SKILL.md +97 -0
  33. package/templates/standards/.source +8 -0
  34. package/templates/standards/README.md +104 -0
  35. package/templates/standards/architecture/api-design.md +212 -0
  36. package/templates/standards/architecture/dependency-boundaries.md +104 -0
  37. package/templates/standards/architecture/feature-driven.md +102 -0
  38. package/templates/standards/architecture/folder-structure.md +108 -0
  39. package/templates/standards/architecture/monorepos.md +107 -0
  40. package/templates/standards/architecture/refactoring.md +97 -0
  41. package/templates/standards/architecture/shared-code.md +81 -0
  42. package/templates/standards/docs/PHP-SPEC.md +1195 -0
  43. package/templates/standards/docs/SPEC.md +540 -0
  44. package/templates/standards/examples/bad/data-fetching.tsx +52 -0
  45. package/templates/standards/examples/bad/derived-state.tsx +60 -0
  46. package/templates/standards/examples/bad/feature-component.tsx +80 -0
  47. package/templates/standards/examples/bad/prop-drilling.tsx +70 -0
  48. package/templates/standards/examples/good/data-fetching.tsx +53 -0
  49. package/templates/standards/examples/good/derived-state.tsx +52 -0
  50. package/templates/standards/examples/good/feature-component.tsx +141 -0
  51. package/templates/standards/examples/good/invoice.schema.ts +20 -0
  52. package/templates/standards/examples/good/prop-drilling.tsx +83 -0
  53. package/templates/standards/philosophy/ai-agent-behavior.md +93 -0
  54. package/templates/standards/philosophy/core-principles.md +49 -0
  55. package/templates/standards/philosophy/incremental-abstraction.md +56 -0
  56. package/templates/standards/philosophy/maintainability.md +66 -0
  57. package/templates/standards/philosophy/readability.md +87 -0
  58. package/templates/standards/php/README.md +62 -0
  59. package/templates/standards/php/anti-patterns.md +259 -0
  60. package/templates/standards/php/architecture.md +241 -0
  61. package/templates/standards/php/frameworks.md +197 -0
  62. package/templates/standards/php/rules.md +205 -0
  63. package/templates/standards/php/security.md +190 -0
  64. package/templates/standards/php/testing.md +139 -0
  65. package/templates/standards/php/tooling.md +245 -0
  66. package/templates/standards/react/accessibility.md +130 -0
  67. package/templates/standards/react/anti-patterns.md +187 -0
  68. package/templates/standards/react/component-design.md +139 -0
  69. package/templates/standards/react/error-boundaries.md +195 -0
  70. package/templates/standards/react/forms.md +147 -0
  71. package/templates/standards/react/hooks.md +88 -0
  72. package/templates/standards/react/memoization.md +87 -0
  73. package/templates/standards/react/state-management.md +123 -0
  74. package/templates/standards/react/testing.md +118 -0
  75. package/templates/standards/react/use-effect.md +110 -0
  76. package/templates/standards/security/api-security.md +136 -0
  77. package/templates/standards/security/auth.md +112 -0
  78. package/templates/standards/security/secrets.md +105 -0
  79. package/templates/standards/security/validation.md +89 -0
  80. package/templates/standards/templates/.editorconfig +16 -0
  81. package/templates/standards/templates/.nvmrc +1 -0
  82. package/templates/standards/templates/AGENTS.md +46 -0
  83. package/templates/standards/templates/CLAUDE.md +3 -0
  84. package/templates/standards/templates/_dot_gitignore +50 -0
  85. package/templates/standards/templates/biome.json +53 -0
  86. package/templates/standards/templates/eslint.config.js +61 -0
  87. package/templates/standards/templates/project-readme.md +76 -0
  88. package/templates/standards/templates/pull-request-template.md +46 -0
  89. package/templates/standards/templates/tsconfig.json +35 -0
  90. package/templates/standards/tooling/biome.md +92 -0
  91. package/templates/standards/tooling/ci.md +128 -0
  92. package/templates/standards/tooling/dates.md +194 -0
  93. package/templates/standards/tooling/dependencies.md +90 -0
  94. package/templates/standards/tooling/nx.md +113 -0
  95. package/templates/standards/tooling/observability.md +183 -0
  96. package/templates/standards/tooling/prisma.md +152 -0
  97. package/templates/standards/tooling/shadcn.md +115 -0
  98. package/templates/standards/tooling/tailwind.md +135 -0
  99. package/templates/standards/tooling/tanstack-query.md +214 -0
  100. package/templates/standards/tooling/tanstack-router.md +365 -0
  101. package/templates/standards/tooling/tanstack.md +31 -0
  102. package/templates/standards/tooling/vite.md +135 -0
  103. package/templates/standards/typescript/anti-patterns.md +107 -0
  104. package/templates/standards/typescript/error-handling.md +216 -0
  105. package/templates/standards/typescript/naming.md +78 -0
  106. package/templates/standards/typescript/rules.md +159 -0
  107. package/templates/standards/typescript/tsconfig/base.json +32 -0
  108. package/templates/standards/typescript/tsconfig/nextjs.json +12 -0
  109. package/templates/standards/typescript/tsconfig/react.json +9 -0
  110. package/templates/standards/typescript/validation.md +104 -0
  111. package/templates/stubs/executors.md +49 -0
  112. package/templates/stubs/findings.md +40 -0
  113. package/templates/stubs/history.md +13 -0
  114. package/templates/stubs/roadmap.md +29 -0
  115. package/templates/stubs/stack.md +35 -0
  116. package/templates/stubs/verify.md +48 -0
@@ -0,0 +1,197 @@
1
+ # PHP Frameworks
2
+
3
+ ## Decision Matrix
4
+
5
+ | Project type | Recommended framework |
6
+ |---|---|
7
+ | Product team, API, internal tool, startup | **Laravel** |
8
+ | Large enterprise, long-lived platform, complex domain | **Symfony** |
9
+ | Lightweight API, custom architecture, minimal system | **Slim** |
10
+ | No framework needed | Plain PHP with chosen PSR components |
11
+
12
+ Pick one and stick with it. Don't mix frameworks in the same service.
13
+
14
+ ## Laravel
15
+
16
+ Best for: product teams, rapid development, startups, internal systems, APIs.
17
+
18
+ ### Strengths
19
+
20
+ - Excellent developer experience
21
+ - Mature ecosystem (Cashier, Horizon, Nova, Telescope, Sanctum)
22
+ - Strong tooling for queues, auth, testing
23
+ - Eloquent for fast development
24
+
25
+ ### Risks
26
+
27
+ - Facade overuse leads to hidden coupling
28
+ - Magic-heavy patterns hurt long-term maintainability
29
+ - Fat models and fat controllers are common anti-patterns
30
+ - Easy to skip layered architecture and regret it later
31
+
32
+ ### Laravel-Specific Patterns
33
+
34
+ #### DO
35
+
36
+ - **Form Requests** for validation — keep validation out of controllers
37
+ - **Service classes / Application handlers** for business logic — keep controllers thin
38
+ - **Jobs, Events, Listeners** for async work and decoupling
39
+ - **Policies** for authorization
40
+ - **API Resources** for response serialization
41
+ - **Scoped bindings** to avoid duplicated singletons
42
+ - **Eager loading** (`with()`) to prevent N+1 queries
43
+ - **Form requests + DTOs together** — Form Request validates, then maps to a DTO that the handler consumes
44
+
45
+ #### DO NOT
46
+
47
+ - Put business logic in routes (`Route::post('/...', fn () => /* logic */)`)
48
+ - Put business logic in controllers
49
+ - Put business logic in Eloquent models (keep them as data + relationships)
50
+ - Build massive `AppServiceProvider`s — split into focused providers
51
+ - Define magic macros for core business logic — use explicit classes
52
+ - Use global helpers (`auth()`, `request()`, `now()`) inside domain/application code — inject instead
53
+
54
+ ### Example: Thin Controller + Handler + DTO
55
+
56
+ ```php
57
+ // app/Http/Requests/CreateInvoiceRequest.php
58
+ final class CreateInvoiceRequest extends FormRequest
59
+ {
60
+ public function rules(): array
61
+ {
62
+ return [
63
+ 'customer_id' => ['required', 'uuid'],
64
+ 'amount' => ['required', 'integer', 'min:1'],
65
+ 'currency' => ['required', 'in:USD,EUR,GBP'],
66
+ ];
67
+ }
68
+
69
+ public function toDto(): CreateInvoiceDto
70
+ {
71
+ return new CreateInvoiceDto(
72
+ customerId: $this->validated('customer_id'),
73
+ amount: new Money($this->validated('amount'), $this->validated('currency')),
74
+ );
75
+ }
76
+ }
77
+
78
+ // app/Application/Invoices/CreateInvoiceHandler.php
79
+ final readonly class CreateInvoiceHandler
80
+ {
81
+ public function __construct(
82
+ private InvoiceRepository $invoices,
83
+ private EventDispatcher $events,
84
+ ) {}
85
+
86
+ public function handle(CreateInvoiceDto $dto): Invoice
87
+ {
88
+ $invoice = Invoice::create($dto);
89
+ $this->events->dispatch(new InvoiceCreated($invoice));
90
+ return $invoice;
91
+ }
92
+ }
93
+
94
+ // app/Http/Controllers/CreateInvoiceController.php
95
+ final class CreateInvoiceController
96
+ {
97
+ public function __construct(private CreateInvoiceHandler $handler) {}
98
+
99
+ public function __invoke(CreateInvoiceRequest $request): JsonResponse
100
+ {
101
+ $invoice = $this->handler->handle($request->toDto());
102
+ return new JsonResponse(InvoiceResource::from($invoice), 201);
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### Recommended Companions
108
+
109
+ - **Pest** — testing
110
+ - **Larastan** (`nunomaduro/larastan`) — PHPStan for Laravel
111
+ - **Pint** — formatting
112
+ - **Horizon** — queue dashboard
113
+ - **Telescope** — local debugging
114
+ - **Sanctum** — API tokens / SPA auth
115
+
116
+ ## Symfony
117
+
118
+ Best for: large enterprise systems, long-lived platforms, highly structured teams, complex domains.
119
+
120
+ ### Strengths
121
+
122
+ - Explicit, predictable architecture
123
+ - Powerful DI container with autowiring
124
+ - Stable, mature, conservative release cadence
125
+ - Reusable components (HttpFoundation, Console, Messenger) work outside the full framework
126
+ - Strong typing throughout
127
+
128
+ ### Trade-offs
129
+
130
+ - Steeper learning curve
131
+ - More ceremony than Laravel
132
+ - Slower initial development velocity
133
+
134
+ ### Symfony Patterns
135
+
136
+ - **Controllers** are services; inject dependencies via constructor
137
+ - **Form component** for HTML forms; **Validator component** for any validation
138
+ - **Messenger** for commands, queries, async messages
139
+ - **API Platform** for fast typed REST/GraphQL APIs
140
+ - **Doctrine** for the ORM (more explicit than Eloquent, better for complex domains)
141
+
142
+ ### Recommended Companions
143
+
144
+ - **Pest** or **PHPUnit**
145
+ - **PHPStan** with `phpstan/phpstan-symfony`
146
+ - **PHP-CS-Fixer** with Symfony preset
147
+ - **API Platform** for typed APIs
148
+
149
+ ## Slim
150
+
151
+ Best for: lightweight APIs, custom architectures, embedded services, projects that need maximum control with minimum framework opinions.
152
+
153
+ ### Strengths
154
+
155
+ - Tiny, PSR-7/PSR-15 native
156
+ - You assemble the pieces (DI container, ORM, validation) yourself
157
+ - Zero magic
158
+
159
+ ### Trade-offs
160
+
161
+ - You build everything beyond routing
162
+ - Less ecosystem support than Laravel/Symfony
163
+
164
+ ### When to Pick Slim Over Plain PHP
165
+
166
+ If you need routing + middleware and want PSR conformance without committing to a full framework.
167
+
168
+ ## No Framework
169
+
170
+ For very small services (a single endpoint, a CLI tool), plain PHP with carefully chosen libraries is fine:
171
+
172
+ - Routing: `nikic/fast-route`
173
+ - HTTP: `guzzlehttp/psr7`, `laminas/laminas-diactoros`
174
+ - DI: `php-di/php-di`
175
+ - Validation: `symfony/validator` or `respect/validation`
176
+ - ORM: `doctrine/dbal` (lightweight) or just PDO
177
+
178
+ ## DO NOT
179
+
180
+ - Pick a framework based on tutorials you've seen recently — pick based on project shape and team experience
181
+ - Mix frameworks (Laravel + Symfony components fight each other's conventions)
182
+ - Build a custom mini-framework when Slim or Symfony covers the use case
183
+ - Skip a framework "because it's just a small project" and reimplement routing, DI, validation, and logging from scratch
184
+
185
+ ## PRIORITY
186
+
187
+ ```
188
+ Right framework for the project shape > Familiar framework
189
+ Thin controllers + handlers > Fat controllers
190
+ Symfony Messenger / Laravel Jobs > Inline synchronous processing
191
+ ```
192
+
193
+ ## See Also
194
+
195
+ - [`architecture.md`](architecture.md) — layered architecture works under any framework
196
+ - [`tooling.md`](tooling.md) — framework-specific tooling (Larastan, Symfony PHPStan)
197
+ - [`anti-patterns.md`](anti-patterns.md) — fat controllers, facade abuse
@@ -0,0 +1,205 @@
1
+ # PHP Rules
2
+
3
+ ## DO
4
+
5
+ - `declare(strict_types=1);` at the top of every PHP file
6
+ - Type every parameter, return value, and property
7
+ - Use modern PHP 8.3+ features: constructor property promotion, readonly properties/classes, enums, match expressions, attributes, union/intersection types, first-class callable syntax
8
+ - Use value objects for domain concepts (Money, EmailAddress, UserId) — not raw strings/ints
9
+ - Use DTOs for API/queue payloads — readonly classes, no setters
10
+ - Follow PSR-12 formatting (auto-enforced by Pint or PHP-CS-Fixer)
11
+ - Follow PSR-4 autoloading via Composer
12
+ - Use PSR-3 (`LoggerInterface`) for logging — not custom logger classes
13
+ - Name things by responsibility (`CalculateInvoiceTotals`) — not by role (`InvoiceManager`)
14
+
15
+ ## DO NOT
16
+
17
+ - Use untyped parameters or return values
18
+ - Pass `array` as a data contract — use DTOs or value objects
19
+ - Use generic class names: `Manager`, `Helper`, `Util`, `Common`, `Base`, `Handler` (unless it's actually a PSR-15 handler)
20
+ - Use deep inheritance chains — prefer composition
21
+ - Use traits for hidden cross-cutting behavior — prefer composition
22
+ - Use global state, service locators, static singletons, or facade abuse
23
+ - Reach for magic methods (`__call`, `__get`) when explicit code works
24
+ - Mix HTML and PHP in the same file (use Blade, Twig, or a frontend)
25
+
26
+ ## Strict Types
27
+
28
+ ```php
29
+ <?php declare(strict_types=1);
30
+
31
+ namespace App\Billing;
32
+
33
+ final class OrderService
34
+ {
35
+ public function calculateTotal(Order $order): Money
36
+ {
37
+ // ...
38
+ }
39
+ }
40
+ ```
41
+
42
+ Without `strict_types`, PHP silently coerces types — `int(5)` becomes `string("5")`, `string("abc")` becomes `int(0)`. Strict types make these errors loud at the call site.
43
+
44
+ ## Type Everything
45
+
46
+ ```php
47
+ // BAD — implicit any
48
+ public function process($order)
49
+ {
50
+ return $order->total;
51
+ }
52
+
53
+ // GOOD — explicit input and output types
54
+ public function process(Order $order): Money
55
+ {
56
+ return $order->total;
57
+ }
58
+ ```
59
+
60
+ For collections, use PHPStan/Psalm generics annotations since PHP doesn't have native generics:
61
+
62
+ ```php
63
+ /**
64
+ * @param list<Order> $orders
65
+ * @return list<Money>
66
+ */
67
+ public function extractTotals(array $orders): array
68
+ {
69
+ return array_map(fn (Order $o) => $o->total, $orders);
70
+ }
71
+ ```
72
+
73
+ ## Modern Features in Action
74
+
75
+ ### Constructor Property Promotion
76
+
77
+ ```php
78
+ // GOOD — concise, no boilerplate
79
+ final class OrderService
80
+ {
81
+ public function __construct(
82
+ private readonly PaymentGateway $payments,
83
+ private readonly OrderRepository $orders,
84
+ ) {}
85
+ }
86
+
87
+ // BAD — manual property declaration and assignment
88
+ final class OrderService
89
+ {
90
+ private PaymentGateway $payments;
91
+ private OrderRepository $orders;
92
+
93
+ public function __construct(PaymentGateway $payments, OrderRepository $orders)
94
+ {
95
+ $this->payments = $payments;
96
+ $this->orders = $orders;
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### Readonly Classes (PHP 8.2+)
102
+
103
+ ```php
104
+ final readonly class Money
105
+ {
106
+ public function __construct(
107
+ public int $amount,
108
+ public string $currency,
109
+ ) {}
110
+ }
111
+ ```
112
+
113
+ All properties are immutable after construction. Add a method that returns a new instance for "mutations":
114
+
115
+ ```php
116
+ public function add(Money $other): Money
117
+ {
118
+ return new Money($this->amount + $other->amount, $this->currency);
119
+ }
120
+ ```
121
+
122
+ ### Enums
123
+
124
+ ```php
125
+ enum InvoiceStatus: string
126
+ {
127
+ case Draft = 'draft';
128
+ case Open = 'open';
129
+ case Paid = 'paid';
130
+ case Void = 'void';
131
+ }
132
+
133
+ // Pattern match
134
+ $label = match ($invoice->status) {
135
+ InvoiceStatus::Draft => 'Draft',
136
+ InvoiceStatus::Open => 'Awaiting payment',
137
+ InvoiceStatus::Paid => 'Paid',
138
+ InvoiceStatus::Void => 'Voided',
139
+ };
140
+ ```
141
+
142
+ ### Value Objects Over Primitives
143
+
144
+ ```php
145
+ // BAD — primitive obsession; what's $email's shape? validated?
146
+ public function createUser(string $email, string $name): User
147
+
148
+ // GOOD — types enforce the contract
149
+ public function createUser(EmailAddress $email, FullName $name): User
150
+ ```
151
+
152
+ Common value objects to introduce early:
153
+ - `EmailAddress`, `PhoneNumber`, `Url`
154
+ - `Money`, `Currency`, `Percentage`
155
+ - `UserId`, `OrderId`, `InvoiceId` (typed IDs prevent passing the wrong one)
156
+ - `DateRange`, `TimeOfDay`
157
+
158
+ ## DTOs
159
+
160
+ For API requests, queue payloads, and service-to-service messages:
161
+
162
+ ```php
163
+ final readonly class CreateUserDTO
164
+ {
165
+ public function __construct(
166
+ public string $name,
167
+ public EmailAddress $email,
168
+ ) {}
169
+
170
+ public static function fromRequest(Request $request): self
171
+ {
172
+ return new self(
173
+ name: $request->validated('name'),
174
+ email: new EmailAddress($request->validated('email')),
175
+ );
176
+ }
177
+ }
178
+ ```
179
+
180
+ ## Naming
181
+
182
+ | Pattern | Convention | Example |
183
+ |---|---|---|
184
+ | Classes | `PascalCase` | `OrderService`, `EmailAddress` |
185
+ | Methods | `camelCase` | `calculateTotal`, `findActiveSubscribers` |
186
+ | Properties | `camelCase` | `$totalAmount` |
187
+ | Constants | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES` |
188
+ | Interfaces | `PascalCase`, no `I` prefix | `PaymentGateway` not `IPaymentGateway` |
189
+ | Namespaces | match folder structure (PSR-4) | `App\Billing\OrderService` in `src/Billing/OrderService.php` |
190
+
191
+ ## PRIORITY
192
+
193
+ ```
194
+ Strict types > Loose types
195
+ Value objects > Primitive obsession
196
+ DTOs > Arrays as contracts
197
+ Composition > Inheritance
198
+ Explicit > Magic
199
+ ```
200
+
201
+ ## See Also
202
+
203
+ - [`anti-patterns.md`](anti-patterns.md) — what to avoid
204
+ - [`architecture.md`](architecture.md) — where typed code lives
205
+ - [`tooling.md`](tooling.md) — PHPStan, Pint, Rector
@@ -0,0 +1,190 @@
1
+ # PHP Security
2
+
3
+ PHP-specific security non-negotiables. For cross-language concerns (secrets, auth, API security), see also [`../security/`](../security/).
4
+
5
+ ## Passwords
6
+
7
+ ```php
8
+ // Hashing — use password_hash with Argon2id (preferred) or bcrypt
9
+ $hash = password_hash($plaintext, PASSWORD_ARGON2ID);
10
+
11
+ // Verification — timing-safe
12
+ if (password_verify($plaintext, $user->password_hash)) {
13
+ // ...
14
+ }
15
+
16
+ // Rehash if algorithm or cost has been upgraded
17
+ if (password_needs_rehash($user->password_hash, PASSWORD_ARGON2ID)) {
18
+ $user->password_hash = password_hash($plaintext, PASSWORD_ARGON2ID);
19
+ $user->save();
20
+ }
21
+ ```
22
+
23
+ Never store plaintext passwords. Never use MD5, SHA-1, or unsalted hashes.
24
+
25
+ ## SQL Injection Prevention
26
+
27
+ Always use prepared statements. Never concatenate user input into SQL.
28
+
29
+ ```php
30
+ // SAFE — parameterized via PDO
31
+ $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
32
+ $stmt->execute(['email' => $email]);
33
+
34
+ // SAFE — Eloquent / Doctrine parameterize automatically
35
+ $user = User::where('email', $email)->first();
36
+
37
+ // DANGEROUS — string concatenation
38
+ $users = $pdo->query("SELECT * FROM users WHERE email = '$email'");
39
+
40
+ // SAFE — raw queries WITH bindings if you must
41
+ $users = DB::select("SELECT * FROM users WHERE email = ?", [$email]);
42
+ ```
43
+
44
+ ## XSS Prevention
45
+
46
+ Escape all output rendered to HTML. Templating engines do this by default:
47
+
48
+ ```php
49
+ // Blade — auto-escapes
50
+ {{ $user->name }}
51
+
52
+ // Twig — auto-escapes
53
+ {{ user.name }}
54
+
55
+ // Manual — escape with htmlspecialchars
56
+ <?= htmlspecialchars($user->name, ENT_QUOTES, 'UTF-8') ?>
57
+
58
+ // DANGEROUS — raw output
59
+ {!! $user->name !!} // Blade unescaped
60
+ {{ user.name|raw }} // Twig unescaped
61
+ <?= $user->name ?> // manual unescaped
62
+ ```
63
+
64
+ Use `{!!` or `|raw` only for trusted content you control (e.g., rendered markdown that you sanitized yourself).
65
+
66
+ ## CSRF Protection
67
+
68
+ Web forms must include and validate CSRF tokens.
69
+
70
+ - **Laravel** — the `web` middleware group includes `VerifyCsrfToken`; templates use `@csrf`
71
+ - **Symfony** — the form component includes CSRF protection by default
72
+ - **Slim / custom** — use a CSRF middleware (`slim/csrf` or similar)
73
+
74
+ APIs that use session cookies need CSRF protection too. APIs using bearer tokens generally don't (the cross-origin protection comes from CORS + the token header).
75
+
76
+ ## File Upload Validation
77
+
78
+ ```php
79
+ final class UploadInvoicePdfController
80
+ {
81
+ public function __invoke(Request $request): JsonResponse
82
+ {
83
+ $validated = $request->validate([
84
+ 'file' => [
85
+ 'required',
86
+ 'file',
87
+ 'mimes:pdf',
88
+ 'max:5120', // 5MB
89
+ ],
90
+ ]);
91
+
92
+ $path = $validated['file']->store('invoices');
93
+ // ...
94
+ }
95
+ }
96
+ ```
97
+
98
+ Validate:
99
+ - File size (reject huge uploads early)
100
+ - MIME type (verify the actual content, not just the extension or `Content-Type` header)
101
+ - File extension (whitelist, not blacklist)
102
+ - Image dimensions (for images, to prevent decompression attacks)
103
+
104
+ Store uploads outside the web root or behind an authenticated handler.
105
+
106
+ ## Path Traversal Prevention
107
+
108
+ ```php
109
+ // BAD — user input flows directly into a file path
110
+ $filename = $_GET['file'];
111
+ return readfile("/uploads/$filename");
112
+ // Attacker: ?file=../../../etc/passwd
113
+
114
+ // GOOD — validate against an allow-list or sanitize
115
+ $allowed = ['report.pdf', 'invoice.pdf'];
116
+ if (!in_array($filename, $allowed, true)) {
117
+ abort(404);
118
+ }
119
+ return readfile("/uploads/$filename");
120
+
121
+ // GOOD — use realpath and verify it stays inside the allowed directory
122
+ $base = realpath('/uploads');
123
+ $path = realpath("/uploads/$filename");
124
+ if ($path === false || !str_starts_with($path, $base)) {
125
+ abort(404);
126
+ }
127
+ ```
128
+
129
+ ## Rate Limiting
130
+
131
+ Apply rate limits to all public APIs and especially to auth endpoints.
132
+
133
+ - **Laravel** — `throttle` middleware: `Route::middleware('throttle:60,1')->...`
134
+ - **Symfony** — `RateLimiter` component
135
+
136
+ ```php
137
+ Route::post('/login', LoginController::class)
138
+ ->middleware('throttle:5,15'); // 5 attempts per 15 minutes
139
+ ```
140
+
141
+ ## Secrets
142
+
143
+ Never commit secrets. Always load from environment:
144
+
145
+ ```php
146
+ // Read at config layer once, not throughout the app
147
+ return [
148
+ 'stripe' => [
149
+ 'secret' => env('STRIPE_SECRET_KEY'),
150
+ ],
151
+ ];
152
+
153
+ // Access via config()
154
+ $stripeKey = config('services.stripe.secret');
155
+ ```
156
+
157
+ For environment variable validation at startup, see [`../security/secrets.md`](../security/secrets.md).
158
+
159
+ ## Session Security
160
+
161
+ - Use `HttpOnly`, `Secure`, `SameSite=Lax` (or `Strict`) cookies
162
+ - Regenerate session ID on login (`session_regenerate_id(true)` in raw PHP; framework handles it usually)
163
+ - Set a session timeout
164
+ - Invalidate the session on logout
165
+
166
+ ## DO NOT
167
+
168
+ - Build SQL strings manually
169
+ - Trust `$_GET` / `$_POST` / `$_REQUEST` / `$_FILES` without validation
170
+ - Use `eval()` or `unserialize()` on user input
171
+ - Store sensitive data in `$_SESSION` without encryption if the session driver writes to disk
172
+ - Log passwords, tokens, or credit card data
173
+ - Use `Content-Type` header alone to determine file type
174
+ - Roll your own crypto or auth — use a battle-tested library
175
+
176
+ ## PRIORITY
177
+
178
+ ```
179
+ Parameterized queries > String SQL
180
+ Argon2id > bcrypt > anything else
181
+ Validated, allow-listed inputs > Defensive sanitization
182
+ HttpOnly cookies > localStorage for auth
183
+ ```
184
+
185
+ ## See Also
186
+
187
+ - [`../security/secrets.md`](../security/secrets.md) — env vars, rotation, scanning
188
+ - [`../security/auth.md`](../security/auth.md) — auth patterns (cross-language)
189
+ - [`../security/api-security.md`](../security/api-security.md) — CORS, error responses, rate limiting
190
+ - [`rules.md`](rules.md) — strict types prevent many injection paths at compile time