ima-claude 2.20.0 → 2.26.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/README.md +74 -9
- package/dist/cli.js +2 -1
- package/package.json +1 -1
- package/plugins/ima-claude/.claude-plugin/plugin.json +2 -2
- package/plugins/ima-claude/agents/explorer.md +29 -15
- package/plugins/ima-claude/agents/implementer.md +58 -13
- package/plugins/ima-claude/agents/memory.md +19 -19
- package/plugins/ima-claude/agents/reviewer.md +84 -34
- package/plugins/ima-claude/agents/tester.md +59 -16
- package/plugins/ima-claude/agents/wp-developer.md +66 -21
- package/plugins/ima-claude/hooks/bootstrap.sh +42 -44
- package/plugins/ima-claude/hooks/prompt_coach_digest.md +14 -17
- package/plugins/ima-claude/hooks/prompt_coach_system.md +10 -12
- package/plugins/ima-claude/personalities/README.md +17 -6
- package/plugins/ima-claude/personalities/enable-efficient.md +61 -0
- package/plugins/ima-claude/personalities/enable-terse.md +71 -0
- package/plugins/ima-claude/skills/agentic-workflows/SKILL.md +35 -71
- package/plugins/ima-claude/skills/architect/SKILL.md +54 -168
- package/plugins/ima-claude/skills/compound-bridge/SKILL.md +41 -94
- package/plugins/ima-claude/skills/design-to-code/SKILL.md +43 -78
- package/plugins/ima-claude/skills/discourse/SKILL.md +79 -194
- package/plugins/ima-claude/skills/discourse-admin/SKILL.md +41 -103
- package/plugins/ima-claude/skills/docs-organize/SKILL.md +63 -203
- package/plugins/ima-claude/skills/ember-discourse/SKILL.md +90 -200
- package/plugins/ima-claude/skills/espocrm/SKILL.md +14 -23
- package/plugins/ima-claude/skills/espocrm-api/SKILL.md +79 -192
- package/plugins/ima-claude/skills/functional-programmer/SKILL.md +33 -237
- package/plugins/ima-claude/skills/gh-cli/SKILL.md +26 -65
- package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +71 -104
- package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +32 -22
- package/plugins/ima-claude/skills/ima-brand/SKILL.md +18 -23
- package/plugins/ima-claude/skills/ima-copywriting/SKILL.md +68 -179
- package/plugins/ima-claude/skills/ima-doc2pdf/SKILL.md +32 -102
- package/plugins/ima-claude/skills/ima-editorial-scorecard/SKILL.md +38 -63
- package/plugins/ima-claude/skills/ima-editorial-workflow/SKILL.md +69 -114
- package/plugins/ima-claude/skills/ima-email-creator/SKILL.md +16 -22
- package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +21 -37
- package/plugins/ima-claude/skills/ima-git/SKILL.md +81 -0
- package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +39 -120
- package/plugins/ima-claude/skills/jquery/SKILL.md +107 -233
- package/plugins/ima-claude/skills/js-fp/SKILL.md +75 -296
- package/plugins/ima-claude/skills/js-fp-api/SKILL.md +52 -162
- package/plugins/ima-claude/skills/js-fp-react/SKILL.md +47 -270
- package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +55 -209
- package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +59 -204
- package/plugins/ima-claude/skills/livecanvas/SKILL.md +19 -32
- package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +92 -162
- package/plugins/ima-claude/skills/mcp-context7/SKILL.md +32 -64
- package/plugins/ima-claude/skills/mcp-gitea/SKILL.md +98 -188
- package/plugins/ima-claude/skills/mcp-github/SKILL.md +60 -124
- package/plugins/ima-claude/skills/mcp-memory/SKILL.md +1 -177
- package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +58 -115
- package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +32 -87
- package/plugins/ima-claude/skills/mcp-serena/SKILL.md +54 -80
- package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +40 -63
- package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +75 -116
- package/plugins/ima-claude/skills/php-authnet/SKILL.md +32 -65
- package/plugins/ima-claude/skills/php-fp/SKILL.md +50 -129
- package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +25 -73
- package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +103 -463
- package/plugins/ima-claude/skills/playwright/SKILL.md +69 -220
- package/plugins/ima-claude/skills/prompt-starter/SKILL.md +33 -83
- package/plugins/ima-claude/skills/prompt-starter/references/code-review.md +38 -0
- package/plugins/ima-claude/skills/py-fp/SKILL.md +78 -384
- package/plugins/ima-claude/skills/quasar-fp/SKILL.md +54 -255
- package/plugins/ima-claude/skills/quickstart/SKILL.md +7 -11
- package/plugins/ima-claude/skills/rails/SKILL.md +63 -184
- package/plugins/ima-claude/skills/resume-session/SKILL.md +14 -35
- package/plugins/ima-claude/skills/rg/SKILL.md +61 -146
- package/plugins/ima-claude/skills/ruby-fp/SKILL.md +66 -163
- package/plugins/ima-claude/skills/save-session/SKILL.md +10 -39
- package/plugins/ima-claude/skills/scorecard/SKILL.md +42 -40
- package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +42 -71
- package/plugins/ima-claude/skills/skill-creator/SKILL.md +79 -250
- package/plugins/ima-claude/skills/task-master/SKILL.md +11 -31
- package/plugins/ima-claude/skills/task-planner/SKILL.md +44 -153
- package/plugins/ima-claude/skills/task-runner/SKILL.md +61 -143
- package/plugins/ima-claude/skills/unit-testing/SKILL.md +59 -134
- package/plugins/ima-claude/skills/wp-ddev/SKILL.md +38 -120
- package/plugins/ima-claude/skills/wp-local/SKILL.md +26 -108
|
@@ -5,84 +5,59 @@ description: "Core FP principles with anti-over-engineering focus - Simple > Com
|
|
|
5
5
|
|
|
6
6
|
# PHP Functional Programming
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Anti-Over-Engineering (PRIMARY)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
"Simple > Complex | Evidence > Assumptions"
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
- Need FP architectural guidance for PHP
|
|
14
|
-
- Preventing over-engineering and custom FP utility creation
|
|
15
|
-
- Comprehensive testing strategies with PHPUnit
|
|
16
|
-
- Evidence-based performance optimization
|
|
17
|
-
|
|
18
|
-
## CRITICAL: Anti-Over-Engineering (PRIMARY FOCUS)
|
|
19
|
-
|
|
20
|
-
**Core Principle**: "Simple > Complex | Evidence > Assumptions"
|
|
21
|
-
|
|
22
|
-
> **Clarification**: This skill prevents CREATING custom FP utility functions (pipe, compose, curry) to make PHP "feel" like Haskell. Using established libraries (Carbon, Collections, etc.) is perfectly fine. FP is a mindset—pure functions, immutability, composition—not a rigid API signature.
|
|
23
|
-
|
|
24
|
-
### Don't Create Custom FP Utilities
|
|
12
|
+
Don't CREATE custom FP utilities (pipe, compose, curry) to make PHP feel like Haskell. Using established libraries (Carbon, Collections) is fine. FP is a mindset — pure functions, immutability, composition — not an API signature.
|
|
25
13
|
|
|
26
14
|
```php
|
|
27
15
|
<?php
|
|
28
|
-
// DON'T
|
|
16
|
+
// DON'T: pipe() utility
|
|
29
17
|
function pipe(...$functions) {
|
|
30
18
|
return fn($value) => array_reduce($functions, fn($carry, $fn) => $fn($carry), $value);
|
|
31
19
|
}
|
|
32
20
|
|
|
33
|
-
//
|
|
21
|
+
// DO: native early returns
|
|
34
22
|
function validateUser(array $userData): array {
|
|
35
23
|
$requiredCheck = validateRequired(['email', 'name'], $userData);
|
|
36
24
|
if (!$requiredCheck['valid']) return $requiredCheck;
|
|
37
25
|
return validateEmail($userData);
|
|
38
26
|
}
|
|
39
27
|
|
|
40
|
-
// DON'T
|
|
41
|
-
//
|
|
28
|
+
// DON'T: curry() utility
|
|
29
|
+
// DO: native closures
|
|
42
30
|
function createValidator(array $rules): callable {
|
|
43
31
|
return fn($value): array => ['valid' => empty(array_filter($rules, fn($r) => !$r['validator']($value)))];
|
|
44
32
|
}
|
|
45
33
|
|
|
46
|
-
// DON'T
|
|
47
|
-
//
|
|
34
|
+
// DON'T: complex monad implementations
|
|
35
|
+
// DO: simple result arrays
|
|
48
36
|
function divide(float $a, float $b): array {
|
|
49
37
|
return $b === 0.0 ? ['success' => false, 'error' => 'Division by zero'] : ['success' => true, 'data' => $a / $b];
|
|
50
38
|
}
|
|
51
39
|
```
|
|
52
40
|
|
|
53
|
-
###
|
|
54
|
-
|
|
55
|
-
Before extracting or abstracting, ask:
|
|
56
|
-
|
|
57
|
-
1. **"Can this be pure?"** - Separate business logic from side effects
|
|
58
|
-
2. **"Can this use native patterns?"** - Avoid creating custom FP utilities, use PHP features
|
|
59
|
-
3. **"Can this be simplified?"** - Choose simple solution over complex abstraction
|
|
60
|
-
4. **"Is this complexity justified?"** - Evidence-based complexity decisions
|
|
41
|
+
### 4-Question Quality Framework
|
|
61
42
|
|
|
62
|
-
|
|
43
|
+
Before extracting or abstracting:
|
|
63
44
|
|
|
64
|
-
**
|
|
65
|
-
|
|
66
|
-
**
|
|
67
|
-
|
|
68
|
-
- `sanitize_data()` uses `sanitize_text_field()` which applies WordPress filters - NOT PURE
|
|
69
|
-
- `send_api_request()` uses WordPress HTTP API - NATIVE PATTERNS ALREADY
|
|
70
|
-
- Current structure: 239 lines, ~35 lines of logic - ALREADY SIMPLE
|
|
71
|
-
- Extraction would add indirection without benefits - NOT JUSTIFIED
|
|
72
|
-
|
|
73
|
-
**Result**: Grade B+ (Appropriate WordPress Wrapper). Don't extract pure functions when WordPress integration IS the business logic.
|
|
45
|
+
1. **"Can this be pure?"** — separate business logic from side effects
|
|
46
|
+
2. **"Can this use native patterns?"** — avoid custom FP utilities
|
|
47
|
+
3. **"Can this be simplified?"** — simple > complex
|
|
48
|
+
4. **"Is this complexity justified?"** — evidence required
|
|
74
49
|
|
|
75
50
|
### Context-Appropriate Complexity
|
|
76
51
|
|
|
77
52
|
```php
|
|
78
53
|
<?php
|
|
79
|
-
// CLI Script:
|
|
54
|
+
// CLI Script: direct
|
|
80
55
|
function processFile(string $filePath): array {
|
|
81
56
|
$data = file_get_contents($filePath);
|
|
82
57
|
return array_map('strtoupper', array_filter(explode("\n", $data), fn($l) => trim($l) !== ''));
|
|
83
58
|
}
|
|
84
59
|
|
|
85
|
-
// Production Service:
|
|
60
|
+
// Production Service: appropriate error handling + logging
|
|
86
61
|
function processFile(string $filePath, LoggerInterface $logger): array {
|
|
87
62
|
try {
|
|
88
63
|
if (!file_exists($filePath)) throw new InvalidArgumentException("File not found: {$filePath}");
|
|
@@ -96,20 +71,18 @@ function processFile(string $filePath, LoggerInterface $logger): array {
|
|
|
96
71
|
}
|
|
97
72
|
```
|
|
98
73
|
|
|
99
|
-
## Core
|
|
74
|
+
## Core Patterns
|
|
100
75
|
|
|
101
|
-
###
|
|
76
|
+
### Purity + Side Effect Isolation
|
|
102
77
|
|
|
103
78
|
```php
|
|
104
79
|
<?php
|
|
105
80
|
declare(strict_types=1);
|
|
106
81
|
|
|
107
|
-
// PURE: business logic
|
|
108
82
|
function calculateTotal(array $items): float {
|
|
109
83
|
return array_reduce($items, fn($sum, $item) => $sum + $item['price'], 0.0);
|
|
110
84
|
}
|
|
111
85
|
|
|
112
|
-
// ISOLATED: side effects separate
|
|
113
86
|
function logAndCalculate(array $items, LoggerInterface $logger): float {
|
|
114
87
|
$total = calculateTotal($items);
|
|
115
88
|
$logger->info("Total: {$total}");
|
|
@@ -117,18 +90,16 @@ function logAndCalculate(array $items, LoggerInterface $logger): float {
|
|
|
117
90
|
}
|
|
118
91
|
```
|
|
119
92
|
|
|
120
|
-
###
|
|
93
|
+
### Composition Over Inheritance
|
|
121
94
|
|
|
122
95
|
```php
|
|
123
96
|
<?php
|
|
124
|
-
// Simple validators that compose
|
|
125
97
|
function validateRequired($value): bool { return $value !== null && $value !== ''; }
|
|
126
98
|
function validateEmail(string $value): bool { return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; }
|
|
127
99
|
function validateLength(int $min, int $max): callable {
|
|
128
100
|
return fn(string $value): bool => strlen($value) >= $min && strlen($value) <= $max;
|
|
129
101
|
}
|
|
130
102
|
|
|
131
|
-
// Composition without utilities
|
|
132
103
|
function validateUserEmail(string $email): array {
|
|
133
104
|
if (!validateRequired($email)) return ['valid' => false, 'error' => 'Required'];
|
|
134
105
|
if (!validateEmail($email)) return ['valid' => false, 'error' => 'Invalid email'];
|
|
@@ -137,16 +108,14 @@ function validateUserEmail(string $email): array {
|
|
|
137
108
|
}
|
|
138
109
|
```
|
|
139
110
|
|
|
140
|
-
###
|
|
111
|
+
### Dependency Injection
|
|
141
112
|
|
|
142
113
|
```php
|
|
143
114
|
<?php
|
|
144
|
-
// Explicit dependencies, fully testable
|
|
145
115
|
function saveUser(array $userData, PasswordHasherInterface $hasher, DatabaseInterface $db): array {
|
|
146
116
|
return $db->save(['name' => $userData['name'], 'password' => $hasher->hash($userData['password'])]);
|
|
147
117
|
}
|
|
148
118
|
|
|
149
|
-
// Service with constructor DI
|
|
150
119
|
class UserService {
|
|
151
120
|
public function __construct(
|
|
152
121
|
private readonly PasswordHasherInterface $hasher,
|
|
@@ -159,11 +128,10 @@ class UserService {
|
|
|
159
128
|
}
|
|
160
129
|
```
|
|
161
130
|
|
|
162
|
-
###
|
|
131
|
+
### Immutability
|
|
163
132
|
|
|
164
133
|
```php
|
|
165
134
|
<?php
|
|
166
|
-
// Always return new arrays
|
|
167
135
|
function updateUserSettings(array $user, array $settings): array {
|
|
168
136
|
return [...$user, 'settings' => array_merge($user['settings'] ?? [], $settings), 'updated_at' => time()];
|
|
169
137
|
}
|
|
@@ -175,15 +143,12 @@ function updateItem(array $items, int $id, array $updates): array {
|
|
|
175
143
|
}
|
|
176
144
|
```
|
|
177
145
|
|
|
178
|
-
## PHP-Specific
|
|
179
|
-
|
|
180
|
-
### Native Array Functions + Strict Types (MANDATORY)
|
|
146
|
+
## PHP-Specific: Native Array Functions + Strict Types (MANDATORY)
|
|
181
147
|
|
|
182
148
|
```php
|
|
183
149
|
<?php
|
|
184
150
|
declare(strict_types=1);
|
|
185
151
|
|
|
186
|
-
// Native array methods over loops
|
|
187
152
|
function processUsers(array $users): array {
|
|
188
153
|
return array_slice(
|
|
189
154
|
array_map(fn($u) => [...$u, 'display_name' => "{$u['first_name']} {$u['last_name']}"],
|
|
@@ -192,7 +157,7 @@ function processUsers(array $users): array {
|
|
|
192
157
|
);
|
|
193
158
|
}
|
|
194
159
|
|
|
195
|
-
//
|
|
160
|
+
// match over switch (PHP 8.0+)
|
|
196
161
|
function getTierDiscount(string $tier): float {
|
|
197
162
|
return match($tier) {
|
|
198
163
|
'bronze' => 0.05, 'silver' => 0.10, 'gold' => 0.15, 'platinum' => 0.20, default => 0.0
|
|
@@ -209,12 +174,11 @@ function processResult(array|false $result): array {
|
|
|
209
174
|
|
|
210
175
|
```php
|
|
211
176
|
<?php
|
|
212
|
-
// Standard result shape
|
|
213
177
|
function createResult($data = null, ?string $error = null): array {
|
|
214
178
|
return $error !== null ? ['success' => false, 'error' => $error] : ['success' => true, 'data' => $data];
|
|
215
179
|
}
|
|
216
180
|
|
|
217
|
-
// Chain
|
|
181
|
+
// Chain with early return
|
|
218
182
|
function calculate(float $a, float $b, float $c): array {
|
|
219
183
|
$r1 = divide($a, $b);
|
|
220
184
|
if (!$r1['success']) return $r1;
|
|
@@ -222,20 +186,16 @@ function calculate(float $a, float $b, float $c): array {
|
|
|
222
186
|
}
|
|
223
187
|
```
|
|
224
188
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
## Testing Essentials
|
|
189
|
+
## Testing
|
|
228
190
|
|
|
229
|
-
Pure functions enable
|
|
191
|
+
Pure functions enable systematic edge-case coverage.
|
|
230
192
|
|
|
231
193
|
```php
|
|
232
194
|
<?php
|
|
233
195
|
use PHPUnit\Framework\TestCase;
|
|
234
196
|
|
|
235
197
|
class CalculatorTest extends TestCase {
|
|
236
|
-
/**
|
|
237
|
-
* @dataProvider discountProvider
|
|
238
|
-
*/
|
|
198
|
+
/** @dataProvider discountProvider */
|
|
239
199
|
public function testCalculateDiscount(float $price, float $rate, float $expected): void {
|
|
240
200
|
$this->assertEquals($expected, calculateDiscount($price, $rate));
|
|
241
201
|
}
|
|
@@ -243,26 +203,22 @@ class CalculatorTest extends TestCase {
|
|
|
243
203
|
public function discountProvider(): array {
|
|
244
204
|
return [
|
|
245
205
|
'standard' => [100.0, 0.1, 90.0],
|
|
246
|
-
'zero'
|
|
247
|
-
'full'
|
|
206
|
+
'zero' => [100.0, 0.0, 100.0],
|
|
207
|
+
'full' => [100.0, 1.0, 0.0],
|
|
248
208
|
];
|
|
249
209
|
}
|
|
250
210
|
}
|
|
251
211
|
```
|
|
252
212
|
|
|
253
|
-
|
|
213
|
+
## Performance: Configuration Pre-Compilation (Evidence-Based)
|
|
254
214
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
Optimize only when needed with evidence. Key pattern: **Configuration Pre-Compilation**.
|
|
215
|
+
Optimize only with evidence. Key pattern: pre-compile config once, execute linearly.
|
|
258
216
|
|
|
259
217
|
```php
|
|
260
218
|
<?php
|
|
261
|
-
// Problem: O(records
|
|
262
|
-
// Solution:
|
|
263
|
-
|
|
219
|
+
// Problem: O(records × config) — config accessed every iteration
|
|
220
|
+
// Solution: pre-compile once
|
|
264
221
|
function createRecordProcessor(array $schema): callable {
|
|
265
|
-
// Setup once
|
|
266
222
|
$fieldProcessors = array_map(fn($f) => fn($v) => transformField($v, $f['type']), $schema['fields']);
|
|
267
223
|
|
|
268
224
|
return function(array $record) use ($schema, $fieldProcessors): array {
|
|
@@ -274,60 +230,25 @@ function createRecordProcessor(array $schema): callable {
|
|
|
274
230
|
};
|
|
275
231
|
}
|
|
276
232
|
|
|
277
|
-
$processor = createRecordProcessor($schema); //
|
|
278
|
-
$results = array_map($processor, $records); //
|
|
233
|
+
$processor = createRecordProcessor($schema); // setup once
|
|
234
|
+
$results = array_map($processor, $records); // linear execution
|
|
279
235
|
```
|
|
280
236
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
## Quality Gates Checklist
|
|
284
|
-
|
|
285
|
-
Before implementation:
|
|
286
|
-
|
|
287
|
-
1. **"Can this be pure?"** - Separate business logic from side effects
|
|
288
|
-
2. **"Can this use native patterns?"** - Avoid creating custom FP utilities
|
|
289
|
-
3. **"Can this be simplified?"** - Simple > complex
|
|
290
|
-
4. **"Is this complexity justified?"** - Evidence required
|
|
291
|
-
5. **"Is this testable?"** - Pure functions enable comprehensive testing
|
|
292
|
-
6. **"Are strict types used?"** - `declare(strict_types=1)` at file top
|
|
293
|
-
|
|
294
|
-
## When to Load Reference Files
|
|
295
|
-
|
|
296
|
-
### Deep Principles and Explanations
|
|
297
|
-
**File**: `references/core-principles.md`
|
|
298
|
-
**Load when**:
|
|
299
|
-
- Learning mode or explaining WHY behind patterns
|
|
300
|
-
- Making architectural decisions
|
|
301
|
-
- Need complete Result Type patterns
|
|
302
|
-
- Anti-pattern recognition details
|
|
303
|
-
- Cross-pattern comparisons
|
|
304
|
-
|
|
305
|
-
### Testing Methodology
|
|
306
|
-
**File**: `references/testing-patterns.md`
|
|
307
|
-
**Load when**:
|
|
308
|
-
- Building comprehensive test suites
|
|
309
|
-
- Improving test coverage
|
|
310
|
-
- Edge case analysis and boundary testing
|
|
311
|
-
- Setting up mocking strategies
|
|
312
|
-
- Performance testing pure functions
|
|
313
|
-
|
|
314
|
-
### Working Examples
|
|
315
|
-
**Directory**: `examples/`
|
|
316
|
-
**Load when**:
|
|
317
|
-
- Need complete working code
|
|
318
|
-
- Integration examples
|
|
319
|
-
- Learning implementation patterns
|
|
320
|
-
|
|
321
|
-
## Integration with Domain Skills
|
|
322
|
-
|
|
323
|
-
This core skill provides the foundation for:
|
|
237
|
+
Real result: ima-espo email validation — 2-5x speedup, 130 tests, 236 assertions, <30ms total.
|
|
324
238
|
|
|
325
|
-
|
|
326
|
-
- **php-fp-laravel**: Laravel patterns with FP principles (future)
|
|
327
|
-
- **php-fp-symfony**: Symfony patterns with FP principles (future)
|
|
239
|
+
## Quality Gates
|
|
328
240
|
|
|
329
|
-
|
|
241
|
+
1. Pure? — business logic separated from side effects
|
|
242
|
+
2. Native? — no custom FP utilities
|
|
243
|
+
3. Simple? — simple > complex
|
|
244
|
+
4. Justified? — evidence for complexity
|
|
245
|
+
5. Testable? — pure functions have tests
|
|
246
|
+
6. Strict types? — `declare(strict_types=1)` at file top
|
|
330
247
|
|
|
331
|
-
##
|
|
248
|
+
## Reference Files
|
|
332
249
|
|
|
333
|
-
|
|
250
|
+
| File | Load When |
|
|
251
|
+
|------|-----------|
|
|
252
|
+
| `references/core-principles.md` | Architectural decisions, full Result Type patterns, anti-pattern recognition |
|
|
253
|
+
| `references/testing-patterns.md` | Comprehensive test suites, edge cases, mocking, performance testing |
|
|
254
|
+
| `examples/` | Complete working code, integration examples |
|
|
@@ -5,28 +5,13 @@ description: "Security-first WordPress development with PHP FP principles - pure
|
|
|
5
5
|
|
|
6
6
|
# PHP FP - WordPress
|
|
7
7
|
|
|
8
|
-
Security-first WordPress development
|
|
8
|
+
Security-first WordPress development: pure functions for business logic, WordPress wrappers with mandatory security.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- Building WordPress plugins or themes
|
|
13
|
-
- Need security-first development practices
|
|
14
|
-
- Implementing pure business logic with WordPress integration
|
|
15
|
-
- Testing WordPress functionality
|
|
16
|
-
|
|
17
|
-
## Core Philosophy
|
|
18
|
-
|
|
19
|
-
**Security practices prevent vulnerabilities, not architectural patterns.**
|
|
20
|
-
|
|
21
|
-
Hybrid approach:
|
|
22
|
-
1. **Pure functions** for business logic (testable, no WordPress deps)
|
|
23
|
-
2. **WordPress wrappers** with mandatory security (capability, nonce, sanitize, escape, prepare)
|
|
24
|
-
|
|
25
|
-
**Foundation**: Reference `../php-fp/SKILL.md` for PHP FP core principles.
|
|
10
|
+
**Foundation**: `../php-fp/SKILL.md` for PHP FP core.
|
|
26
11
|
|
|
27
12
|
## The 5 Non-Negotiable Security Practices
|
|
28
13
|
|
|
29
|
-
|
|
14
|
+
Evidence: 7,966 vulnerabilities (2024) — these prevent 95%+ of WordPress plugin vulnerabilities.
|
|
30
15
|
|
|
31
16
|
| Practice | Prevents | Rule |
|
|
32
17
|
|----------|----------|------|
|
|
@@ -36,18 +21,20 @@ Hybrid approach:
|
|
|
36
21
|
| **Output Escaping** | XSS | Escape ALL output by context |
|
|
37
22
|
| **Prepared Statements** | SQL injection | `$wpdb->prepare()` for ALL queries |
|
|
38
23
|
|
|
39
|
-
###
|
|
24
|
+
### Security Function Reference
|
|
40
25
|
|
|
41
26
|
**Sanitization (Input)**:
|
|
27
|
+
|
|
42
28
|
| Function | Use For |
|
|
43
29
|
|----------|---------|
|
|
44
30
|
| `sanitize_text_field()` | Plain text |
|
|
45
31
|
| `sanitize_email()` | Email addresses |
|
|
46
32
|
| `absint()` | Positive integers |
|
|
47
33
|
| `wp_kses_post()` | HTML content |
|
|
48
|
-
| `esc_url_raw()` | URLs (
|
|
34
|
+
| `esc_url_raw()` | URLs (storage) |
|
|
49
35
|
|
|
50
36
|
**Escaping (Output)**:
|
|
37
|
+
|
|
51
38
|
| Function | Context |
|
|
52
39
|
|----------|---------|
|
|
53
40
|
| `esc_html()` | HTML body |
|
|
@@ -60,37 +47,30 @@ Hybrid approach:
|
|
|
60
47
|
```php
|
|
61
48
|
<?php
|
|
62
49
|
add_action('wp_ajax_my_action', function() {
|
|
63
|
-
// 1. Capability check
|
|
64
50
|
if (!current_user_can('edit_posts')) {
|
|
65
51
|
wp_send_json_error('Unauthorized', 403);
|
|
66
52
|
}
|
|
67
53
|
|
|
68
|
-
// 2. Nonce verification
|
|
69
54
|
check_ajax_referer('my_action_nonce', 'nonce');
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
$id = absint($_POST['id']);
|
|
56
|
+
$id = absint($_POST['id']);
|
|
73
57
|
$name = sanitize_text_field($_POST['name']);
|
|
74
58
|
|
|
75
|
-
// 4. Use prepared statement
|
|
76
59
|
global $wpdb;
|
|
77
60
|
$result = $wpdb->get_row($wpdb->prepare(
|
|
78
61
|
"SELECT * FROM {$wpdb->prefix}my_table WHERE id = %d",
|
|
79
62
|
$id
|
|
80
63
|
));
|
|
81
64
|
|
|
82
|
-
// 5. Escape output
|
|
83
65
|
wp_send_json_success(['name' => esc_html($result->name)]);
|
|
84
66
|
});
|
|
85
67
|
```
|
|
86
68
|
|
|
87
69
|
## Pure Logic + WordPress Wrapper Pattern
|
|
88
70
|
|
|
89
|
-
**Separate testable business logic from WordPress integration.**
|
|
90
|
-
|
|
91
71
|
```php
|
|
92
72
|
<?php
|
|
93
|
-
// PURE:
|
|
73
|
+
// PURE: zero WordPress dependencies, fully testable
|
|
94
74
|
namespace MyPlugin\Pure;
|
|
95
75
|
|
|
96
76
|
function calculate_discount(float $price, string $tier): float {
|
|
@@ -103,7 +83,6 @@ namespace MyPlugin;
|
|
|
103
83
|
|
|
104
84
|
add_filter('product_price', function($price) {
|
|
105
85
|
if (!is_user_logged_in()) return $price;
|
|
106
|
-
|
|
107
86
|
$tier = get_user_meta(get_current_user_id(), 'tier', true);
|
|
108
87
|
return Pure\calculate_discount($price, $tier);
|
|
109
88
|
});
|
|
@@ -111,7 +90,7 @@ add_filter('product_price', function($price) {
|
|
|
111
90
|
|
|
112
91
|
## Inter-Plugin Communication: Hooks Only
|
|
113
92
|
|
|
114
|
-
|
|
93
|
+
ALL cross-plugin calls use WordPress hooks. NEVER `function_exists()`.
|
|
115
94
|
|
|
116
95
|
Hooks are safe no-ops — if nobody listens, nothing happens. `function_exists()` is tight coupling disguised as loose coupling.
|
|
117
96
|
|
|
@@ -125,21 +104,20 @@ if (function_exists('ima_discourse_refresh_user_meta')) {
|
|
|
125
104
|
// GOOD — fire-and-forget side effect
|
|
126
105
|
do_action('ima_discourse_refresh_user_meta', $user_id);
|
|
127
106
|
|
|
128
|
-
// GOOD — transform with safe default
|
|
107
|
+
// GOOD — transform with safe default
|
|
129
108
|
$result = apply_filters('ima_membership_cancel_subscription', ['success' => true], $user_id, $sub_id);
|
|
130
109
|
```
|
|
131
110
|
|
|
132
|
-
**Actions** (`do_action`):
|
|
133
|
-
**Filters** (`apply_filters`):
|
|
111
|
+
- **Actions** (`do_action`): side effects — "something happened, react if you care"
|
|
112
|
+
- **Filters** (`apply_filters`): data transformation — chained composition with default return
|
|
134
113
|
|
|
135
|
-
The handler registers itself:
|
|
136
114
|
```php
|
|
137
115
|
<?php
|
|
138
|
-
//
|
|
116
|
+
// Handler registers itself — callers never crash if plugin is absent
|
|
139
117
|
add_action('ima_discourse_refresh_user_meta', 'ima_discourse_refresh_user_meta', 10, 1);
|
|
140
118
|
```
|
|
141
119
|
|
|
142
|
-
|
|
120
|
+
`function_exists()` is acceptable only for internal guards within a single plugin, or checking PHP extensions (`function_exists('sodium_crypto_secretbox')`).
|
|
143
121
|
|
|
144
122
|
## Plugin Complexity Guide
|
|
145
123
|
|
|
@@ -149,7 +127,7 @@ add_action('ima_discourse_refresh_user_meta', 'ima_discourse_refresh_user_meta',
|
|
|
149
127
|
| Medium | 500-2000 | Classes + pure functions |
|
|
150
128
|
| Complex | 2000+ | DI Container + Services |
|
|
151
129
|
|
|
152
|
-
|
|
130
|
+
Start simple, add complexity only when needed.
|
|
153
131
|
|
|
154
132
|
## File Organization
|
|
155
133
|
|
|
@@ -165,7 +143,7 @@ my-plugin/
|
|
|
165
143
|
└── integration/ # WordPress tests
|
|
166
144
|
```
|
|
167
145
|
|
|
168
|
-
##
|
|
146
|
+
## Quality Gates
|
|
169
147
|
|
|
170
148
|
- [ ] Capability checks on all privileged operations
|
|
171
149
|
- [ ] Nonces on all form/AJAX submissions
|
|
@@ -176,41 +154,15 @@ my-plugin/
|
|
|
176
154
|
- [ ] No hardcoded credentials
|
|
177
155
|
- [ ] Cross-plugin calls use hooks, not `function_exists()`
|
|
178
156
|
|
|
179
|
-
##
|
|
180
|
-
|
|
181
|
-
1. **Security**: All 5 mandatory practices implemented?
|
|
182
|
-
2. **Pure logic**: Business logic separated from WordPress?
|
|
183
|
-
3. **Testability**: Pure functions have unit tests?
|
|
184
|
-
4. **Complexity**: Architecture matches plugin size?
|
|
185
|
-
|
|
186
|
-
## When to Load Reference Files
|
|
187
|
-
|
|
188
|
-
### Security Deep-Dive
|
|
189
|
-
**File**: [`references/security-examples.md`](references/security-examples.md)
|
|
190
|
-
**Load when**: Need detailed security patterns, vulnerable vs. safe comparisons
|
|
191
|
-
**Contains**: Full examples for all 5 practices, security function reference tables
|
|
192
|
-
|
|
193
|
-
### FP Patterns
|
|
194
|
-
**File**: [`references/fp-patterns.md`](references/fp-patterns.md)
|
|
195
|
-
**Load when**: Implementing pure logic + wrapper pattern, function factories
|
|
196
|
-
**Contains**: Complete membership system example, production email validator example
|
|
197
|
-
|
|
198
|
-
### Plugin Architecture
|
|
199
|
-
**File**: [`references/plugin-architecture.md`](references/plugin-architecture.md)
|
|
200
|
-
**Load when**: Deciding plugin structure, implementing DI container
|
|
201
|
-
**Contains**: Simple/medium/complex plugin patterns, file organization examples
|
|
202
|
-
|
|
203
|
-
### Testing Strategy
|
|
204
|
-
**File**: [`references/testing-strategy.md`](references/testing-strategy.md)
|
|
205
|
-
**Load when**: Setting up tests, writing security tests
|
|
206
|
-
**Contains**: Unit/integration test examples, minimal mock bootstrap, security test patterns
|
|
207
|
-
|
|
208
|
-
## Success Metrics
|
|
157
|
+
## Reference Files
|
|
209
158
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
-
|
|
159
|
+
| File | Load When |
|
|
160
|
+
|------|-----------|
|
|
161
|
+
| `references/security-examples.md` | Detailed security patterns, vulnerable vs. safe comparisons |
|
|
162
|
+
| `references/fp-patterns.md` | Pure logic + wrapper pattern, function factories, production examples |
|
|
163
|
+
| `references/plugin-architecture.md` | Plugin structure decisions, DI container implementation |
|
|
164
|
+
| `references/testing-strategy.md` | Unit/integration tests, security tests, minimal mock bootstrap |
|
|
213
165
|
|
|
214
166
|
---
|
|
215
167
|
|
|
216
|
-
|
|
168
|
+
Evidence: 7,966 WordPress vulnerabilities (2024), WordPress Core Team standards, Wordfence/Patchstack research.
|